Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/foreign/zstr/zstr.hpp
169678 views
1
//---------------------------------------------------------
2
// Copyright 2015 Ontario Institute for Cancer Research
3
// Written by Matei David ([email protected])
4
//---------------------------------------------------------
5
6
// Reference:
7
// http://stackoverflow.com/questions/14086417/how-to-write-custom-input-stream-in-c
8
9
#pragma once
10
11
#include <cassert>
12
#include <cstdint>
13
#include <fstream>
14
#include <sstream>
15
#include <zlib.h>
16
#include <memory>
17
#include <iostream>
18
#include "strict_fstream.hpp"
19
20
#if defined(__GNUC__) && !defined(__clang__)
21
#if (__GNUC__ > 5) || (__GNUC__ == 5 && __GNUC_MINOR__>0)
22
#define CAN_MOVE_IOSTREAM
23
#endif
24
#else
25
#define CAN_MOVE_IOSTREAM
26
#endif
27
28
namespace zstr
29
{
30
31
static const std::size_t default_buff_size = static_cast<std::size_t>(1 << 20);
32
33
/// Exception class thrown by failed zlib operations.
34
class Exception
35
: public std::ios_base::failure
36
{
37
public:
38
static std::string error_to_message(z_stream * zstrm_p, int ret)
39
{
40
std::string msg = "zlib: ";
41
switch (ret)
42
{
43
case Z_STREAM_ERROR:
44
msg += "Z_STREAM_ERROR: ";
45
break;
46
case Z_DATA_ERROR:
47
msg += "Z_DATA_ERROR: ";
48
break;
49
case Z_MEM_ERROR:
50
msg += "Z_MEM_ERROR: ";
51
break;
52
case Z_VERSION_ERROR:
53
msg += "Z_VERSION_ERROR: ";
54
break;
55
case Z_BUF_ERROR:
56
msg += "Z_BUF_ERROR: ";
57
break;
58
default:
59
std::ostringstream oss;
60
oss << ret;
61
msg += "[" + oss.str() + "]: ";
62
break;
63
}
64
if (zstrm_p->msg) {
65
msg += zstrm_p->msg;
66
}
67
msg += " ("
68
"next_in: " +
69
std::to_string(uintptr_t(zstrm_p->next_in)) +
70
", avail_in: " +
71
std::to_string(uintptr_t(zstrm_p->avail_in)) +
72
", next_out: " +
73
std::to_string(uintptr_t(zstrm_p->next_out)) +
74
", avail_out: " +
75
std::to_string(uintptr_t(zstrm_p->avail_out)) +
76
")";
77
return msg;
78
}
79
80
Exception(z_stream * zstrm_p, int ret)
81
: std::ios_base::failure(error_to_message(zstrm_p, ret))
82
{
83
}
84
}; // class Exception
85
86
namespace detail
87
{
88
89
class z_stream_wrapper
90
: public z_stream
91
{
92
public:
93
z_stream_wrapper(bool _is_input, int _level, int _window_bits)
94
: is_input(_is_input)
95
{
96
this->zalloc = nullptr;//Z_NULL
97
this->zfree = nullptr;//Z_NULL
98
this->opaque = nullptr;//Z_NULL
99
int ret;
100
if (is_input)
101
{
102
this->avail_in = 0;
103
this->next_in = nullptr;//Z_NULL
104
ret = inflateInit2(this, _window_bits ? _window_bits : 15+32);
105
}
106
else
107
{
108
ret = deflateInit2(this, _level, Z_DEFLATED, _window_bits ? _window_bits : 15+16, 8, Z_DEFAULT_STRATEGY);
109
}
110
if (ret != Z_OK) throw Exception(this, ret);
111
}
112
~z_stream_wrapper()
113
{
114
if (is_input)
115
{
116
inflateEnd(this);
117
}
118
else
119
{
120
deflateEnd(this);
121
}
122
}
123
private:
124
bool is_input;
125
}; // class z_stream_wrapper
126
127
} // namespace detail
128
129
class istreambuf
130
: public std::streambuf
131
{
132
public:
133
istreambuf(std::streambuf * _sbuf_p,
134
std::size_t _buff_size = default_buff_size, bool _auto_detect = true, int _window_bits = 0)
135
: sbuf_p(_sbuf_p),
136
in_buff(),
137
in_buff_start(nullptr),
138
in_buff_end(nullptr),
139
out_buff(),
140
zstrm_p(nullptr),
141
buff_size(_buff_size),
142
auto_detect(_auto_detect),
143
auto_detect_run(false),
144
is_text(false),
145
window_bits(_window_bits)
146
{
147
assert(sbuf_p);
148
in_buff = std::unique_ptr<char[]>(new char[buff_size]);
149
in_buff_start = in_buff.get();
150
in_buff_end = in_buff.get();
151
out_buff = std::unique_ptr<char[]>(new char[buff_size]);
152
setg(out_buff.get(), out_buff.get(), out_buff.get());
153
}
154
155
istreambuf(const istreambuf &) = delete;
156
istreambuf & operator = (const istreambuf &) = delete;
157
158
pos_type seekoff(off_type off, std::ios_base::seekdir dir,
159
std::ios_base::openmode which) override
160
{
161
if (off != 0 || dir != std::ios_base::cur) {
162
return std::streambuf::seekoff(off, dir, which);
163
}
164
165
if (!zstrm_p) {
166
return 0;
167
}
168
169
return static_cast<long int>(zstrm_p->total_out - static_cast<uLong>(in_avail()));
170
}
171
172
std::streambuf::int_type underflow() override
173
{
174
if (this->gptr() == this->egptr())
175
{
176
// pointers for free region in output buffer
177
char * out_buff_free_start = out_buff.get();
178
int tries = 0;
179
do
180
{
181
if (++tries > 1000) {
182
throw std::ios_base::failure("Failed to fill buffer after 1000 tries");
183
}
184
185
// read more input if none available
186
if (in_buff_start == in_buff_end)
187
{
188
// empty input buffer: refill from the start
189
in_buff_start = in_buff.get();
190
std::streamsize sz = sbuf_p->sgetn(in_buff.get(), static_cast<std::streamsize>(buff_size));
191
in_buff_end = in_buff_start + sz;
192
if (in_buff_end == in_buff_start) break; // end of input
193
}
194
// auto detect if the stream contains text or deflate data
195
if (auto_detect && ! auto_detect_run)
196
{
197
auto_detect_run = true;
198
unsigned char b0 = *reinterpret_cast< unsigned char * >(in_buff_start);
199
unsigned char b1 = *reinterpret_cast< unsigned char * >(in_buff_start + 1);
200
// Ref:
201
// http://en.wikipedia.org/wiki/Gzip
202
// http://stackoverflow.com/questions/9050260/what-does-a-zlib-header-look-like
203
is_text = ! (in_buff_start + 2 <= in_buff_end
204
&& ((b0 == 0x1F && b1 == 0x8B) // gzip header
205
|| (b0 == 0x78 && (b1 == 0x01 // zlib header
206
|| b1 == 0x9C
207
|| b1 == 0xDA))));
208
}
209
if (is_text)
210
{
211
// simply swap in_buff and out_buff, and adjust pointers
212
assert(in_buff_start == in_buff.get());
213
std::swap(in_buff, out_buff);
214
out_buff_free_start = in_buff_end;
215
in_buff_start = in_buff.get();
216
in_buff_end = in_buff.get();
217
}
218
else
219
{
220
// run inflate() on input
221
if (! zstrm_p) zstrm_p = std::unique_ptr<detail::z_stream_wrapper>(new detail::z_stream_wrapper(true, Z_DEFAULT_COMPRESSION, window_bits));
222
zstrm_p->next_in = reinterpret_cast< decltype(zstrm_p->next_in) >(in_buff_start);
223
zstrm_p->avail_in = uint32_t(in_buff_end - in_buff_start);
224
zstrm_p->next_out = reinterpret_cast< decltype(zstrm_p->next_out) >(out_buff_free_start);
225
zstrm_p->avail_out = uint32_t((out_buff.get() + buff_size) - out_buff_free_start);
226
int ret = inflate(zstrm_p.get(), Z_NO_FLUSH);
227
// process return code
228
if (ret != Z_OK && ret != Z_STREAM_END) throw Exception(zstrm_p.get(), ret);
229
// update in&out pointers following inflate()
230
in_buff_start = reinterpret_cast< decltype(in_buff_start) >(zstrm_p->next_in);
231
in_buff_end = in_buff_start + zstrm_p->avail_in;
232
out_buff_free_start = reinterpret_cast< decltype(out_buff_free_start) >(zstrm_p->next_out);
233
assert(out_buff_free_start + zstrm_p->avail_out == out_buff.get() + buff_size);
234
235
if (ret == Z_STREAM_END) {
236
// if stream ended, deallocate inflator
237
zstrm_p.reset();
238
}
239
}
240
} while (out_buff_free_start == out_buff.get());
241
// 2 exit conditions:
242
// - end of input: there might or might not be output available
243
// - out_buff_free_start != out_buff: output available
244
this->setg(out_buff.get(), out_buff.get(), out_buff_free_start);
245
}
246
return this->gptr() == this->egptr()
247
? traits_type::eof()
248
: traits_type::to_int_type(*this->gptr());
249
}
250
private:
251
std::streambuf * sbuf_p;
252
std::unique_ptr<char[]> in_buff;
253
char * in_buff_start;
254
char * in_buff_end;
255
std::unique_ptr<char[]> out_buff;
256
std::unique_ptr<detail::z_stream_wrapper> zstrm_p;
257
std::size_t buff_size;
258
bool auto_detect;
259
bool auto_detect_run;
260
bool is_text;
261
int window_bits;
262
263
}; // class istreambuf
264
265
class ostreambuf
266
: public std::streambuf
267
{
268
public:
269
ostreambuf(std::streambuf * _sbuf_p,
270
std::size_t _buff_size = default_buff_size, int _level = Z_DEFAULT_COMPRESSION, int _window_bits = 0)
271
: sbuf_p(_sbuf_p),
272
in_buff(),
273
out_buff(),
274
zstrm_p(new detail::z_stream_wrapper(false, _level, _window_bits)),
275
buff_size(_buff_size)
276
{
277
assert(sbuf_p);
278
in_buff = std::unique_ptr<char[]>(new char[buff_size]);
279
out_buff = std::unique_ptr<char[]>(new char[buff_size]);
280
setp(in_buff.get(), in_buff.get() + buff_size);
281
}
282
283
ostreambuf(const ostreambuf &) = delete;
284
ostreambuf & operator = (const ostreambuf &) = delete;
285
286
int deflate_loop(int flush)
287
{
288
while (true)
289
{
290
zstrm_p->next_out = reinterpret_cast< decltype(zstrm_p->next_out) >(out_buff.get());
291
zstrm_p->avail_out = uint32_t(buff_size);
292
int ret = deflate(zstrm_p.get(), flush);
293
if (ret != Z_OK && ret != Z_STREAM_END && ret != Z_BUF_ERROR) {
294
failed = true;
295
throw Exception(zstrm_p.get(), ret);
296
}
297
std::streamsize sz = sbuf_p->sputn(out_buff.get(), reinterpret_cast< decltype(out_buff.get()) >(zstrm_p->next_out) - out_buff.get());
298
if (sz != reinterpret_cast< decltype(out_buff.get()) >(zstrm_p->next_out) - out_buff.get())
299
{
300
// there was an error in the sink stream
301
return -1;
302
}
303
if (ret == Z_STREAM_END || ret == Z_BUF_ERROR || sz == 0)
304
{
305
break;
306
}
307
}
308
return 0;
309
}
310
311
virtual ~ostreambuf()
312
{
313
// flush the zlib stream
314
//
315
// NOTE: Errors here (sync() return value not 0) are ignored, because we
316
// cannot throw in a destructor. This mirrors the behaviour of
317
// std::basic_filebuf::~basic_filebuf(). To see an exception on error,
318
// close the ofstream with an explicit call to close(), and do not rely
319
// on the implicit call in the destructor.
320
//
321
if (!failed) try {
322
sync();
323
} catch (...) {}
324
}
325
std::streambuf::int_type overflow(std::streambuf::int_type c = traits_type::eof()) override
326
{
327
zstrm_p->next_in = reinterpret_cast< decltype(zstrm_p->next_in) >(pbase());
328
zstrm_p->avail_in = uint32_t(pptr() - pbase());
329
while (zstrm_p->avail_in > 0)
330
{
331
int r = deflate_loop(Z_NO_FLUSH);
332
if (r != 0)
333
{
334
setp(nullptr, nullptr);
335
return traits_type::eof();
336
}
337
}
338
setp(in_buff.get(), in_buff.get() + buff_size);
339
return traits_type::eq_int_type(c, traits_type::eof()) ? traits_type::eof() : sputc(char_type(c));
340
}
341
int sync() override
342
{
343
// first, call overflow to clear in_buff
344
overflow();
345
if (! pptr()) return -1;
346
// then, call deflate asking to finish the zlib stream
347
zstrm_p->next_in = nullptr;
348
zstrm_p->avail_in = 0;
349
if (deflate_loop(Z_FINISH) != 0) return -1;
350
deflateReset(zstrm_p.get());
351
return 0;
352
}
353
private:
354
std::streambuf * sbuf_p = nullptr;
355
std::unique_ptr<char[]> in_buff;
356
std::unique_ptr<char[]> out_buff;
357
std::unique_ptr<detail::z_stream_wrapper> zstrm_p;
358
std::size_t buff_size;
359
bool failed = false;
360
361
}; // class ostreambuf
362
363
class istream
364
: public std::istream
365
{
366
public:
367
istream(std::istream & is,
368
std::size_t _buff_size = default_buff_size, bool _auto_detect = true, int _window_bits = 0)
369
: std::istream(new istreambuf(is.rdbuf(), _buff_size, _auto_detect, _window_bits))
370
{
371
exceptions(std::ios_base::badbit);
372
}
373
explicit istream(std::streambuf * sbuf_p)
374
: std::istream(new istreambuf(sbuf_p))
375
{
376
exceptions(std::ios_base::badbit);
377
}
378
virtual ~istream()
379
{
380
delete rdbuf();
381
}
382
}; // class istream
383
384
class ostream
385
: public std::ostream
386
{
387
public:
388
ostream(std::ostream & os,
389
std::size_t _buff_size = default_buff_size, int _level = Z_DEFAULT_COMPRESSION, int _window_bits = 0)
390
: std::ostream(new ostreambuf(os.rdbuf(), _buff_size, _level, _window_bits))
391
{
392
exceptions(std::ios_base::badbit);
393
}
394
explicit ostream(std::streambuf * sbuf_p)
395
: std::ostream(new ostreambuf(sbuf_p))
396
{
397
exceptions(std::ios_base::badbit);
398
}
399
virtual ~ostream()
400
{
401
delete rdbuf();
402
}
403
}; // class ostream
404
405
namespace detail
406
{
407
408
template < typename FStream_Type >
409
struct strict_fstream_holder
410
{
411
strict_fstream_holder(const std::string& filename, std::ios_base::openmode mode = std::ios_base::in)
412
: _fs(filename, mode)
413
{}
414
strict_fstream_holder() = default;
415
FStream_Type _fs {};
416
}; // class strict_fstream_holder
417
418
} // namespace detail
419
420
class ifstream
421
: private detail::strict_fstream_holder< strict_fstream::ifstream >,
422
public std::istream
423
{
424
public:
425
explicit ifstream(const std::string filename, std::ios_base::openmode mode = std::ios_base::in, size_t buff_size = default_buff_size)
426
: detail::strict_fstream_holder< strict_fstream::ifstream >(filename, mode),
427
std::istream(new istreambuf(_fs.rdbuf(), buff_size))
428
{
429
exceptions(std::ios_base::badbit);
430
}
431
explicit ifstream(): detail::strict_fstream_holder< strict_fstream::ifstream >(), std::istream(new istreambuf(_fs.rdbuf())){}
432
void close() {
433
_fs.close();
434
}
435
#ifdef CAN_MOVE_IOSTREAM
436
void open(const std::string filename, std::ios_base::openmode mode = std::ios_base::in) {
437
_fs.open(filename, mode);
438
std::istream::operator=(std::istream(new istreambuf(_fs.rdbuf())));
439
}
440
#endif
441
bool is_open() const {
442
return _fs.is_open();
443
}
444
virtual ~ifstream()
445
{
446
if (_fs.is_open()) close();
447
if (rdbuf()) delete rdbuf();
448
}
449
450
/// Return the position within the compressed file (wrapped filestream)
451
std::streampos compressed_tellg()
452
{
453
return _fs.tellg();
454
}
455
}; // class ifstream
456
457
class ofstream
458
: private detail::strict_fstream_holder< strict_fstream::ofstream >,
459
public std::ostream
460
{
461
public:
462
explicit ofstream(const std::string filename, std::ios_base::openmode mode = std::ios_base::out,
463
int level = Z_DEFAULT_COMPRESSION, size_t buff_size = default_buff_size)
464
: detail::strict_fstream_holder< strict_fstream::ofstream >(filename, mode | std::ios_base::binary),
465
std::ostream(new ostreambuf(_fs.rdbuf(), buff_size, level))
466
{
467
exceptions(std::ios_base::badbit);
468
}
469
explicit ofstream(): detail::strict_fstream_holder< strict_fstream::ofstream >(), std::ostream(new ostreambuf(_fs.rdbuf())){}
470
void close() {
471
std::ostream::flush();
472
_fs.close();
473
}
474
#ifdef CAN_MOVE_IOSTREAM
475
void open(const std::string filename, std::ios_base::openmode mode = std::ios_base::out, int level = Z_DEFAULT_COMPRESSION) {
476
flush();
477
_fs.open(filename, mode | std::ios_base::binary);
478
std::ostream::operator=(std::ostream(new ostreambuf(_fs.rdbuf(), default_buff_size, level)));
479
}
480
#endif
481
bool is_open() const {
482
return _fs.is_open();
483
}
484
ofstream& flush() {
485
std::ostream::flush();
486
_fs.flush();
487
return *this;
488
}
489
virtual ~ofstream()
490
{
491
if (_fs.is_open()) close();
492
if (rdbuf()) delete rdbuf();
493
}
494
495
// Return the position within the compressed file (wrapped filestream)
496
std::streampos compressed_tellp()
497
{
498
return _fs.tellp();
499
}
500
}; // class ofstream
501
502
} // namespace zstr
503
504