4#include "Stroika/Foundation/StroikaPreComp.h"
12#if qStroika_HasComponent_zlib
22using namespace Stroika::Foundation::Streams;
28 void ThrowIfZLibErr_ (
int err)
31 if (err != Z_OK) [[unlikely]] {
33 case Z_VERSION_ERROR: {
35 Execution::Throw (kException_);
39 Execution::Throw (kException_);
41 case Z_STREAM_ERROR: {
43 Execution::Throw (kException_);
55 static constexpr size_t CHUNK_ = 16384;
62 optional<byte> _fNextOutputByte_;
68 virtual ~BaseRep_ () =
default;
69 virtual bool IsSeekable ()
const override
73 virtual void CloseRead ()
override
75 if (fInStream_ !=
nullptr) {
78 Assert (fInStream_ ==
nullptr);
79 Ensure (not IsOpenRead ());
81 virtual bool IsOpenRead ()
const override
83 return fInStream_ !=
nullptr;
87 Require (IsOpenRead ());
91 template <invocable<
bool> PROCESS>
92 optional<size_t> PullEnufForDeflate1Byte_ (
NoDataAvailableHandling blockFlag, span<byte> intoBuffer, PROCESS processInputZLibFunction)
94 Assert (_fNextOutputByte_ == nullopt);
96 if (blockFlag == NoDataAvailableHandling::eDontBlock and fZStream_.avail_in == 0 and fInStream_.
AvailableToRead () == nullopt) {
101 if (fZStream_.avail_in == 0) {
102 Assert (Memory::NEltsOf (fInBuf_) < numeric_limits<uInt>::max ());
103 fZStream_.avail_in =
static_cast<uInt
> (fInStream_.
ReadBlocking (span{fInBuf_}).size ());
104 fZStream_.next_in =
reinterpret_cast<Bytef*
> (begin (fInBuf_));
106 bool isAtSrcEOF = fZStream_.avail_in == 0;
108 ptrdiff_t outBufSize = intoBuffer.size ();
112 fZStream_.avail_out =
static_cast<uInt
> (outBufSize);
113 fZStream_.next_out =
reinterpret_cast<Bytef*
> (intoBuffer.data ());
115 switch (ret = processInputZLibFunction (isAtSrcEOF)) {
121 ThrowIfZLibErr_ (ret);
123 ptrdiff_t pulledOut = outBufSize - fZStream_.avail_out;
124 Assert (pulledOut <= outBufSize);
125 if (pulledOut == 0 and not isAtSrcEOF) {
130 template <invocable<
bool> PROCESS>
131 optional<size_t> _Available2Read (PROCESS processInputZLibFunction)
133 Require (IsOpenRead ());
134 if (_fNextOutputByte_) {
138 auto pulledButMustCache = PullEnufForDeflate1Byte_ (NoDataAvailableHandling::eDontBlock, span{&tmp, 1}, processInputZLibFunction);
139 if (pulledButMustCache) {
140 if (*pulledButMustCache == 0) {
144 Assert (*pulledButMustCache == 1);
145 _fNextOutputByte_ = tmp;
153 template <invocable<
bool> PROCESS>
154 optional<span<byte>> _Read (span<byte> intoBuffer,
NoDataAvailableHandling blockFlag, PROCESS processInputZLibFunction)
156 Require (not intoBuffer.empty ());
157 Require (IsOpenRead ());
158 if (_fNextOutputByte_) {
159 intoBuffer[0] = *_fNextOutputByte_;
160 _fNextOutputByte_.reset ();
161 return intoBuffer.subspan (0, 1);
163 if (
auto o = PullEnufForDeflate1Byte_ (blockFlag, intoBuffer, processInputZLibFunction)) {
164 size_t pulledOut = *o;
165 _fSeekOffset += pulledOut;
166 return intoBuffer.subspan (0, pulledOut);
173 struct DeflateRep_ : BaseRep_ {
177 int level = Z_DEFAULT_COMPRESSION;
178 ThrowIfZLibErr_ (::deflateInit (&fZStream_, level));
180 virtual ~DeflateRep_ ()
182 Verify (::deflateEnd (&fZStream_) == Z_OK);
184 virtual optional<size_t> AvailableToRead ()
override
186 return _Available2Read ([
this] (
bool isEOF) {
return DoProcess_ (isEOF); });
188 virtual optional<SeekOffsetType> RemainingLength ()
override
194 return _Read (intoBuffer, blockFlag, [
this] (
bool isEOF) {
return DoProcess_ (isEOF); });
196 int DoProcess_ (
bool isEOF)
198 return ::deflate (&fZStream_, isEOF ? Z_FINISH : Z_NO_FLUSH);
201 struct InflateRep_ : BaseRep_ {
206 constexpr int windowBits = 15;
207 constexpr int ENABLE_ZLIB_GZIP = 32;
208 ThrowIfZLibErr_ (::inflateInit2 (&fZStream_, windowBits | ENABLE_ZLIB_GZIP));
210 virtual ~InflateRep_ ()
212 Verify (::inflateEnd (&fZStream_) == Z_OK);
214 virtual optional<size_t> AvailableToRead ()
override
216 return _Available2Read ([
this] (
bool isEOF) {
return DoProcess_ (isEOF); });
218 virtual optional<SeekOffsetType> RemainingLength ()
override
224 return _Read (intoBuffer, blockFlag, [
this] (
bool isEOF) {
return DoProcess_ (isEOF); });
226 int DoProcess_ ([[maybe_unused]]
bool isEOF)
228 return ::inflate (&fZStream_, Z_NO_FLUSH);
234#if qStroika_HasComponent_zlib
236 class Rep_ :
public Reader::IRep {
249Zip::Reader::Reader ()
250 : DataExchange::Compression::
Reader{make_shared<Rep_> ()}
NoDataAvailableHandling
If eDontBlock passed to most Stream APIs, then when the code would do a blocking read,...