VirtualBox

source: vbox/trunk/src/VBox/Runtime/zip.cpp@ 2397

Last change on this file since 2397 was 1, checked in by vboxsync, 55 years ago

import

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