Eclipse SUMO - Simulation of Urban MObility
zstr.hpp
Go to the documentation of this file.
1 //---------------------------------------------------------
2 // Copyright 2015 Ontario Institute for Cancer Research
3 // Written by Matei David (matei@cs.toronto.edu)
4 //---------------------------------------------------------
5 
6 // Reference:
7 // http://stackoverflow.com/questions/14086417/how-to-write-custom-input-stream-in-c
8 
9 #ifndef __ZSTR_HPP
10 #define __ZSTR_HPP
11 
12 #include <cassert>
13 #include <fstream>
14 #include <sstream>
15 #include <zlib.h>
16 #include "strict_fstream.hpp"
17 
18 namespace zstr
19 {
20 
22 class Exception
23  : public std::exception
24 {
25 public:
26  Exception(z_stream * zstrm_p, int ret)
27  : _msg("zlib: ")
28  {
29  switch (ret)
30  {
31  case Z_STREAM_ERROR:
32  _msg += "Z_STREAM_ERROR: ";
33  break;
34  case Z_DATA_ERROR:
35  _msg += "Z_DATA_ERROR: ";
36  break;
37  case Z_MEM_ERROR:
38  _msg += "Z_MEM_ERROR: ";
39  break;
40  case Z_VERSION_ERROR:
41  _msg += "Z_VERSION_ERROR: ";
42  break;
43  case Z_BUF_ERROR:
44  _msg += "Z_BUF_ERROR: ";
45  break;
46  default:
47  std::ostringstream oss;
48  oss << ret;
49  _msg += "[" + oss.str() + "]: ";
50  break;
51  }
52  _msg += zstrm_p->msg;
53  }
54  Exception(const std::string msg) : _msg(msg) {}
55  const char * what() const NOEXCEPT { return _msg.c_str(); }
56 private:
57  std::string _msg;
58 }; // class Exception
59 
60 namespace detail
61 {
62 
64  : public z_stream
65 {
66 public:
67  z_stream_wrapper(bool _is_input = true, int _level = Z_DEFAULT_COMPRESSION)
68  : is_input(_is_input)
69  {
70  this->zalloc = Z_NULL;
71  this->zfree = Z_NULL;
72  this->opaque = Z_NULL;
73  int ret;
74  if (is_input)
75  {
76  this->avail_in = 0;
77  this->next_in = Z_NULL;
78  ret = inflateInit2(this, 15+32);
79  }
80  else
81  {
82  ret = deflateInit2(this, _level, Z_DEFLATED, 15+16, 8, Z_DEFAULT_STRATEGY);
83  }
84  if (ret != Z_OK) throw Exception(this, ret);
85  }
87  {
88  if (is_input)
89  {
90  inflateEnd(this);
91  }
92  else
93  {
94  deflateEnd(this);
95  }
96  }
97 private:
98  bool is_input;
99 }; // class z_stream_wrapper
100 
101 } // namespace detail
102 
104  : public std::streambuf
105 {
106 public:
107  istreambuf(std::streambuf * _sbuf_p,
108  std::size_t _buff_size = default_buff_size, bool _auto_detect = true)
109  : sbuf_p(_sbuf_p),
110  zstrm_p(nullptr),
111  buff_size(_buff_size),
112  auto_detect(_auto_detect),
113  auto_detect_run(false),
114  is_text(false)
115  {
116  assert(sbuf_p);
117  in_buff = new char [buff_size];
118  in_buff_start = in_buff;
119  in_buff_end = in_buff;
120  out_buff = new char [buff_size];
121  setg(out_buff, out_buff, out_buff);
122  }
123 
124  istreambuf(const istreambuf &) = delete;
125  istreambuf & operator = (const istreambuf &) = delete;
126 
127  virtual ~istreambuf()
128  {
129  delete [] in_buff;
130  delete [] out_buff;
131  if (zstrm_p) delete zstrm_p;
132  }
133 
134  virtual std::streambuf::int_type underflow()
135  {
136  if (this->gptr() == this->egptr())
137  {
138  // pointers for free region in output buffer
139  char * out_buff_free_start = out_buff;
140  do
141  {
142  // read more input if none available
143  if (in_buff_start == in_buff_end)
144  {
145  // empty input buffer: refill from the start
146  in_buff_start = in_buff;
147  std::streamsize sz = sbuf_p->sgetn(in_buff, buff_size);
148  in_buff_end = in_buff + sz;
149  if (in_buff_end == in_buff_start) break; // end of input
150  }
151  // auto detect if the stream contains text or deflate data
152  if (auto_detect && ! auto_detect_run)
153  {
154  auto_detect_run = true;
155  unsigned char b0 = *reinterpret_cast< unsigned char * >(in_buff_start);
156  unsigned char b1 = *reinterpret_cast< unsigned char * >(in_buff_start + 1);
157  // Ref:
158  // http://en.wikipedia.org/wiki/Gzip
159  // http://stackoverflow.com/questions/9050260/what-does-a-zlib-header-look-like
160  is_text = ! (in_buff_start + 2 <= in_buff_end
161  && ((b0 == 0x1F && b1 == 0x8B) // gzip header
162  || (b0 == 0x78 && (b1 == 0x01 // zlib header
163  || b1 == 0x9C
164  || b1 == 0xDA))));
165  }
166  if (is_text)
167  {
168  // simply swap in_buff and out_buff, and adjust pointers
169  assert(in_buff_start == in_buff);
170  std::swap(in_buff, out_buff);
171  out_buff_free_start = in_buff_end;
172  in_buff_start = in_buff;
173  in_buff_end = in_buff;
174  }
175  else
176  {
177  // run inflate() on input
178  if (! zstrm_p) zstrm_p = new detail::z_stream_wrapper(true);
179  zstrm_p->next_in = reinterpret_cast< decltype(zstrm_p->next_in) >(in_buff_start);
180  zstrm_p->avail_in = (uInt)(in_buff_end - in_buff_start);
181  zstrm_p->next_out = reinterpret_cast< decltype(zstrm_p->next_out) >(out_buff_free_start);
182  zstrm_p->avail_out = (uInt)((out_buff + buff_size) - out_buff_free_start);
183  int ret = inflate(zstrm_p, Z_NO_FLUSH);
184  // process return code
185  if (ret != Z_OK && ret != Z_STREAM_END) throw Exception(zstrm_p, ret);
186  // update in&out pointers following inflate()
187  in_buff_start = reinterpret_cast< decltype(in_buff_start) >(zstrm_p->next_in);
188  in_buff_end = in_buff_start + zstrm_p->avail_in;
189  out_buff_free_start = reinterpret_cast< decltype(out_buff_free_start) >(zstrm_p->next_out);
190  assert(out_buff_free_start + zstrm_p->avail_out == out_buff + buff_size);
191  // if stream ended, deallocate inflator
192  if (ret == Z_STREAM_END)
193  {
194  delete zstrm_p;
195  zstrm_p = nullptr;
196  }
197  }
198  } while (out_buff_free_start == out_buff);
199  // 2 exit conditions:
200  // - end of input: there might or might not be output available
201  // - out_buff_free_start != out_buff: output available
202  this->setg(out_buff, out_buff, out_buff_free_start);
203  }
204  return this->gptr() == this->egptr()
205  ? traits_type::eof()
206  : traits_type::to_int_type(*this->gptr());
207  }
208 private:
209  std::streambuf * sbuf_p;
210  char * in_buff;
212  char * in_buff_end;
213  char * out_buff;
215  std::size_t buff_size;
218  bool is_text;
219 
220  static const std::size_t default_buff_size = (std::size_t)1 << 20;
221 }; // class istreambuf
222 
224  : public std::streambuf
225 {
226 public:
227  ostreambuf(std::streambuf * _sbuf_p,
228  std::size_t _buff_size = default_buff_size, int _level = Z_DEFAULT_COMPRESSION)
229  : sbuf_p(_sbuf_p),
230  zstrm_p(new detail::z_stream_wrapper(false, _level)),
231  buff_size(_buff_size)
232  {
233  assert(sbuf_p);
234  in_buff = new char [buff_size];
235  out_buff = new char [buff_size];
236  setp(in_buff, in_buff + buff_size);
237  }
238 
239  ostreambuf(const ostreambuf &) = delete;
240  ostreambuf & operator = (const ostreambuf &) = delete;
241 
242  int deflate_loop(int flush)
243  {
244  int ret = Z_OK;
245  while (ret != Z_STREAM_END && ret != Z_BUF_ERROR)
246  {
247  zstrm_p->next_out = reinterpret_cast< decltype(zstrm_p->next_out) >(out_buff);
248  zstrm_p->avail_out = (uInt)buff_size;
249  ret = deflate(zstrm_p, flush);
250  if (ret != Z_OK && ret != Z_STREAM_END && ret != Z_BUF_ERROR) throw Exception(zstrm_p, ret);
251  std::streamsize sz = sbuf_p->sputn(out_buff, reinterpret_cast< decltype(out_buff) >(zstrm_p->next_out) - out_buff);
252  if (sz != reinterpret_cast< decltype(out_buff) >(zstrm_p->next_out) - out_buff)
253  {
254  // there was an error in the sink stream
255  return -1;
256  }
257  if (sz == 0)
258  {
259  break;
260  }
261  }
262  return 0;
263  }
264 
265  virtual ~ostreambuf()
266  {
267  // flush the zlib stream
268  //
269  // NOTE: Errors here (sync() return value not 0) are ignored, because we
270  // cannot throw in a destructor. This mirrors the behaviour of
271  // std::basic_filebuf::~basic_filebuf(). To see an exception on error,
272  // close the ofstream with an explicit call to close(), and do not rely
273  // on the implicit call in the destructor.
274  //
275  sync();
276  delete [] in_buff;
277  delete [] out_buff;
278  delete zstrm_p;
279  }
280  virtual std::streambuf::int_type overflow(std::streambuf::int_type c = traits_type::eof())
281  {
282  zstrm_p->next_in = reinterpret_cast< decltype(zstrm_p->next_in) >(pbase());
283  zstrm_p->avail_in = (uInt)(pptr() - pbase());
284  while (zstrm_p->avail_in > 0)
285  {
286  int r = deflate_loop(Z_NO_FLUSH);
287  if (r != 0)
288  {
289  setp(nullptr, nullptr);
290  return traits_type::eof();
291  }
292  }
293  setp(in_buff, in_buff + buff_size);
294  return traits_type::eq_int_type(c, traits_type::eof()) ? traits_type::eof() : sputc((char)c);
295  }
296  virtual int sync()
297  {
298  // first, call overflow to clear in_buff
299  overflow();
300  if (! pptr()) return -1;
301  // then, call deflate asking to finish the zlib stream
302  zstrm_p->next_in = nullptr;
303  zstrm_p->avail_in = 0;
304  if (deflate_loop(Z_FINISH) != 0) return -1;
305  deflateReset(zstrm_p);
306  return 0;
307  }
308 private:
309  std::streambuf * sbuf_p;
310  char * in_buff;
311  char * out_buff;
313  std::size_t buff_size;
314 
315  static const std::size_t default_buff_size = (std::size_t)1 << 20;
316 }; // class ostreambuf
317 
318 class istream
319  : public std::istream
320 {
321 public:
322  istream(std::istream & is)
323  : std::istream(new istreambuf(is.rdbuf()))
324  {
325  exceptions(std::ios_base::badbit);
326  }
327  explicit istream(std::streambuf * sbuf_p)
328  : std::istream(new istreambuf(sbuf_p))
329  {
330  exceptions(std::ios_base::badbit);
331  }
332  virtual ~istream()
333  {
334  delete rdbuf();
335  }
336 }; // class istream
337 
338 class ostream
339  : public std::ostream
340 {
341 public:
342  ostream(std::ostream & os)
343  : std::ostream(new ostreambuf(os.rdbuf()))
344  {
345  exceptions(std::ios_base::badbit);
346  }
347  explicit ostream(std::streambuf * sbuf_p)
348  : std::ostream(new ostreambuf(sbuf_p))
349  {
350  exceptions(std::ios_base::badbit);
351  }
352  virtual ~ostream()
353  {
354  delete rdbuf();
355  }
356 }; // class ostream
357 
358 namespace detail
359 {
360 
361 template < typename FStream_Type >
363 {
364  strict_fstream_holder(const std::string& filename, std::ios_base::openmode mode = std::ios_base::in)
365  : _fs(filename, mode)
366  {}
367  FStream_Type _fs;
368 }; // class strict_fstream_holder
369 
370 } // namespace detail
371 
372 class ifstream
373  : private detail::strict_fstream_holder< strict_fstream::ifstream >,
374  public std::istream
375 {
376 public:
377  explicit ifstream(const std::string& filename, std::ios_base::openmode mode = std::ios_base::in)
378  : detail::strict_fstream_holder< strict_fstream::ifstream >(filename, mode),
379  std::istream(new istreambuf(_fs.rdbuf()))
380  {
381  exceptions(std::ios_base::badbit);
382  }
383  virtual ~ifstream()
384  {
385  if (rdbuf()) delete rdbuf();
386  }
387 }; // class ifstream
388 
389 class ofstream
390  : private detail::strict_fstream_holder< strict_fstream::ofstream >,
391  public std::ostream
392 {
393 public:
394  explicit ofstream(const std::string& filename, std::ios_base::openmode mode = std::ios_base::out)
395  : detail::strict_fstream_holder< strict_fstream::ofstream >(filename, mode | std::ios_base::binary),
396  std::ostream(new ostreambuf(_fs.rdbuf()))
397  {
398  exceptions(std::ios_base::badbit);
399  }
400  virtual ~ofstream()
401  {
402  if (rdbuf()) delete rdbuf();
403  }
404 }; // class ofstream
405 
406 } // namespace zstr
407 
408 #endif
Exception(const std::string msg)
Definition: zstr.hpp:54
detail::z_stream_wrapper * zstrm_p
Definition: zstr.hpp:312
#define NOEXCEPT
virtual std::streambuf::int_type underflow()
Definition: zstr.hpp:134
virtual ~ifstream()
Definition: zstr.hpp:383
virtual int sync()
Definition: zstr.hpp:296
char * in_buff
Definition: zstr.hpp:310
istream(std::istream &is)
Definition: zstr.hpp:322
ostreambuf(std::streambuf *_sbuf_p, std::size_t _buff_size=default_buff_size, int _level=Z_DEFAULT_COMPRESSION)
Definition: zstr.hpp:227
std::size_t buff_size
Definition: zstr.hpp:215
Exception class thrown by failed zlib operations.
Definition: zstr.hpp:22
Exception(z_stream *zstrm_p, int ret)
Definition: zstr.hpp:26
virtual ~istream()
Definition: zstr.hpp:332
bool auto_detect
Definition: zstr.hpp:216
bool auto_detect_run
Definition: zstr.hpp:217
virtual ~ostream()
Definition: zstr.hpp:352
char * out_buff
Definition: zstr.hpp:213
const char * what() const NOEXCEPT
Definition: zstr.hpp:55
virtual ~ostreambuf()
Definition: zstr.hpp:265
detail::z_stream_wrapper * zstrm_p
Definition: zstr.hpp:214
std::streambuf * sbuf_p
Definition: zstr.hpp:309
ostream(std::streambuf *sbuf_p)
Definition: zstr.hpp:347
std::size_t buff_size
Definition: zstr.hpp:313
int deflate_loop(int flush)
Definition: zstr.hpp:242
z_stream_wrapper(bool _is_input=true, int _level=Z_DEFAULT_COMPRESSION)
Definition: zstr.hpp:67
Definition: zstr.hpp:18
ostream(std::ostream &os)
Definition: zstr.hpp:342
char * out_buff
Definition: zstr.hpp:311
virtual ~istreambuf()
Definition: zstr.hpp:127
char * in_buff_end
Definition: zstr.hpp:212
ifstream(const std::string &filename, std::ios_base::openmode mode=std::ios_base::in)
Definition: zstr.hpp:377
virtual std::streambuf::int_type overflow(std::streambuf::int_type c=traits_type::eof())
Definition: zstr.hpp:280
std::streambuf * sbuf_p
Definition: zstr.hpp:209
char * in_buff_start
Definition: zstr.hpp:211
std::string _msg
Definition: zstr.hpp:57
ofstream(const std::string &filename, std::ios_base::openmode mode=std::ios_base::out)
Definition: zstr.hpp:394
istream(std::streambuf *sbuf_p)
Definition: zstr.hpp:327
virtual ~ofstream()
Definition: zstr.hpp:400
strict_fstream_holder(const std::string &filename, std::ios_base::openmode mode=std::ios_base::in)
Definition: zstr.hpp:364
istreambuf(std::streambuf *_sbuf_p, std::size_t _buff_size=default_buff_size, bool _auto_detect=true)
Definition: zstr.hpp:107
char * in_buff
Definition: zstr.hpp:210