4#include "Stroika/Foundation/StroikaPreComp.h"
13#if qStroika_HasComponent_zlib
23using namespace Stroika::Foundation::Streams;
29 void ThrowIfZLibErr_ (
int err)
32 if (err != Z_OK) [[unlikely]] {
34 case Z_VERSION_ERROR: {
36 Execution::Throw (kException_);
40 Execution::Throw (kException_);
42 case Z_STREAM_ERROR: {
44 Execution::Throw (kException_);
56 static constexpr size_t CHUNK_ = 16384;
63 optional<byte> _fNextOutputByte_;
69 virtual ~BaseRep_ () =
default;
70 virtual bool IsSeekable ()
const override
74 virtual void CloseRead ()
override
76 if (fInStream_ !=
nullptr) {
79 Assert (fInStream_ ==
nullptr);
80 Ensure (not IsOpenRead ());
82 virtual bool IsOpenRead ()
const override
84 return fInStream_ !=
nullptr;
88 Require (IsOpenRead ());
92 template <invocable<
bool> PROCESS>
93 optional<size_t> PullEnufForDeflate1Byte_ (
NoDataAvailableHandling blockFlag, span<byte> intoBuffer, PROCESS processInputZLibFunction)
95 Assert (_fNextOutputByte_ == nullopt);
97 if (blockFlag == NoDataAvailableHandling::eDontBlock and fZStream_.avail_in == 0 and fInStream_.
AvailableToRead () == nullopt) {
102 if (fZStream_.avail_in == 0) {
103 Assert (std::size (fInBuf_) < numeric_limits<uInt>::max ());
104 fZStream_.avail_in =
static_cast<uInt
> (fInStream_.
ReadBlocking (span{fInBuf_}).size ());
105 fZStream_.next_in =
reinterpret_cast<Bytef*
> (begin (fInBuf_));
107 bool isAtSrcEOF = fZStream_.avail_in == 0;
109 ptrdiff_t outBufSize = intoBuffer.size ();
113 fZStream_.avail_out =
static_cast<uInt
> (outBufSize);
114 fZStream_.next_out =
reinterpret_cast<Bytef*
> (intoBuffer.data ());
116 switch (ret = processInputZLibFunction (isAtSrcEOF)) {
122 ThrowIfZLibErr_ (ret);
124 ptrdiff_t pulledOut = outBufSize - fZStream_.avail_out;
125 Assert (pulledOut <= outBufSize);
126 if (pulledOut == 0 and not isAtSrcEOF) {
131 template <invocable<
bool> PROCESS>
132 optional<size_t> _Available2Read (PROCESS processInputZLibFunction)
134 Require (IsOpenRead ());
135 if (_fNextOutputByte_) {
139 auto pulledButMustCache = PullEnufForDeflate1Byte_ (NoDataAvailableHandling::eDontBlock, span{&tmp, 1}, processInputZLibFunction);
140 if (pulledButMustCache) {
141 if (*pulledButMustCache == 0) {
145 Assert (*pulledButMustCache == 1);
146 _fNextOutputByte_ = tmp;
154 template <invocable<
bool> PROCESS>
155 optional<span<byte>> _Read (span<byte> intoBuffer,
NoDataAvailableHandling blockFlag, PROCESS processInputZLibFunction)
157 Require (not intoBuffer.empty ());
158 Require (IsOpenRead ());
159 if (_fNextOutputByte_) {
160 intoBuffer[0] = *_fNextOutputByte_;
161 _fNextOutputByte_.reset ();
162 return intoBuffer.subspan (0, 1);
164 if (
auto o = PullEnufForDeflate1Byte_ (blockFlag, intoBuffer, processInputZLibFunction)) {
165 size_t pulledOut = *o;
166 _fSeekOffset += pulledOut;
167 return intoBuffer.subspan (0, pulledOut);
174 struct DeflateRep_ : BaseRep_ {
178 int level = Z_DEFAULT_COMPRESSION;
179 ThrowIfZLibErr_ (::deflateInit (&fZStream_, level));
181 virtual ~DeflateRep_ ()
183 Verify (::deflateEnd (&fZStream_) == Z_OK);
185 virtual optional<size_t> AvailableToRead ()
override
187 return _Available2Read ([
this] (
bool isEOF) {
return DoProcess_ (isEOF); });
189 virtual optional<SeekOffsetType> RemainingLength ()
override
195 return _Read (intoBuffer, blockFlag, [
this] (
bool isEOF) {
return DoProcess_ (isEOF); });
197 int DoProcess_ (
bool isEOF)
199 return ::deflate (&fZStream_, isEOF ? Z_FINISH : Z_NO_FLUSH);
202 struct InflateRep_ : BaseRep_ {
207 constexpr int windowBits = 15;
208 constexpr int ENABLE_ZLIB_GZIP = 32;
209 ThrowIfZLibErr_ (::inflateInit2 (&fZStream_, windowBits | ENABLE_ZLIB_GZIP));
211 virtual ~InflateRep_ ()
213 Verify (::inflateEnd (&fZStream_) == Z_OK);
215 virtual optional<size_t> AvailableToRead ()
override
217 return _Available2Read ([
this] (
bool isEOF) {
return DoProcess_ (isEOF); });
219 virtual optional<SeekOffsetType> RemainingLength ()
override
225 return _Read (intoBuffer, blockFlag, [
this] (
bool isEOF) {
return DoProcess_ (isEOF); });
227 int DoProcess_ ([[maybe_unused]]
bool isEOF)
229 return ::inflate (&fZStream_, Z_NO_FLUSH);
235#if qStroika_HasComponent_zlib
237 class Rep_ :
public Reader::IRep {
250Zip::Reader::Reader ()
auto MakeSharedPtr(ARGS_TYPE &&... args) -> shared_ptr< T >
same as make_shared, but if type T has block allocation, then use block allocation for the 'shared pa...
NoDataAvailableHandling
If eDontBlock passed to most Stream APIs, then when the code would do a blocking read,...