CPPMyth
Library to interoperate with MythTV server
Loading...
Searching...
No Matches
compressor.cpp
1/*
2 * Copyright (C) 2016 Jean-Luc Barriere
3 *
4 * This library is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published
6 * by the Free Software Foundation; either version 3, or (at your option)
7 * any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this library; see the file COPYING. If not, write to
16 * the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston,
17 * MA 02110-1301 USA
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 */
21
22#include "compressor.h"
23
24#if HAVE_ZLIB
25#include <zlib.h>
26#define DFLT_WINDOWS_BIT (-MAX_WBITS)
27#define GZIP_WINDOWS_BIT ((MAX_WBITS)+16)
28#define GZIP_CHUNK_SIZE 16384
29#else
30#define Z_NO_FLUSH 0
31#define Z_OK 0
32#define Z_STREAM_END 1
33#define Z_STREAM_ERROR (-2)
34#define Z_BUF_ERROR (-5)
35#define DFLT_WINDOWS_BIT 0
36#define GZIP_WINDOWS_BIT 0
37#define GZIP_CHUNK_SIZE 0
38#endif
39
40#define MIN(a,b) (a > b ? b : a)
41#define MAX(a,b) (a > b ? a : b)
42
43using namespace NSROOT;
44
45Compressor::Compressor(const char *input, size_t len, bool gzip, int level /*= -1*/)
46: m_status(Z_STREAM_ERROR)
47, m_flush(Z_NO_FLUSH)
48, m_stop(true)
49, m_chunk_size(GZIP_CHUNK_SIZE)
50, m_type_in(MEM_BUFFER)
51, m_input_len(len)
52, m_input(input)
53, m_rstream(0)
54, m_rstream_hdl(0)
55, m_rstream_buf(0)
56, m_output(0)
57, m_output_pos(0)
58, m_output_len(0)
59, _opaque(0)
60{
61#if HAVE_ZLIB
62 m_output = new char[m_chunk_size];
63 _opaque = new z_stream;
64 m_status = _init(_opaque, m_output, m_chunk_size,
65 (gzip ? GZIP_WINDOWS_BIT : DFLT_WINDOWS_BIT),
66 level);
67 m_stop = (m_status != Z_OK);
68#endif
69}
70
71Compressor::Compressor(STREAM_READER reader, void *handle, bool gzip, int level /*= -1*/)
72: m_status(Z_STREAM_ERROR)
73, m_flush(Z_NO_FLUSH)
74, m_stop(true)
75, m_chunk_size(GZIP_CHUNK_SIZE)
76, m_type_in(FCB_READER)
77, m_input_len(0)
78, m_input(0)
79, m_rstream(reader)
80, m_rstream_hdl(handle)
81, m_rstream_buf(0)
82, m_output(0)
83, m_output_pos(0)
84, m_output_len(0)
85, _opaque(0)
86{
87#if HAVE_ZLIB
88 m_rstream_buf = new char[m_chunk_size];
89 m_output = new char[m_chunk_size];
90 _opaque = new z_stream;
91 m_status = _init(_opaque, m_output, m_chunk_size,
92 (gzip ? GZIP_WINDOWS_BIT : DFLT_WINDOWS_BIT),
93 level);
94 m_stop = (m_status != Z_OK);
95#endif
96}
97
98Compressor::~Compressor()
99{
100#if HAVE_ZLIB
101 z_stream *strm = static_cast<z_stream*>(_opaque);
102 if (strm)
103 {
104 deflateEnd(strm);
105 delete strm;
106 }
107 if (m_output)
108 delete [] m_output;
109 m_output = nullptr;
110 if (m_rstream_buf)
111 delete [] m_rstream_buf;
112 m_rstream_buf = nullptr;
113#endif
114}
115
117{
118 return (m_status == Z_STREAM_END);
119}
120
122{
123 return (m_status == Z_BUF_ERROR);
124}
125
127{
128 switch(m_status)
129 {
130 case Z_OK:
131 case Z_STREAM_END:
132 case Z_BUF_ERROR:
133 return false;
134 default:
135 return true;
136 }
137}
138
139size_t Compressor::ReadOutput(char *buf, size_t len)
140{
141 size_t out = 0;
142#if HAVE_ZLIB
143 while (len)
144 {
145 if (m_output_len)
146 {
147 size_t sz = MIN(m_output_len, len);
148 memcpy(buf, m_output + m_output_pos, sz);
149 out += sz;
150 buf += sz;
151 len -= sz;
152 m_output_pos += sz;
153 m_output_len -= sz;
154 }
155 else if (m_status != Z_STREAM_END)
156 {
157 z_stream *strm = static_cast<z_stream*>(_opaque);
158 if (!strm->avail_in)
159 NextChunk();
160 if (!strm->avail_out)
161 {
162 strm->next_out = (unsigned char*)m_output;
163 strm->avail_out = m_chunk_size;
164 m_output_pos = 0;
165 }
166 // Try to deflate chunk
167 m_status = deflate(strm, m_flush);
168 if (m_status < 0)
169 {
170 m_stop = true;
171 return 0;
172 }
173 m_output_len = m_chunk_size - m_output_pos - strm->avail_out;
174 m_stop = false;
175 }
176 else
177 {
178 m_stop = true;
179 break;
180 }
181 }
182#endif
183 return out;
184}
185
186size_t Compressor::FetchOutput(const char **data)
187{
188 *data = 0;
189#if HAVE_ZLIB
190 size_t len = 0;
191 while (!m_stop)
192 {
193 if (m_output_len)
194 {
195 *data = m_output + m_output_pos;
196 len = m_output_len;
197 m_output_pos += m_output_len;
198 m_output_len = 0;
199 return len;
200 }
201 else if (m_status != Z_STREAM_END)
202 {
203 z_stream *strm = static_cast<z_stream*>(_opaque);
204 if (!strm->avail_in)
205 NextChunk();
206 if (!strm->avail_out)
207 {
208 strm->next_out = (unsigned char*)m_output;
209 strm->avail_out = m_chunk_size;
210 m_output_pos = 0;
211 }
212 // Try to inflate chunk
213 m_status = deflate(strm, m_flush);
214 if (m_status < 0)
215 {
216 m_stop = true;
217 return 0;
218 }
219 m_output_len = m_chunk_size - m_output_pos - strm->avail_out;
220 m_stop = false;
221 }
222 else
223 {
224 m_stop = true;
225 break;
226 }
227 }
228#endif
229 return 0;
230}
231
232int Compressor::_init(void *zp, void *out, size_t len, int wbits, int level)
233{
234#if HAVE_ZLIB
235 z_stream *strm = static_cast<z_stream*>(zp);
236 // Prepare inflater status
237 strm->zalloc = Z_NULL;
238 strm->zfree = Z_NULL;
239 strm->opaque = Z_NULL;
240 strm->avail_in = 0;
241 strm->next_in = Z_NULL;
242 strm->avail_out = len;
243 strm->next_out = (unsigned char*)out;
244 return deflateInit2(strm, MAX(-1, MIN(level, 9)), Z_DEFLATED, wbits, 8, Z_DEFAULT_STRATEGY);
245#else
246 return Z_STREAM_ERROR;
247#endif
248}
249
250size_t Compressor::NextChunk()
251{
252 size_t sz = 0;
253#if HAVE_ZLIB
254 if (m_flush != Z_FINISH)
255 {
256 z_stream *strm = static_cast<z_stream*>(_opaque);
257 switch(m_type_in)
258 {
259 case MEM_BUFFER:
260 // Determine current chunk size
261 sz = MIN(m_chunk_size, m_input_len);
262 // Check for end of data
263 if (sz)
264 {
265 // Set stream
266 strm->next_in = (unsigned char*)m_input;
267 strm->avail_in = (unsigned)sz;
268 // Update next chunk
269 m_input += sz;
270 m_input_len -= sz;
271 // flush on last chunk
272 m_flush = (m_input_len > 0 ? Z_NO_FLUSH : Z_FINISH);
273 }
274 break;
275 case FCB_READER:
276 {
277 int ret = (*m_rstream)(m_rstream_hdl, (void*)m_rstream_buf, m_chunk_size);
278 if (ret < 0)
279 sz = 0;
280 else
281 {
282 m_flush = (ret > 0 ? Z_NO_FLUSH : Z_FINISH);
283 sz = ret;
284 }
285 // Set stream
286 strm->next_in = (unsigned char*)m_rstream_buf;
287 strm->avail_in = (unsigned)sz;
288 }
289 break;
290 default:
291 break;
292 }
293 }
294#endif
295 return sz;
296}
297
298Decompressor::Decompressor(const char *input, size_t len, bool gzip)
299: m_status(Z_STREAM_ERROR)
300, m_stop(true)
301, m_chunk_size(GZIP_CHUNK_SIZE)
302, m_type_in(MEM_BUFFER)
303, m_input_len(len)
304, m_input(input)
305, m_rstream(0)
306, m_rstream_hdl(0)
307, m_rstream_buf(0)
308, m_output(0)
309, m_output_pos(0)
310, m_output_len(0)
311, _opaque(0)
312{
313#if HAVE_ZLIB
314 m_output = new char[m_chunk_size];
315 _opaque = new z_stream;
316 m_status = _init(_opaque, m_output, m_chunk_size,
317 (gzip ? GZIP_WINDOWS_BIT : DFLT_WINDOWS_BIT));
318 m_stop = (m_status != Z_OK);
319#endif
320}
321
322Decompressor::Decompressor(STREAM_READER reader, void *handle, bool gzip)
323: m_status(Z_STREAM_ERROR)
324, m_stop(true)
325, m_chunk_size(GZIP_CHUNK_SIZE)
326, m_type_in(FCB_READER)
327, m_input_len(0)
328, m_input(0)
329, m_rstream(reader)
330, m_rstream_hdl(handle)
331, m_rstream_buf(0)
332, m_output(0)
333, m_output_pos(0)
334, m_output_len(0)
335, _opaque(0)
336{
337#if HAVE_ZLIB
338 m_rstream_buf = new char[m_chunk_size];
339 m_output = new char[m_chunk_size];
340 _opaque = new z_stream;
341 m_status = _init(_opaque, m_output, m_chunk_size,
342 (gzip ? GZIP_WINDOWS_BIT : DFLT_WINDOWS_BIT));
343 m_stop = (m_status != Z_OK);
344#endif
345}
346
347Decompressor::~Decompressor()
348{
349#if HAVE_ZLIB
350 z_stream *strm = static_cast<z_stream*>(_opaque);
351 if (strm)
352 {
353 inflateEnd(strm);
354 delete strm;
355 }
356 if (m_output)
357 delete [] m_output;
358 m_output = nullptr;
359 if (m_rstream_buf)
360 delete [] m_rstream_buf;
361 m_rstream_buf = nullptr;
362#endif
363}
364
366{
367 return (m_status == Z_STREAM_END);
368}
369
371{
372 return (m_status == Z_BUF_ERROR);
373}
374
376{
377 switch(m_status)
378 {
379 case Z_OK:
380 case Z_STREAM_END:
381 case Z_BUF_ERROR:
382 return false;
383 default:
384 return true;
385 }
386}
387
388size_t Decompressor::ReadOutput(char *buf, size_t len)
389{
390 size_t out = 0;
391#if HAVE_ZLIB
392 while (len)
393 {
394 if (m_output_len)
395 {
396 size_t sz = MIN(m_output_len, len);
397 memcpy(buf, m_output + m_output_pos, sz);
398 out += sz;
399 buf += sz;
400 len -= sz;
401 m_output_pos += sz;
402 m_output_len -= sz;
403 }
404 else if (m_status != Z_STREAM_END)
405 {
406 z_stream *strm = static_cast<z_stream*>(_opaque);
407 if (!strm->avail_in)
408 NextChunk();
409 if (!strm->avail_out)
410 {
411 strm->next_out = (unsigned char*)m_output;
412 strm->avail_out = m_chunk_size;
413 m_output_pos = 0;
414 }
415 // Try to inflate chunk
416 m_status = inflate(strm, Z_NO_FLUSH);
417 if (m_status < 0)
418 {
419 m_stop = true;
420 return 0;
421 }
422 m_output_len = m_chunk_size - m_output_pos - strm->avail_out;
423 m_stop = false;
424 }
425 else
426 {
427 m_stop = true;
428 break;
429 }
430 }
431#endif
432 return out;
433}
434
435size_t Decompressor::FetchOutput(const char **data)
436{
437 *data = 0;
438#if HAVE_ZLIB
439 size_t len = 0;
440 do
441 {
442 if (m_output_len)
443 {
444 *data = m_output + m_output_pos;
445 len = m_output_len;
446 m_output_pos += m_output_len;
447 m_output_len = 0;
448 return len;
449 }
450 else if (m_status != Z_STREAM_END)
451 {
452 z_stream *strm = static_cast<z_stream*>(_opaque);
453 if (!strm->avail_in)
454 NextChunk();
455 if (!strm->avail_out)
456 {
457 strm->next_out = (unsigned char*)m_output;
458 strm->avail_out = m_chunk_size;
459 m_output_pos = 0;
460 }
461 // Try to inflate chunk
462 m_status = inflate(strm, Z_NO_FLUSH);
463 if (m_status < 0)
464 {
465 m_stop = true;
466 return 0;
467 }
468 m_output_len = m_chunk_size - m_output_pos - strm->avail_out;
469 m_stop = false;
470 }
471 else
472 {
473 m_stop = true;
474 break;
475 }
476 } while (!m_stop);
477#endif
478 return 0;
479}
480
481int Decompressor::_init(void *zp, void *out, size_t len, int wbits)
482{
483#if HAVE_ZLIB
484 z_stream *strm = static_cast<z_stream*>(zp);
485 // Prepare inflater status
486 strm->zalloc = Z_NULL;
487 strm->zfree = Z_NULL;
488 strm->opaque = Z_NULL;
489 strm->avail_in = 0;
490 strm->next_in = Z_NULL;
491 strm->avail_out = len;
492 strm->next_out = (unsigned char*)out;
493 return inflateInit2(strm, wbits);
494#else
495 return Z_STREAM_ERROR;
496#endif
497}
498
499size_t Decompressor::NextChunk()
500{
501 size_t sz = 0;
502#if HAVE_ZLIB
503 z_stream *strm = static_cast<z_stream*>(_opaque);
504 switch(m_type_in)
505 {
506 case MEM_BUFFER:
507 // Determine current chunk size
508 sz = MIN(m_chunk_size, m_input_len);
509 // Check for end of data
510 if (sz)
511 {
512 // Set stream
513 strm->next_in = (unsigned char*)m_input;
514 strm->avail_in = (unsigned)sz;
515 // Update next chunk
516 m_input += sz;
517 m_input_len -= sz;
518 }
519 break;
520 case FCB_READER:
521 {
522 int ret = (*m_rstream)(m_rstream_hdl, (void*)m_rstream_buf, m_chunk_size);
523 if (ret > 0)
524 sz = ret;
525 else
526 sz = 0;
527 // Set stream
528 strm->next_in = (unsigned char*)m_rstream_buf;
529 strm->avail_in = (unsigned)sz;
530 }
531 break;
532 default:
533 break;
534 }
535#endif
536 return sz;
537}
bool IsCompleted()
Output stream is completed.
bool HasStreamError()
Data error occurred from stream.
bool HasBufferError()
Data cannot be read from input.
size_t ReadOutput(char *buf, size_t len)
Copy data from output stream to the given pointer until size limit.
size_t FetchOutput(const char **data)
Fetch next chunk of data from output stream. No copy of data is performed and result can be used as i...
bool HasBufferError()
Data cannot be read from input.
size_t ReadOutput(char *buf, size_t len)
Copy data from output stream to the given pointer until size limit.
bool IsCompleted()
Output stream is completed.
bool HasStreamError()
Data error occurred from stream.
size_t FetchOutput(const char **data)
Fetch next chunk of data from output stream. No copy of data is performed and result can be used as i...