Panda3D
zStreamBuf.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file zStreamBuf.cxx
10  * @author drose
11  * @date 2002-08-05
12  */
13 
14 #include "zStreamBuf.h"
15 
16 #ifdef HAVE_ZLIB
17 
18 #include "pnotify.h"
19 #include "config_express.h"
20 
21 using std::ios;
22 using std::streamoff;
23 using std::streampos;
24 
25 #if !defined(USE_MEMORY_NOWRAPPERS) && !defined(CPPPARSER)
26 // Define functions that hook zlib into panda's memory allocation system.
27 static void *
28 do_zlib_alloc(voidpf opaque, uInt items, uInt size) {
29  return PANDA_MALLOC_ARRAY(items * size);
30 }
31 static void
32 do_zlib_free(voidpf opaque, voidpf address) {
33  PANDA_FREE_ARRAY(address);
34 }
35 #endif // !USE_MEMORY_NOWRAPPERS
36 
37 /**
38  *
39  */
40 ZStreamBuf::
41 ZStreamBuf() {
42  _source = nullptr;
43  _owns_source = false;
44  _dest = nullptr;
45  _owns_dest = false;
46 
47 #ifdef PHAVE_IOSTREAM
48  _buffer = (char *)PANDA_MALLOC_ARRAY(4096);
49  char *ebuf = _buffer + 4096;
50  setg(_buffer, ebuf, ebuf);
51  setp(_buffer, ebuf);
52 
53 #else
54  allocate();
55  setg(base(), ebuf(), ebuf());
56  setp(base(), ebuf());
57 #endif
58 }
59 
60 /**
61  *
62  */
63 ZStreamBuf::
64 ~ZStreamBuf() {
65  close_read();
66  close_write();
67 #ifdef PHAVE_IOSTREAM
68  PANDA_FREE_ARRAY(_buffer);
69 #endif
70 }
71 
72 /**
73  *
74  */
75 void ZStreamBuf::
76 open_read(std::istream *source, bool owns_source) {
77  _source = source;
78  _owns_source = owns_source;
79 
80  _z_source.next_in = Z_NULL;
81  _z_source.avail_in = 0;
82  _z_source.next_out = Z_NULL;
83  _z_source.avail_out = 0;
84 #ifdef USE_MEMORY_NOWRAPPERS
85  _z_source.zalloc = Z_NULL;
86  _z_source.zfree = Z_NULL;
87 #else
88  _z_source.zalloc = (alloc_func)&do_zlib_alloc;
89  _z_source.zfree = (free_func)&do_zlib_free;
90 #endif
91  _z_source.opaque = Z_NULL;
92  _z_source.msg = (char *)"no error message";
93 
94  int result = inflateInit2(&_z_source, 32 + 15);
95  if (result < 0) {
96  show_zlib_error("inflateInit2", result, _z_source);
97  close_read();
98  }
99  thread_consider_yield();
100 }
101 
102 /**
103  *
104  */
105 void ZStreamBuf::
106 close_read() {
107  if (_source != nullptr) {
108 
109  int result = inflateEnd(&_z_source);
110  if (result < 0) {
111  show_zlib_error("inflateEnd", result, _z_source);
112  }
113  thread_consider_yield();
114 
115  if (_owns_source) {
116  delete _source;
117  _owns_source = false;
118  }
119  _source = nullptr;
120  }
121 }
122 
123 /**
124  *
125  */
126 void ZStreamBuf::
127 open_write(std::ostream *dest, bool owns_dest, int compression_level) {
128  _dest = dest;
129  _owns_dest = owns_dest;
130 
131  _z_dest.next_in = Z_NULL;
132  _z_dest.avail_in = 0;
133  _z_dest.next_out = Z_NULL;
134  _z_dest.avail_out = 0;
135 #ifdef USE_MEMORY_NOWRAPPERS
136  _z_dest.zalloc = Z_NULL;
137  _z_dest.zfree = Z_NULL;
138 #else
139  _z_dest.zalloc = (alloc_func)&do_zlib_alloc;
140  _z_dest.zfree = (free_func)&do_zlib_free;
141 #endif
142  _z_dest.opaque = Z_NULL;
143  _z_dest.msg = (char *)"no error message";
144 
145  int result = deflateInit(&_z_dest, compression_level);
146  if (result < 0) {
147  show_zlib_error("deflateInit", result, _z_dest);
148  close_write();
149  }
150  thread_consider_yield();
151 }
152 
153 /**
154  *
155  */
156 void ZStreamBuf::
157 close_write() {
158  if (_dest != nullptr) {
159  size_t n = pptr() - pbase();
160  write_chars(pbase(), n, Z_FINISH);
161  pbump(-(int)n);
162 
163  int result = deflateEnd(&_z_dest);
164  if (result < 0) {
165  show_zlib_error("deflateEnd", result, _z_dest);
166  }
167  thread_consider_yield();
168 
169  if (_owns_dest) {
170  delete _dest;
171  _owns_dest = false;
172  }
173  _dest = nullptr;
174  }
175 }
176 
177 /**
178  * Implements seeking within the stream. ZStreamBuf only allows seeking back
179  * to the beginning of the stream.
180  */
181 streampos ZStreamBuf::
182 seekoff(streamoff off, ios_seekdir dir, ios_openmode which) {
183  if (which != ios::in) {
184  // We can only do this with the input stream.
185  return -1;
186  }
187 
188  // Determine the current position.
189  size_t n = egptr() - gptr();
190  streampos gpos = _z_source.total_out - n;
191 
192  // Implement tellg() and seeks to current position.
193  if ((dir == ios::cur && off == 0) ||
194  (dir == ios::beg && off == gpos)) {
195  return gpos;
196  }
197 
198  if (off != 0 || dir != ios::beg) {
199  // We only know how to reposition to the beginning.
200  return -1;
201  }
202 
203  gbump(n);
204 
205  _source->seekg(0, ios::beg);
206  if (_source->tellg() == (streampos)0) {
207  _z_source.next_in = Z_NULL;
208  _z_source.avail_in = 0;
209  _z_source.next_out = Z_NULL;
210  _z_source.avail_out = 0;
211  int result = inflateReset(&_z_source);
212  if (result < 0) {
213  show_zlib_error("inflateReset", result, _z_source);
214  }
215  return 0;
216  }
217 
218  return -1;
219 }
220 
221 /**
222  * Implements seeking within the stream. ZStreamBuf only allows seeking back
223  * to the beginning of the stream.
224  */
225 streampos ZStreamBuf::
226 seekpos(streampos pos, ios_openmode which) {
227  return seekoff(pos, ios::beg, which);
228 }
229 
230 /**
231  * Called by the system ostream implementation when its internal buffer is
232  * filled, plus one character.
233  */
234 int ZStreamBuf::
235 overflow(int ch) {
236  size_t n = pptr() - pbase();
237  if (n != 0) {
238  write_chars(pbase(), n, 0);
239  pbump(-(int)n);
240  }
241 
242  if (ch != EOF) {
243  // Write one more character.
244  char c = ch;
245  write_chars(&c, 1, 0);
246  }
247 
248  return 0;
249 }
250 
251 /**
252  * Called by the system iostream implementation to implement a flush
253  * operation.
254  */
255 int ZStreamBuf::
256 sync() {
257  if (_source != nullptr) {
258  size_t n = egptr() - gptr();
259  gbump(n);
260  }
261 
262  if (_dest != nullptr) {
263  size_t n = pptr() - pbase();
264  write_chars(pbase(), n, Z_SYNC_FLUSH);
265  pbump(-(int)n);
266  }
267 
268  _dest->flush();
269  return 0;
270 }
271 
272 /**
273  * Called by the system istream implementation when its internal buffer needs
274  * more characters.
275  */
276 int ZStreamBuf::
277 underflow() {
278  // Sometimes underflow() is called even if the buffer is not empty.
279  if (gptr() >= egptr()) {
280  size_t buffer_size = egptr() - eback();
281  gbump(-(int)buffer_size);
282 
283  size_t num_bytes = buffer_size;
284  size_t read_count = read_chars(gptr(), buffer_size);
285 
286  if (read_count != num_bytes) {
287  // Oops, we didn't read what we thought we would.
288  if (read_count == 0) {
289  gbump(num_bytes);
290  return EOF;
291  }
292 
293  // Slide what we did read to the top of the buffer.
294  nassertr(read_count < num_bytes, EOF);
295  size_t delta = num_bytes - read_count;
296  memmove(gptr() + delta, gptr(), read_count);
297  gbump(delta);
298  }
299  }
300 
301  return (unsigned char)*gptr();
302 }
303 
304 
305 /**
306  * Gets some characters from the source stream.
307  */
308 size_t ZStreamBuf::
309 read_chars(char *start, size_t length) {
310  _z_source.next_out = (Bytef *)start;
311  _z_source.avail_out = length;
312 
313  bool eof = (_source->eof() || _source->fail());
314  int flush = 0;
315 
316  while (_z_source.avail_out > 0) {
317  if (_z_source.avail_in == 0 && !eof) {
318  _source->read(decompress_buffer, decompress_buffer_size);
319  size_t read_count = _source->gcount();
320  eof = (read_count == 0 || _source->eof() || _source->fail());
321 
322  _z_source.next_in = (Bytef *)decompress_buffer;
323  _z_source.avail_in = read_count;
324  }
325  int result = inflate(&_z_source, flush);
326  thread_consider_yield();
327  size_t bytes_read = length - _z_source.avail_out;
328 
329  if (result == Z_STREAM_END) {
330  // Here's the end of the file.
331  return bytes_read;
332 
333  } else if (result == Z_BUF_ERROR && flush == 0) {
334  // We might get this if no progress is possible, for instance if the
335  // input stream is truncated. In this case, tell zlib to dump
336  // everything it's got.
337  flush = Z_FINISH;
338 
339  } else if (result < 0) {
340  show_zlib_error("inflate", result, _z_source);
341  return bytes_read;
342  }
343  }
344 
345  return length;
346 }
347 
348 /**
349  * Sends some characters to the dest stream. The flush parameter is passed to
350  * deflate().
351  */
352 void ZStreamBuf::
353 write_chars(const char *start, size_t length, int flush) {
354  static const size_t compress_buffer_size = 4096;
355  char compress_buffer[compress_buffer_size];
356 
357  _z_dest.next_in = (Bytef *)(char *)start;
358  _z_dest.avail_in = length;
359 
360  _z_dest.next_out = (Bytef *)compress_buffer;
361  _z_dest.avail_out = compress_buffer_size;
362 
363  int result = deflate(&_z_dest, flush);
364  if (result < 0 && result != Z_BUF_ERROR) {
365  show_zlib_error("deflate", result, _z_dest);
366  }
367  thread_consider_yield();
368 
369  while (_z_dest.avail_in != 0) {
370  if (_z_dest.avail_out != compress_buffer_size) {
371  _dest->write(compress_buffer, compress_buffer_size - _z_dest.avail_out);
372  _z_dest.next_out = (Bytef *)compress_buffer;
373  _z_dest.avail_out = compress_buffer_size;
374  }
375  result = deflate(&_z_dest, flush);
376  if (result < 0) {
377  show_zlib_error("deflate", result, _z_dest);
378  }
379  thread_consider_yield();
380  }
381 
382  while (_z_dest.avail_out != compress_buffer_size) {
383  _dest->write(compress_buffer, compress_buffer_size - _z_dest.avail_out);
384  _z_dest.next_out = (Bytef *)compress_buffer;
385  _z_dest.avail_out = compress_buffer_size;
386  result = deflate(&_z_dest, flush);
387  if (result < 0 && result != Z_BUF_ERROR) {
388  show_zlib_error("deflate", result, _z_dest);
389  }
390  thread_consider_yield();
391  }
392 }
393 
394 /**
395  * Reports a recent error code returned by zlib.
396  */
397 void ZStreamBuf::
398 show_zlib_error(const char *function, int error_code, z_stream &z) {
399  std::stringstream error_line;
400 
401  error_line
402  << "zlib error in " << function << ": ";
403  switch (error_code) {
404  case Z_OK:
405  error_line << "Z_OK";
406  break;
407  case Z_STREAM_END:
408  error_line << "Z_STREAM_END";
409  break;
410  case Z_NEED_DICT:
411  error_line << "Z_NEED_DICT";
412  break;
413  case Z_ERRNO:
414  error_line << "Z_ERRNO";
415  break;
416  case Z_STREAM_ERROR:
417  error_line << "Z_STREAM_ERROR";
418  break;
419  case Z_DATA_ERROR:
420  error_line << "Z_DATA_ERROR";
421  break;
422  case Z_MEM_ERROR:
423  error_line << "Z_MEM_ERROR";
424  break;
425  case Z_BUF_ERROR:
426  error_line << "Z_BUF_ERROR";
427  break;
428  case Z_VERSION_ERROR:
429  error_line << "Z_VERSION_ERROR";
430  break;
431  default:
432  error_line << error_code;
433  }
434  if (z.msg != nullptr) {
435  error_line
436  << " = " << z.msg;
437  }
438 
439  express_cat.warning() << error_line.str() << "\n";
440 }
441 
442 #endif // HAVE_ZLIB
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.