#include "decompress.hh" #include "buffer.hh" #define ZLIB_CONST #include #include #include #include #include #include #include #include namespace decompress { namespace { const size_t kBufferSizeZ = static_cast(1024) * 1024; class DecompressReader : public io::Reader { public: DecompressReader(std::unique_ptr reader, bool gzip) : reader_(std::move(reader)), gzip_(gzip) {} ~DecompressReader() override { if (initialized_) inflateEnd(&stream_); } std::expected read(void* dst, size_t max) override { auto err = fill(); if (err.has_value()) return std::unexpected(err.value()); // NOLINTNEXTLINE(misc-include-cleaner) stream_.next_out = reinterpret_cast(dst); stream_.avail_out = max; if (!initialized_) { if (in_eof_ && buffer_->empty()) return std::unexpected(io::ReadError::Eof); stream_.zalloc = Z_NULL; stream_.zfree = Z_NULL; stream_.opaque = Z_NULL; if (inflateInit2(&stream_, gzip_ ? 16 : 0) != Z_OK) { return std::unexpected(io::ReadError::Error); } initialized_ = true; } auto* const rptr = stream_.next_in; auto ret = inflate(&stream_, in_eof_ ? Z_FINISH : Z_NO_FLUSH); auto got = max - stream_.avail_out; if (ret == Z_STREAM_END) { inflateEnd(&stream_); initialized_ = false; buffer_->consume(stream_.next_in - rptr); } else if (ret == Z_OK) { if (!in_eof_) buffer_->consume(stream_.next_in - rptr); } else { switch (ret) { case Z_DATA_ERROR: return std::unexpected(io::ReadError::InvalidData); case Z_BUF_ERROR: // Tricky, could also be because of too little input. // TODO: Improve logic here if we can return std::unexpected(io::ReadError::MaxTooSmall); default: return std::unexpected(io::ReadError::Error); } } return got; } std::expected skip(size_t max) override { auto tmp = std::make_unique_for_overwrite(max); return read(tmp.get(), max); } private: std::optional fill() { size_t avail; auto* rptr = buffer_->rptr(avail); if (!in_eof_ && avail < kBufferSizeZ / 2) { auto* wptr = buffer_->wptr(avail); auto got = reader_->read(wptr, avail); if (got.has_value()) { buffer_->commit(got.value()); } else if (got.error() == io::ReadError::Eof) { in_eof_ = true; } else { return got.error(); } rptr = buffer_->rptr(avail); } // NOLINTNEXTLINE(misc-include-cleaner) stream_.next_in = reinterpret_cast(rptr); stream_.avail_in = std::min( // NOLINTNEXTLINE(misc-include-cleaner) static_cast(std::numeric_limits::max()), avail); return std::nullopt; } std::unique_ptr reader_; bool const gzip_; bool in_eof_{false}; std::unique_ptr buffer_{Buffer::fixed(kBufferSizeZ)}; bool initialized_{false}; z_stream stream_; }; } // namespace std::unique_ptr zlib(std::unique_ptr reader) { return std::make_unique(std::move(reader), /* gzip = */ false); } std::unique_ptr gzip(std::unique_ptr reader) { return std::make_unique(std::move(reader), /* gzip = */ true); } } // namespace decompress