Panda3D
Loading...
Searching...
No Matches
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
21using std::ios;
22using std::streamoff;
23using std::streampos;
24
25#if !defined(USE_MEMORY_NOWRAPPERS) && !defined(CPPPARSER)
26// Define functions that hook zlib into panda's memory allocation system.
27static void *
28do_zlib_alloc(voidpf opaque, uInt items, uInt size) {
29 return PANDA_MALLOC_ARRAY(items * size);
30}
31static void
32do_zlib_free(voidpf opaque, voidpf address) {
33 PANDA_FREE_ARRAY(address);
34}
35#endif // !USE_MEMORY_NOWRAPPERS
36
37/**
38 *
39 */
40ZStreamBuf::
41ZStreamBuf() {
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 */
63ZStreamBuf::
64~ZStreamBuf() {
65 close_read();
66 close_write();
67#ifdef PHAVE_IOSTREAM
68 PANDA_FREE_ARRAY(_buffer);
69#endif
70}
71
72/**
73 *
74 */
75void ZStreamBuf::
76open_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 */
105void ZStreamBuf::
106close_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 */
126void ZStreamBuf::
127open_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 */
156void ZStreamBuf::
157close_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 */
181streampos ZStreamBuf::
182seekoff(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 if (_source->rdbuf()->pubseekpos(0, ios::in) == (streampos)0) {
206 _source->clear();
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 */
225streampos ZStreamBuf::
226seekpos(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 */
234int ZStreamBuf::
235overflow(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 */
255int ZStreamBuf::
256sync() {
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 _dest->flush();
267 }
268
269 return 0;
270}
271
272/**
273 * Called by the system istream implementation when its internal buffer needs
274 * more characters.
275 */
276int ZStreamBuf::
277underflow() {
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 */
308size_t ZStreamBuf::
309read_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 */
352void ZStreamBuf::
353write_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 */
397void ZStreamBuf::
398show_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.