summaryrefslogtreecommitdiffstats
path: root/lib/lz4frame.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/lz4frame.c')
-rw-r--r--lib/lz4frame.c1330
1 files changed, 1330 insertions, 0 deletions
diff --git a/lib/lz4frame.c b/lib/lz4frame.c
new file mode 100644
index 0000000..aa46152
--- /dev/null
+++ b/lib/lz4frame.c
@@ -0,0 +1,1330 @@
+/*
+LZ4 auto-framing library
+Copyright (C) 2011-2014, Yann Collet.
+BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+You can contact the author at :
+- LZ4 source repository : http://code.google.com/p/lz4/
+- LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c
+*/
+
+/* LZ4F is a stand-alone API to create LZ4-compressed Frames
+* fully conformant to specification v1.4.1.
+* All related operations, including memory management, are handled by the library.
+* */
+
+
+/**************************************
+Compiler Options
+**************************************/
+#ifdef _MSC_VER /* Visual Studio */
+# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
+#endif
+
+#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
+#ifdef __GNUC__
+# pragma GCC diagnostic ignored "-Wmissing-braces" /* GCC bug 53119 : doesn't accept { 0 } as initializer (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53119) */
+# pragma GCC diagnostic ignored "-Wmissing-field-initializers" /* GCC bug 53119 : doesn't accept { 0 } as initializer (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53119) */
+#endif
+
+
+/**************************************
+Memory routines
+**************************************/
+#include <stdlib.h> /* malloc, calloc, free */
+#define ALLOCATOR(s) calloc(1,s)
+#define FREEMEM free
+#include <string.h> /* memset, memcpy, memmove */
+#define MEM_INIT memset
+
+
+/**************************************
+Includes
+**************************************/
+#include "lz4frame.h"
+#include "lz4.h"
+#include "lz4hc.h"
+#include "xxhash.h"
+
+
+/**************************************
+Basic Types
+**************************************/
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */
+# include <stdint.h>
+typedef uint8_t BYTE;
+typedef uint16_t U16;
+typedef uint32_t U32;
+typedef int32_t S32;
+typedef uint64_t U64;
+#else
+typedef unsigned char BYTE;
+typedef unsigned short U16;
+typedef unsigned int U32;
+typedef signed int S32;
+typedef unsigned long long U64;
+#endif
+
+
+/**************************************
+Constants
+**************************************/
+#define KB *(1<<10)
+#define MB *(1<<20)
+#define GB *(1<<30)
+
+#define _1BIT 0x01
+#define _2BITS 0x03
+#define _3BITS 0x07
+#define _4BITS 0x0F
+#define _8BITS 0xFF
+
+#define LZ4F_MAGICNUMBER 0x184D2204U
+#define LZ4F_BLOCKUNCOMPRESSED_FLAG 0x80000000U
+#define LZ4F_MAXHEADERFRAME_SIZE 7
+#define LZ4F_BLOCKSIZEID_DEFAULT max64KB
+
+static const U32 minHClevel = 3;
+
+/**************************************
+Structures and local types
+**************************************/
+typedef struct
+{
+ LZ4F_preferences_t prefs;
+ U32 version;
+ U32 cStage;
+ size_t maxBlockSize;
+ size_t maxBufferSize;
+ BYTE* tmpBuff;
+ BYTE* tmpIn;
+ size_t tmpInSize;
+ XXH32_state_t xxh;
+ void* lz4CtxPtr;
+ U32 lz4CtxLevel; /* 0: unallocated; 1: LZ4_stream_t; 3: LZ4_streamHC_t */
+} LZ4F_cctx_internal_t;
+
+typedef struct
+{
+ LZ4F_frameInfo_t frameInfo;
+ unsigned version;
+ unsigned dStage;
+ size_t maxBlockSize;
+ size_t maxBufferSize;
+ const BYTE* srcExpect;
+ BYTE* tmpIn;
+ size_t tmpInSize;
+ size_t tmpInTarget;
+ BYTE* tmpOutBuffer;
+ BYTE* dict;
+ size_t dictSize;
+ BYTE* tmpOut;
+ size_t tmpOutSize;
+ size_t tmpOutStart;
+ XXH32_state_t xxh;
+ BYTE header[8];
+} LZ4F_dctx_internal_t;
+
+
+/**************************************
+Macros
+**************************************/
+
+
+/**************************************
+Error management
+**************************************/
+#define LZ4F_GENERATE_STRING(STRING) #STRING,
+static const char* LZ4F_errorStrings[] = { LZ4F_LIST_ERRORS(LZ4F_GENERATE_STRING) };
+
+
+int LZ4F_isError(LZ4F_errorCode_t code)
+{
+ return (code > (LZ4F_errorCode_t)(-ERROR_maxCode));
+}
+
+const char* LZ4F_getErrorName(LZ4F_errorCode_t code)
+{
+ static const char* codeError = "Unspecified error code";
+ if (LZ4F_isError(code)) return LZ4F_errorStrings[-(int)(code)];
+ return codeError;
+}
+
+
+/**************************************
+Private functions
+**************************************/
+static size_t LZ4F_getBlockSize(unsigned blockSizeID)
+{
+ static const size_t blockSizes[4] = { 64 KB, 256 KB, 1 MB, 4 MB };
+
+ if (blockSizeID == 0) blockSizeID = LZ4F_BLOCKSIZEID_DEFAULT;
+ blockSizeID -= 4;
+ if (blockSizeID > 3) return (size_t)-ERROR_maxBlockSize_invalid;
+ return blockSizes[blockSizeID];
+}
+
+
+/* unoptimized version; solves endianess & alignment issues */
+static void LZ4F_writeLE32 (BYTE* dstPtr, U32 value32)
+{
+ dstPtr[0] = (BYTE)value32;
+ dstPtr[1] = (BYTE)(value32 >> 8);
+ dstPtr[2] = (BYTE)(value32 >> 16);
+ dstPtr[3] = (BYTE)(value32 >> 24);
+}
+
+static U32 LZ4F_readLE32 (const BYTE* srcPtr)
+{
+ U32 value32 = srcPtr[0];
+ value32 += (srcPtr[1]<<8);
+ value32 += (srcPtr[2]<<16);
+ value32 += (srcPtr[3]<<24);
+ return value32;
+}
+
+
+static BYTE LZ4F_headerChecksum (const BYTE* header, size_t length)
+{
+ U32 xxh = XXH32(header, (U32)length, 0);
+ return (BYTE)(xxh >> 8);
+}
+
+
+/**************************************
+Simple compression functions
+**************************************/
+size_t LZ4F_compressFrameBound(size_t srcSize, const LZ4F_preferences_t* preferencesPtr)
+{
+ LZ4F_preferences_t prefs = { 0 };
+ size_t headerSize;
+ size_t streamSize;
+
+ if (preferencesPtr!=NULL) prefs = *preferencesPtr;
+ {
+ blockSizeID_t proposedBSID = max64KB;
+ size_t maxBlockSize = 64 KB;
+ while (prefs.frameInfo.blockSizeID > proposedBSID)
+ {
+ if (srcSize <= maxBlockSize)
+ {
+ prefs.frameInfo.blockSizeID = proposedBSID;
+ break;
+ }
+ proposedBSID++;
+ maxBlockSize <<= 2;
+ }
+ }
+ prefs.autoFlush = 1;
+
+ headerSize = 7; /* basic header size (no option) including magic number */
+ streamSize = LZ4F_compressBound(srcSize, &prefs);
+
+ return headerSize + streamSize;
+}
+
+
+/* LZ4F_compressFrame()
+* Compress an entire srcBuffer into a valid LZ4 frame, as defined by specification v1.4.1, in a single step.
+* The most important rule is that dstBuffer MUST be large enough (dstMaxSize) to ensure compression completion even in worst case.
+* You can get the minimum value of dstMaxSize by using LZ4F_compressFrameBound()
+* If this condition is not respected, LZ4F_compressFrame() will fail (result is an errorCode)
+* The LZ4F_preferences_t structure is optional : you can provide NULL as argument. All preferences will be set to default.
+* The result of the function is the number of bytes written into dstBuffer.
+* The function outputs an error code if it fails (can be tested using LZ4F_isError())
+*/
+size_t LZ4F_compressFrame(void* dstBuffer, size_t dstMaxSize, const void* srcBuffer, size_t srcSize, const LZ4F_preferences_t* preferencesPtr)
+{
+ LZ4F_cctx_internal_t cctxI = { 0 }; /* works because no allocation */
+ LZ4F_preferences_t prefs = { 0 };
+ LZ4F_compressOptions_t options = { 0 };
+ LZ4F_errorCode_t errorCode;
+ BYTE* const dstStart = (BYTE*) dstBuffer;
+ BYTE* dstPtr = dstStart;
+ BYTE* const dstEnd = dstStart + dstMaxSize;
+
+
+ cctxI.version = LZ4F_VERSION;
+ cctxI.maxBufferSize = 5 MB; /* mess with real buffer size to prevent allocation; works because autoflush==1 & stableSrc==1 */
+
+ if (preferencesPtr!=NULL) prefs = *preferencesPtr;
+ {
+ blockSizeID_t proposedBSID = max64KB;
+ size_t maxBlockSize = 64 KB;
+ while (prefs.frameInfo.blockSizeID > proposedBSID)
+ {
+ if (srcSize <= maxBlockSize)
+ {
+ prefs.frameInfo.blockSizeID = proposedBSID;
+ break;
+ }
+ proposedBSID++;
+ maxBlockSize <<= 2;
+ }
+ }
+ prefs.autoFlush = 1;
+ if (srcSize <= LZ4F_getBlockSize(prefs.frameInfo.blockSizeID))
+ prefs.frameInfo.blockMode = blockIndependent; /* no need for linked blocks */
+
+ options.stableSrc = 1;
+
+ if (dstMaxSize < LZ4F_compressFrameBound(srcSize, &prefs))
+ return (size_t)-ERROR_dstMaxSize_tooSmall;
+
+ errorCode = LZ4F_compressBegin(&cctxI, dstBuffer, dstMaxSize, &prefs); /* write header */
+ if (LZ4F_isError(errorCode)) return errorCode;
+ dstPtr += errorCode; /* header size */
+
+ dstMaxSize -= errorCode;
+ errorCode = LZ4F_compressUpdate(&cctxI, dstPtr, dstMaxSize, srcBuffer, srcSize, &options);
+ if (LZ4F_isError(errorCode)) return errorCode;
+ dstPtr += errorCode;
+
+ errorCode = LZ4F_compressEnd(&cctxI, dstPtr, dstEnd-dstPtr, &options); /* flush last block, and generate suffix */
+ if (LZ4F_isError(errorCode)) return errorCode;
+ dstPtr += errorCode;
+
+ FREEMEM(cctxI.lz4CtxPtr);
+
+ return (dstPtr - dstStart);
+}
+
+
+/***********************************
+* Advanced compression functions
+* *********************************/
+
+/* LZ4F_createCompressionContext() :
+* The first thing to do is to create a compressionContext object, which will be used in all compression operations.
+* This is achieved using LZ4F_createCompressionContext(), which takes as argument a version and an LZ4F_preferences_t structure.
+* The version provided MUST be LZ4F_VERSION. It is intended to track potential version differences between different binaries.
+* The function will provide a pointer to an allocated LZ4F_compressionContext_t object.
+* If the result LZ4F_errorCode_t is not OK_NoError, there was an error during context creation.
+* Object can release its memory using LZ4F_freeCompressionContext();
+*/
+LZ4F_errorCode_t LZ4F_createCompressionContext(LZ4F_compressionContext_t* LZ4F_compressionContextPtr, unsigned version)
+{
+ LZ4F_cctx_internal_t* cctxPtr;
+
+ cctxPtr = (LZ4F_cctx_internal_t*)ALLOCATOR(sizeof(LZ4F_cctx_internal_t));
+ if (cctxPtr==NULL) return (LZ4F_errorCode_t)(-ERROR_allocation_failed);
+
+ cctxPtr->version = version;
+ cctxPtr->cStage = 0; /* Next stage : write header */
+
+ *LZ4F_compressionContextPtr = (LZ4F_compressionContext_t)cctxPtr;
+
+ return OK_NoError;
+}
+
+
+LZ4F_errorCode_t LZ4F_freeCompressionContext(LZ4F_compressionContext_t LZ4F_compressionContext)
+{
+ LZ4F_cctx_internal_t* cctxPtr = (LZ4F_cctx_internal_t*)LZ4F_compressionContext;
+
+ FREEMEM(cctxPtr->lz4CtxPtr);
+ FREEMEM(cctxPtr->tmpBuff);
+ FREEMEM(LZ4F_compressionContext);
+
+ return OK_NoError;
+}
+
+
+/* LZ4F_compressBegin() :
+* will write the frame header into dstBuffer.
+* dstBuffer must be large enough to accommodate a header (dstMaxSize). Maximum header size is LZ4F_MAXHEADERFRAME_SIZE bytes.
+* The result of the function is the number of bytes written into dstBuffer for the header
+* or an error code (can be tested using LZ4F_isError())
+*/
+size_t LZ4F_compressBegin(LZ4F_compressionContext_t compressionContext, void* dstBuffer, size_t dstMaxSize, const LZ4F_preferences_t* preferencesPtr)
+{
+ LZ4F_preferences_t prefNull = { 0 };
+ LZ4F_cctx_internal_t* cctxPtr = (LZ4F_cctx_internal_t*)compressionContext;
+ BYTE* const dstStart = (BYTE*)dstBuffer;
+ BYTE* dstPtr = dstStart;
+ BYTE* headerStart;
+ size_t requiredBuffSize;
+
+ if (dstMaxSize < LZ4F_MAXHEADERFRAME_SIZE) return (size_t)-ERROR_dstMaxSize_tooSmall;
+ if (cctxPtr->cStage != 0) return (size_t)-ERROR_GENERIC;
+ if (preferencesPtr == NULL) preferencesPtr = &prefNull;
+ cctxPtr->prefs = *preferencesPtr;
+
+ /* ctx Management */
+ {
+ U32 targetCtxLevel = cctxPtr->prefs.compressionLevel<minHClevel ? 1 : 2;
+ if (cctxPtr->lz4CtxLevel < targetCtxLevel)
+ {
+ FREEMEM(cctxPtr->lz4CtxPtr);
+ if (cctxPtr->prefs.compressionLevel<minHClevel)
+ cctxPtr->lz4CtxPtr = (void*)LZ4_createStream();
+ else
+ cctxPtr->lz4CtxPtr = (void*)LZ4_createStreamHC();
+ cctxPtr->lz4CtxLevel = targetCtxLevel;
+ }
+ }
+
+ /* Buffer Management */
+ if (cctxPtr->prefs.frameInfo.blockSizeID == 0) cctxPtr->prefs.frameInfo.blockSizeID = LZ4F_BLOCKSIZEID_DEFAULT;
+ cctxPtr->maxBlockSize = LZ4F_getBlockSize(cctxPtr->prefs.frameInfo.blockSizeID);
+
+ requiredBuffSize = cctxPtr->maxBlockSize + ((cctxPtr->prefs.frameInfo.blockMode == blockLinked) * 128 KB);
+ if (preferencesPtr->autoFlush)
+ requiredBuffSize = (cctxPtr->prefs.frameInfo.blockMode == blockLinked) * 64 KB; /* just needs dict */
+
+ if (cctxPtr->maxBufferSize < requiredBuffSize)
+ {
+ cctxPtr->maxBufferSize = requiredBuffSize;
+ FREEMEM(cctxPtr->tmpBuff);
+ cctxPtr->tmpBuff = (BYTE*)ALLOCATOR(requiredBuffSize);
+ if (cctxPtr->tmpBuff == NULL) return (size_t)-ERROR_allocation_failed;
+ }
+ cctxPtr->tmpIn = cctxPtr->tmpBuff;
+ cctxPtr->tmpInSize = 0;
+ XXH32_reset(&(cctxPtr->xxh), 0);
+ if (cctxPtr->prefs.compressionLevel<minHClevel)
+ LZ4_resetStream((LZ4_stream_t*)(cctxPtr->lz4CtxPtr));
+ else
+ LZ4_resetStreamHC((LZ4_streamHC_t*)(cctxPtr->lz4CtxPtr), cctxPtr->prefs.compressionLevel);
+
+ /* Magic Number */
+ LZ4F_writeLE32(dstPtr, LZ4F_MAGICNUMBER);
+ dstPtr += 4;
+ headerStart = dstPtr;
+
+ /* FLG Byte */
+ *dstPtr++ = ((1 & _2BITS) << 6) /* Version('01') */
+ + ((cctxPtr->prefs.frameInfo.blockMode & _1BIT ) << 5) /* Block mode */
+ + (char)((cctxPtr->prefs.frameInfo.contentChecksumFlag & _1BIT ) << 2); /* Stream checksum */
+ /* BD Byte */
+ *dstPtr++ = (char)((cctxPtr->prefs.frameInfo.blockSizeID & _3BITS) << 4);
+ /* CRC Byte */
+ *dstPtr++ = LZ4F_headerChecksum(headerStart, 2);
+
+ cctxPtr->cStage = 1; /* header written, wait for data block */
+
+ return (dstPtr - dstStart);
+}
+
+
+/* LZ4F_compressBound() : gives the size of Dst buffer given a srcSize to handle worst case situations.
+* The LZ4F_frameInfo_t structure is optional :
+* you can provide NULL as argument, all preferences will then be set to default.
+* */
+size_t LZ4F_compressBound(size_t srcSize, const LZ4F_preferences_t* preferencesPtr)
+{
+ const LZ4F_preferences_t prefsNull = { 0 };
+ const LZ4F_preferences_t* prefsPtr = (preferencesPtr==NULL) ? &prefsNull : preferencesPtr;
+ blockSizeID_t bid = prefsPtr->frameInfo.blockSizeID;
+ size_t blockSize = LZ4F_getBlockSize(bid);
+ unsigned nbBlocks = (unsigned)(srcSize / blockSize) + 1;
+ size_t lastBlockSize = prefsPtr->autoFlush ? srcSize % blockSize : blockSize;
+ size_t blockInfo = 4; /* default, without block CRC option */
+ size_t frameEnd = 4 + (prefsPtr->frameInfo.contentChecksumFlag*4);
+ size_t result = (blockInfo * nbBlocks) + (blockSize * (nbBlocks-1)) + lastBlockSize + frameEnd;
+
+ return result;
+}
+
+
+typedef int (*compressFunc_t)(void* ctx, const char* src, char* dst, int srcSize, int dstSize, int level);
+
+static size_t LZ4F_compressBlock(void* dst, const void* src, size_t srcSize, compressFunc_t compress, void* lz4ctx, int level)
+{
+ /* compress one block */
+ BYTE* cSizePtr = (BYTE*)dst;
+ U32 cSize;
+ cSize = (U32)compress(lz4ctx, (const char*)src, (char*)(cSizePtr+4), (int)(srcSize), (int)(srcSize-1), level);
+ LZ4F_writeLE32(cSizePtr, cSize);
+ if (cSize == 0) /* compression failed */
+ {
+ cSize = (U32)srcSize;
+ LZ4F_writeLE32(cSizePtr, cSize + LZ4F_BLOCKUNCOMPRESSED_FLAG);
+ memcpy(cSizePtr+4, src, srcSize);
+ }
+ return cSize + 4;
+}
+
+
+static int LZ4F_localLZ4_compress_limitedOutput_withState(void* ctx, const char* src, char* dst, int srcSize, int dstSize, int level)
+{
+ (void) level;
+ return LZ4_compress_limitedOutput_withState(ctx, src, dst, srcSize, dstSize);
+}
+
+static int LZ4F_localLZ4_compress_limitedOutput_continue(void* ctx, const char* src, char* dst, int srcSize, int dstSize, int level)
+{
+ (void) level;
+ return LZ4_compress_limitedOutput_continue((LZ4_stream_t*)ctx, src, dst, srcSize, dstSize);
+}
+
+static int LZ4F_localLZ4_compressHC_limitedOutput_continue(void* ctx, const char* src, char* dst, int srcSize, int dstSize, int level)
+{
+ (void) level;
+ return LZ4_compressHC_limitedOutput_continue((LZ4_streamHC_t*)ctx, src, dst, srcSize, dstSize);
+}
+
+static compressFunc_t LZ4F_selectCompression(blockMode_t blockMode, U32 level)
+{
+ if (level < minHClevel)
+ {
+ if (blockMode == blockIndependent) return LZ4F_localLZ4_compress_limitedOutput_withState;
+ return LZ4F_localLZ4_compress_limitedOutput_continue;
+ }
+ if (blockMode == blockIndependent) return LZ4_compressHC2_limitedOutput_withStateHC;
+ return LZ4F_localLZ4_compressHC_limitedOutput_continue;
+}
+
+static int LZ4F_localSaveDict(LZ4F_cctx_internal_t* cctxPtr)
+{
+ if (cctxPtr->prefs.compressionLevel < minHClevel)
+ return LZ4_saveDict ((LZ4_stream_t*)(cctxPtr->lz4CtxPtr), (char*)(cctxPtr->tmpBuff), 64 KB);
+ return LZ4_saveDictHC ((LZ4_streamHC_t*)(cctxPtr->lz4CtxPtr), (char*)(cctxPtr->tmpBuff), 64 KB);
+}
+
+typedef enum { notDone, fromTmpBuffer, fromSrcBuffer } LZ4F_lastBlockStatus;
+
+/* LZ4F_compressUpdate()
+* LZ4F_compressUpdate() can be called repetitively to compress as much data as necessary.
+* The most important rule is that dstBuffer MUST be large enough (dstMaxSize) to ensure compression completion even in worst case.
+* If this condition is not respected, LZ4F_compress() will fail (result is an errorCode)
+* You can get the minimum value of dstMaxSize by using LZ4F_compressBound()
+* The LZ4F_compressOptions_t structure is optional : you can provide NULL as argument.
+* The result of the function is the number of bytes written into dstBuffer : it can be zero, meaning input data was just buffered.
+* The function outputs an error code if it fails (can be tested using LZ4F_isError())
+*/
+size_t LZ4F_compressUpdate(LZ4F_compressionContext_t compressionContext, void* dstBuffer, size_t dstMaxSize, const void* srcBuffer, size_t srcSize, const LZ4F_compressOptions_t* compressOptionsPtr)
+{
+ LZ4F_compressOptions_t cOptionsNull = { 0 };
+ LZ4F_cctx_internal_t* cctxPtr = (LZ4F_cctx_internal_t*)compressionContext;
+ size_t blockSize = cctxPtr->maxBlockSize;
+ const BYTE* srcPtr = (const BYTE*)srcBuffer;
+ const BYTE* const srcEnd = srcPtr + srcSize;
+ BYTE* const dstStart = (BYTE*)dstBuffer;
+ BYTE* dstPtr = dstStart;
+ LZ4F_lastBlockStatus lastBlockCompressed = notDone;
+ compressFunc_t compress;
+
+
+ if (cctxPtr->cStage != 1) return (size_t)-ERROR_GENERIC;
+ if (dstMaxSize < LZ4F_compressBound(srcSize, &(cctxPtr->prefs))) return (size_t)-ERROR_dstMaxSize_tooSmall;
+ if (compressOptionsPtr == NULL) compressOptionsPtr = &cOptionsNull;
+
+ /* select compression function */
+ compress = LZ4F_selectCompression(cctxPtr->prefs.frameInfo.blockMode, cctxPtr->prefs.compressionLevel);
+
+ /* complete tmp buffer */
+ if (cctxPtr->tmpInSize > 0) /* some data already within tmp buffer */
+ {
+ size_t sizeToCopy = blockSize - cctxPtr->tmpInSize;
+ if (sizeToCopy > srcSize)
+ {
+ /* add src to tmpIn buffer */
+ memcpy(cctxPtr->tmpIn + cctxPtr->tmpInSize, srcBuffer, srcSize);
+ srcPtr = srcEnd;
+ cctxPtr->tmpInSize += srcSize;
+ /* still needs some CRC */
+ }
+ else
+ {
+ /* complete tmpIn block and then compress it */
+ lastBlockCompressed = fromTmpBuffer;
+ memcpy(cctxPtr->tmpIn + cctxPtr->tmpInSize, srcBuffer, sizeToCopy);
+ srcPtr += sizeToCopy;
+
+ dstPtr += LZ4F_compressBlock(dstPtr, cctxPtr->tmpIn, blockSize, compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel);
+
+ if (cctxPtr->prefs.frameInfo.blockMode==blockLinked) cctxPtr->tmpIn += blockSize;
+ cctxPtr->tmpInSize = 0;
+ }
+ }
+
+ while ((size_t)(srcEnd - srcPtr) >= blockSize)
+ {
+ /* compress full block */
+ lastBlockCompressed = fromSrcBuffer;
+ dstPtr += LZ4F_compressBlock(dstPtr, srcPtr, blockSize, compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel);
+ srcPtr += blockSize;
+ }
+
+ if ((cctxPtr->prefs.autoFlush) && (srcPtr < srcEnd))
+ {
+ /* compress remaining input < blockSize */
+ lastBlockCompressed = fromSrcBuffer;
+ dstPtr += LZ4F_compressBlock(dstPtr, srcPtr, srcEnd - srcPtr, compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel);
+ srcPtr = srcEnd;
+ }
+
+ /* preserve dictionary if necessary */
+ if ((cctxPtr->prefs.frameInfo.blockMode==blockLinked) && (lastBlockCompressed==fromSrcBuffer))
+ {
+ if (compressOptionsPtr->stableSrc)
+ {
+ cctxPtr->tmpIn = cctxPtr->tmpBuff;
+ }
+ else
+ {
+ int realDictSize = LZ4F_localSaveDict(cctxPtr);
+ if (realDictSize==0) return (size_t)-ERROR_GENERIC;
+ cctxPtr->tmpIn = cctxPtr->tmpBuff + realDictSize;
+ }
+ }
+
+ /* keep tmpIn within limits */
+ if ((cctxPtr->tmpIn + blockSize) > (cctxPtr->tmpBuff + cctxPtr->maxBufferSize) /* necessarily blockLinked && lastBlockCompressed==fromTmpBuffer */
+ && !(cctxPtr->prefs.autoFlush))
+ {
+ LZ4F_localSaveDict(cctxPtr);
+ cctxPtr->tmpIn = cctxPtr->tmpBuff + 64 KB;
+ }
+
+ /* some input data left, necessarily < blockSize */
+ if (srcPtr < srcEnd)
+ {
+ /* fill tmp buffer */
+ size_t sizeToCopy = srcEnd - srcPtr;
+ memcpy(cctxPtr->tmpIn, srcPtr, sizeToCopy);
+ cctxPtr->tmpInSize = sizeToCopy;
+ }
+
+ if (cctxPtr->prefs.frameInfo.contentChecksumFlag == contentChecksumEnabled)
+ XXH32_update(&(cctxPtr->xxh), srcBuffer, (unsigned)srcSize);
+
+ return dstPtr - dstStart;
+}
+
+
+/* LZ4F_flush()
+* Should you need to create compressed data immediately, without waiting for a block to be filled,
+* you can call LZ4_flush(), which will immediately compress any remaining data stored within compressionContext.
+* The result of the function is the number of bytes written into dstBuffer
+* (it can be zero, this means there was no data left within compressionContext)
+* The function outputs an error code if it fails (can be tested using LZ4F_isError())
+* The LZ4F_compressOptions_t structure is optional : you can provide NULL as argument.
+*/
+size_t LZ4F_flush(LZ4F_compressionContext_t compressionContext, void* dstBuffer, size_t dstMaxSize, const LZ4F_compressOptions_t* compressOptionsPtr)
+{
+ LZ4F_compressOptions_t cOptionsNull = { 0 };
+ LZ4F_cctx_internal_t* cctxPtr = (LZ4F_cctx_internal_t*)compressionContext;
+ BYTE* const dstStart = (BYTE*)dstBuffer;
+ BYTE* dstPtr = dstStart;
+ compressFunc_t compress;
+
+
+ if (cctxPtr->tmpInSize == 0) return 0; /* nothing to flush */
+ if (cctxPtr->cStage != 1) return (size_t)-ERROR_GENERIC;
+ if (dstMaxSize < (cctxPtr->tmpInSize + 16)) return (size_t)-ERROR_dstMaxSize_tooSmall;
+ if (compressOptionsPtr == NULL) compressOptionsPtr = &cOptionsNull;
+ (void)compressOptionsPtr; /* not yet useful */
+
+ /* select compression function */
+ compress = LZ4F_selectCompression(cctxPtr->prefs.frameInfo.blockMode, cctxPtr->prefs.compressionLevel);
+
+ /* compress tmp buffer */
+ dstPtr += LZ4F_compressBlock(dstPtr, cctxPtr->tmpIn, cctxPtr->tmpInSize, compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel);
+ if (cctxPtr->prefs.frameInfo.blockMode==blockLinked) cctxPtr->tmpIn += cctxPtr->tmpInSize;
+ cctxPtr->tmpInSize = 0;
+
+ /* keep tmpIn within limits */
+ if ((cctxPtr->tmpIn + cctxPtr->maxBlockSize) > (cctxPtr->tmpBuff + cctxPtr->maxBufferSize)) /* necessarily blockLinked */
+ {
+ LZ4F_localSaveDict(cctxPtr);
+ cctxPtr->tmpIn = cctxPtr->tmpBuff + 64 KB;
+ }
+
+ return dstPtr - dstStart;
+}
+
+
+/* LZ4F_compressEnd()
+* When you want to properly finish the compressed frame, just call LZ4F_compressEnd().
+* It will flush whatever data remained within compressionContext (like LZ4_flush())
+* but also properly finalize the frame, with an endMark and a checksum.
+* The result of the function is the number of bytes written into dstBuffer (necessarily >= 4 (endMark size))
+* The function outputs an error code if it fails (can be tested using LZ4F_isError())
+* The LZ4F_compressOptions_t structure is optional : you can provide NULL as argument.
+* compressionContext can then be used again, starting with LZ4F_compressBegin(). The preferences will remain the same.
+*/
+size_t LZ4F_compressEnd(LZ4F_compressionContext_t compressionContext, void* dstBuffer, size_t dstMaxSize, const LZ4F_compressOptions_t* compressOptionsPtr)
+{
+ LZ4F_cctx_internal_t* cctxPtr = (LZ4F_cctx_internal_t*)compressionContext;
+ BYTE* const dstStart = (BYTE*)dstBuffer;
+ BYTE* dstPtr = dstStart;
+ size_t errorCode;
+
+ errorCode = LZ4F_flush(compressionContext, dstBuffer, dstMaxSize, compressOptionsPtr);
+ if (LZ4F_isError(errorCode)) return errorCode;
+ dstPtr += errorCode;
+
+ LZ4F_writeLE32(dstPtr, 0);
+ dstPtr+=4; /* endMark */
+
+ if (cctxPtr->prefs.frameInfo.contentChecksumFlag == contentChecksumEnabled)
+ {
+ U32 xxh = XXH32_digest(&(cctxPtr->xxh));
+ LZ4F_writeLE32(dstPtr, xxh);
+ dstPtr+=4; /* content Checksum */
+ }
+
+ cctxPtr->cStage = 0; /* state is now re-usable (with identical preferences) */
+
+ return dstPtr - dstStart;
+}
+
+
+/***********************************
+* Decompression functions
+* *********************************/
+
+/* Resource management */
+
+/* LZ4F_createDecompressionContext() :
+* The first thing to do is to create a decompressionContext object, which will be used in all decompression operations.
+* This is achieved using LZ4F_createDecompressionContext().
+* The function will provide a pointer to a fully allocated and initialized LZ4F_decompressionContext object.
+* If the result LZ4F_errorCode_t is not zero, there was an error during context creation.
+* Object can release its memory using LZ4F_freeDecompressionContext();
+*/
+LZ4F_errorCode_t LZ4F_createDecompressionContext(LZ4F_compressionContext_t* LZ4F_decompressionContextPtr, unsigned versionNumber)
+{
+ LZ4F_dctx_internal_t* dctxPtr;
+
+ dctxPtr = ALLOCATOR(sizeof(LZ4F_dctx_internal_t));
+ if (dctxPtr==NULL) return (LZ4F_errorCode_t)-ERROR_GENERIC;
+
+ dctxPtr->version = versionNumber;
+ *LZ4F_decompressionContextPtr = (LZ4F_compressionContext_t)dctxPtr;
+ return OK_NoError;
+}
+
+LZ4F_errorCode_t LZ4F_freeDecompressionContext(LZ4F_compressionContext_t LZ4F_decompressionContext)
+{
+ LZ4F_dctx_internal_t* dctxPtr = (LZ4F_dctx_internal_t*)LZ4F_decompressionContext;
+ FREEMEM(dctxPtr->tmpIn);
+ FREEMEM(dctxPtr->tmpOutBuffer);
+ FREEMEM(dctxPtr);
+ return OK_NoError;
+}
+
+
+/* Decompression */
+
+static size_t LZ4F_decodeHeader(LZ4F_dctx_internal_t* dctxPtr, const BYTE* srcPtr, size_t srcSize)
+{
+ BYTE FLG, BD, HC;
+ unsigned version, blockMode, blockChecksumFlag, contentSizeFlag, contentChecksumFlag, dictFlag, blockSizeID;
+ size_t bufferNeeded;
+
+ /* need to decode header to get frameInfo */
+ if (srcSize < 7) return (size_t)-ERROR_GENERIC; /* minimal header size */
+
+ /* control magic number */
+ if (LZ4F_readLE32(srcPtr) != LZ4F_MAGICNUMBER) return (size_t)-ERROR_GENERIC;
+ srcPtr += 4;
+
+ /* Flags */
+ FLG = srcPtr[0];
+ version = (FLG>>6)&_2BITS;
+ blockMode = (FLG>>5) & _1BIT;
+ blockChecksumFlag = (FLG>>4) & _1BIT;
+ contentSizeFlag = (FLG>>3) & _1BIT;
+ contentChecksumFlag = (FLG>>2) & _1BIT;
+ dictFlag = (FLG>>0) & _1BIT;
+ BD = srcPtr[1];
+ blockSizeID = (BD>>4) & _3BITS;
+
+ /* check */
+ HC = LZ4F_headerChecksum(srcPtr, 2);
+ if (HC != srcPtr[2]) return (size_t)-ERROR_GENERIC; /* Bad header checksum error */
+
+ /* validate */
+ if (version != 1) return (size_t)-ERROR_GENERIC; /* Version Number, only supported value */
+ if (blockChecksumFlag != 0) return (size_t)-ERROR_GENERIC; /* Only supported value for the time being */
+ if (contentSizeFlag != 0) return (size_t)-ERROR_GENERIC; /* Only supported value for the time being */
+ if (((FLG>>1)&_1BIT) != 0) return (size_t)-ERROR_GENERIC; /* Reserved bit */
+ if (dictFlag != 0) return (size_t)-ERROR_GENERIC; /* Only supported value for the time being */
+ if (((BD>>7)&_1BIT) != 0) return (size_t)-ERROR_GENERIC; /* Reserved bit */
+ if (blockSizeID < 4) return (size_t)-ERROR_GENERIC; /* Only supported values for the time being */
+ if (((BD>>0)&_4BITS) != 0) return (size_t)-ERROR_GENERIC; /* Reserved bits */
+
+ /* save */
+ dctxPtr->frameInfo.blockMode = blockMode;
+ dctxPtr->frameInfo.contentChecksumFlag = contentChecksumFlag;
+ dctxPtr->frameInfo.blockSizeID = blockSizeID;
+ dctxPtr->maxBlockSize = LZ4F_getBlockSize(blockSizeID);
+
+ /* init */
+ if (contentChecksumFlag) XXH32_reset(&(dctxPtr->xxh), 0);
+
+ /* alloc */
+ bufferNeeded = dctxPtr->maxBlockSize + ((dctxPtr->frameInfo.blockMode==blockLinked) * 128 KB);
+ if (bufferNeeded > dctxPtr->maxBufferSize) /* tmp buffers too small */
+ {
+ FREEMEM(dctxPtr->tmpIn);
+ FREEMEM(dctxPtr->tmpOutBuffer);
+ dctxPtr->maxBufferSize = bufferNeeded;
+ dctxPtr->tmpIn = ALLOCATOR(dctxPtr->maxBlockSize);
+ if (dctxPtr->tmpIn == NULL) return (size_t)-ERROR_GENERIC;
+ dctxPtr->tmpOutBuffer= ALLOCATOR(dctxPtr->maxBufferSize);
+ if (dctxPtr->tmpOutBuffer== NULL) return (size_t)-ERROR_GENERIC;
+ }
+ dctxPtr->tmpInSize = 0;
+ dctxPtr->tmpInTarget = 0;
+ dctxPtr->dict = dctxPtr->tmpOutBuffer;
+ dctxPtr->dictSize = 0;
+ dctxPtr->tmpOut = dctxPtr->tmpOutBuffer;
+ dctxPtr->tmpOutStart = 0;
+ dctxPtr->tmpOutSize = 0;
+
+ return 7;
+}
+
+
+typedef enum { dstage_getHeader=0, dstage_storeHeader, dstage_decodeHeader,
+ dstage_getCBlockSize, dstage_storeCBlockSize, dstage_decodeCBlockSize,
+ dstage_copyDirect,
+ dstage_getCBlock, dstage_storeCBlock, dstage_decodeCBlock,
+ dstage_decodeCBlock_intoDst, dstage_decodeCBlock_intoTmp, dstage_flushOut,
+ dstage_getSuffix, dstage_storeSuffix, dstage_checkSuffix
+} dStage_t;
+
+
+/* LZ4F_getFrameInfo()
+* This function decodes frame header information, such as blockSize.
+* It is optional : you could start by calling directly LZ4F_decompress() instead.
+* The objective is to extract header information without starting decompression, typically for allocation purposes.
+* LZ4F_getFrameInfo() can also be used *after* starting decompression, on a valid LZ4F_decompressionContext_t.
+* The number of bytes read from srcBuffer will be provided within *srcSizePtr (necessarily <= original value).
+* You are expected to resume decompression from where it stopped (srcBuffer + *srcSizePtr)
+* The function result is an hint of the better srcSize to use for next call to LZ4F_decompress,
+* or an error code which can be tested using LZ4F_isError().
+*/
+LZ4F_errorCode_t LZ4F_getFrameInfo(LZ4F_decompressionContext_t decompressionContext, LZ4F_frameInfo_t* frameInfoPtr, const void* srcBuffer, size_t* srcSizePtr)
+{
+ LZ4F_dctx_internal_t* dctxPtr = (LZ4F_dctx_internal_t*)decompressionContext;
+
+ if (dctxPtr->dStage == dstage_getHeader)
+ {
+ LZ4F_errorCode_t errorCode = LZ4F_decodeHeader(dctxPtr, srcBuffer, *srcSizePtr);
+ if (LZ4F_isError(errorCode)) return errorCode;
+ *srcSizePtr = errorCode;
+ *frameInfoPtr = dctxPtr->frameInfo;
+ dctxPtr->srcExpect = NULL;
+ dctxPtr->dStage = dstage_getCBlockSize;
+ return 4;
+ }
+
+ /* frameInfo already decoded */
+ *srcSizePtr = 0;
+ *frameInfoPtr = dctxPtr->frameInfo;
+ return 0;
+}
+
+
+static int LZ4F_decompress_safe (const char* source, char* dest, int compressedSize, int maxDecompressedSize, const char* dictStart, int dictSize)
+{
+ (void)dictStart;
+ (void)dictSize;
+ return LZ4_decompress_safe (source, dest, compressedSize, maxDecompressedSize);
+}
+
+
+
+static void LZ4F_updateDict(LZ4F_dctx_internal_t* dctxPtr, const BYTE* dstPtr, size_t dstSize, const BYTE* dstPtr0, unsigned withinTmp)
+{
+ if (dctxPtr->dictSize==0)
+ dctxPtr->dict = (BYTE*)dstPtr; /* priority to dictionary continuity */
+
+ if (dctxPtr->dict + dctxPtr->dictSize == dstPtr) /* dictionary continuity */
+ {
+ dctxPtr->dictSize += dstSize;
+ return;
+ }
+
+ if (dstPtr - dstPtr0 + dstSize >= 64 KB) /* dstBuffer large enough to become dictionary */
+ {
+ dctxPtr->dict = (BYTE*)dstPtr0;
+ dctxPtr->dictSize = dstPtr - dstPtr0 + dstSize;
+ return;
+ }
+
+ if ((withinTmp) && (dctxPtr->dict == dctxPtr->tmpOutBuffer))
+ {
+ /* assumption : dctxPtr->dict + dctxPtr->dictSize == dctxPtr->tmpOut + dctxPtr->tmpOutStart */
+ dctxPtr->dictSize += dstSize;
+ return;
+ }
+
+ if (withinTmp) /* copy relevant dict portion in front of tmpOut within tmpOutBuffer */
+ {
+#if 0
+ size_t savedDictSize = dctxPtr->tmpOut - dctxPtr->tmpOutBuffer;
+ memcpy(dctxPtr->tmpOutBuffer, dctxPtr->dict + dctxPtr->dictSize - dctxPtr->tmpOutStart- savedDictSize, savedDictSize);
+ dctxPtr->dict = dctxPtr->tmpOutBuffer;
+ dctxPtr->dictSize = savedDictSize + dctxPtr->tmpOutStart + dstSize;
+ return;
+
+#else
+
+ size_t preserveSize = dctxPtr->tmpOut - dctxPtr->tmpOutBuffer;
+ size_t copySize = 64 KB - dctxPtr->tmpOutSize;
+ BYTE* oldDictEnd = dctxPtr->dict + dctxPtr->dictSize - dctxPtr->tmpOutStart;
+ if (dctxPtr->tmpOutSize > 64 KB) copySize = 0;
+ if (copySize > preserveSize) copySize = preserveSize;
+
+ memcpy(dctxPtr->tmpOutBuffer + preserveSize - copySize, oldDictEnd - copySize, copySize);
+
+ dctxPtr->dict = dctxPtr->tmpOutBuffer;
+ dctxPtr->dictSize = preserveSize + dctxPtr->tmpOutStart + dstSize;
+ return;
+#endif
+ }
+
+ if (dctxPtr->dict == dctxPtr->tmpOutBuffer) /* copy dst into tmp to complete dict */
+ {
+ if (dctxPtr->dictSize + dstSize > dctxPtr->maxBufferSize) /* tmp buffer not large enough */
+ {
+ size_t preserveSize = 64 KB - dstSize; /* note : dstSize < 64 KB */
+ memcpy(dctxPtr->dict, dctxPtr->dict + dctxPtr->dictSize - preserveSize, preserveSize);
+ dctxPtr->dictSize = preserveSize;
+ }
+ memcpy(dctxPtr->dict + dctxPtr->dictSize, dstPtr, dstSize);
+ dctxPtr->dictSize += dstSize;
+ return;
+ }
+
+ /* join dict & dest into tmp */
+ {
+ size_t preserveSize = 64 KB - dstSize; /* note : dstSize < 64 KB */
+ if (preserveSize > dctxPtr->dictSize) preserveSize = dctxPtr->dictSize;
+ memcpy(dctxPtr->tmpOutBuffer, dctxPtr->dict + dctxPtr->dictSize - preserveSize, preserveSize);
+ memcpy(dctxPtr->tmpOutBuffer + preserveSize, dstPtr, dstSize);
+ dctxPtr->dict = dctxPtr->tmpOutBuffer;
+ dctxPtr->dictSize = preserveSize + dstSize;
+ }
+}
+
+
+
+/* LZ4F_decompress()
+* Call this function repetitively to regenerate data compressed within srcBuffer.
+* The function will attempt to decode *srcSizePtr from srcBuffer, into dstBuffer of maximum size *dstSizePtr.
+*
+* The number of bytes regenerated into dstBuffer will be provided within *dstSizePtr (necessarily <= original value).
+*
+* The number of bytes effectively read from srcBuffer will be provided within *srcSizePtr (necessarily <= original value).
+* If the number of bytes read is < number of bytes provided, then the decompression operation is not complete.
+* You will have to call it again, continuing from where it stopped.
+*
+* The function result is an hint of the better srcSize to use for next call to LZ4F_decompress.
+* Basically, it's the size of the current (or remaining) compressed block + header of next block.
+* Respecting the hint provides some boost to performance, since it allows less buffer shuffling.
+* Note that this is just a hint, you can always provide any srcSize you want.
+* When a frame is fully decoded, the function result will be 0.
+* If decompression failed, function result is an error code which can be tested using LZ4F_isError().
+*/
+size_t LZ4F_decompress(LZ4F_decompressionContext_t decompressionContext,
+ void* dstBuffer, size_t* dstSizePtr,
+ const void* srcBuffer, size_t* srcSizePtr,
+ const LZ4F_decompressOptions_t* decompressOptionsPtr)
+{
+ LZ4F_dctx_internal_t* dctxPtr = (LZ4F_dctx_internal_t*)decompressionContext;
+ static const LZ4F_decompressOptions_t optionsNull = { 0 };
+ const BYTE* const srcStart = (const BYTE*)srcBuffer;
+ const BYTE* const srcEnd = srcStart + *srcSizePtr;
+ const BYTE* srcPtr = srcStart;
+ BYTE* const dstStart = (BYTE*)dstBuffer;
+ BYTE* const dstEnd = dstStart + *dstSizePtr;
+ BYTE* dstPtr = dstStart;
+ const BYTE* selectedIn=NULL;
+ unsigned doAnotherStage = 1;
+ size_t nextSrcSizeHint = 1;
+
+
+ if (decompressOptionsPtr==NULL) decompressOptionsPtr = &optionsNull;
+ *srcSizePtr = 0;
+ *dstSizePtr = 0;
+
+ /* expect to continue decoding src buffer where it left previously */
+ if (dctxPtr->srcExpect != NULL)
+ {
+ if (srcStart != dctxPtr->srcExpect) return (size_t)-ERROR_GENERIC;
+ }
+
+ /* programmed as a state machine */
+
+ while (doAnotherStage)
+ {
+
+ switch(dctxPtr->dStage)
+ {
+
+ case dstage_getHeader:
+ {
+ if (srcEnd-srcPtr >= 7)
+ {
+ selectedIn = srcPtr;
+ srcPtr += 7;
+ dctxPtr->dStage = dstage_decodeHeader;
+ break;
+ }
+ dctxPtr->tmpInSize = 0;
+ dctxPtr->dStage = dstage_storeHeader;
+ break;
+ }
+
+ case dstage_storeHeader:
+ {
+ size_t sizeToCopy = 7 - dctxPtr->tmpInSize;
+ if (sizeToCopy > (size_t)(srcEnd - srcPtr)) sizeToCopy = srcEnd - srcPtr;
+ memcpy(dctxPtr->header + dctxPtr->tmpInSize, srcPtr, sizeToCopy);
+ dctxPtr->tmpInSize += sizeToCopy;
+ srcPtr += sizeToCopy;
+ if (dctxPtr->tmpInSize < 7)
+ {
+ nextSrcSizeHint = (7 - dctxPtr->tmpInSize) + 4;
+ doAnotherStage = 0; /* no enough src, wait to get some more */
+ break;
+ }
+ selectedIn = dctxPtr->header;
+ dctxPtr->dStage = dstage_decodeHeader;
+ break;
+ }
+
+ case dstage_decodeHeader:
+ {
+ LZ4F_errorCode_t errorCode = LZ4F_decodeHeader(dctxPtr, selectedIn, 7);
+ if (LZ4F_isError(errorCode)) return errorCode;
+ dctxPtr->dStage = dstage_getCBlockSize;
+ break;
+ }
+
+ case dstage_getCBlockSize:
+ {
+ if ((srcEnd - srcPtr) >= 4)
+ {
+ selectedIn = srcPtr;
+ srcPtr += 4;
+ dctxPtr->dStage = dstage_decodeCBlockSize;
+ break;
+ }
+ /* not enough input to read cBlockSize field */
+ dctxPtr->tmpInSize = 0;
+ dctxPtr->dStage = dstage_storeCBlockSize;
+ break;
+ }
+
+ case dstage_storeCBlockSize:
+ {
+ size_t sizeToCopy = 4 - dctxPtr->tmpInSize;
+ if (sizeToCopy > (size_t)(srcEnd - srcPtr)) sizeToCopy = srcEnd - srcPtr;
+ memcpy(dctxPtr->tmpIn + dctxPtr->tmpInSize, srcPtr, sizeToCopy);
+ srcPtr += sizeToCopy;
+ dctxPtr->tmpInSize += sizeToCopy;
+ if (dctxPtr->tmpInSize < 4) /* not enough input to get full cBlockSize; wait for more */
+ {
+ nextSrcSizeHint = 4 - dctxPtr->tmpInSize;
+ doAnotherStage=0;
+ break;
+ }
+ selectedIn = dctxPtr->tmpIn;
+ dctxPtr->dStage = dstage_decodeCBlockSize;
+ break;
+ }
+
+ case dstage_decodeCBlockSize:
+ {
+ size_t nextCBlockSize = LZ4F_readLE32(selectedIn) & 0x7FFFFFFFU;
+ if (nextCBlockSize==0) /* frameEnd signal, no more CBlock */
+ {
+ dctxPtr->dStage = dstage_getSuffix;
+ break;
+ }
+ if (nextCBlockSize > dctxPtr->maxBlockSize) return (size_t)-ERROR_GENERIC; /* invalid cBlockSize */
+ dctxPtr->tmpInTarget = nextCBlockSize;
+ if (LZ4F_readLE32(selectedIn) & LZ4F_BLOCKUNCOMPRESSED_FLAG)
+ {
+ dctxPtr->dStage = dstage_copyDirect;
+ break;
+ }
+ dctxPtr->dStage = dstage_getCBlock;
+ if (dstPtr==dstEnd)
+ {
+ nextSrcSizeHint = nextCBlockSize + 4;
+ doAnotherStage = 0;
+ }
+ break;
+ }
+
+ case dstage_copyDirect: /* uncompressed block */
+ {
+ size_t sizeToCopy = dctxPtr->tmpInTarget;
+ if ((size_t)(srcEnd-srcPtr) < sizeToCopy) sizeToCopy = srcEnd - srcPtr; /* not enough input to read full block */
+ if ((size_t)(dstEnd-dstPtr) < sizeToCopy) sizeToCopy = dstEnd - dstPtr;
+ memcpy(dstPtr, srcPtr, sizeToCopy);
+ if (dctxPtr->frameInfo.contentChecksumFlag) XXH32_update(&(dctxPtr->xxh), srcPtr, (U32)sizeToCopy);
+
+ /* dictionary management */
+ if (dctxPtr->frameInfo.blockMode==blockLinked)
+ LZ4F_updateDict(dctxPtr, dstPtr, sizeToCopy, dstStart, 0);
+
+ srcPtr += sizeToCopy;
+ dstPtr += sizeToCopy;
+ if (sizeToCopy == dctxPtr->tmpInTarget) /* all copied */
+ {
+ dctxPtr->dStage = dstage_getCBlockSize;
+ break;
+ }
+ dctxPtr->tmpInTarget -= sizeToCopy; /* still need to copy more */
+ nextSrcSizeHint = dctxPtr->tmpInTarget + 4;
+ doAnotherStage = 0;
+ break;
+ }
+
+ case dstage_getCBlock: /* entry from dstage_decodeCBlockSize */
+ {
+ if ((size_t)(srcEnd-srcPtr) < dctxPtr->tmpInTarget)
+ {
+ dctxPtr->tmpInSize = 0;
+ dctxPtr->dStage = dstage_storeCBlock;
+ break;
+ }
+ selectedIn = srcPtr;
+ srcPtr += dctxPtr->tmpInTarget;
+ dctxPtr->dStage = dstage_decodeCBlock;
+ break;
+ }
+
+ case dstage_storeCBlock:
+ {
+ size_t sizeToCopy = dctxPtr->tmpInTarget - dctxPtr->tmpInSize;
+ if (sizeToCopy > (size_t)(srcEnd-srcPtr)) sizeToCopy = srcEnd-srcPtr;
+ memcpy(dctxPtr->tmpIn + dctxPtr->tmpInSize, srcPtr, sizeToCopy);
+ dctxPtr->tmpInSize += sizeToCopy;
+ srcPtr += sizeToCopy;
+ if (dctxPtr->tmpInSize < dctxPtr->tmpInTarget) /* need more input */
+ {
+ nextSrcSizeHint = (dctxPtr->tmpInTarget - dctxPtr->tmpInSize) + 4;
+ doAnotherStage=0;
+ break;
+ }
+ selectedIn = dctxPtr->tmpIn;
+ dctxPtr->dStage = dstage_decodeCBlock;
+ break;
+ }
+
+ case dstage_decodeCBlock:
+ {
+ if ((size_t)(dstEnd-dstPtr) < dctxPtr->maxBlockSize) /* not enough place into dst : decode into tmpOut */
+ dctxPtr->dStage = dstage_decodeCBlock_intoTmp;
+ else
+ dctxPtr->dStage = dstage_decodeCBlock_intoDst;
+ break;
+ }
+
+ case dstage_decodeCBlock_intoDst:
+ {
+ int (*decoder)(const char*, char*, int, int, const char*, int);
+ int decodedSize;
+
+ if (dctxPtr->frameInfo.blockMode == blockLinked)
+ decoder = LZ4_decompress_safe_usingDict;
+ else
+ decoder = LZ4F_decompress_safe;
+
+ decodedSize = decoder((const char*)selectedIn, (char*)dstPtr, (int)dctxPtr->tmpInTarget, (int)dctxPtr->maxBlockSize, (const char*)dctxPtr->dict, (int)dctxPtr->dictSize);
+ if (decodedSize < 0) return (size_t)-ERROR_GENERIC; /* decompression failed */
+ if (dctxPtr->frameInfo.contentChecksumFlag) XXH32_update(&(dctxPtr->xxh), dstPtr, decodedSize);
+
+ /* dictionary management */
+ if (dctxPtr->frameInfo.blockMode==blockLinked)
+ LZ4F_updateDict(dctxPtr, dstPtr, decodedSize, dstStart, 0);
+
+ dstPtr += decodedSize;
+ dctxPtr->dStage = dstage_getCBlockSize;
+ break;
+ }
+
+ case dstage_decodeCBlock_intoTmp:
+ {
+ /* not enough place into dst : decode into tmpOut */
+ int (*decoder)(const char*, char*, int, int, const char*, int);
+ int decodedSize;
+
+ if (dctxPtr->frameInfo.blockMode == blockLinked)
+ decoder = LZ4_decompress_safe_usingDict;
+ else
+ decoder = LZ4F_decompress_safe;
+
+ /* ensure enough place for tmpOut */
+ if (dctxPtr->frameInfo.blockMode == blockLinked)
+ {
+ if (dctxPtr->dict == dctxPtr->tmpOutBuffer)
+ {
+ if (dctxPtr->dictSize > 128 KB)
+ {
+ memcpy(dctxPtr->dict, dctxPtr->dict + dctxPtr->dictSize - 64 KB, 64 KB);
+ dctxPtr->dictSize = 64 KB;
+ }
+ dctxPtr->tmpOut = dctxPtr->dict + dctxPtr->dictSize;
+ }
+ else /* dict not within tmp */
+ {
+ size_t reservedDictSpace = dctxPtr->dictSize;
+ if (reservedDictSpace > 64 KB) reservedDictSpace = 64 KB;
+ dctxPtr->tmpOut = dctxPtr->tmpOutBuffer + reservedDictSpace;
+ }
+ }
+
+ /* Decode */
+ decodedSize = decoder((const char*)selectedIn, (char*)dctxPtr->tmpOut, (int)dctxPtr->tmpInTarget, (int)dctxPtr->maxBlockSize, (const char*)dctxPtr->dict, (int)dctxPtr->dictSize);
+ if (decodedSize < 0) return (size_t)-ERROR_decompressionFailed; /* decompression failed */
+ if (dctxPtr->frameInfo.contentChecksumFlag) XXH32_update(&(dctxPtr->xxh), dctxPtr->tmpOut, decodedSize);
+ dctxPtr->tmpOutSize = decodedSize;
+ dctxPtr->tmpOutStart = 0;
+ dctxPtr->dStage = dstage_flushOut;
+ break;
+ }
+
+ case dstage_flushOut: /* flush decoded data from tmpOut to dstBuffer */
+ {
+ size_t sizeToCopy = dctxPtr->tmpOutSize - dctxPtr->tmpOutStart;
+ if (sizeToCopy > (size_t)(dstEnd-dstPtr)) sizeToCopy = dstEnd-dstPtr;
+ memcpy(dstPtr, dctxPtr->tmpOut + dctxPtr->tmpOutStart, sizeToCopy);
+
+ /* dictionary management */
+ if (dctxPtr->frameInfo.blockMode==blockLinked)
+ LZ4F_updateDict(dctxPtr, dstPtr, sizeToCopy, dstStart, 1);
+
+ dctxPtr->tmpOutStart += sizeToCopy;
+ dstPtr += sizeToCopy;
+
+ /* end of flush ? */
+ if (dctxPtr->tmpOutStart == dctxPtr->tmpOutSize)
+ {
+ dctxPtr->dStage = dstage_getCBlockSize;
+ break;
+ }
+ nextSrcSizeHint = 4;
+ doAnotherStage = 0; /* still some data to flush */
+ break;
+ }
+
+ case dstage_getSuffix:
+ {
+ size_t suffixSize = dctxPtr->frameInfo.contentChecksumFlag * 4;
+ if (suffixSize == 0) /* frame completed */
+ {
+ nextSrcSizeHint = 0;
+ dctxPtr->dStage = dstage_getHeader;
+ doAnotherStage = 0;
+ break;
+ }
+ if ((srcEnd - srcPtr) >= 4) /* CRC present */
+ {
+ selectedIn = srcPtr;
+ srcPtr += 4;
+ dctxPtr->dStage = dstage_checkSuffix;
+ break;
+ }
+ dctxPtr->tmpInSize = 0;
+ dctxPtr->dStage = dstage_storeSuffix;
+ break;
+ }
+
+ case dstage_storeSuffix:
+ {
+ size_t sizeToCopy = 4 - dctxPtr->tmpInSize;
+ if (sizeToCopy > (size_t)(srcEnd - srcPtr)) sizeToCopy = srcEnd - srcPtr;
+ memcpy(dctxPtr->tmpIn + dctxPtr->tmpInSize, srcPtr, sizeToCopy);
+ srcPtr += sizeToCopy;
+ dctxPtr->tmpInSize += sizeToCopy;
+ if (dctxPtr->tmpInSize < 4) /* not enough input to read complete suffix */
+ {
+ nextSrcSizeHint = 4 - dctxPtr->tmpInSize;
+ doAnotherStage=0;
+ break;
+ }
+ selectedIn = dctxPtr->tmpIn;
+ dctxPtr->dStage = dstage_checkSuffix;
+ break;
+ }
+
+ case dstage_checkSuffix:
+ {
+ U32 readCRC = LZ4F_readLE32(selectedIn);
+ U32 resultCRC = XXH32_digest(&(dctxPtr->xxh));
+ if (readCRC != resultCRC) return (size_t)-ERROR_checksum_invalid;
+ nextSrcSizeHint = 0;
+ dctxPtr->dStage = dstage_getHeader;
+ doAnotherStage = 0;
+ break;
+ }
+ }
+ }
+
+ /* preserve dictionary within tmp if necessary */
+ if ( (dctxPtr->frameInfo.blockMode==blockLinked)
+ &&(dctxPtr->dict != dctxPtr->tmpOutBuffer)
+ &&(!decompressOptionsPtr->stableDst)
+ &&((unsigned)(dctxPtr->dStage-1) < (unsigned)(dstage_getSuffix-1))
+ )
+ {
+ if (dctxPtr->dStage == dstage_flushOut)
+ {
+ size_t preserveSize = dctxPtr->tmpOut - dctxPtr->tmpOutBuffer;
+ size_t copySize = 64 KB - dctxPtr->tmpOutSize;
+ BYTE* oldDictEnd = dctxPtr->dict + dctxPtr->dictSize - dctxPtr->tmpOutStart;
+ if (dctxPtr->tmpOutSize > 64 KB) copySize = 0;
+ if (copySize > preserveSize) copySize = preserveSize;
+
+ memcpy(dctxPtr->tmpOutBuffer + preserveSize - copySize, oldDictEnd - copySize, copySize);
+
+ dctxPtr->dict = dctxPtr->tmpOutBuffer;
+ dctxPtr->dictSize = preserveSize + dctxPtr->tmpOutStart;
+ }
+ else
+ {
+ size_t newDictSize = dctxPtr->dictSize;
+ BYTE* oldDictEnd = dctxPtr->dict + dctxPtr->dictSize;
+ if ((newDictSize) > 64 KB) newDictSize = 64 KB;
+
+ memcpy(dctxPtr->tmpOutBuffer, oldDictEnd - newDictSize, newDictSize);
+
+ dctxPtr->dict = dctxPtr->tmpOutBuffer;
+ dctxPtr->dictSize = newDictSize;
+ dctxPtr->tmpOut = dctxPtr->tmpOutBuffer + newDictSize;
+ }
+ }
+
+ if (srcPtr<srcEnd) /* function must be called again with following source data */
+ dctxPtr->srcExpect = srcPtr;
+ else
+ dctxPtr->srcExpect = NULL;
+ *srcSizePtr = (srcPtr - srcStart);
+ *dstSizePtr = (dstPtr - dstStart);
+ return nextSrcSizeHint;
+}