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