VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/misc/zip.cpp@ 17141

Last change on this file since 17141 was 16823, checked in by vboxsync, 16 years ago

Runtime/zip: make zlib reader more greedy to fix issue with incompressible blocks which are slightly larger than the input buffer in a more sane way.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 43.1 KB
Line 
1/* $Id: zip.cpp 16823 2009-02-17 10:41:45Z vboxsync $ */
2/** @file
3 * IPRT - Compression.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 *
26 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
27 * Clara, CA 95054 USA or visit http://www.sun.com if you need
28 * additional information or have any questions.
29 */
30
31
32/*******************************************************************************
33* Defined Constants And Macros *
34*******************************************************************************/
35#define RTZIP_USE_STORE 1
36#define RTZIP_USE_ZLIB 1
37//#define RTZIP_USE_BZLIB 1
38#define RTZIP_USE_LZF 1
39
40
41/*******************************************************************************
42* Header Files *
43*******************************************************************************/
44#ifdef RTZIP_USE_BZLIB
45# include <bzlib.h>
46#endif
47#ifdef RTZIP_USE_ZLIB
48# include <zlib.h>
49#endif
50#ifdef RTZIP_USE_LZF
51# include <lzf.h>
52# include <iprt/crc32.h>
53#endif
54
55#include <iprt/zip.h>
56#include <iprt/alloc.h>
57#include <iprt/assert.h>
58#include <iprt/err.h>
59#include <iprt/log.h>
60#include <iprt/string.h>
61
62#include <errno.h>
63
64
65/*******************************************************************************
66* Structures and Typedefs *
67*******************************************************************************/
68
69#ifdef RTZIP_USE_LZF
70
71/**
72 * LZF block header.
73 */
74#pragma pack(1) /* paranoia */
75typedef struct RTZIPLZFHDR
76{
77 /** Magic word (RTZIPLZFHDR_MAGIC). */
78 uint16_t u16Magic;
79 /** The number of bytes of data following this header. */
80 uint16_t cbData;
81 /** The CRC32 of the block. */
82 uint32_t u32CRC;
83 /** The size of the uncompressed data in bytes. */
84 uint16_t cbUncompressed;
85} RTZIPLZFHDR;
86#pragma pack()
87/** Pointer to a LZF block header. */
88typedef RTZIPLZFHDR *PRTZIPLZFHDR;
89/** Pointer to a const LZF block header. */
90typedef const RTZIPLZFHDR *PCRTZIPLZFHDR;
91
92/** The magic of a LZF block header. */
93#define RTZIPLZFHDR_MAGIC ('Z' | ('V' << 8))
94
95/** The max compressed data size.
96 * The maximum size of a block is currently 16KB.
97 * This is very important so we don't have to move input buffers around. */
98#define RTZIPLZF_MAX_DATA_SIZE (16384 - sizeof(RTZIPLZFHDR))
99
100/** The max uncompressed data size.
101 * This is important so we don't overflow the spill buffer in the decompressor. */
102#define RTZIPLZF_MAX_UNCOMPRESSED_DATA_SIZE (32*_1K)
103
104#endif /* RTZIP_USE_LZF */
105
106
107/**
108 * Compressor/Decompressor instance data.
109 */
110typedef struct RTZIPCOMP
111{
112 /** Output buffer. */
113#ifdef RTZIP_USE_LZF
114 uint8_t abBuffer[128 * 1024];
115#else
116 uint8_t abBuffer[64 * 1024];
117#endif
118 /** Compression output consumer. */
119 PFNRTZIPOUT pfnOut;
120 /** User argument for the callback. */
121 void *pvUser;
122
123 /**
124 * @copydoc RTZipCompress
125 */
126 DECLCALLBACKMEMBER(int, pfnCompress)(PRTZIPCOMP pZip, const void *pvBuf, size_t cbBuf);
127
128 /**
129 * @copydoc RTZipCompFinish
130 */
131 DECLCALLBACKMEMBER(int, pfnFinish)(PRTZIPCOMP pZip);
132
133 /**
134 * @copydoc RTZipCompDestroy
135 */
136 DECLCALLBACKMEMBER(int, pfnDestroy)(PRTZIPCOMP pZip);
137
138 /** Compression type. */
139 RTZIPTYPE enmType;
140 /** Type specific data. */
141 union
142 {
143#ifdef RTZIP_USE_STORE
144 /** Simple storing. */
145 struct
146 {
147 /** Current buffer postition. (where to start write) */
148 uint8_t *pb;
149 } Store;
150#endif
151#ifdef RTZIP_USE_ZLIB
152 /** Zlib stream. */
153 z_stream Zlib;
154#endif
155#ifdef RTZIP_USE_BZLIB
156 /** BZlib stream. */
157 bz_stream BZlib;
158#endif
159#ifdef RTZIP_USE_LZF
160 /** LZF stream. */
161 struct
162 {
163 /** Current output buffer postition. */
164 uint8_t *pbOutput;
165 /** The input buffer position. */
166 uint8_t *pbInput;
167 /** The number of free bytes in the input buffer. */
168 size_t cbInputFree;
169 /** The input buffer. */
170 uint8_t abInput[RTZIPLZF_MAX_UNCOMPRESSED_DATA_SIZE];
171 } LZF;
172#endif
173
174 } u;
175} RTZIPCOMP;
176
177
178
179/**
180 * Decompressor instance data.
181 */
182typedef struct RTZIPDECOMP
183{
184 /** Input buffer. */
185 uint8_t abBuffer[1024 * 64];
186 /** Decompression input producer. */
187 PFNRTZIPIN pfnIn;
188 /** User argument for the callback. */
189 void *pvUser;
190
191 /**
192 * @copydoc RTZipDecompress
193 */
194 DECLCALLBACKMEMBER(int, pfnDecompress)(PRTZIPDECOMP pZip, void *pvBuf, size_t cbBuf, size_t *pcbWritten);
195
196 /**
197 * @copydoc RTZipDecompDestroy
198 */
199 DECLCALLBACKMEMBER(int, pfnDestroy)(PRTZIPDECOMP pZip);
200
201 /** Compression type. */
202 RTZIPTYPE enmType;
203 /** Type specific data. */
204 union
205 {
206#ifdef RTZIP_USE_STORE
207 /** Simple storing. */
208 struct
209 {
210 /** Current buffer postition. (where to start read) */
211 uint8_t *pb;
212 /** Number of bytes left in the buffer. */
213 size_t cbBuffer;
214 } Store;
215#endif
216#ifdef RTZIP_USE_ZLIB
217 /** Zlib stream. */
218 z_stream Zlib;
219#endif
220#ifdef RTZIP_USE_BZLIB
221 /** BZlib stream. */
222 bz_stream BZlib;
223#endif
224#ifdef RTZIP_USE_LZF
225 /** LZF 'stream'. */
226 struct
227 {
228 /** Current input buffer postition. */
229 uint8_t *pbInput;
230 /** The number of bytes left in the input buffer. */
231 size_t cbInput;
232 /** The spill buffer.
233 * LZF is a block based compressor and not a stream compressor. So,
234 * we have to decompress full blocks if we want to get any of the data.
235 * This buffer is to store the spill after decompressing a block. */
236 uint8_t abSpill[RTZIPLZF_MAX_UNCOMPRESSED_DATA_SIZE];
237 /** The number of bytes left spill buffer. */
238 unsigned cbSpill;
239 /** The current spill buffer position. */
240 uint8_t *pbSpill;
241 } LZF;
242#endif
243
244 } u;
245} RTZIPDECOM;
246
247
248
249#ifdef RTZIP_USE_STORE
250#include <stdio.h>
251
252/**
253 * @copydoc RTZipCompress
254 */
255static DECLCALLBACK(int) rtZipStoreCompress(PRTZIPCOMP pZip, const void *pvBuf, size_t cbBuf)
256{
257 uint8_t *pbDst = pZip->u.Store.pb;
258 while (cbBuf)
259 {
260 /*
261 * Flush.
262 */
263 size_t cb = sizeof(pZip->abBuffer) - (size_t)(pbDst - &pZip->abBuffer[0]); /* careful here, g++ 4.1.2 screws up easily */
264 if (cb == 0)
265 {
266 int rc = pZip->pfnOut(pZip->pvUser, &pZip->abBuffer[0], sizeof(pZip->abBuffer));
267 if (RT_FAILURE(rc))
268 return rc;
269
270 cb = sizeof(pZip->abBuffer);
271 pbDst = &pZip->abBuffer[0];
272 }
273
274 /*
275 * Add to the buffer and advance.
276 */
277 if (cbBuf < cb)
278 cb = cbBuf;
279 memcpy(pbDst, pvBuf, cb);
280
281 pbDst += cb;
282 cbBuf -= cb;
283 pvBuf = (uint8_t *)pvBuf + cb;
284 }
285 pZip->u.Store.pb = pbDst;
286 return VINF_SUCCESS;
287}
288
289
290/**
291 * @copydoc RTZipCompFinish
292 */
293static DECLCALLBACK(int) rtZipStoreCompFinish(PRTZIPCOMP pZip)
294{
295 size_t cb = (uintptr_t)pZip->u.Store.pb - (uintptr_t)&pZip->abBuffer[0];
296 if (cb > 0)
297 {
298 int rc = pZip->pfnOut(pZip->pvUser, &pZip->abBuffer[0], cb);
299 if (RT_FAILURE(rc))
300 return rc;
301 }
302 return VINF_SUCCESS;
303}
304
305
306/**
307 * @copydoc RTZipCompDestroy
308 */
309static DECLCALLBACK(int) rtZipStoreCompDestroy(PRTZIPCOMP pZip)
310{
311 return VINF_SUCCESS;
312}
313
314
315/**
316 * Initializes the compressor instance.
317 * @returns iprt status code.
318 * @param pZip The compressor instance.
319 * @param enmLevel The desired compression level.
320 */
321static DECLCALLBACK(int) rtZipStoreCompInit(PRTZIPCOMP pZip, RTZIPLEVEL enmLevel)
322{
323 pZip->pfnCompress = rtZipStoreCompress;
324 pZip->pfnFinish = rtZipStoreCompFinish;
325 pZip->pfnDestroy = rtZipStoreCompDestroy;
326
327 pZip->u.Store.pb = &pZip->abBuffer[1];
328 return VINF_SUCCESS;
329}
330
331
332/**
333 * @copydoc RTZipDecompress
334 */
335static DECLCALLBACK(int) rtZipStoreDecompress(PRTZIPDECOMP pZip, void *pvBuf, size_t cbBuf, size_t *pcbWritten)
336{
337 size_t cbWritten = 0;
338 while (cbBuf)
339 {
340 /*
341 * Fill buffer.
342 */
343 size_t cb = pZip->u.Store.cbBuffer;
344 if (cb <= 0)
345 {
346 int rc = pZip->pfnIn(pZip->pvUser, &pZip->abBuffer[0], sizeof(pZip->abBuffer), &cb);
347 if (RT_FAILURE(rc))
348 return rc;
349 pZip->u.Store.cbBuffer = cb;
350 pZip->u.Store.pb = &pZip->abBuffer[0];
351 }
352
353 /*
354 * No more data?
355 */
356 if (cb == 0)
357 {
358 if (pcbWritten)
359 {
360 *pcbWritten = cbWritten;
361 return VINF_SUCCESS;
362 }
363 return VERR_NO_DATA;
364 }
365
366 /*
367 * Add to the buffer and advance.
368 */
369 if (cbBuf < cb)
370 cb = cbBuf;
371 memcpy(pvBuf, pZip->u.Store.pb, cb);
372 pZip->u.Store.pb += cb;
373 pZip->u.Store.cbBuffer -= cb;
374 cbBuf -= cb;
375 pvBuf = (char *)pvBuf + cb;
376 cbWritten += cb;
377 }
378 if (pcbWritten)
379 *pcbWritten = cbWritten;
380 return VINF_SUCCESS;
381}
382
383
384/**
385 * @copydoc RTZipDecompDestroy
386 */
387static DECLCALLBACK(int) rtZipStoreDecompDestroy(PRTZIPDECOMP pZip)
388{
389 return VINF_SUCCESS;
390}
391
392
393/**
394 * Initialize the decompressor instance.
395 * @returns iprt status code.
396 * @param pZip The decompressor instance.
397 */
398static DECLCALLBACK(int) rtZipStoreDecompInit(PRTZIPDECOMP pZip)
399{
400 pZip->pfnDecompress = rtZipStoreDecompress;
401 pZip->pfnDestroy = rtZipStoreDecompDestroy;
402
403 pZip->u.Store.pb = &pZip->abBuffer[0];
404 pZip->u.Store.cbBuffer = 0;
405 return VINF_SUCCESS;
406}
407
408#endif
409
410
411#ifdef RTZIP_USE_ZLIB
412/**
413 * Convert from zlib errno to iprt status code.
414 * @returns iprt status code.
415 * @param rc Zlib error code.
416 */
417static int zipErrConvertFromZlib(int rc)
418{
419 /** @todo proper zlib error convertion. */
420 switch (rc)
421 {
422 case Z_ERRNO:
423 return RTErrConvertFromErrno(errno);
424 case Z_STREAM_ERROR:
425 case Z_DATA_ERROR:
426 case Z_MEM_ERROR:
427 case Z_BUF_ERROR:
428 case Z_VERSION_ERROR:
429 return VERR_GENERAL_FAILURE;
430 default:
431 if (rc >= 0)
432 return VINF_SUCCESS;
433 return VERR_GENERAL_FAILURE;
434 }
435}
436
437
438/**
439 * @copydoc RTZipCompress
440 */
441static DECLCALLBACK(int) rtZipZlibCompress(PRTZIPCOMP pZip, const void *pvBuf, size_t cbBuf)
442{
443 pZip->u.Zlib.next_in = (Bytef *)pvBuf;
444 pZip->u.Zlib.avail_in = cbBuf;
445 while (pZip->u.Zlib.avail_in > 0)
446 {
447 /*
448 * Flush output buffer?
449 */
450 if (pZip->u.Zlib.avail_out <= 0)
451 {
452 int rc = pZip->pfnOut(pZip->pvUser, &pZip->abBuffer[0], sizeof(pZip->abBuffer) - pZip->u.Zlib.avail_out);
453 if (RT_FAILURE(rc))
454 return rc;
455 pZip->u.Zlib.avail_out = sizeof(pZip->abBuffer);
456 pZip->u.Zlib.next_out = &pZip->abBuffer[0];
457 }
458
459 /*
460 * Pass it on to zlib.
461 */
462 int rc = deflate(&pZip->u.Zlib, Z_NO_FLUSH);
463 if (rc != Z_OK)
464 return zipErrConvertFromZlib(rc);
465 }
466 return VINF_SUCCESS;
467}
468
469
470/**
471 * @copydoc RTZipCompFinish
472 */
473static DECLCALLBACK(int) rtZipZlibCompFinish(PRTZIPCOMP pZip)
474{
475 int rc = Z_OK;
476 for (;;)
477 {
478 /*
479 * Flush outstanding stuff. writes.
480 */
481 if (rc == Z_STREAM_END || pZip->u.Zlib.avail_out <= 0)
482 {
483 int rc2 = pZip->pfnOut(pZip->pvUser, &pZip->abBuffer[0], sizeof(pZip->abBuffer) - pZip->u.Zlib.avail_out);
484 if (RT_FAILURE(rc2))
485 return rc2;
486 pZip->u.Zlib.avail_out = sizeof(pZip->abBuffer);
487 pZip->u.Zlib.next_out = &pZip->abBuffer[0];
488 if (rc == Z_STREAM_END)
489 return VINF_SUCCESS;
490 }
491
492 /*
493 * Tell zlib to flush.
494 */
495 rc = deflate(&pZip->u.Zlib, Z_FINISH);
496 if (rc != Z_OK && rc != Z_STREAM_END)
497 return zipErrConvertFromZlib(rc);
498 }
499 return VINF_SUCCESS;
500}
501
502
503/**
504 * @copydoc RTZipCompDestroy
505 */
506static DECLCALLBACK(int) rtZipZlibCompDestroy(PRTZIPCOMP pZip)
507{
508 /*
509 * Terminate the deflate instance.
510 */
511 int rc = deflateEnd(&pZip->u.Zlib);
512 if (rc != Z_OK)
513 rc = zipErrConvertFromZlib(rc);
514 return rc;
515}
516
517
518/**
519 * Initializes the compressor instance.
520 * @returns iprt status code.
521 * @param pZip The compressor instance.
522 * @param enmLevel The desired compression level.
523 */
524static DECLCALLBACK(int) rtZipZlibCompInit(PRTZIPCOMP pZip, RTZIPLEVEL enmLevel)
525{
526 pZip->pfnCompress = rtZipZlibCompress;
527 pZip->pfnFinish = rtZipZlibCompFinish;
528 pZip->pfnDestroy = rtZipZlibCompDestroy;
529
530 int iLevel = Z_DEFAULT_COMPRESSION;
531 switch (enmLevel)
532 {
533 case RTZIPLEVEL_STORE: iLevel = 0; break;
534 case RTZIPLEVEL_FAST: iLevel = 2; break;
535 case RTZIPLEVEL_DEFAULT: iLevel = Z_DEFAULT_COMPRESSION; break;
536 case RTZIPLEVEL_MAX: iLevel = 9; break;
537 }
538
539 memset(&pZip->u.Zlib, 0, sizeof(pZip->u.Zlib));
540 pZip->u.Zlib.next_out = &pZip->abBuffer[1];
541 pZip->u.Zlib.avail_out = sizeof(pZip->abBuffer) - 1;
542 pZip->u.Zlib.opaque = pZip;
543
544 int rc = deflateInit(&pZip->u.Zlib, enmLevel);
545 return rc >= 0 ? rc = VINF_SUCCESS : zipErrConvertFromZlib(rc);
546}
547
548
549/**
550 * @copydoc RTZipDecompress
551 */
552static DECLCALLBACK(int) rtZipZlibDecompress(PRTZIPDECOMP pZip, void *pvBuf, size_t cbBuf, size_t *pcbWritten)
553{
554 pZip->u.Zlib.next_out = (Bytef *)pvBuf;
555 pZip->u.Zlib.avail_out = cbBuf;
556 int rc = Z_OK;
557 /* Be greedy reading input, even if no output buffer is left. It's possible
558 * that it's just the end of stream marker which needs to be read. Happens
559 * for incompressible blocks just larger than the input buffer size.*/
560 while (pZip->u.Zlib.avail_out > 0 || pZip->u.Zlib.avail_in <= 0)
561 {
562 /*
563 * Read more input?
564 */
565 if (pZip->u.Zlib.avail_in <= 0)
566 {
567 size_t cb = sizeof(pZip->abBuffer);
568 int rc = pZip->pfnIn(pZip->pvUser, &pZip->abBuffer[0], sizeof(pZip->abBuffer), &cb);
569 if (RT_FAILURE(rc))
570 return rc;
571 pZip->u.Zlib.avail_in = cb;
572 pZip->u.Zlib.next_in = &pZip->abBuffer[0];
573 }
574
575 /*
576 * Pass it on to zlib.
577 */
578 rc = inflate(&pZip->u.Zlib, Z_NO_FLUSH);
579 if (rc == Z_STREAM_END)
580 {
581 if (pcbWritten)
582 *pcbWritten = cbBuf - pZip->u.Zlib.avail_out;
583 else if (pZip->u.Zlib.avail_out > 0)
584 return VERR_NO_DATA;
585 break;
586 }
587 if (rc != Z_OK)
588 return zipErrConvertFromZlib(rc);
589 }
590 return VINF_SUCCESS;
591}
592
593
594/**
595 * @copydoc RTZipDecompDestroy
596 */
597static DECLCALLBACK(int) rtZipZlibDecompDestroy(PRTZIPDECOMP pZip)
598{
599 /*
600 * Terminate the deflate instance.
601 */
602 int rc = inflateEnd(&pZip->u.Zlib);
603 if (rc != Z_OK)
604 rc = zipErrConvertFromZlib(rc);
605 return rc;
606}
607
608
609/**
610 * Initialize the decompressor instance.
611 * @returns iprt status code.
612 * @param pZip The decompressor instance.
613 */
614static DECLCALLBACK(int) rtZipZlibDecompInit(PRTZIPDECOMP pZip)
615{
616 pZip->pfnDecompress = rtZipZlibDecompress;
617 pZip->pfnDestroy = rtZipZlibDecompDestroy;
618
619 memset(&pZip->u.Zlib, 0, sizeof(pZip->u.Zlib));
620 pZip->u.Zlib.opaque = pZip;
621
622 int rc = inflateInit(&pZip->u.Zlib);
623 return rc >= 0 ? VINF_SUCCESS : zipErrConvertFromZlib(rc);
624}
625
626#endif
627
628
629#ifdef RTZIP_USE_BZLIB
630/**
631 * Convert from BZlib errno to iprt status code.
632 * @returns iprt status code.
633 * @param rc BZlib error code.
634 */
635static int zipErrConvertFromBZlib(int rc)
636{
637 /** @todo proper bzlib error convertion. */
638 switch (rc)
639 {
640 case BZ_SEQUENCE_ERROR:
641 AssertMsgFailed(("BZ_SEQUENCE_ERROR shall not happen!\n"));
642 return VERR_GENERAL_FAILURE;
643 case BZ_PARAM_ERROR:
644 return VERR_INVALID_PARAMETER;
645 case BZ_MEM_ERROR:
646 return VERR_NO_MEMORY;
647 case BZ_DATA_ERROR:
648 case BZ_DATA_ERROR_MAGIC:
649 case BZ_IO_ERROR:
650 case BZ_UNEXPECTED_EOF:
651 case BZ_CONFIG_ERROR:
652 return VERR_GENERAL_FAILURE;
653 case BZ_OUTBUFF_FULL:
654 AssertMsgFailed(("BZ_OUTBUFF_FULL shall not happen!\n"));
655 return VERR_GENERAL_FAILURE;
656 default:
657 if (rc >= 0)
658 return VINF_SUCCESS;
659 return VERR_GENERAL_FAILURE;
660 }
661}
662
663
664/**
665 * @copydoc RTZipCompress
666 */
667static DECLCALLBACK(int) rtZipBZlibCompress(PRTZIPCOMP pZip, const void *pvBuf, size_t cbBuf)
668{
669 pZip->u.BZlib.next_in = (char *)pvBuf;
670 pZip->u.BZlib.avail_in = cbBuf;
671 while (pZip->u.BZlib.avail_in > 0)
672 {
673 /*
674 * Flush output buffer?
675 */
676 if (pZip->u.BZlib.avail_out <= 0)
677 {
678 int rc = pZip->pfnOut(pZip->pvUser, &pZip->abBuffer[0], sizeof(pZip->abBuffer) - pZip->u.BZlib.avail_out);
679 if (RT_FAILURE(rc))
680 return rc;
681 pZip->u.BZlib.avail_out = sizeof(pZip->abBuffer);
682 pZip->u.BZlib.next_out = (char *)&pZip->abBuffer[0];
683 }
684
685 /*
686 * Pass it on to zlib.
687 */
688 int rc = BZ2_bzCompress(&pZip->u.BZlib, BZ_RUN);
689 if (rc < 0 && rc != BZ_OUTBUFF_FULL)
690 return zipErrConvertFromBZlib(rc);
691 }
692 return VINF_SUCCESS;
693}
694
695
696/**
697 * @copydoc RTZipCompFinish
698 */
699static DECLCALLBACK(int) rtZipBZlibCompFinish(PRTZIPCOMP pZip)
700{
701 int rc = BZ_FINISH_OK;
702 for (;;)
703 {
704 /*
705 * Flush output buffer?
706 */
707 if (rc == BZ_STREAM_END || pZip->u.BZlib.avail_out <= 0)
708 {
709 int rc2 = pZip->pfnOut(pZip->pvUser, &pZip->abBuffer[0], sizeof(pZip->abBuffer) - pZip->u.BZlib.avail_out);
710 if (RT_FAILURE(rc2))
711 return rc2;
712 pZip->u.BZlib.avail_out = sizeof(pZip->abBuffer);
713 pZip->u.BZlib.next_out = (char *)&pZip->abBuffer[0];
714 if (rc == BZ_STREAM_END)
715 return VINF_SUCCESS;
716 }
717
718 /*
719 * Tell BZlib to finish it.
720 */
721 rc = BZ2_bzCompress(&pZip->u.BZlib, BZ_FINISH);
722 if (rc < 0 && rc != BZ_OUTBUFF_FULL)
723 return zipErrConvertFromBZlib(rc);
724 }
725 return VINF_SUCCESS;
726}
727
728
729/**
730 * @copydoc RTZipCompDestroy
731 */
732static DECLCALLBACK(int) rtZipBZlibCompDestroy(PRTZIPCOMP pZip)
733{
734 /*
735 * Terminate the deflate instance.
736 */
737 int rc = BZ2_bzCompressEnd(&pZip->u.BZlib);
738 if (rc != BZ_OK)
739 rc = zipErrConvertFromBZlib(rc);
740 return rc;
741}
742
743
744/**
745 * Initializes the compressor instance.
746 * @returns iprt status code.
747 * @param pZip The compressor instance.
748 * @param enmLevel The desired compression level.
749 */
750static DECLCALLBACK(int) rtZipBZlibCompInit(PRTZIPCOMP pZip, RTZIPLEVEL enmLevel)
751{
752 pZip->pfnCompress = rtZipBZlibCompress;
753 pZip->pfnFinish = rtZipBZlibCompFinish;
754 pZip->pfnDestroy = rtZipBZlibCompDestroy;
755
756 int iSize = 6;
757 int iWork = 0;
758 switch (enmLevel)
759 {
760 case RTZIPLEVEL_STORE: iSize = 1; iWork = 2; break;
761 case RTZIPLEVEL_FAST: iSize = 2; iWork = 0; break;
762 case RTZIPLEVEL_DEFAULT: iSize = 5; iWork = 0; break;
763 case RTZIPLEVEL_MAX: iSize = 9; iWork = 0; break;
764 }
765
766 memset(&pZip->u.BZlib, 0, sizeof(pZip->u.BZlib));
767 pZip->u.BZlib.next_out = (char *)&pZip->abBuffer[1];
768 pZip->u.BZlib.avail_out = sizeof(pZip->abBuffer) - 1;
769 pZip->u.BZlib.opaque = pZip;
770
771 int rc = BZ2_bzCompressInit(&pZip->u.BZlib, iSize, 0, iWork);
772 return rc >= 0 ? VINF_SUCCESS : zipErrConvertFromBZlib(rc);;
773}
774
775
776/**
777 * @copydoc RTZipDecompress
778 */
779static DECLCALLBACK(int) rtZipBZlibDecompress(PRTZIPDECOMP pZip, void *pvBuf, size_t cbBuf, size_t *pcbWritten)
780{
781 pZip->u.BZlib.next_out = (char *)pvBuf;
782 pZip->u.BZlib.avail_out = cbBuf;
783 while (pZip->u.BZlib.avail_out > 0)
784 {
785 /*
786 * Read more output buffer?
787 */
788 if (pZip->u.BZlib.avail_in <= 0)
789 {
790 size_t cb;
791 int rc = pZip->pfnIn(pZip->pvUser, &pZip->abBuffer[0], sizeof(pZip->abBuffer), &cb);
792 if (RT_FAILURE(rc))
793 return rc;
794 pZip->u.BZlib.avail_in = cb;
795 pZip->u.BZlib.next_in = (char *)&pZip->abBuffer[0];
796 }
797
798 /*
799 * Pass it on to zlib.
800 */
801 int rc = BZ2_bzDecompress(&pZip->u.BZlib);
802 if (rc == BZ_STREAM_END || rc == BZ_OUTBUFF_FULL)
803 {
804 if (pcbWritten)
805 *pcbWritten = cbBuf - pZip->u.BZlib.avail_out;
806 else if (pZip->u.BZlib.avail_out > 0)
807 return VERR_NO_DATA;
808 break;
809 }
810 if (rc < 0)
811 return zipErrConvertFromBZlib(rc);
812 }
813 return VINF_SUCCESS;
814}
815
816
817/**
818 * @copydoc RTZipDecompDestroy
819 */
820static DECLCALLBACK(int) rtZipBZlibDecompDestroy(PRTZIPDECOMP pZip)
821{
822 /*
823 * Terminate the deflate instance.
824 */
825 int rc = BZ2_bzDecompressEnd(&pZip->u.BZlib);
826 if (rc != BZ_OK)
827 rc = zipErrConvertFromBZlib(rc);
828 return rc;
829}
830
831
832/**
833 * Initialize the decompressor instance.
834 * @returns iprt status code.
835 * @param pZip The decompressor instance.
836 */
837static DECLCALLBACK(int) rtZipBZlibDecompInit(PRTZIPDECOMP pZip)
838{
839 pZip->pfnDecompress = rtZipBZlibDecompress;
840 pZip->pfnDestroy = rtZipBZlibDecompDestroy;
841
842 memset(&pZip->u.BZlib, 0, sizeof(pZip->u.BZlib));
843 pZip->u.BZlib.opaque = pZip;
844
845 int rc = BZ2_bzDecompressInit(&pZip->u.BZlib, 0, 0);
846 return rc >= 0 ? VINF_SUCCESS : zipErrConvertFromBZlib(rc);
847}
848
849#endif
850
851
852#ifdef RTZIP_USE_LZF
853
854/**
855 * Flushes the output buffer.
856 * @returns iprt status code.
857 * @param pZip The compressor instance.
858 */
859static int rtZipLZFCompFlushOutput(PRTZIPCOMP pZip)
860{
861 size_t cb = pZip->u.LZF.pbOutput - &pZip->abBuffer[0];
862 pZip->u.LZF.pbOutput = &pZip->abBuffer[0];
863 return pZip->pfnOut(pZip->pvUser, &pZip->abBuffer[0], cb);
864}
865
866
867/**
868 * Compresses a buffer using LZF.
869 *
870 * @returns VBox status code.
871 * @param pZip The compressor instance.
872 * @param pbBuf What to compress.
873 * @param cbBuf How much to compress.
874 */
875static int rtZipLZFCompressBuffer(PRTZIPCOMP pZip, const uint8_t *pbBuf, size_t cbBuf)
876{
877 bool fForceFlush = false;
878 while (cbBuf > 0)
879 {
880 /*
881 * Flush output buffer?
882 */
883 unsigned cbFree = (unsigned)(sizeof(pZip->abBuffer) - (pZip->u.LZF.pbOutput - &pZip->abBuffer[0]));
884 if ( fForceFlush
885 || cbFree < RTZIPLZF_MAX_DATA_SIZE + sizeof(RTZIPLZFHDR))
886 {
887 int rc = rtZipLZFCompFlushOutput(pZip);
888 if (RT_FAILURE(rc))
889 return rc;
890 fForceFlush = false;
891 cbFree = sizeof(pZip->abBuffer);
892 }
893
894 /*
895 * Setup the block header.
896 */
897 PRTZIPLZFHDR pHdr = (PRTZIPLZFHDR)pZip->u.LZF.pbOutput; /* warning: This might be unaligned! */
898 pHdr->u16Magic = RTZIPLZFHDR_MAGIC;
899 pHdr->cbData = 0;
900 pHdr->u32CRC = 0;
901 pHdr->cbUncompressed = 0;
902 cbFree -= sizeof(*pHdr);
903 pZip->u.LZF.pbOutput += sizeof(*pHdr);
904
905 /*
906 * Compress data for the block.
907 *
908 * We try compress as much as we have freespace for at first,
909 * but if it turns out the compression is inefficient, we'll
910 * reduce the size of data we try compress till it fits the
911 * output space.
912 */
913 cbFree = RT_MIN(cbFree, RTZIPLZF_MAX_DATA_SIZE);
914 unsigned cbInput = (unsigned)RT_MIN(RTZIPLZF_MAX_UNCOMPRESSED_DATA_SIZE, cbBuf);
915 unsigned cbOutput = lzf_compress(pbBuf, cbInput, pZip->u.LZF.pbOutput, cbFree);
916 if (!cbOutput)
917 {
918 /** @todo add an alternative method which stores the raw data if bad compression. */
919 do
920 {
921 cbInput /= 2;
922 if (!cbInput)
923 {
924 AssertMsgFailed(("lzf_compress bug! cbFree=%zu\n", cbFree));
925 return VERR_INTERNAL_ERROR;
926 }
927 cbOutput = lzf_compress(pbBuf, cbInput, pZip->u.LZF.pbOutput, cbFree);
928 } while (!cbOutput);
929 fForceFlush = true;
930 }
931
932 /*
933 * Upate the header and advance the input buffer.
934 */
935 pHdr->cbData = cbOutput;
936 //pHdr->u32CRC = RTCrc32(pbBuf, cbInput); - too slow
937 pHdr->cbUncompressed = cbInput;
938
939 pZip->u.LZF.pbOutput += cbOutput;
940 cbBuf -= cbInput;
941 pbBuf += cbInput;
942 }
943 return VINF_SUCCESS;
944}
945
946
947/**
948 * Flushes the input buffer.
949 * @returns iprt status code.
950 * @param pZip The compressor instance.
951 */
952static int rtZipLZFCompFlushInput(PRTZIPCOMP pZip)
953{
954 size_t cb = pZip->u.LZF.pbInput - &pZip->u.LZF.abInput[0];
955 pZip->u.LZF.pbInput = &pZip->u.LZF.abInput[0];
956 pZip->u.LZF.cbInputFree = sizeof(pZip->u.LZF.abInput);
957 if (cb)
958 return rtZipLZFCompressBuffer(pZip, pZip->u.LZF.abInput, cb);
959 return VINF_SUCCESS;
960}
961
962
963/**
964 * @copydoc RTZipCompress
965 */
966static DECLCALLBACK(int) rtZipLZFCompress(PRTZIPCOMP pZip, const void *pvBuf, size_t cbBuf)
967{
968#define RTZIPLZF_SMALL_CHUNK (128)
969
970 /*
971 * Flush the input buffer if necessary.
972 */
973 if ( ( cbBuf <= RTZIPLZF_SMALL_CHUNK
974 && cbBuf > pZip->u.LZF.cbInputFree)
975 || ( cbBuf > RTZIPLZF_SMALL_CHUNK
976 && pZip->u.LZF.cbInputFree != sizeof(pZip->u.LZF.abInput))
977 )
978 {
979 int rc = rtZipLZFCompFlushInput(pZip);
980 if (RT_FAILURE(rc))
981 return rc;
982 }
983
984 /*
985 * If it's a relativly small block put it in the input buffer, elsewise
986 * compress directly it.
987 */
988 if (cbBuf <= RTZIPLZF_SMALL_CHUNK)
989 {
990 Assert(pZip->u.LZF.cbInputFree >= cbBuf);
991 memcpy(pZip->u.LZF.pbInput, pvBuf, cbBuf);
992 pZip->u.LZF.pbInput += cbBuf;
993 pZip->u.LZF.cbInputFree -= cbBuf;
994 }
995 else
996 {
997 Assert(pZip->u.LZF.cbInputFree == sizeof(pZip->u.LZF.abInput));
998 int rc = rtZipLZFCompressBuffer(pZip, (const uint8_t *)pvBuf, cbBuf);
999 if (RT_FAILURE(rc))
1000 return rc;
1001 }
1002 return VINF_SUCCESS;
1003}
1004
1005
1006/**
1007 * @copydoc RTZipCompFinish
1008 */
1009static DECLCALLBACK(int) rtZipLZFCompFinish(PRTZIPCOMP pZip)
1010{
1011 int rc = rtZipLZFCompFlushInput(pZip);
1012 if (RT_SUCCESS(rc))
1013 rc = rtZipLZFCompFlushOutput(pZip);
1014 return rc;
1015}
1016
1017
1018/**
1019 * @copydoc RTZipCompDestroy
1020 */
1021static DECLCALLBACK(int) rtZipLZFCompDestroy(PRTZIPCOMP pZip)
1022{
1023 return VINF_SUCCESS;
1024}
1025
1026
1027/**
1028 * Initializes the compressor instance.
1029 * @returns iprt status code.
1030 * @param pZip The compressor instance.
1031 * @param enmLevel The desired compression level.
1032 */
1033static DECLCALLBACK(int) rtZipLZFCompInit(PRTZIPCOMP pZip, RTZIPLEVEL enmLevel)
1034{
1035 pZip->pfnCompress = rtZipLZFCompress;
1036 pZip->pfnFinish = rtZipLZFCompFinish;
1037 pZip->pfnDestroy = rtZipLZFCompDestroy;
1038
1039 pZip->u.LZF.pbOutput = &pZip->abBuffer[1];
1040 pZip->u.LZF.pbInput = &pZip->u.LZF.abInput[0];
1041 pZip->u.LZF.cbInputFree = sizeof(pZip->u.LZF.abInput);
1042 return VINF_SUCCESS;
1043}
1044
1045
1046/**
1047 * This will validate a header and to all the necessary bitching if it's invalid.
1048 * @returns true if valid.
1049 * @returns false if invalid.
1050 * @param pHdr Pointer to the header.\
1051 */
1052static bool rtZipLZFValidHeader(PCRTZIPLZFHDR pHdr)
1053{
1054 if ( pHdr->u16Magic != RTZIPLZFHDR_MAGIC
1055 || !pHdr->cbData
1056 || pHdr->cbData > RTZIPLZF_MAX_DATA_SIZE
1057 || !pHdr->cbUncompressed
1058 || pHdr->cbUncompressed > RTZIPLZF_MAX_UNCOMPRESSED_DATA_SIZE
1059 )
1060 {
1061 AssertMsgFailed(("Invalid LZF header! %.*%Rhxs\n", sizeof(pHdr), pHdr));
1062 return false;
1063 }
1064 return true;
1065}
1066
1067
1068/**
1069 * @copydoc RTZipDecompress
1070 */
1071static DECLCALLBACK(int) rtZipLZFDecompress(PRTZIPDECOMP pZip, void *pvBuf, size_t cbBuf, size_t *pcbWritten)
1072{
1073 /*
1074 * Decompression loop.
1075 *
1076 * This a bit ugly because we have to deal with reading block...
1077 * To simplify matters we've put a max block size and will never
1078 * fill the input buffer with more than allows us to complete
1079 * any partially read blocks.
1080 *
1081 * When possible we decompress directly to the user buffer, when
1082 * not possible we'll use the spill buffer.
1083 */
1084 while (cbBuf > 0)
1085 {
1086 /*
1087 * Anything in the spill buffer?
1088 */
1089 if (pZip->u.LZF.cbSpill > 0)
1090 {
1091 unsigned cb = (unsigned)RT_MIN(pZip->u.LZF.cbSpill, cbBuf);
1092 memcpy(pvBuf, pZip->u.LZF.pbSpill, cb);
1093 pZip->u.LZF.pbSpill += cb;
1094 pZip->u.LZF.cbSpill -= cb;
1095 cbBuf -= cb;
1096 if (pcbWritten)
1097 *pcbWritten = cb;
1098 if (!cbBuf)
1099 break;
1100 pvBuf = (uint8_t *)pvBuf + cb;
1101 }
1102
1103 /*
1104 * Incomplete header or nothing at all.
1105 */
1106 PCRTZIPLZFHDR pHdr;
1107 if (pZip->u.LZF.cbInput < sizeof(RTZIPLZFHDR))
1108 {
1109 if (pZip->u.LZF.cbInput <= 0)
1110 {
1111 /* empty, fill the buffer. */
1112 size_t cb = 0;
1113 int rc = pZip->pfnIn(pZip->pvUser, &pZip->abBuffer[0],
1114 sizeof(pZip->abBuffer) - RTZIPLZF_MAX_DATA_SIZE, &cb);
1115 if (RT_FAILURE(rc))
1116 return rc;
1117 pZip->u.LZF.pbInput = &pZip->abBuffer[0];
1118 pZip->u.LZF.cbInput = cb;
1119 pHdr = (PCRTZIPLZFHDR)pZip->u.LZF.pbInput;
1120 }
1121 else
1122 {
1123 /* move the header up and fill the buffer. */
1124 size_t cbCur = pZip->u.LZF.cbInput;
1125 memmove(&pZip->abBuffer[0], pZip->u.LZF.pbInput, cbCur);
1126 pZip->u.LZF.pbInput = &pZip->abBuffer[0];
1127
1128 size_t cb = 0;
1129 int rc = pZip->pfnIn(pZip->pvUser, &pZip->abBuffer[cbCur],
1130 sizeof(pZip->abBuffer) - RTZIPLZF_MAX_DATA_SIZE - cbCur, &cb);
1131 if (RT_FAILURE(rc))
1132 return rc;
1133 pHdr = (PCRTZIPLZFHDR)pZip->u.LZF.pbInput;
1134 pZip->u.LZF.cbInput += cb;
1135 }
1136
1137 /*
1138 * Validate the header.
1139 */
1140 if (!rtZipLZFValidHeader(pHdr))
1141 return VERR_GENERAL_FAILURE; /** @todo Get better error codes for RTZip! */
1142 }
1143 else
1144 {
1145 /*
1146 * Validate the header and check if it's an incomplete block.
1147 */
1148 pHdr = (PCRTZIPLZFHDR)pZip->u.LZF.pbInput;
1149 if (!rtZipLZFValidHeader(pHdr))
1150 return VERR_GENERAL_FAILURE; /** @todo Get better error codes for RTZip! */
1151
1152 if (pHdr->cbData > pZip->u.LZF.cbInput - sizeof(*pHdr))
1153 {
1154 /* read the remainder of the block. */
1155 size_t cbToRead = pHdr->cbData - (pZip->u.LZF.cbInput - sizeof(*pHdr));
1156 Assert(&pZip->u.LZF.pbInput[pZip->u.LZF.cbInput + cbToRead] <= &pZip->u.LZF.pbInput[sizeof(pZip->abBuffer)]);
1157 int rc = pZip->pfnIn(pZip->pvUser, &pZip->u.LZF.pbInput[pZip->u.LZF.cbInput],
1158 cbToRead, NULL);
1159 if (RT_FAILURE(rc))
1160 return rc;
1161 pZip->u.LZF.cbInput += cbToRead;
1162 }
1163 }
1164 AssertMsgReturn(sizeof(*pHdr) + pHdr->cbData <= pZip->u.LZF.cbInput,
1165 ("cbData=%#x cbInput=%#x\n", pHdr->cbData, pZip->u.LZF.cbInput),
1166 VERR_GENERAL_FAILURE); /** @todo Get better error codes for RTZip! */
1167
1168 /*
1169 * Does the uncompressed data fit into the supplied buffer?
1170 * If so we uncompress it directly into the user buffer, else we'll have to use the spill buffer.
1171 */
1172 unsigned cbUncompressed = pHdr->cbUncompressed;
1173 if (cbUncompressed <= cbBuf)
1174 {
1175 unsigned cbOutput = lzf_decompress(pHdr + 1, pHdr->cbData, pvBuf, cbUncompressed);
1176 if (cbOutput != cbUncompressed)
1177 {
1178 AssertMsgFailed(("Decompression error, errno=%d. cbOutput=%#x cbUncompressed=%#x\n",
1179 errno, cbOutput, cbUncompressed));
1180 return VERR_GENERAL_FAILURE; /** @todo Get better error codes for RTZip! */
1181 }
1182 cbBuf -= cbUncompressed;
1183 pvBuf = (uint8_t *)pvBuf + cbUncompressed;
1184 }
1185 else
1186 {
1187 unsigned cbOutput = lzf_decompress(pHdr + 1, pHdr->cbData, pZip->u.LZF.abSpill, cbUncompressed);
1188 if (cbOutput != cbUncompressed)
1189 {
1190 AssertMsgFailed(("Decompression error, errno=%d. cbOutput=%#x cbUncompressed=%#x\n",
1191 errno, cbOutput, cbUncompressed));
1192 return VERR_GENERAL_FAILURE; /** @todo Get better error codes for RTZip! */
1193 }
1194 pZip->u.LZF.pbSpill = &pZip->u.LZF.abSpill[0];
1195 pZip->u.LZF.cbSpill = cbUncompressed;
1196 }
1197
1198 /* advance the input buffer */
1199 pZip->u.LZF.cbInput -= pHdr->cbData + sizeof(*pHdr);
1200 pZip->u.LZF.pbInput += pHdr->cbData + sizeof(*pHdr);
1201 if (pcbWritten)
1202 *pcbWritten += cbUncompressed;
1203 }
1204
1205 return VINF_SUCCESS;
1206}
1207
1208
1209/**
1210 * @copydoc RTZipDecompDestroy
1211 */
1212static DECLCALLBACK(int) rtZipLZFDecompDestroy(PRTZIPDECOMP pZip)
1213{
1214 return VINF_SUCCESS;
1215}
1216
1217
1218/**
1219 * Initalize the decompressor instance.
1220 * @returns iprt status code.
1221 * @param pZip The decompressor instance.
1222 */
1223static DECLCALLBACK(int) rtZipLZFDecompInit(PRTZIPDECOMP pZip)
1224{
1225 pZip->pfnDecompress = rtZipLZFDecompress;
1226 pZip->pfnDestroy = rtZipLZFDecompDestroy;
1227
1228 pZip->u.LZF.pbInput = NULL;
1229 pZip->u.LZF.cbInput = 0;
1230 pZip->u.LZF.cbSpill = 0;
1231 pZip->u.LZF.pbSpill = NULL;
1232
1233 return VINF_SUCCESS;
1234}
1235
1236#endif /* RTZIP_USE_LZF */
1237
1238
1239/**
1240 * Create a compressor instance.
1241 *
1242 * @returns iprt status code.
1243 * @param ppZip Where to store the instance handle.
1244 * @param pvUser User argument which will be passed on to pfnOut and pfnIn.
1245 * @param pfnOut Callback for consuming output of compression.
1246 * @param enmType Type of compressor to create.
1247 * @param enmLevel Compression level.
1248 */
1249RTDECL(int) RTZipCompCreate(PRTZIPCOMP *ppZip, void *pvUser, PFNRTZIPOUT pfnOut, RTZIPTYPE enmType, RTZIPLEVEL enmLevel)
1250{
1251 /*
1252 * Validate input.
1253 */
1254 if ( enmType < RTZIPTYPE_AUTO
1255 || enmType > RTZIPTYPE_LZF)
1256 {
1257 AssertMsgFailed(("Invalid enmType=%d\n", enmType));
1258 return VERR_INVALID_PARAMETER;
1259 }
1260 if ( enmLevel < RTZIPLEVEL_STORE
1261 || enmLevel > RTZIPLEVEL_MAX)
1262 {
1263 AssertMsgFailed(("Invalid enmLevel=%d\n", enmLevel));
1264 return VERR_INVALID_PARAMETER;
1265 }
1266 if (!pfnOut || !ppZip)
1267 {
1268 AssertMsgFailed(("Must supply pfnOut and ppZip!\n"));
1269 return VERR_INVALID_PARAMETER;
1270 }
1271
1272 /*
1273 * Allocate memory for the instance data.
1274 */
1275 PRTZIPCOMP pZip = (PRTZIPCOMP)RTMemAlloc(sizeof(RTZIPCOMP));
1276 if (!pZip)
1277 return VERR_NO_MEMORY;
1278
1279 /*
1280 * Determin auto type.
1281 */
1282 if (enmType == RTZIPTYPE_AUTO)
1283 {
1284 if (enmLevel == RTZIPLEVEL_STORE)
1285 enmType = RTZIPTYPE_STORE;
1286 else
1287 {
1288#if defined(RTZIP_USE_ZLIB) && defined(RTZIP_USE_BZLIB)
1289 if (enmLevel == RTZIPLEVEL_MAX)
1290 enmType = RTZIPTYPE_BZLIB;
1291 else
1292 enmType = RTZIPTYPE_ZLIB;
1293#elif defined(RTZIP_USE_ZLIB)
1294 enmType = RTZIPTYPE_ZLIB;
1295#elif defined(RTZIP_USE_BZLIB)
1296 enmType = RTZIPTYPE_BZLIB;
1297#else
1298 enmType = RTZIPTYPE_STORE;
1299#endif
1300 }
1301 }
1302
1303 /*
1304 * Init instance.
1305 */
1306 pZip->pfnOut = pfnOut;
1307 pZip->enmType = enmType;
1308 pZip->pvUser = pvUser;
1309 pZip->abBuffer[0] = enmType; /* first byte is the compression type. */
1310 int rc = VINF_SUCCESS;
1311 switch (enmType)
1312 {
1313#ifdef RTZIP_USE_STORE
1314 case RTZIPTYPE_STORE:
1315 rc = rtZipStoreCompInit(pZip, enmLevel);
1316 break;
1317#endif
1318
1319#ifdef RTZIP_USE_ZLIB
1320 case RTZIPTYPE_ZLIB:
1321 rc = rtZipZlibCompInit(pZip, enmLevel);
1322 break;
1323#endif
1324
1325#ifdef RTZIP_USE_BZLIB
1326 case RTZIPTYPE_BZLIB:
1327 rc = rtZipBZlibCompInit(pZip, enmLevel);
1328 break;
1329#endif
1330
1331#ifdef RTZIP_USE_LZF
1332 case RTZIPTYPE_LZF:
1333 rc = rtZipLZFCompInit(pZip, enmLevel);
1334 break;
1335#endif
1336
1337 default:
1338 AssertMsgFailed(("Not implemented!\n"));
1339 rc = VERR_NOT_IMPLEMENTED;
1340 break;
1341 }
1342
1343 if (RT_SUCCESS(rc))
1344 *ppZip = pZip;
1345 else
1346 RTMemFree(pZip);
1347 return rc;
1348}
1349
1350
1351/**
1352 * Compresses a chunk of memory.
1353 *
1354 * @returns iprt status code.
1355 * @param pZip The compressor instance.
1356 * @param pvBuf Pointer to buffer containing the bits to compress.
1357 * @param cbBuf Number of bytes to compress.
1358 */
1359RTDECL(int) RTZipCompress(PRTZIPCOMP pZip, const void *pvBuf, size_t cbBuf)
1360{
1361 if (!cbBuf)
1362 return VINF_SUCCESS;
1363 return pZip->pfnCompress(pZip, pvBuf, cbBuf);
1364}
1365
1366
1367/**
1368 * Finishes the compression.
1369 * This will flush all data and terminate the compression data stream.
1370 *
1371 * @returns iprt status code.
1372 * @param pZip The compressor instance.
1373 */
1374RTDECL(int) RTZipCompFinish(PRTZIPCOMP pZip)
1375{
1376 return pZip->pfnFinish(pZip);
1377}
1378
1379
1380/**
1381 * Destroys the compressor instance.
1382 *
1383 * @returns iprt status code.
1384 * @param pZip The compressor instance.
1385 */
1386RTDECL(int) RTZipCompDestroy(PRTZIPCOMP pZip)
1387{
1388 /*
1389 * Compressor specific destruction attempt first.
1390 */
1391 int rc = pZip->pfnDestroy(pZip);
1392 AssertRCReturn(rc, rc);
1393
1394 /*
1395 * Free the instance memory.
1396 */
1397 pZip->enmType = RTZIPTYPE_INVALID;
1398 RTMemFree(pZip);
1399 return VINF_SUCCESS;
1400}
1401
1402
1403/**
1404 * @copydoc RTZipDecompress
1405 */
1406static DECLCALLBACK(int) rtZipStubDecompress(PRTZIPDECOMP pZip, void *pvBuf, size_t cbBuf, size_t *pcbWritten)
1407{
1408 return VERR_NOT_SUPPORTED;
1409}
1410
1411
1412/**
1413 * @copydoc RTZipDecompDestroy
1414 */
1415static DECLCALLBACK(int) rtZipStubDecompDestroy(PRTZIPDECOMP pZip)
1416{
1417 return VINF_SUCCESS;
1418}
1419
1420
1421/**
1422 * Create a decompressor instance.
1423 *
1424 * @returns iprt status code.
1425 * @param ppZip Where to store the instance handle.
1426 * @param pvUser User argument which will be passed on to pfnOut and pfnIn.
1427 * @param pfnIn Callback for producing input for decompression.
1428 */
1429RTDECL(int) RTZipDecompCreate(PRTZIPDECOMP *ppZip, void *pvUser, PFNRTZIPIN pfnIn)
1430{
1431 /*
1432 * Validate input.
1433 */
1434 if (!pfnIn || !ppZip)
1435 {
1436 AssertMsgFailed(("Must supply pfnIn and ppZip!\n"));
1437 return VERR_INVALID_PARAMETER;
1438 }
1439
1440 /*
1441 * Allocate memory for the instance data.
1442 */
1443 PRTZIPDECOMP pZip = (PRTZIPDECOMP)RTMemAlloc(sizeof(RTZIPDECOMP));
1444 if (!pZip)
1445 return VERR_NO_MEMORY;
1446
1447 /*
1448 * Init instance.
1449 */
1450 pZip->pfnIn = pfnIn;
1451 pZip->enmType = RTZIPTYPE_INVALID;
1452 pZip->pvUser = pvUser;
1453 pZip->pfnDecompress = NULL;
1454 pZip->pfnDestroy = rtZipStubDecompDestroy;
1455
1456 *ppZip = pZip;
1457 return VINF_SUCCESS;
1458}
1459
1460
1461/**
1462 * Lazy init of the decompressor.
1463 * @returns iprt status code.
1464 * @param pZip The decompressor instance.
1465 */
1466static int rtzipDecompInit(PRTZIPDECOMP pZip)
1467{
1468 /*
1469 * Read the first byte from the stream so we can determin the type.
1470 */
1471 uint8_t u8Type;
1472 int rc = pZip->pfnIn(pZip->pvUser, &u8Type, sizeof(u8Type), NULL);
1473 if (RT_FAILURE(rc))
1474 return rc;
1475
1476 /*
1477 * Determin type and do type specific init.
1478 */
1479 pZip->enmType = (RTZIPTYPE)u8Type;
1480 switch (pZip->enmType)
1481 {
1482#ifdef RTZIP_USE_STORE
1483 case RTZIPTYPE_STORE:
1484 rc = rtZipStoreDecompInit(pZip);
1485 break;
1486#endif
1487
1488 case RTZIPTYPE_ZLIB:
1489#ifdef RTZIP_USE_ZLIB
1490 rc = rtZipZlibDecompInit(pZip);
1491#else
1492 AssertMsgFailedReturn(("Zlib is not include in this build!\n"), VERR_NOT_IMPLEMENTED);
1493#endif
1494 break;
1495
1496 case RTZIPTYPE_BZLIB:
1497#ifdef RTZIP_USE_BZLIB
1498 rc = rtZipBZlibDecompInit(pZip);
1499#else
1500 AssertMsgFailedReturn(("BZlib is not include in this build!\n"), VERR_NOT_IMPLEMENTED);
1501#endif
1502 break;
1503
1504 case RTZIPTYPE_LZF:
1505#ifdef RTZIP_USE_LZF
1506 rc = rtZipLZFDecompInit(pZip);
1507#else
1508 AssertMsgFailedReturn(("LZF is not include in this build!\n"), VERR_NOT_IMPLEMENTED);
1509#endif
1510 break;
1511
1512 case RTZIPTYPE_INVALID:
1513 AssertMsgFailed(("Invalid compression type RTZIPTYPE_INVALID!\n"));
1514 rc = VERR_NOT_IMPLEMENTED;
1515 break;
1516
1517 case RTZIPTYPE_AUTO:
1518 AssertMsgFailed(("Invalid compression type RTZIPTYPE_AUTO!\n"));
1519 rc = VERR_INVALID_MAGIC;
1520 break;
1521
1522 default:
1523 AssertMsgFailed(("Unknown compression type %d\n\n", pZip->enmType));
1524 rc = VERR_INVALID_MAGIC;
1525 break;
1526 }
1527 if (RT_FAILURE(rc))
1528 {
1529 pZip->pfnDecompress = rtZipStubDecompress;
1530 pZip->pfnDestroy = rtZipStubDecompDestroy;
1531 }
1532
1533 return rc;
1534}
1535
1536/**
1537 * Decompresses a chunk of memory.
1538 *
1539 * @returns iprt status code.
1540 * @param pZip The decompressor instance.
1541 * @param pvBuf Where to store the decompressed data.
1542 * @param cbBuf Number of bytes to produce. If pcbWritten is set
1543 * any number of bytes up to cbBuf might be returned.
1544 * @param pcbWritten Number of bytes actually written to the buffer. If NULL
1545 * cbBuf number of bytes must be written.
1546 */
1547RTDECL(int) RTZipDecompress(PRTZIPDECOMP pZip, void *pvBuf, size_t cbBuf, size_t *pcbWritten)
1548{
1549 /*
1550 * Skip empty requests.
1551 */
1552 if (!cbBuf)
1553 return VINF_SUCCESS;
1554
1555 /*
1556 * Lazy init.
1557 */
1558 if (!pZip->pfnDecompress)
1559 {
1560 int rc = rtzipDecompInit(pZip);
1561 if (RT_FAILURE(rc))
1562 return rc;
1563 }
1564
1565 /*
1566 * 'Read' the decompressed stream.
1567 */
1568 return pZip->pfnDecompress(pZip, pvBuf, cbBuf, pcbWritten);
1569}
1570
1571/**
1572 * Destroys the decompressor instance.
1573 *
1574 * @returns iprt status code.
1575 * @param pZip The decompressor instance.
1576 */
1577RTDECL(int) RTZipDecompDestroy(PRTZIPDECOMP pZip)
1578{
1579 /*
1580 * Destroy compressor instance and flush the output buffer.
1581 */
1582 int rc = pZip->pfnDestroy(pZip);
1583 AssertRCReturn(rc, rc);
1584
1585 /*
1586 * Free the instance memory.
1587 */
1588 pZip->enmType = RTZIPTYPE_INVALID;
1589 RTMemFree(pZip);
1590 return rc;
1591}
1592
1593
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette