diff options
Diffstat (limited to '')
-rw-r--r-- | src/VBox/Runtime/common/zip/zip.cpp | 1987 |
1 files changed, 1987 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/zip/zip.cpp b/src/VBox/Runtime/common/zip/zip.cpp new file mode 100644 index 00000000..5b6ce1ef --- /dev/null +++ b/src/VBox/Runtime/common/zip/zip.cpp @@ -0,0 +1,1987 @@ +/* $Id: zip.cpp $ */ +/** @file + * IPRT - Compression. + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +#define RTZIP_USE_STORE 1 +#define RTZIP_USE_ZLIB 1 +//#define RTZIP_USE_BZLIB 1 +#ifndef IN_GUEST +# define RTZIP_USE_LZF 1 +#endif +#define RTZIP_LZF_BLOCK_BY_BLOCK +//#define RTZIP_USE_LZJB 1 +//#define RTZIP_USE_LZO 1 + +/** @todo FastLZ? QuickLZ? Others? */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#ifdef RTZIP_USE_BZLIB +# include <bzlib.h> +#endif +#ifdef RTZIP_USE_ZLIB +# include <zlib.h> +#endif +#ifdef RTZIP_USE_LZF +# include <lzf.h> +# include <iprt/crc.h> +#endif +#ifdef RTZIP_USE_LZJB +# include "lzjb.h" +#endif +#ifdef RTZIP_USE_LZO +# include <lzo/lzo1x.h> +#endif + +#include <iprt/zip.h> +#include "internal/iprt.h" + +/*#include <iprt/asm.h>*/ +#include <iprt/alloc.h> +#include <iprt/assert.h> +#include <iprt/err.h> +#include <iprt/log.h> +#include <iprt/string.h> + +#include <errno.h> + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ + +#ifdef RTZIP_USE_LZF + +/** + * LZF block header. + */ +#pragma pack(1) /* paranoia */ +typedef struct RTZIPLZFHDR +{ + /** Magic word (RTZIPLZFHDR_MAGIC). */ + uint16_t u16Magic; + /** The number of bytes of data following this header. */ + uint16_t cbData; + /** The CRC32 of the block. */ + uint32_t u32CRC; + /** The size of the uncompressed data in bytes. */ + uint16_t cbUncompressed; +} RTZIPLZFHDR; +#pragma pack() +/** Pointer to a LZF block header. */ +typedef RTZIPLZFHDR *PRTZIPLZFHDR; +/** Pointer to a const LZF block header. */ +typedef const RTZIPLZFHDR *PCRTZIPLZFHDR; + +/** The magic of a LZF block header. */ +#define RTZIPLZFHDR_MAGIC ('Z' | ('V' << 8)) + +/** The max compressed data size. + * The maximum size of a block is currently 16KB. + * This is very important so we don't have to move input buffers around. */ +#define RTZIPLZF_MAX_DATA_SIZE (16384 - sizeof(RTZIPLZFHDR)) + +/** The max uncompressed data size. + * This is important so we don't overflow the spill buffer in the decompressor. */ +#define RTZIPLZF_MAX_UNCOMPRESSED_DATA_SIZE (32*_1K) + +#endif /* RTZIP_USE_LZF */ + + +/** + * Compressor/Decompressor instance data. + */ +typedef struct RTZIPCOMP +{ + /** Output buffer. */ + uint8_t abBuffer[_128K]; + /** Compression output consumer. */ + PFNRTZIPOUT pfnOut; + /** User argument for the callback. */ + void *pvUser; + + /** + * @copydoc RTZipCompress + */ + DECLCALLBACKMEMBER(int, pfnCompress)(PRTZIPCOMP pZip, const void *pvBuf, size_t cbBuf); + + /** + * @copydoc RTZipCompFinish + */ + DECLCALLBACKMEMBER(int, pfnFinish)(PRTZIPCOMP pZip); + + /** + * @copydoc RTZipCompDestroy + */ + DECLCALLBACKMEMBER(int, pfnDestroy)(PRTZIPCOMP pZip); + + /** Compression type. */ + RTZIPTYPE enmType; + /** Type specific data. */ + union + { +#ifdef RTZIP_USE_STORE + /** Simple storing. */ + struct + { + /** Current buffer position. (where to start write) */ + uint8_t *pb; + } Store; +#endif +#ifdef RTZIP_USE_ZLIB + /** Zlib stream. */ + z_stream Zlib; +#endif +#ifdef RTZIP_USE_BZLIB + /** BZlib stream. */ + bz_stream BZlib; +#endif +#ifdef RTZIP_USE_LZF + /** LZF stream. */ + struct + { + /** Current output buffer position. */ + uint8_t *pbOutput; + /** The input buffer position. */ + uint8_t *pbInput; + /** The number of free bytes in the input buffer. */ + size_t cbInputFree; + /** The input buffer. */ + uint8_t abInput[RTZIPLZF_MAX_UNCOMPRESSED_DATA_SIZE]; + } LZF; +#endif + + } u; +} RTZIPCOMP; + + + +/** + * Decompressor instance data. + */ +typedef struct RTZIPDECOMP +{ + /** Input buffer. */ + uint8_t abBuffer[_128K]; + /** Decompression input producer. */ + PFNRTZIPIN pfnIn; + /** User argument for the callback. */ + void *pvUser; + + /** + * @copydoc RTZipDecompress + */ + DECLCALLBACKMEMBER(int, pfnDecompress)(PRTZIPDECOMP pZip, void *pvBuf, size_t cbBuf, size_t *pcbWritten); + + /** + * @copydoc RTZipDecompDestroy + */ + DECLCALLBACKMEMBER(int, pfnDestroy)(PRTZIPDECOMP pZip); + + /** Compression type. */ + RTZIPTYPE enmType; + /** Type specific data. */ + union + { +#ifdef RTZIP_USE_STORE + /** Simple storing. */ + struct + { + /** Current buffer position. (where to start read) */ + uint8_t *pb; + /** Number of bytes left in the buffer. */ + size_t cbBuffer; + } Store; +#endif +#ifdef RTZIP_USE_ZLIB + /** Zlib stream. */ + z_stream Zlib; +#endif +#ifdef RTZIP_USE_BZLIB + /** BZlib stream. */ + bz_stream BZlib; +#endif +#ifdef RTZIP_USE_LZF + /** LZF 'stream'. */ + struct + { +# ifndef RTZIP_LZF_BLOCK_BY_BLOCK + /** Current input buffer position. */ + uint8_t *pbInput; + /** The number of bytes left in the input buffer. */ + size_t cbInput; +# endif + /** The spill buffer. + * LZF is a block based compressor and not a stream compressor. So, + * we have to decompress full blocks if we want to get any of the data. + * This buffer is to store the spill after decompressing a block. */ + uint8_t abSpill[RTZIPLZF_MAX_UNCOMPRESSED_DATA_SIZE]; + /** The number of bytes left spill buffer. */ + unsigned cbSpill; + /** The current spill buffer position. */ + uint8_t *pbSpill; + } LZF; +#endif + + } u; +} RTZIPDECOM; + + + +#ifdef RTZIP_USE_STORE + +/** + * @copydoc RTZipCompress + */ +static DECLCALLBACK(int) rtZipStoreCompress(PRTZIPCOMP pZip, const void *pvBuf, size_t cbBuf) +{ + uint8_t *pbDst = pZip->u.Store.pb; + while (cbBuf) + { + /* + * Flush. + */ + size_t cb = sizeof(pZip->abBuffer) - (size_t)(pbDst - &pZip->abBuffer[0]); /* careful here, g++ 4.1.2 screws up easily */ + if (cb == 0) + { + int rc = pZip->pfnOut(pZip->pvUser, &pZip->abBuffer[0], sizeof(pZip->abBuffer)); + if (RT_FAILURE(rc)) + return rc; + + cb = sizeof(pZip->abBuffer); + pbDst = &pZip->abBuffer[0]; + } + + /* + * Add to the buffer and advance. + */ + if (cbBuf < cb) + cb = cbBuf; + memcpy(pbDst, pvBuf, cb); + + pbDst += cb; + cbBuf -= cb; + pvBuf = (uint8_t *)pvBuf + cb; + } + pZip->u.Store.pb = pbDst; + return VINF_SUCCESS; +} + + +/** + * @copydoc RTZipCompFinish + */ +static DECLCALLBACK(int) rtZipStoreCompFinish(PRTZIPCOMP pZip) +{ + size_t cb = (uintptr_t)pZip->u.Store.pb - (uintptr_t)&pZip->abBuffer[0]; + if (cb > 0) + { + int rc = pZip->pfnOut(pZip->pvUser, &pZip->abBuffer[0], cb); + if (RT_FAILURE(rc)) + return rc; + } + return VINF_SUCCESS; +} + + +/** + * @copydoc RTZipCompDestroy + */ +static DECLCALLBACK(int) rtZipStoreCompDestroy(PRTZIPCOMP pZip) +{ + NOREF(pZip); + return VINF_SUCCESS; +} + + +/** + * Initializes the compressor instance. + * @returns iprt status code. + * @param pZip The compressor instance. + * @param enmLevel The desired compression level. + */ +static DECLCALLBACK(int) rtZipStoreCompInit(PRTZIPCOMP pZip, RTZIPLEVEL enmLevel) +{ + NOREF(enmLevel); + pZip->pfnCompress = rtZipStoreCompress; + pZip->pfnFinish = rtZipStoreCompFinish; + pZip->pfnDestroy = rtZipStoreCompDestroy; + + pZip->u.Store.pb = &pZip->abBuffer[1]; + return VINF_SUCCESS; +} + + +/** + * @copydoc RTZipDecompress + */ +static DECLCALLBACK(int) rtZipStoreDecompress(PRTZIPDECOMP pZip, void *pvBuf, size_t cbBuf, size_t *pcbWritten) +{ + size_t cbWritten = 0; + while (cbBuf) + { + /* + * Fill buffer. + */ + size_t cb = pZip->u.Store.cbBuffer; + if (cb <= 0) + { + int rc = pZip->pfnIn(pZip->pvUser, &pZip->abBuffer[0], sizeof(pZip->abBuffer), &cb); + if (RT_FAILURE(rc)) + return rc; + pZip->u.Store.cbBuffer = cb; + pZip->u.Store.pb = &pZip->abBuffer[0]; + } + + /* + * No more data? + */ + if (cb == 0) + { + if (pcbWritten) + { + *pcbWritten = cbWritten; + return VINF_SUCCESS; + } + return VERR_NO_DATA; + } + + /* + * Add to the buffer and advance. + */ + if (cbBuf < cb) + cb = cbBuf; + memcpy(pvBuf, pZip->u.Store.pb, cb); + pZip->u.Store.pb += cb; + pZip->u.Store.cbBuffer -= cb; + cbBuf -= cb; + pvBuf = (char *)pvBuf + cb; + cbWritten += cb; + } + if (pcbWritten) + *pcbWritten = cbWritten; + return VINF_SUCCESS; +} + + +/** + * @copydoc RTZipDecompDestroy + */ +static DECLCALLBACK(int) rtZipStoreDecompDestroy(PRTZIPDECOMP pZip) +{ + NOREF(pZip); + return VINF_SUCCESS; +} + + +/** + * Initialize the decompressor instance. + * @returns iprt status code. + * @param pZip The decompressor instance. + */ +static DECLCALLBACK(int) rtZipStoreDecompInit(PRTZIPDECOMP pZip) +{ + pZip->pfnDecompress = rtZipStoreDecompress; + pZip->pfnDestroy = rtZipStoreDecompDestroy; + + pZip->u.Store.pb = &pZip->abBuffer[0]; + pZip->u.Store.cbBuffer = 0; + return VINF_SUCCESS; +} + +#endif /* RTZIP_USE_STORE */ + + +#ifdef RTZIP_USE_ZLIB + +/* + * Missing definitions from zutil.h. We need these constants for calling + * inflateInit2() / deflateInit2(). + */ +# ifndef Z_DEF_WBITS +# define Z_DEF_WBITS MAX_WBITS +# endif +# ifndef Z_DEF_MEM_LEVEL +# define Z_DEF_MEM_LEVEL 8 +# endif + +/** + * Convert from zlib errno to iprt status code. + * @returns iprt status code. + * @param rc Zlib error code. + * @param fCompressing Set if we're compressing, clear if decompressing. + */ +static int zipErrConvertFromZlib(int rc, bool fCompressing) +{ + switch (rc) + { + case Z_OK: + return VINF_SUCCESS; + + case Z_STREAM_ERROR: + return VERR_ZIP_CORRUPTED; + + case Z_DATA_ERROR: + return fCompressing ? VERR_ZIP_ERROR : VERR_ZIP_CORRUPTED; + + case Z_MEM_ERROR: + return VERR_ZIP_NO_MEMORY; + + case Z_BUF_ERROR: + return VERR_ZIP_ERROR; + + case Z_VERSION_ERROR: + return VERR_ZIP_UNSUPPORTED_VERSION; + + case Z_ERRNO: /* We shouldn't see this status! */ + default: + AssertMsgFailed(("%d\n", rc)); + if (rc >= 0) + return VINF_SUCCESS; + return VERR_ZIP_ERROR; + } +} + + +/** + * @copydoc RTZipCompress + */ +static DECLCALLBACK(int) rtZipZlibCompress(PRTZIPCOMP pZip, const void *pvBuf, size_t cbBuf) +{ + pZip->u.Zlib.next_in = (Bytef *)pvBuf; + pZip->u.Zlib.avail_in = (uInt)cbBuf; Assert(pZip->u.Zlib.avail_in == cbBuf); + while (pZip->u.Zlib.avail_in > 0) + { + /* + * Flush output buffer? + */ + if (pZip->u.Zlib.avail_out <= 0) + { + int rc = pZip->pfnOut(pZip->pvUser, &pZip->abBuffer[0], sizeof(pZip->abBuffer) - pZip->u.Zlib.avail_out); + if (RT_FAILURE(rc)) + return rc; + pZip->u.Zlib.avail_out = sizeof(pZip->abBuffer); + pZip->u.Zlib.next_out = &pZip->abBuffer[0]; + } + + /* + * Pass it on to zlib. + */ + int rc = deflate(&pZip->u.Zlib, Z_NO_FLUSH); + if (rc != Z_OK) + return zipErrConvertFromZlib(rc, true /*fCompressing*/); + } + return VINF_SUCCESS; +} + + +/** + * @copydoc RTZipCompFinish + */ +static DECLCALLBACK(int) rtZipZlibCompFinish(PRTZIPCOMP pZip) +{ + int rc = Z_OK; + for (;;) + { + /* + * Flush outstanding stuff. writes. + */ + if (rc == Z_STREAM_END || pZip->u.Zlib.avail_out <= 0) + { + int rc2 = pZip->pfnOut(pZip->pvUser, &pZip->abBuffer[0], sizeof(pZip->abBuffer) - pZip->u.Zlib.avail_out); + if (RT_FAILURE(rc2)) + return rc2; + pZip->u.Zlib.avail_out = sizeof(pZip->abBuffer); + pZip->u.Zlib.next_out = &pZip->abBuffer[0]; + if (rc == Z_STREAM_END) + return VINF_SUCCESS; + } + + /* + * Tell zlib to flush. + */ + rc = deflate(&pZip->u.Zlib, Z_FINISH); + if (rc != Z_OK && rc != Z_STREAM_END) + return zipErrConvertFromZlib(rc, true /*fCompressing*/); + } +} + + +/** + * @copydoc RTZipCompDestroy + */ +static DECLCALLBACK(int) rtZipZlibCompDestroy(PRTZIPCOMP pZip) +{ + /* + * Terminate the deflate instance. + */ + int rc = deflateEnd(&pZip->u.Zlib); + if (rc != Z_OK) + rc = zipErrConvertFromZlib(rc, true /*fCompressing*/); + return rc; +} + + +/** + * Initializes the compressor instance. + * @returns iprt status code. + * @param pZip The compressor instance. + * @param enmLevel The desired compression level. + * @param fZlibHeader If true, write the Zlib header. + */ +static DECLCALLBACK(int) rtZipZlibCompInit(PRTZIPCOMP pZip, RTZIPLEVEL enmLevel, bool fZlibHeader) +{ + pZip->pfnCompress = rtZipZlibCompress; + pZip->pfnFinish = rtZipZlibCompFinish; + pZip->pfnDestroy = rtZipZlibCompDestroy; + + int iLevel = Z_DEFAULT_COMPRESSION; + switch (enmLevel) + { + case RTZIPLEVEL_STORE: iLevel = 0; break; + case RTZIPLEVEL_FAST: iLevel = 2; break; + case RTZIPLEVEL_DEFAULT: iLevel = Z_DEFAULT_COMPRESSION; break; + case RTZIPLEVEL_MAX: iLevel = 9; break; + } + + memset(&pZip->u.Zlib, 0, sizeof(pZip->u.Zlib)); + pZip->u.Zlib.next_out = &pZip->abBuffer[1]; + pZip->u.Zlib.avail_out = sizeof(pZip->abBuffer) - 1; + pZip->u.Zlib.opaque = pZip; + + int rc = deflateInit2(&pZip->u.Zlib, iLevel, Z_DEFLATED, fZlibHeader ? Z_DEF_WBITS : -Z_DEF_WBITS, + Z_DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); + return rc >= 0 ? rc = VINF_SUCCESS : zipErrConvertFromZlib(rc, true /*fCompressing*/); +} + + +/** + * @copydoc RTZipDecompress + */ +static DECLCALLBACK(int) rtZipZlibDecompress(PRTZIPDECOMP pZip, void *pvBuf, size_t cbBuf, size_t *pcbWritten) +{ + pZip->u.Zlib.next_out = (Bytef *)pvBuf; + pZip->u.Zlib.avail_out = (uInt)cbBuf; + Assert(pZip->u.Zlib.avail_out == cbBuf); + + /* + * Be greedy reading input, even if no output buffer is left. It's possible + * that it's just the end of stream marker which needs to be read. Happens + * for incompressible blocks just larger than the input buffer size. + */ + while (pZip->u.Zlib.avail_out > 0 || pZip->u.Zlib.avail_in <= 0) + { + /* + * Read more input? + */ + if (pZip->u.Zlib.avail_in <= 0) + { + size_t cb = sizeof(pZip->abBuffer); + int rc = pZip->pfnIn(pZip->pvUser, &pZip->abBuffer[0], sizeof(pZip->abBuffer), &cb); + if (RT_FAILURE(rc)) + return rc; + pZip->u.Zlib.avail_in = (uInt)cb; Assert(pZip->u.Zlib.avail_in == cb); + pZip->u.Zlib.next_in = &pZip->abBuffer[0]; + } + + /* + * Pass it on to zlib. + */ + int rc = inflate(&pZip->u.Zlib, Z_NO_FLUSH); + if (rc == Z_STREAM_END) + { + if (pcbWritten) + *pcbWritten = cbBuf - pZip->u.Zlib.avail_out; + else if (pZip->u.Zlib.avail_out > 0) + return VERR_NO_DATA; + break; + } + if (rc != Z_OK) + return zipErrConvertFromZlib(rc, false /*fCompressing*/); + } + return VINF_SUCCESS; +} + + +/** + * @copydoc RTZipDecompDestroy + */ +static DECLCALLBACK(int) rtZipZlibDecompDestroy(PRTZIPDECOMP pZip) +{ + /* + * Terminate the deflate instance. + */ + int rc = inflateEnd(&pZip->u.Zlib); + if (rc != Z_OK) + rc = zipErrConvertFromZlib(rc, false /*fCompressing*/); + return rc; +} + + +/** + * Initialize the decompressor instance. + * @returns iprt status code. + * @param pZip The decompressor instance. + * @param fZlibHeader If true, expect the Zlib header. + */ +static DECLCALLBACK(int) rtZipZlibDecompInit(PRTZIPDECOMP pZip, bool fZlibHeader) +{ + pZip->pfnDecompress = rtZipZlibDecompress; + pZip->pfnDestroy = rtZipZlibDecompDestroy; + + memset(&pZip->u.Zlib, 0, sizeof(pZip->u.Zlib)); + pZip->u.Zlib.opaque = pZip; + + int rc = inflateInit2(&pZip->u.Zlib, fZlibHeader ? Z_DEF_WBITS : -Z_DEF_WBITS); + return rc >= 0 ? VINF_SUCCESS : zipErrConvertFromZlib(rc, false /*fCompressing*/); +} + +#endif /* RTZIP_USE_ZLIB */ + + +#ifdef RTZIP_USE_BZLIB +/** + * Convert from BZlib errno to iprt status code. + * @returns iprt status code. + * @param rc BZlib error code. + */ +static int zipErrConvertFromBZlib(int rc) +{ + /** @todo proper bzlib error conversion. */ + switch (rc) + { + case BZ_SEQUENCE_ERROR: + AssertMsgFailed(("BZ_SEQUENCE_ERROR shall not happen!\n")); + return VERR_GENERAL_FAILURE; + case BZ_PARAM_ERROR: + return VERR_INVALID_PARAMETER; + case BZ_MEM_ERROR: + return VERR_NO_MEMORY; + case BZ_DATA_ERROR: + case BZ_DATA_ERROR_MAGIC: + case BZ_IO_ERROR: + case BZ_UNEXPECTED_EOF: + case BZ_CONFIG_ERROR: + return VERR_GENERAL_FAILURE; + case BZ_OUTBUFF_FULL: + AssertMsgFailed(("BZ_OUTBUFF_FULL shall not happen!\n")); + return VERR_GENERAL_FAILURE; + default: + if (rc >= 0) + return VINF_SUCCESS; + return VERR_GENERAL_FAILURE; + } +} + + +/** + * @copydoc RTZipCompress + */ +static DECLCALLBACK(int) rtZipBZlibCompress(PRTZIPCOMP pZip, const void *pvBuf, size_t cbBuf) +{ + pZip->u.BZlib.next_in = (char *)pvBuf; + pZip->u.BZlib.avail_in = cbBuf; + while (pZip->u.BZlib.avail_in > 0) + { + /* + * Flush output buffer? + */ + if (pZip->u.BZlib.avail_out <= 0) + { + int rc = pZip->pfnOut(pZip->pvUser, &pZip->abBuffer[0], sizeof(pZip->abBuffer) - pZip->u.BZlib.avail_out); + if (RT_FAILURE(rc)) + return rc; + pZip->u.BZlib.avail_out = sizeof(pZip->abBuffer); + pZip->u.BZlib.next_out = (char *)&pZip->abBuffer[0]; + } + + /* + * Pass it on to zlib. + */ + int rc = BZ2_bzCompress(&pZip->u.BZlib, BZ_RUN); + if (rc < 0 && rc != BZ_OUTBUFF_FULL) + return zipErrConvertFromBZlib(rc); + } + return VINF_SUCCESS; +} + + +/** + * @copydoc RTZipCompFinish + */ +static DECLCALLBACK(int) rtZipBZlibCompFinish(PRTZIPCOMP pZip) +{ + int rc = BZ_FINISH_OK; + for (;;) + { + /* + * Flush output buffer? + */ + if (rc == BZ_STREAM_END || pZip->u.BZlib.avail_out <= 0) + { + int rc2 = pZip->pfnOut(pZip->pvUser, &pZip->abBuffer[0], sizeof(pZip->abBuffer) - pZip->u.BZlib.avail_out); + if (RT_FAILURE(rc2)) + return rc2; + pZip->u.BZlib.avail_out = sizeof(pZip->abBuffer); + pZip->u.BZlib.next_out = (char *)&pZip->abBuffer[0]; + if (rc == BZ_STREAM_END) + return VINF_SUCCESS; + } + + /* + * Tell BZlib to finish it. + */ + rc = BZ2_bzCompress(&pZip->u.BZlib, BZ_FINISH); + if (rc < 0 && rc != BZ_OUTBUFF_FULL) + return zipErrConvertFromBZlib(rc); + } + return VINF_SUCCESS; +} + + +/** + * @copydoc RTZipCompDestroy + */ +static DECLCALLBACK(int) rtZipBZlibCompDestroy(PRTZIPCOMP pZip) +{ + /* + * Terminate the deflate instance. + */ + int rc = BZ2_bzCompressEnd(&pZip->u.BZlib); + if (rc != BZ_OK) + rc = zipErrConvertFromBZlib(rc); + return rc; +} + + +/** + * Initializes the compressor instance. + * @returns iprt status code. + * @param pZip The compressor instance. + * @param enmLevel The desired compression level. + */ +static DECLCALLBACK(int) rtZipBZlibCompInit(PRTZIPCOMP pZip, RTZIPLEVEL enmLevel) +{ + pZip->pfnCompress = rtZipBZlibCompress; + pZip->pfnFinish = rtZipBZlibCompFinish; + pZip->pfnDestroy = rtZipBZlibCompDestroy; + + int iSize = 6; + int iWork = 0; + switch (enmLevel) + { + case RTZIPLEVEL_STORE: iSize = 1; iWork = 2; break; + case RTZIPLEVEL_FAST: iSize = 2; iWork = 0; break; + case RTZIPLEVEL_DEFAULT: iSize = 5; iWork = 0; break; + case RTZIPLEVEL_MAX: iSize = 9; iWork = 0; break; + } + + memset(&pZip->u.BZlib, 0, sizeof(pZip->u.BZlib)); + pZip->u.BZlib.next_out = (char *)&pZip->abBuffer[1]; + pZip->u.BZlib.avail_out = sizeof(pZip->abBuffer) - 1; + pZip->u.BZlib.opaque = pZip; + + int rc = BZ2_bzCompressInit(&pZip->u.BZlib, iSize, 0, iWork); + return rc >= 0 ? VINF_SUCCESS : zipErrConvertFromBZlib(rc);; +} + + +/** + * @copydoc RTZipDecompress + */ +static DECLCALLBACK(int) rtZipBZlibDecompress(PRTZIPDECOMP pZip, void *pvBuf, size_t cbBuf, size_t *pcbWritten) +{ + pZip->u.BZlib.next_out = (char *)pvBuf; + pZip->u.BZlib.avail_out = cbBuf; + while (pZip->u.BZlib.avail_out > 0) + { + /* + * Read more output buffer? + */ + if (pZip->u.BZlib.avail_in <= 0) + { + size_t cb; + int rc = pZip->pfnIn(pZip->pvUser, &pZip->abBuffer[0], sizeof(pZip->abBuffer), &cb); + if (RT_FAILURE(rc)) + return rc; + pZip->u.BZlib.avail_in = cb; + pZip->u.BZlib.next_in = (char *)&pZip->abBuffer[0]; + } + + /* + * Pass it on to zlib. + */ + int rc = BZ2_bzDecompress(&pZip->u.BZlib); + if (rc == BZ_STREAM_END || rc == BZ_OUTBUFF_FULL) + { + if (pcbWritten) + *pcbWritten = cbBuf - pZip->u.BZlib.avail_out; + else if (pZip->u.BZlib.avail_out > 0) + return VERR_NO_DATA; + break; + } + if (rc < 0) + return zipErrConvertFromBZlib(rc); + } + return VINF_SUCCESS; +} + + +/** + * @copydoc RTZipDecompDestroy + */ +static DECLCALLBACK(int) rtZipBZlibDecompDestroy(PRTZIPDECOMP pZip) +{ + /* + * Terminate the deflate instance. + */ + int rc = BZ2_bzDecompressEnd(&pZip->u.BZlib); + if (rc != BZ_OK) + rc = zipErrConvertFromBZlib(rc); + return rc; +} + + +/** + * Initialize the decompressor instance. + * @returns iprt status code. + * @param pZip The decompressor instance. + */ +static DECLCALLBACK(int) rtZipBZlibDecompInit(PRTZIPDECOMP pZip) +{ + pZip->pfnDecompress = rtZipBZlibDecompress; + pZip->pfnDestroy = rtZipBZlibDecompDestroy; + + memset(&pZip->u.BZlib, 0, sizeof(pZip->u.BZlib)); + pZip->u.BZlib.opaque = pZip; + + int rc = BZ2_bzDecompressInit(&pZip->u.BZlib, 0, 0); + return rc >= 0 ? VINF_SUCCESS : zipErrConvertFromBZlib(rc); +} + +#endif /* RTZIP_USE_BZLIB */ + + +#ifdef RTZIP_USE_LZF + +/** + * Flushes the output buffer. + * @returns iprt status code. + * @param pZip The compressor instance. + */ +static int rtZipLZFCompFlushOutput(PRTZIPCOMP pZip) +{ + size_t cb = pZip->u.LZF.pbOutput - &pZip->abBuffer[0]; + pZip->u.LZF.pbOutput = &pZip->abBuffer[0]; + return pZip->pfnOut(pZip->pvUser, &pZip->abBuffer[0], cb); +} + + +/** + * Compresses a buffer using LZF. + * + * @returns VBox status code. + * @param pZip The compressor instance. + * @param pbBuf What to compress. + * @param cbBuf How much to compress. + */ +static int rtZipLZFCompressBuffer(PRTZIPCOMP pZip, const uint8_t *pbBuf, size_t cbBuf) +{ + bool fForceFlush = false; + while (cbBuf > 0) + { + /* + * Flush output buffer? + */ + unsigned cbFree = (unsigned)(sizeof(pZip->abBuffer) - (pZip->u.LZF.pbOutput - &pZip->abBuffer[0])); + if ( fForceFlush + || cbFree < RTZIPLZF_MAX_DATA_SIZE + sizeof(RTZIPLZFHDR)) + { + int rc = rtZipLZFCompFlushOutput(pZip); + if (RT_FAILURE(rc)) + return rc; + fForceFlush = false; + cbFree = sizeof(pZip->abBuffer); + } + + /* + * Setup the block header. + */ + PRTZIPLZFHDR pHdr = (PRTZIPLZFHDR)pZip->u.LZF.pbOutput; /* warning: This might be unaligned! */ + pHdr->u16Magic = RTZIPLZFHDR_MAGIC; + pHdr->cbData = 0; + pHdr->u32CRC = 0; + pHdr->cbUncompressed = 0; + cbFree -= sizeof(*pHdr); + pZip->u.LZF.pbOutput += sizeof(*pHdr); + + /* + * Compress data for the block. + * + * We try compress as much as we have freespace for at first, + * but if it turns out the compression is inefficient, we'll + * reduce the size of data we try compress till it fits the + * output space. + */ + cbFree = RT_MIN(cbFree, RTZIPLZF_MAX_DATA_SIZE); + unsigned cbInput = (unsigned)RT_MIN(RTZIPLZF_MAX_UNCOMPRESSED_DATA_SIZE, cbBuf); + unsigned cbOutput = lzf_compress(pbBuf, cbInput, pZip->u.LZF.pbOutput, cbFree); + if (!cbOutput) + { + /** @todo add an alternative method which stores the raw data if bad compression. */ + do + { + cbInput /= 2; + if (!cbInput) + { + AssertMsgFailed(("lzf_compress bug! cbFree=%zu\n", cbFree)); + return VERR_INTERNAL_ERROR; + } + cbOutput = lzf_compress(pbBuf, cbInput, pZip->u.LZF.pbOutput, cbFree); + } while (!cbOutput); + fForceFlush = true; + } + + /* + * Update the header and advance the input buffer. + */ + pHdr->cbData = cbOutput; + //pHdr->u32CRC = RTCrc32(pbBuf, cbInput); - too slow + pHdr->cbUncompressed = cbInput; + + pZip->u.LZF.pbOutput += cbOutput; + cbBuf -= cbInput; + pbBuf += cbInput; + } + return VINF_SUCCESS; +} + + +/** + * Flushes the input buffer. + * @returns iprt status code. + * @param pZip The compressor instance. + */ +static int rtZipLZFCompFlushInput(PRTZIPCOMP pZip) +{ + size_t cb = pZip->u.LZF.pbInput - &pZip->u.LZF.abInput[0]; + pZip->u.LZF.pbInput = &pZip->u.LZF.abInput[0]; + pZip->u.LZF.cbInputFree = sizeof(pZip->u.LZF.abInput); + if (cb) + return rtZipLZFCompressBuffer(pZip, pZip->u.LZF.abInput, cb); + return VINF_SUCCESS; +} + + +/** + * @copydoc RTZipCompress + */ +static DECLCALLBACK(int) rtZipLZFCompress(PRTZIPCOMP pZip, const void *pvBuf, size_t cbBuf) +{ +#define RTZIPLZF_SMALL_CHUNK (128) + + /* + * Flush the input buffer if necessary. + */ + if ( ( cbBuf <= RTZIPLZF_SMALL_CHUNK + && cbBuf > pZip->u.LZF.cbInputFree) + || ( cbBuf > RTZIPLZF_SMALL_CHUNK + && pZip->u.LZF.cbInputFree != sizeof(pZip->u.LZF.abInput)) + ) + { + int rc = rtZipLZFCompFlushInput(pZip); + if (RT_FAILURE(rc)) + return rc; + } + + /* + * If it's a relativly small block put it in the input buffer, elsewise + * compress directly it. + */ + if (cbBuf <= RTZIPLZF_SMALL_CHUNK) + { + Assert(pZip->u.LZF.cbInputFree >= cbBuf); + memcpy(pZip->u.LZF.pbInput, pvBuf, cbBuf); + pZip->u.LZF.pbInput += cbBuf; + pZip->u.LZF.cbInputFree -= cbBuf; + } + else + { + Assert(pZip->u.LZF.cbInputFree == sizeof(pZip->u.LZF.abInput)); + int rc = rtZipLZFCompressBuffer(pZip, (const uint8_t *)pvBuf, cbBuf); + if (RT_FAILURE(rc)) + return rc; + } + return VINF_SUCCESS; +} + + +/** + * @copydoc RTZipCompFinish + */ +static DECLCALLBACK(int) rtZipLZFCompFinish(PRTZIPCOMP pZip) +{ + int rc = rtZipLZFCompFlushInput(pZip); + if (RT_SUCCESS(rc)) + rc = rtZipLZFCompFlushOutput(pZip); + return rc; +} + + +/** + * @copydoc RTZipCompDestroy + */ +static DECLCALLBACK(int) rtZipLZFCompDestroy(PRTZIPCOMP pZip) +{ + NOREF(pZip); + return VINF_SUCCESS; +} + + +/** + * Initializes the compressor instance. + * @returns iprt status code. + * @param pZip The compressor instance. + * @param enmLevel The desired compression level. + */ +static DECLCALLBACK(int) rtZipLZFCompInit(PRTZIPCOMP pZip, RTZIPLEVEL enmLevel) +{ + NOREF(enmLevel); + pZip->pfnCompress = rtZipLZFCompress; + pZip->pfnFinish = rtZipLZFCompFinish; + pZip->pfnDestroy = rtZipLZFCompDestroy; + + pZip->u.LZF.pbOutput = &pZip->abBuffer[1]; + pZip->u.LZF.pbInput = &pZip->u.LZF.abInput[0]; + pZip->u.LZF.cbInputFree = sizeof(pZip->u.LZF.abInput); + return VINF_SUCCESS; +} + + +/** + * This will validate a header and to all the necessary bitching if it's invalid. + * @returns true if valid. + * @returns false if invalid. + * @param pHdr Pointer to the header.\ + */ +static bool rtZipLZFValidHeader(PCRTZIPLZFHDR pHdr) +{ + if ( pHdr->u16Magic != RTZIPLZFHDR_MAGIC + || !pHdr->cbData + || pHdr->cbData > RTZIPLZF_MAX_DATA_SIZE + || !pHdr->cbUncompressed + || pHdr->cbUncompressed > RTZIPLZF_MAX_UNCOMPRESSED_DATA_SIZE + ) + { + AssertMsgFailed(("Invalid LZF header! %.*Rhxs\n", sizeof(*pHdr), pHdr)); + return false; + } + return true; +} + + +/** + * @copydoc RTZipDecompress + */ +static DECLCALLBACK(int) rtZipLZFDecompress(PRTZIPDECOMP pZip, void *pvBuf, size_t cbBuf, size_t *pcbWritten) +{ + /* + * Decompression loop. + * + * This a bit ugly because we have to deal with reading block... + * To simplify matters we've put a max block size and will never + * fill the input buffer with more than allows us to complete + * any partially read blocks. + * + * When possible we decompress directly to the user buffer, when + * not possible we'll use the spill buffer. + */ +# ifdef RTZIP_LZF_BLOCK_BY_BLOCK + size_t cbWritten = 0; + while (cbBuf > 0) + { + /* + * Anything in the spill buffer? + */ + if (pZip->u.LZF.cbSpill > 0) + { + unsigned cb = (unsigned)RT_MIN(pZip->u.LZF.cbSpill, cbBuf); + memcpy(pvBuf, pZip->u.LZF.pbSpill, cb); + pZip->u.LZF.pbSpill += cb; + pZip->u.LZF.cbSpill -= cb; + cbWritten += cb; + cbBuf -= cb; + if (!cbBuf) + break; + pvBuf = (uint8_t *)pvBuf + cb; + } + + /* + * We always read and work one block at a time. + */ + RTZIPLZFHDR Hdr; + int rc = pZip->pfnIn(pZip->pvUser, &Hdr, sizeof(Hdr), NULL); + if (RT_FAILURE(rc)) + return rc; + if (!rtZipLZFValidHeader(&Hdr)) + return VERR_GENERAL_FAILURE; /** @todo Get better error codes for RTZip! */ + if (Hdr.cbData > 0) + { + rc = pZip->pfnIn(pZip->pvUser, &pZip->abBuffer[0], Hdr.cbData, NULL); + if (RT_FAILURE(rc)) + return rc; + } + + /* + * Does the uncompressed data fit into the supplied buffer? + * If so we uncompress it directly into the user buffer, else we'll have to use the spill buffer. + */ + unsigned cbUncompressed = Hdr.cbUncompressed; + if (cbUncompressed <= cbBuf) + { + unsigned cbOutput = lzf_decompress(&pZip->abBuffer[0], Hdr.cbData, pvBuf, cbUncompressed); + if (cbOutput != cbUncompressed) + { + AssertMsgFailed(("Decompression error, errno=%d. cbOutput=%#x cbUncompressed=%#x\n", + errno, cbOutput, cbUncompressed)); + return VERR_GENERAL_FAILURE; /** @todo Get better error codes for RTZip! */ + } + cbBuf -= cbUncompressed; + pvBuf = (uint8_t *)pvBuf + cbUncompressed; + cbWritten += cbUncompressed; + } + else + { + unsigned cbOutput = lzf_decompress(&pZip->abBuffer[0], Hdr.cbData, pZip->u.LZF.abSpill, cbUncompressed); + if (cbOutput != cbUncompressed) + { + AssertMsgFailed(("Decompression error, errno=%d. cbOutput=%#x cbUncompressed=%#x\n", + errno, cbOutput, cbUncompressed)); + return VERR_GENERAL_FAILURE; /** @todo Get better error codes for RTZip! */ + } + pZip->u.LZF.pbSpill = &pZip->u.LZF.abSpill[0]; + pZip->u.LZF.cbSpill = cbUncompressed; + } + } + + if (pcbWritten) + *pcbWritten = cbWritten; +# else /* !RTZIP_LZF_BLOCK_BY_BLOCK */ + while (cbBuf > 0) + { + /* + * Anything in the spill buffer? + */ + if (pZip->u.LZF.cbSpill > 0) + { + unsigned cb = (unsigned)RT_MIN(pZip->u.LZF.cbSpill, cbBuf); + memcpy(pvBuf, pZip->u.LZF.pbSpill, cb); + pZip->u.LZF.pbSpill += cb; + pZip->u.LZF.cbSpill -= cb; + cbBuf -= cb; + if (pcbWritten) + *pcbWritten = cb; + if (!cbBuf) + break; + pvBuf = (uint8_t *)pvBuf + cb; + } + + /* + * Incomplete header or nothing at all. + */ + PCRTZIPLZFHDR pHdr; + if (pZip->u.LZF.cbInput < sizeof(RTZIPLZFHDR)) + { + if (pZip->u.LZF.cbInput <= 0) + { + /* empty, fill the buffer. */ + size_t cb = 0; + int rc = pZip->pfnIn(pZip->pvUser, &pZip->abBuffer[0], + sizeof(pZip->abBuffer) - RTZIPLZF_MAX_DATA_SIZE, &cb); + if (RT_FAILURE(rc)) + return rc; + pZip->u.LZF.pbInput = &pZip->abBuffer[0]; + pZip->u.LZF.cbInput = cb; + pHdr = (PCRTZIPLZFHDR)pZip->u.LZF.pbInput; + } + else + { + /* move the header up and fill the buffer. */ + size_t cbCur = pZip->u.LZF.cbInput; + memmove(&pZip->abBuffer[0], pZip->u.LZF.pbInput, cbCur); + pZip->u.LZF.pbInput = &pZip->abBuffer[0]; + + size_t cb = 0; + int rc = pZip->pfnIn(pZip->pvUser, &pZip->abBuffer[cbCur], + sizeof(pZip->abBuffer) - RTZIPLZF_MAX_DATA_SIZE - cbCur, &cb); + if (RT_FAILURE(rc)) + return rc; + pHdr = (PCRTZIPLZFHDR)pZip->u.LZF.pbInput; + pZip->u.LZF.cbInput += cb; + } + + /* + * Validate the header. + */ + if (!rtZipLZFValidHeader(pHdr)) + return VERR_GENERAL_FAILURE; /** @todo Get better error codes for RTZip! */ + } + else + { + /* + * Validate the header and check if it's an incomplete block. + */ + pHdr = (PCRTZIPLZFHDR)pZip->u.LZF.pbInput; + if (!rtZipLZFValidHeader(pHdr)) + return VERR_GENERAL_FAILURE; /** @todo Get better error codes for RTZip! */ + + if (pHdr->cbData > pZip->u.LZF.cbInput - sizeof(*pHdr)) + { + /* read the remainder of the block. */ + size_t cbToRead = pHdr->cbData - (pZip->u.LZF.cbInput - sizeof(*pHdr)); + Assert(&pZip->u.LZF.pbInput[pZip->u.LZF.cbInput + cbToRead] <= &pZip->u.LZF.pbInput[sizeof(pZip->abBuffer)]); + int rc = pZip->pfnIn(pZip->pvUser, &pZip->u.LZF.pbInput[pZip->u.LZF.cbInput], + cbToRead, NULL); + if (RT_FAILURE(rc)) + return rc; + pZip->u.LZF.cbInput += cbToRead; + } + } + AssertMsgReturn(sizeof(*pHdr) + pHdr->cbData <= pZip->u.LZF.cbInput, + ("cbData=%#x cbInput=%#x\n", pHdr->cbData, pZip->u.LZF.cbInput), + VERR_GENERAL_FAILURE); /** @todo Get better error codes for RTZip! */ + + /* + * Does the uncompressed data fit into the supplied buffer? + * If so we uncompress it directly into the user buffer, else we'll have to use the spill buffer. + */ + unsigned cbUncompressed = pHdr->cbUncompressed; + if (cbUncompressed <= cbBuf) + { + unsigned cbOutput = lzf_decompress(pHdr + 1, pHdr->cbData, pvBuf, cbUncompressed); + if (cbOutput != cbUncompressed) + { + AssertMsgFailed(("Decompression error, errno=%d. cbOutput=%#x cbUncompressed=%#x\n", + errno, cbOutput, cbUncompressed)); + return VERR_GENERAL_FAILURE; /** @todo Get better error codes for RTZip! */ + } + cbBuf -= cbUncompressed; + pvBuf = (uint8_t *)pvBuf + cbUncompressed; + } + else + { + unsigned cbOutput = lzf_decompress(pHdr + 1, pHdr->cbData, pZip->u.LZF.abSpill, cbUncompressed); + if (cbOutput != cbUncompressed) + { + AssertMsgFailed(("Decompression error, errno=%d. cbOutput=%#x cbUncompressed=%#x\n", + errno, cbOutput, cbUncompressed)); + return VERR_GENERAL_FAILURE; /** @todo Get better error codes for RTZip! */ + } + pZip->u.LZF.pbSpill = &pZip->u.LZF.abSpill[0]; + pZip->u.LZF.cbSpill = cbUncompressed; + } + + /* advance the input buffer */ + pZip->u.LZF.cbInput -= pHdr->cbData + sizeof(*pHdr); + pZip->u.LZF.pbInput += pHdr->cbData + sizeof(*pHdr); + if (pcbWritten) + *pcbWritten += cbUncompressed; + } +# endif /* !RTZIP_LZF_BLOCK_BY_BLOCK */ + return VINF_SUCCESS; +} + + +/** + * @copydoc RTZipDecompDestroy + */ +static DECLCALLBACK(int) rtZipLZFDecompDestroy(PRTZIPDECOMP pZip) +{ + NOREF(pZip); + return VINF_SUCCESS; +} + + +/** + * Initialize the decompressor instance. + * @returns iprt status code. + * @param pZip The decompressor instance. + */ +static DECLCALLBACK(int) rtZipLZFDecompInit(PRTZIPDECOMP pZip) +{ + pZip->pfnDecompress = rtZipLZFDecompress; + pZip->pfnDestroy = rtZipLZFDecompDestroy; + +# ifndef RTZIP_LZF_BLOCK_BY_BLOCK + pZip->u.LZF.pbInput = NULL; + pZip->u.LZF.cbInput = 0; +# endif + pZip->u.LZF.cbSpill = 0; + pZip->u.LZF.pbSpill = NULL; + + return VINF_SUCCESS; +} + +#endif /* RTZIP_USE_LZF */ + + +/** + * Create a compressor instance. + * + * @returns iprt status code. + * @param ppZip Where to store the instance handle. + * @param pvUser User argument which will be passed on to pfnOut and pfnIn. + * @param pfnOut Callback for consuming output of compression. + * @param enmType Type of compressor to create. + * @param enmLevel Compression level. + */ +RTDECL(int) RTZipCompCreate(PRTZIPCOMP *ppZip, void *pvUser, PFNRTZIPOUT pfnOut, RTZIPTYPE enmType, RTZIPLEVEL enmLevel) +{ + /* + * Validate input. + */ + AssertReturn(enmType >= RTZIPTYPE_INVALID && enmType < RTZIPTYPE_END, VERR_INVALID_PARAMETER); + AssertReturn(enmLevel >= RTZIPLEVEL_STORE && enmLevel <= RTZIPLEVEL_MAX, VERR_INVALID_PARAMETER); + AssertPtrReturn(pfnOut, VERR_INVALID_POINTER); + AssertPtrReturn(ppZip, VERR_INVALID_POINTER); + + /* + * Allocate memory for the instance data. + */ + PRTZIPCOMP pZip = (PRTZIPCOMP)RTMemAlloc(sizeof(RTZIPCOMP)); + if (!pZip) + return VERR_NO_MEMORY; + + /* + * Determine auto type. + */ + if (enmType == RTZIPTYPE_AUTO) + { + if (enmLevel == RTZIPLEVEL_STORE) + enmType = RTZIPTYPE_STORE; + else + { +#if defined(RTZIP_USE_ZLIB) && defined(RTZIP_USE_BZLIB) + if (enmLevel == RTZIPLEVEL_MAX) + enmType = RTZIPTYPE_BZLIB; + else + enmType = RTZIPTYPE_ZLIB; +#elif defined(RTZIP_USE_ZLIB) + enmType = RTZIPTYPE_ZLIB; +#elif defined(RTZIP_USE_BZLIB) + enmType = RTZIPTYPE_BZLIB; +#else + enmType = RTZIPTYPE_STORE; +#endif + } + } + + /* + * Init instance. + */ + pZip->pfnOut = pfnOut; + pZip->enmType = enmType; + pZip->pvUser = pvUser; + pZip->abBuffer[0] = enmType; /* first byte is the compression type. */ + int rc = VERR_NOT_IMPLEMENTED; + switch (enmType) + { + case RTZIPTYPE_STORE: +#ifdef RTZIP_USE_STORE + rc = rtZipStoreCompInit(pZip, enmLevel); +#endif + break; + + case RTZIPTYPE_ZLIB: + case RTZIPTYPE_ZLIB_NO_HEADER: +#ifdef RTZIP_USE_ZLIB + rc = rtZipZlibCompInit(pZip, enmLevel, enmType == RTZIPTYPE_ZLIB /*fZlibHeader*/); +#endif + break; + + case RTZIPTYPE_BZLIB: +#ifdef RTZIP_USE_BZLIB + rc = rtZipBZlibCompInit(pZip, enmLevel); +#endif + break; + + case RTZIPTYPE_LZF: +#ifdef RTZIP_USE_LZF + rc = rtZipLZFCompInit(pZip, enmLevel); +#endif + break; + + case RTZIPTYPE_LZJB: + case RTZIPTYPE_LZO: + break; + + default: + AssertFailedBreak(); + } + + if (RT_SUCCESS(rc)) + *ppZip = pZip; + else + RTMemFree(pZip); + return rc; +} +RT_EXPORT_SYMBOL(RTZipCompCreate); + + +/** + * Compresses a chunk of memory. + * + * @returns iprt status code. + * @param pZip The compressor instance. + * @param pvBuf Pointer to buffer containing the bits to compress. + * @param cbBuf Number of bytes to compress. + */ +RTDECL(int) RTZipCompress(PRTZIPCOMP pZip, const void *pvBuf, size_t cbBuf) +{ + if (!cbBuf) + return VINF_SUCCESS; + return pZip->pfnCompress(pZip, pvBuf, cbBuf); +} +RT_EXPORT_SYMBOL(RTZipCompress); + + +/** + * Finishes the compression. + * This will flush all data and terminate the compression data stream. + * + * @returns iprt status code. + * @param pZip The compressor instance. + */ +RTDECL(int) RTZipCompFinish(PRTZIPCOMP pZip) +{ + return pZip->pfnFinish(pZip); +} +RT_EXPORT_SYMBOL(RTZipCompFinish); + + +/** + * Destroys the compressor instance. + * + * @returns iprt status code. + * @param pZip The compressor instance. + */ +RTDECL(int) RTZipCompDestroy(PRTZIPCOMP pZip) +{ + /* + * Compressor specific destruction attempt first. + */ + int rc = pZip->pfnDestroy(pZip); + AssertRCReturn(rc, rc); + + /* + * Free the instance memory. + */ + pZip->enmType = RTZIPTYPE_INVALID; + RTMemFree(pZip); + return VINF_SUCCESS; +} +RT_EXPORT_SYMBOL(RTZipCompDestroy); + + +/** + * @copydoc RTZipDecompress + */ +static DECLCALLBACK(int) rtZipStubDecompress(PRTZIPDECOMP pZip, void *pvBuf, size_t cbBuf, size_t *pcbWritten) +{ + NOREF(pZip); NOREF(pvBuf); NOREF(cbBuf); NOREF(pcbWritten); + return VERR_NOT_SUPPORTED; +} + + +/** + * @copydoc RTZipDecompDestroy + */ +static DECLCALLBACK(int) rtZipStubDecompDestroy(PRTZIPDECOMP pZip) +{ + NOREF(pZip); + return VINF_SUCCESS; +} + + +/** + * Create a decompressor instance. + * + * @returns iprt status code. + * @param ppZip Where to store the instance handle. + * @param pvUser User argument which will be passed on to pfnOut and pfnIn. + * @param pfnIn Callback for producing input for decompression. + */ +RTDECL(int) RTZipDecompCreate(PRTZIPDECOMP *ppZip, void *pvUser, PFNRTZIPIN pfnIn) +{ + /* + * Validate input. + */ + AssertPtrReturn(pfnIn, VERR_INVALID_POINTER); + AssertPtrReturn(ppZip, VERR_INVALID_POINTER); + + /* + * Allocate memory for the instance data. + */ + PRTZIPDECOMP pZip = (PRTZIPDECOMP)RTMemAlloc(sizeof(RTZIPDECOMP)); + if (!pZip) + return VERR_NO_MEMORY; + + /* + * Init instance. + */ + pZip->pfnIn = pfnIn; + pZip->enmType = RTZIPTYPE_INVALID; + pZip->pvUser = pvUser; + pZip->pfnDecompress = NULL; + pZip->pfnDestroy = rtZipStubDecompDestroy; + + *ppZip = pZip; + return VINF_SUCCESS; +} +RT_EXPORT_SYMBOL(RTZipDecompCreate); + + +/** + * Lazy init of the decompressor. + * @returns iprt status code. + * @param pZip The decompressor instance. + */ +static int rtzipDecompInit(PRTZIPDECOMP pZip) +{ + /* + * Read the first byte from the stream so we can determine the type. + */ + uint8_t u8Type; + int rc = pZip->pfnIn(pZip->pvUser, &u8Type, sizeof(u8Type), NULL); + if (RT_FAILURE(rc)) + return rc; + + /* + * Determine type and do type specific init. + */ + pZip->enmType = (RTZIPTYPE)u8Type; + rc = VERR_NOT_SUPPORTED; + switch (pZip->enmType) + { + case RTZIPTYPE_STORE: +#ifdef RTZIP_USE_STORE + rc = rtZipStoreDecompInit(pZip); +#else + AssertMsgFailed(("Store is not include in this build!\n")); +#endif + break; + + case RTZIPTYPE_ZLIB: + case RTZIPTYPE_ZLIB_NO_HEADER: +#ifdef RTZIP_USE_ZLIB + rc = rtZipZlibDecompInit(pZip, pZip->enmType == RTZIPTYPE_ZLIB /*fHeader*/); +#else + AssertMsgFailed(("Zlib is not include in this build!\n")); +#endif + break; + + case RTZIPTYPE_BZLIB: +#ifdef RTZIP_USE_BZLIB + rc = rtZipBZlibDecompInit(pZip); +#else + AssertMsgFailed(("BZlib is not include in this build!\n")); +#endif + break; + + case RTZIPTYPE_LZF: +#ifdef RTZIP_USE_LZF + rc = rtZipLZFDecompInit(pZip); +#else + AssertMsgFailed(("LZF is not include in this build!\n")); +#endif + break; + + case RTZIPTYPE_LZJB: +#ifdef RTZIP_USE_LZJB + AssertMsgFailed(("LZJB streaming support is not implemented yet!\n")); +#else + AssertMsgFailed(("LZJB is not include in this build!\n")); +#endif + break; + + case RTZIPTYPE_LZO: +#ifdef RTZIP_USE_LZJB + AssertMsgFailed(("LZO streaming support is not implemented yet!\n")); +#else + AssertMsgFailed(("LZO is not include in this build!\n")); +#endif + break; + + default: + AssertMsgFailed(("Invalid compression type %d (%#x)!\n", pZip->enmType, pZip->enmType)); + rc = VERR_INVALID_MAGIC; + break; + } + if (RT_FAILURE(rc)) + { + pZip->pfnDecompress = rtZipStubDecompress; + pZip->pfnDestroy = rtZipStubDecompDestroy; + } + + return rc; +} + + +/** + * Decompresses a chunk of memory. + * + * @returns iprt status code. + * @param pZip The decompressor instance. + * @param pvBuf Where to store the decompressed data. + * @param cbBuf Number of bytes to produce. If pcbWritten is set + * any number of bytes up to cbBuf might be returned. + * @param pcbWritten Number of bytes actually written to the buffer. If NULL + * cbBuf number of bytes must be written. + */ +RTDECL(int) RTZipDecompress(PRTZIPDECOMP pZip, void *pvBuf, size_t cbBuf, size_t *pcbWritten) +{ + /* + * Skip empty requests. + */ + if (!cbBuf) + return VINF_SUCCESS; + + /* + * Lazy init. + */ + if (!pZip->pfnDecompress) + { + int rc = rtzipDecompInit(pZip); + if (RT_FAILURE(rc)) + return rc; + } + + /* + * 'Read' the decompressed stream. + */ + return pZip->pfnDecompress(pZip, pvBuf, cbBuf, pcbWritten); +} +RT_EXPORT_SYMBOL(RTZipDecompress); + + +/** + * Destroys the decompressor instance. + * + * @returns iprt status code. + * @param pZip The decompressor instance. + */ +RTDECL(int) RTZipDecompDestroy(PRTZIPDECOMP pZip) +{ + /* + * Destroy compressor instance and flush the output buffer. + */ + int rc = pZip->pfnDestroy(pZip); + AssertRCReturn(rc, rc); + + /* + * Free the instance memory. + */ + pZip->enmType = RTZIPTYPE_INVALID; + RTMemFree(pZip); + return rc; +} +RT_EXPORT_SYMBOL(RTZipDecompDestroy); + + +RTDECL(int) RTZipBlockCompress(RTZIPTYPE enmType, RTZIPLEVEL enmLevel, uint32_t fFlags, + void const *pvSrc, size_t cbSrc, + void *pvDst, size_t cbDst, size_t *pcbDstActual) RT_NO_THROW_DEF +{ + /* input validation - the crash and burn approach as speed is essential here. */ + Assert(enmLevel <= RTZIPLEVEL_MAX && enmLevel >= RTZIPLEVEL_STORE); RT_NOREF_PV(enmLevel); + Assert(!fFlags); RT_NOREF_PV(fFlags); + + /* + * Deal with flags involving prefixes. + */ + /** @todo later: type and/or compressed length prefix. */ + + /* + * The type specific part. + */ + switch (enmType) + { + case RTZIPTYPE_LZF: + { +#ifdef RTZIP_USE_LZF +# if 0 + static const uint8_t s_abZero4K[] = + { + 0x01, 0x00, 0x00, 0xe0, 0xff, 0x00, 0xe0, 0xff, + 0x00, 0xe0, 0xff, 0x00, 0xe0, 0xff, 0x00, 0xe0, + 0xff, 0x00, 0xe0, 0xff, 0x00, 0xe0, 0xff, 0x00, + 0xe0, 0xff, 0x00, 0xe0, 0xff, 0x00, 0xe0, 0xff, + 0x00, 0xe0, 0xff, 0x00, 0xe0, 0xff, 0x00, 0xe0, + 0xff, 0x00, 0xe0, 0xff, 0x00, 0xe0, 0xff, 0x00, + 0xe0, 0x7d, 0x00 + }; + if ( cbSrc == _4K + && !((uintptr_t)pvSrc & 15) + && ASMMemIsZeroPage(pvSrc)) + { + if (RT_UNLIKELY(cbDst < sizeof(s_abZero4K))) + return VERR_BUFFER_OVERFLOW; + memcpy(pvDst, s_abZero4K, sizeof(s_abZero4K)); + *pcbDstActual = sizeof(s_abZero4K); + break; + } +# endif + + unsigned cbDstActual = lzf_compress(pvSrc, (unsigned)cbSrc, pvDst, (unsigned)cbDst); /** @todo deal with size type overflows */ + if (RT_UNLIKELY(cbDstActual < 1)) + return VERR_BUFFER_OVERFLOW; + *pcbDstActual = cbDstActual; + break; +#else + return VERR_NOT_SUPPORTED; +#endif + } + + case RTZIPTYPE_STORE: + { + if (cbDst < cbSrc) + return VERR_BUFFER_OVERFLOW; + memcpy(pvDst, pvSrc, cbSrc); + *pcbDstActual = cbSrc; + break; + } + + case RTZIPTYPE_LZJB: + { +#ifdef RTZIP_USE_LZJB + AssertReturn(cbDst > cbSrc, VERR_BUFFER_OVERFLOW); + size_t cbDstActual = lzjb_compress((void *)pvSrc, (uint8_t *)pvDst + 1, cbSrc, cbSrc, 0 /*??*/); + if (cbDstActual == cbSrc) + *(uint8_t *)pvDst = 0; + else + *(uint8_t *)pvDst = 1; + *pcbDstActual = cbDstActual + 1; + break; +#else + return VERR_NOT_SUPPORTED; +#endif + } + + case RTZIPTYPE_LZO: + { +#ifdef RTZIP_USE_LZO + uint64_t Scratch[RT_ALIGN(LZO1X_1_MEM_COMPRESS, sizeof(uint64_t)) / sizeof(uint64_t)]; + int rc = lzo_init(); + if (RT_UNLIKELY(rc != LZO_E_OK)) + return VERR_INTERNAL_ERROR; + + lzo_uint cbDstInOut = cbDst; + rc = lzo1x_1_compress((const lzo_bytep)pvSrc, cbSrc, (lzo_bytep )pvDst, &cbDstInOut, &Scratch[0]); + if (RT_UNLIKELY(rc != LZO_E_OK)) + switch (rc) + { + case LZO_E_OUTPUT_OVERRUN: return VERR_BUFFER_OVERFLOW; + default: return VERR_GENERAL_FAILURE; + } + *pcbDstActual = cbDstInOut; + break; +#else + return VERR_NOT_SUPPORTED; +#endif + } + + case RTZIPTYPE_ZLIB: + case RTZIPTYPE_BZLIB: + return VERR_NOT_SUPPORTED; + + default: + AssertMsgFailed(("%d\n", enmType)); + return VERR_INVALID_PARAMETER; + } + + return VINF_SUCCESS; +} +RT_EXPORT_SYMBOL(RTZipBlockCompress); + + +RTDECL(int) RTZipBlockDecompress(RTZIPTYPE enmType, uint32_t fFlags, + void const *pvSrc, size_t cbSrc, size_t *pcbSrcActual, + void *pvDst, size_t cbDst, size_t *pcbDstActual) RT_NO_THROW_DEF +{ + /* input validation - the crash and burn approach as speed is essential here. */ + Assert(!fFlags); RT_NOREF_PV(fFlags); + + /* + * Deal with flags involving prefixes. + */ + /** @todo later: type and/or compressed length prefix. */ + + /* + * The type specific part. + */ + switch (enmType) + { + case RTZIPTYPE_LZF: + { +#ifdef RTZIP_USE_LZF + unsigned cbDstActual = lzf_decompress(pvSrc, (unsigned)cbSrc, pvDst, (unsigned)cbDst); /** @todo deal with size type overflows */ + if (RT_UNLIKELY(cbDstActual < 1)) + { + if (errno == E2BIG) + return VERR_BUFFER_OVERFLOW; + Assert(errno == EINVAL); + return VERR_GENERAL_FAILURE; + } + if (pcbDstActual) + *pcbDstActual = cbDstActual; + if (pcbSrcActual) + *pcbSrcActual = cbSrc; + break; +#else + return VERR_NOT_SUPPORTED; +#endif + } + + case RTZIPTYPE_STORE: + { + if (cbDst < cbSrc) + return VERR_BUFFER_OVERFLOW; + memcpy(pvDst, pvSrc, cbSrc); + if (pcbDstActual) + *pcbDstActual = cbSrc; + if (pcbSrcActual) + *pcbSrcActual = cbSrc; + break; + } + + case RTZIPTYPE_LZJB: + { +#ifdef RTZIP_USE_LZJB + if (*(uint8_t *)pvSrc == 1) + { + int rc = lzjb_decompress((uint8_t *)pvSrc + 1, pvDst, cbSrc - 1, cbDst, 0 /*??*/); + if (RT_UNLIKELY(rc != 0)) + return VERR_GENERAL_FAILURE; + if (pcbDstActual) + *pcbDstActual = cbDst; + } + else + { + AssertReturn(cbDst >= cbSrc - 1, VERR_BUFFER_OVERFLOW); + memcpy(pvDst, (uint8_t *)pvSrc + 1, cbSrc - 1); + if (pcbDstActual) + *pcbDstActual = cbSrc - 1; + } + if (pcbSrcActual) + *pcbSrcActual = cbSrc; + break; +#else + return VERR_NOT_SUPPORTED; +#endif + } + + case RTZIPTYPE_LZO: + { +#ifdef RTZIP_USE_LZO + int rc = lzo_init(); + if (RT_UNLIKELY(rc != LZO_E_OK)) + return VERR_INTERNAL_ERROR; + lzo_uint cbDstInOut = cbDst; + rc = lzo1x_decompress((const lzo_bytep)pvSrc, cbSrc, (lzo_bytep)pvDst, &cbDstInOut, NULL); + if (RT_UNLIKELY(rc != LZO_E_OK)) + switch (rc) + { + case LZO_E_OUTPUT_OVERRUN: return VERR_BUFFER_OVERFLOW; + default: + case LZO_E_INPUT_OVERRUN: return VERR_GENERAL_FAILURE; + } + if (pcbSrcActual) + *pcbSrcActual = cbSrc; + if (pcbDstActual) + *pcbDstActual = cbDstInOut; + break; +#else + return VERR_NOT_SUPPORTED; +#endif + } + + case RTZIPTYPE_ZLIB: + { +#ifdef RTZIP_USE_ZLIB + AssertReturn(cbSrc == (uInt)cbSrc, VERR_TOO_MUCH_DATA); + AssertReturn(cbDst == (uInt)cbDst, VERR_OUT_OF_RANGE); + + z_stream ZStrm; + RT_ZERO(ZStrm); + ZStrm.next_in = (Bytef *)pvSrc; + ZStrm.avail_in = (uInt)cbSrc; + ZStrm.next_out = (Bytef *)pvDst; + ZStrm.avail_out = (uInt)cbDst; + + int rc = inflateInit(&ZStrm); + if (RT_UNLIKELY(rc != Z_OK)) + return zipErrConvertFromZlib(rc, false /*fCompressing*/); + rc = inflate(&ZStrm, Z_FINISH); + if (rc != Z_STREAM_END) + { + inflateEnd(&ZStrm); + if ((rc == Z_BUF_ERROR && ZStrm.avail_in == 0) || rc == Z_NEED_DICT) + return VERR_ZIP_CORRUPTED; + if (rc == Z_BUF_ERROR) + return VERR_BUFFER_OVERFLOW; + AssertReturn(rc < Z_OK, VERR_GENERAL_FAILURE); + return zipErrConvertFromZlib(rc, false /*fCompressing*/); + } + rc = inflateEnd(&ZStrm); + if (rc != Z_OK) + return zipErrConvertFromZlib(rc, false /*fCompressing*/); + + if (pcbSrcActual) + *pcbSrcActual = cbSrc - ZStrm.avail_in; + if (pcbDstActual) + *pcbDstActual = ZStrm.total_out; + break; +#else + return VERR_NOT_SUPPORTED; +#endif + } + + case RTZIPTYPE_BZLIB: + return VERR_NOT_SUPPORTED; + + default: + AssertMsgFailed(("%d\n", enmType)); + return VERR_INVALID_PARAMETER; + } + return VINF_SUCCESS; +} +RT_EXPORT_SYMBOL(RTZipBlockDecompress); + |