Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Compression/Zip/Reader.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Foundation/StroikaPreComp.h"
5
7
8#include "Reader.h"
9
10/// FILE DEPRECATED
11
12#if qStroika_HasComponent_zlib
13// SEE http://www.zlib.net/zlib_how.html
14#include <zlib.h>
15
16using std::byte;
17
18using namespace Stroika::Foundation;
22using namespace Stroika::Foundation::Streams;
23
24// Comment this in to turn on aggressive noisy DbgTrace in this module
25//#define USE_NOISY_TRACE_IN_THIS_MODULE_ 1
26
27namespace {
28 void ThrowIfZLibErr_ (int err)
29 {
30 // VERY ROUGH DRAFT - probably need a more specific exception object type
31 if (err != Z_OK) [[unlikely]] {
32 switch (err) {
33 case Z_VERSION_ERROR: {
34 static const Execution::RuntimeErrorException kException_{"ZLIB Z_VERSION_ERROR"sv};
35 Execution::Throw (kException_);
36 }
37 case Z_DATA_ERROR: {
38 static const Execution::RuntimeErrorException kException_{"ZLIB Z_DATA_ERROR"sv};
39 Execution::Throw (kException_);
40 }
41 case Z_STREAM_ERROR: {
42 static const Execution::RuntimeErrorException kException_{"ZLIB Z_STREAM_ERROR"sv};
43 Execution::Throw (kException_);
44 }
45 case Z_ERRNO:
46 Execution::Throw (Execution::RuntimeErrorException{"ZLIB Z_ERRNO (errno={})"_f(errno)});
47 default:
48 Execution::Throw (Execution::RuntimeErrorException{"ZLIB ERR {}"_f(err)});
49 }
50 }
51 }
52
53 struct BaseRep_ : public InputStream::IRep<byte> {
54 private:
55 static constexpr size_t CHUNK_ = 16384;
56
57 public:
58 Streams::InputStream::Ptr<byte> fInStream_; // consider wrapping in StreamReader for efficiency sake - maybe unhelpful due to CHUNK logic below
59 z_stream fZStream_{};
60 byte fInBuf_[CHUNK_]; // uninitialized cuz written before read
61 SeekOffsetType _fSeekOffset{};
62 optional<byte> _fNextOutputByte_; // 'cached' next output byte - if not nullopt - magic needed to make AvailableToRead
63
64 BaseRep_ (const Streams::InputStream::Ptr<byte>& in)
65 : fInStream_{in}
66 {
67 }
68 virtual ~BaseRep_ () = default;
69 virtual bool IsSeekable () const override
70 {
71 return false; // SHOULD allow seekable IFF src is seekable, but tricky because of internal state in compress/decompress library - not sure how to update/manage
72 }
73 virtual void CloseRead () override
74 {
75 if (fInStream_ != nullptr) {
76 fInStream_.Close ();
77 }
78 Assert (fInStream_ == nullptr);
79 Ensure (not IsOpenRead ());
80 }
81 virtual bool IsOpenRead () const override
82 {
83 return fInStream_ != nullptr;
84 }
85 virtual SeekOffsetType GetReadOffset () const override
86 {
87 Require (IsOpenRead ());
88 return _fSeekOffset;
89 }
90 // return number of bytes definitely copied into intoBuffer, else nullopt on EWOULDBLOCK
91 template <invocable<bool> PROCESS>
92 optional<size_t> PullEnufForDeflate1Byte_ (NoDataAvailableHandling blockFlag, span<byte> intoBuffer, PROCESS processInputZLibFunction)
93 {
94 Assert (_fNextOutputByte_ == nullopt); // already handled
95 Again:
96 if (blockFlag == NoDataAvailableHandling::eDontBlock and fZStream_.avail_in == 0 and fInStream_.AvailableToRead () == nullopt) {
97 // if non-blocking call, no data pre-available in zstream, and nothing in upstream, NoDataAvailable!
98 // note MAY not be enuf in zbuf to read a full byte of output, but OK - will come back here
99 return false;
100 }
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 ()); // blocking read always OK by the time we get here
104 fZStream_.next_in = reinterpret_cast<Bytef*> (begin (fInBuf_));
105 }
106 bool isAtSrcEOF = fZStream_.avail_in == 0;
107
108 ptrdiff_t outBufSize = intoBuffer.size ();
109
110 // int flush = isAtSrcEOF ? Z_FINISH : Z_NO_FLUSH;
111
112 fZStream_.avail_out = static_cast<uInt> (outBufSize);
113 fZStream_.next_out = reinterpret_cast<Bytef*> (intoBuffer.data ());
114 int ret;
115 switch (ret = processInputZLibFunction (isAtSrcEOF)) {
116 case Z_OK:
117 break;
118 case Z_STREAM_END:
119 break;
120 default:
121 ThrowIfZLibErr_ (ret);
122 }
123 ptrdiff_t pulledOut = outBufSize - fZStream_.avail_out;
124 Assert (pulledOut <= outBufSize);
125 if (pulledOut == 0 and not isAtSrcEOF) {
126 goto Again;
127 }
128 return pulledOut;
129 }
130 template <invocable<bool> PROCESS>
131 optional<size_t> _Available2Read (PROCESS processInputZLibFunction)
132 {
133 Require (IsOpenRead ());
134 if (_fNextOutputByte_) {
135 return 1;
136 }
137 byte tmp;
138 auto pulledButMustCache = PullEnufForDeflate1Byte_ (NoDataAvailableHandling::eDontBlock, span{&tmp, 1}, processInputZLibFunction);
139 if (pulledButMustCache) {
140 if (*pulledButMustCache == 0) {
141 return 0;
142 }
143 else {
144 Assert (*pulledButMustCache == 1);
145 _fNextOutputByte_ = tmp;
146 return 1;
147 }
148 }
149 else {
150 return nullopt;
151 }
152 }
153 template <invocable<bool> PROCESS>
154 optional<span<byte>> _Read (span<byte> intoBuffer, NoDataAvailableHandling blockFlag, PROCESS processInputZLibFunction)
155 {
156 Require (not intoBuffer.empty ()); // API rule for streams
157 Require (IsOpenRead ());
158 if (_fNextOutputByte_) {
159 intoBuffer[0] = *_fNextOutputByte_;
160 _fNextOutputByte_.reset ();
161 return intoBuffer.subspan (0, 1);
162 }
163 if (auto o = PullEnufForDeflate1Byte_ (blockFlag, intoBuffer, processInputZLibFunction)) {
164 size_t pulledOut = *o;
165 _fSeekOffset += pulledOut;
166 return intoBuffer.subspan (0, pulledOut);
167 }
168 else {
169 return nullopt;
170 }
171 }
172 };
173 struct DeflateRep_ : BaseRep_ {
174 DeflateRep_ (const Streams::InputStream::Ptr<byte>& in)
175 : BaseRep_{in}
176 {
177 int level = Z_DEFAULT_COMPRESSION;
178 ThrowIfZLibErr_ (::deflateInit (&fZStream_, level));
179 }
180 virtual ~DeflateRep_ ()
181 {
182 Verify (::deflateEnd (&fZStream_) == Z_OK);
183 }
184 virtual optional<size_t> AvailableToRead () override
185 {
186 return _Available2Read ([this] (bool isEOF) { return DoProcess_ (isEOF); });
187 }
188 virtual optional<SeekOffsetType> RemainingLength () override
189 {
190 return nullopt; // generally cannot tell without side-effects on input stream
191 }
192 virtual optional<span<byte>> Read (span<byte> intoBuffer, NoDataAvailableHandling blockFlag) override
193 {
194 return _Read (intoBuffer, blockFlag, [this] (bool isEOF) { return DoProcess_ (isEOF); });
195 }
196 int DoProcess_ (bool isEOF)
197 {
198 return ::deflate (&fZStream_, isEOF ? Z_FINISH : Z_NO_FLUSH);
199 }
200 };
201 struct InflateRep_ : BaseRep_ {
202 InflateRep_ (const Streams::InputStream::Ptr<byte>& in)
203 : BaseRep_{in}
204 {
205 // see http://zlib.net/manual.html for meaning of params and http://www.lemoda.net/c/zlib-open-read/ for example
206 constexpr int windowBits = 15;
207 constexpr int ENABLE_ZLIB_GZIP = 32;
208 ThrowIfZLibErr_ (::inflateInit2 (&fZStream_, windowBits | ENABLE_ZLIB_GZIP));
209 }
210 virtual ~InflateRep_ ()
211 {
212 Verify (::inflateEnd (&fZStream_) == Z_OK);
213 }
214 virtual optional<size_t> AvailableToRead () override
215 {
216 return _Available2Read ([this] (bool isEOF) { return DoProcess_ (isEOF); });
217 }
218 virtual optional<SeekOffsetType> RemainingLength () override
219 {
220 return nullopt; // generally cannot tell without side-effects on input stream
221 }
222 virtual optional<span<byte>> Read (span<byte> intoBuffer, NoDataAvailableHandling blockFlag) override
223 {
224 return _Read (intoBuffer, blockFlag, [this] (bool isEOF) { return DoProcess_ (isEOF); });
225 }
226 int DoProcess_ ([[maybe_unused]] bool isEOF)
227 {
228 return ::inflate (&fZStream_, Z_NO_FLUSH);
229 }
230 };
231}
232#endif
233
234#if qStroika_HasComponent_zlib
235namespace {
236 class Rep_ : public Reader::IRep {
237 public:
238 virtual InputStream::Ptr<byte> Compress (const InputStream::Ptr<byte>& src) const override
239 {
240 return InputStream::Ptr<byte>{make_shared<DeflateRep_> (src)};
241 }
242 virtual InputStream::Ptr<byte> Decompress (const InputStream::Ptr<byte>& src) const override
243 {
244 return InputStream::Ptr<byte>{make_shared<InflateRep_> (src)};
245 }
246 };
247}
248
249Zip::Reader::Reader ()
250 : DataExchange::Compression::Reader{make_shared<Rep_> ()}
251{
252}
253#endif
#define Verify(c)
Definition Assertions.h:419
NoDataAvailableHandling
If eDontBlock passed to most Stream APIs, then when the code would do a blocking read,...
Definition Stream.h:90
InputStream<>::Ptr is Smart pointer (with abstract Rep) class defining the interface to reading from ...
nonvirtual optional< ElementType > ReadBlocking() const
ReadBlocking () reads either a single element, or fills in argument intoBuffer - but never blocks (no...
nonvirtual optional< size_t > AvailableToRead() const
returns nullopt if nothing known available, zero if known EOF, and any other number of elements (typi...