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