VirtualBox

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

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

IPRT/zip: use 128K buffer in all cases (both for compressing and decompressing). Optimizes reading compressed 64K chunks which weren't compressible.

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