4#include "Stroika/Foundation/StroikaPreComp.h"
8#include "Stroika/Foundation/Execution/FeatureNotSupportedException.h"
12#if qStroika_HasComponent_zlib
23using namespace Stroika::Foundation::Debug;
24using namespace Stroika::Foundation::Streams;
26#if qStroika_HasComponent_zlib
27namespace Stroika::Foundation::DataExchange::Compression::Private_ {
28 inline void ThrowIfZLibErr_ (
int err)
31 if (err != Z_OK) [[unlikely]] {
33 case Z_VERSION_ERROR: {
41 case Z_STREAM_ERROR: {
55 static constexpr size_t CHUNK_ = 16384;
62 optional<byte> _fNextOutputByte_;
69 virtual ~BaseRep_ () =
default;
70 virtual bool IsSeekable ()
const override
74 virtual void CloseRead ()
override
77 if (fInStream_ !=
nullptr) {
80 Assert (fInStream_ ==
nullptr);
81 Ensure (not IsOpenRead ());
83 virtual bool IsOpenRead ()
const override
86 return fInStream_ !=
nullptr;
90 Require (IsOpenRead ());
95 template <invocable<
bool> PROCESS>
96 optional<size_t> PullEnufForDeflate1Byte_ (
NoDataAvailableHandling blockFlag, span<byte> intoBuffer, PROCESS processInputZLibFunction)
98 Assert (_fNextOutputByte_ == nullopt);
101 if (blockFlag == NoDataAvailableHandling::eDontBlock and fZStream_.avail_in == 0 and fInStream_.
AvailableToRead () == nullopt) {
106 if (fZStream_.avail_in == 0) {
107 Assert (Memory::NEltsOf (fInBuf_) < numeric_limits<uInt>::max ());
108 fZStream_.avail_in =
static_cast<uInt
> (fInStream_.
ReadBlocking (span{fInBuf_}).size ());
109 fZStream_.next_in =
reinterpret_cast<Bytef*
> (begin (fInBuf_));
111 bool isAtSrcEOF = fZStream_.avail_in == 0;
113 ptrdiff_t outBufSize = intoBuffer.size ();
115 fZStream_.avail_out =
static_cast<uInt
> (outBufSize);
116 fZStream_.next_out =
reinterpret_cast<Bytef*
> (intoBuffer.data ());
118 switch (ret = processInputZLibFunction (isAtSrcEOF)) {
124 ThrowIfZLibErr_ (ret);
126 ptrdiff_t pulledOut = outBufSize - fZStream_.avail_out;
127 Assert (pulledOut <= outBufSize);
128 if (pulledOut == 0 and not isAtSrcEOF) {
133 template <invocable<
bool> PROCESS>
134 optional<size_t> _Available2Read (PROCESS processInputZLibFunction)
136 Require (IsOpenRead ());
138 if (_fNextOutputByte_) {
142 auto pulledButMustCache = PullEnufForDeflate1Byte_ (NoDataAvailableHandling::eDontBlock, span{&tmp, 1}, processInputZLibFunction);
143 if (pulledButMustCache) {
144 if (*pulledButMustCache == 0) {
148 Assert (*pulledButMustCache == 1);
149 _fNextOutputByte_ = tmp;
157 template <invocable<
bool> PROCESS>
158 optional<span<byte>> _Read (span<byte> intoBuffer,
NoDataAvailableHandling blockFlag, PROCESS processInputZLibFunction)
160 Require (not intoBuffer.empty ());
161 Require (IsOpenRead ());
163 if (_fNextOutputByte_) {
164 intoBuffer[0] = *_fNextOutputByte_;
165 _fNextOutputByte_.reset ();
167 if (intoBuffer.size () > 1) {
168 if (
auto o = PullEnufForDeflate1Byte_ (NoDataAvailableHandling::eDontBlock, intoBuffer.subspan (1), processInputZLibFunction)) {
169 size_t pulledOut = *o + 1;
170 _fSeekOffset += pulledOut;
171 return intoBuffer.subspan (0, pulledOut);
175 return intoBuffer.subspan (0, 1);
177 if (
auto o = PullEnufForDeflate1Byte_ (blockFlag, intoBuffer, processInputZLibFunction)) {
178 size_t pulledOut = *o;
179 _fSeekOffset += pulledOut;
180 return intoBuffer.subspan (0, pulledOut);
187 struct DeflateRep_ : BaseRep_ {
188 Compress::Options fOptions_;
193 Require (not o.fCompressionLevel.has_value () or (0 <= o.fCompressionLevel and o.fCompressionLevel <= 1));
194 int level = o.fCompressionLevel ? Z_BEST_SPEED + Math::Round<int> (*o.fCompressionLevel * (Z_BEST_COMPRESSION - Z_BEST_SPEED))
195 : Z_DEFAULT_COMPRESSION;
196 Assert (level == Z_DEFAULT_COMPRESSION or (Z_BEST_SPEED <= level and level <= Z_BEST_COMPRESSION));
198 constexpr int kWindowBits = 15 + 16;
199 constexpr int kMemLevel = 8;
200 ThrowIfZLibErr_ (::deflateInit2 (&fZStream_, level, Z_DEFLATED, kWindowBits, kMemLevel, Z_DEFAULT_STRATEGY));
203 ThrowIfZLibErr_ (::deflateInit (&fZStream_, level));
206 virtual ~DeflateRep_ ()
208 Verify (::deflateEnd (&fZStream_) == Z_OK);
210 virtual optional<size_t> AvailableToRead ()
override
213 return _Available2Read ([
this] (
bool isEOF) {
return DoProcess_ (isEOF); });
215 virtual optional<SeekOffsetType> RemainingLength ()
override
222 return _Read (intoBuffer, blockFlag, [
this] (
bool isEOF) {
return DoProcess_ (isEOF); });
224 int DoProcess_ (
bool isEOF)
227 return ::deflate (&fZStream_, isEOF ? Z_FINISH : Z_NO_FLUSH);
230 struct InflateRep_ : BaseRep_ {
235 constexpr int windowBits = 15;
236 constexpr int ENABLE_ZLIB_GZIP = 32;
237 ThrowIfZLibErr_ (::inflateInit2 (&fZStream_, windowBits | (gzip ? ENABLE_ZLIB_GZIP : 0)));
239 virtual ~InflateRep_ ()
242 Verify (::inflateEnd (&fZStream_) == Z_OK);
244 virtual optional<size_t> AvailableToRead ()
override
247 return _Available2Read ([
this] (
bool isEOF) {
return DoProcess_ (isEOF); });
249 virtual optional<SeekOffsetType> RemainingLength ()
override
256 return _Read (intoBuffer, blockFlag, [
this] (
bool isEOF) {
return DoProcess_ (isEOF); });
258 int DoProcess_ ([[maybe_unused]]
bool isEOF)
260 return ::inflate (&fZStream_, Z_NO_FLUSH);
NoDataAvailableHandling
If eDontBlock passed to most Stream APIs, then when the code would do a blocking read,...
NOT a real mutex - just a debugging infrastructure support tool so in debug builds can be assured thr...
shared_lock< const AssertExternallySynchronizedMutex > ReadContext
Instantiate AssertExternallySynchronizedMutex::ReadContext to designate an area of code where protect...
unique_lock< AssertExternallySynchronizedMutex > WriteContext
Instantiate AssertExternallySynchronizedMutex::WriteContext to designate an area of code where protec...
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...