From 9389d63a5f2e64759c91a5dd114aebb14032de4d Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 22 Feb 2025 07:03:18 +0300 Subject: [PATCH] add deflate_istream --- src/io/deflate_istream.hpp | 101 +++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 src/io/deflate_istream.hpp diff --git a/src/io/deflate_istream.hpp b/src/io/deflate_istream.hpp new file mode 100644 index 00000000..ecbc738a --- /dev/null +++ b/src/io/deflate_istream.hpp @@ -0,0 +1,101 @@ +#pragma once + +#define ZLIB_CONST +#include +#include +#include +#include +#include + +class deflate_istream : public std::istream { +public: + explicit deflate_istream(std::unique_ptr src) + : std::istream(&buf), source(std::move(src)), buf(*source) {} + +private: + class deflate_streambuf : public std::streambuf { + public: + explicit deflate_streambuf(std::istream& src) : src(src) { + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.avail_in = 0; + zstream.next_in = Z_NULL; + + int ret = inflateInit2(&zstream, -15); + if (ret != Z_OK) { + throw std::runtime_error("zlib init failed"); + } + } + + ~deflate_streambuf() { + inflateEnd(&zstream); + } + + deflate_streambuf(const deflate_streambuf&) = delete; + deflate_streambuf& operator=(const deflate_streambuf&) = delete; + + protected: + int_type underflow() override { + if (gptr() < egptr()) { + return traits_type::to_int_type(*gptr()); + } + + if (eof) { + return traits_type::eof(); + } + + zstream.next_out = reinterpret_cast(outBuf.data()); + zstream.avail_out = outBuf.size(); + + do { + if (zstream.avail_in == 0) { + src.read(inBuf.data(), inBuf.size()); + zstream.avail_in = static_cast(src.gcount()); + zstream.next_in = reinterpret_cast(inBuf.data()); + + if (src.bad()) { + return traits_type::eof(); + } + } + + int ret = inflate(&zstream, Z_NO_FLUSH); + if (ret == Z_STREAM_END) { + eof = true; + } else if (ret != Z_OK) { + if (ret == Z_BUF_ERROR && zstream.avail_out == outBuf.size()) { + continue; + } + return traits_type::eof(); + } + + const auto decompressed = outBuf.size() - zstream.avail_out; + if (decompressed > 0) { + setg(outBuf.data(), + outBuf.data(), + outBuf.data() + decompressed); + return traits_type::to_int_type(*gptr()); + } + + if (eof) { + return traits_type::eof(); + } + + } while (zstream.avail_in > 0 || !src.eof()); + + return traits_type::eof(); + } + + private: + static constexpr size_t BUFFER_SIZE = 16384; + + std::istream& src; + z_stream zstream {}; + std::array inBuf {}; + std::array outBuf {}; + bool eof = false; + }; + + std::unique_ptr source; + deflate_streambuf buf; +};