summaryrefslogtreecommitdiffstats
path: root/lz4frame.c
diff options
context:
space:
mode:
Diffstat (limited to 'lz4frame.c')
-rw-r--r--lz4frame.c431
1 files changed, 359 insertions, 72 deletions
diff --git a/lz4frame.c b/lz4frame.c
index b3c36a5..39c9402 100644
--- a/lz4frame.c
+++ b/lz4frame.c
@@ -36,22 +36,6 @@
* All related operations, including memory management, are handled by the library.
* You don't need lz4.h when using lz4frame.h.
* */
-
-
-/**************************************
- CPU Feature Detection
-**************************************/
-/* 32 or 64 bits ? */
-static const int LZ4F_32bits = (sizeof(void*)==4);
-static const int LZ4F_64bits = (sizeof(void*)==8);
-
-/* Little Endian or Big Endian ? */
-typedef union {
- int num;
- char bytes[4];
- } endian_t;
-static const endian_t endianTest = { .num = 1 };
-#define LZ4F_isLittleEndian (endianTest.bytes[0])
/**************************************
@@ -62,6 +46,10 @@ static const endian_t endianTest = { .num = 1 };
#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
/**************************************
@@ -101,38 +89,6 @@ static const endian_t endianTest = { .num = 1 };
typedef unsigned long long U64;
#endif
-#if defined(__GNUC__)
-# define _PACKED __attribute__ ((packed))
-#else
-# define _PACKED
-#endif
-
-#if !defined(__GNUC__)
-# if defined(__IBMC__) || defined(__SUNPRO_C) || defined(__SUNPRO_CC)
-# pragma pack(1)
-# else
-# pragma pack(push, 1)
-# endif
-#endif
-
-typedef struct { U16 v; } _PACKED U16_S;
-typedef struct { U32 v; } _PACKED U32_S;
-typedef struct { U64 v; } _PACKED U64_S;
-typedef struct {size_t v;} _PACKED size_t_S;
-
-#if !defined(__GNUC__)
-# if defined(__SUNPRO_C) || defined(__SUNPRO_CC)
-# pragma pack(0)
-# else
-# pragma pack(pop)
-# endif
-#endif
-
-#define A16(x) (((U16_S *)(x))->v)
-#define A32(x) (((U32_S *)(x))->v)
-#define A64(x) (((U64_S *)(x))->v)
-#define AARCH(x) (((size_t_S *)(x))->v)
-
/**************************************
Constants
@@ -141,12 +97,30 @@ typedef struct {size_t v;} _PACKED size_t_S;
#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 19
#define LZ4F_BLOCKSIZEID_DEFAULT 4
/**************************************
Structures and local types
**************************************/
+typedef struct {
+ LZ4F_preferences_t prefs;
+ unsigned version;
+ unsigned cStage;
+ size_t maxBlockSize;
+ XXH32_stateSpace_t xxh;
+ BYTE* tmpInputBuffer;
+ size_t tmpInputFilled;
+} LZ4F_cctx_internal_t;
/**************************************
@@ -160,14 +134,32 @@ typedef struct {size_t v;} _PACKED size_t_S;
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 ERROR_maxBlockSize_invalid;
+ if (blockSizeID > 3) return -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 BYTE LZ4F_headerChecksum (BYTE* header, size_t length)
+{
+ U32 xxh = XXH32(header, length, 0);
+ return (BYTE)(xxh >> 8);
+}
+
+
+
/**************************************
Error management
**************************************/
@@ -178,7 +170,7 @@ int LZ4F_isError(LZ4F_errorCode_t code)
/**************************************
- Compression functions
+ Simple compression functions
**************************************/
size_t LZ4F_compressFrameBound(size_t srcSize, const LZ4F_frameInfo_t* frameInfoPtr)
{
@@ -189,22 +181,22 @@ size_t LZ4F_compressFrameBound(size_t srcSize, const LZ4F_frameInfo_t* frameInfo
unsigned nbBlocks;
size_t frameSuffixSize;
size_t totalBound;
-
+
if (frameInfoPtr==NULL) frameInfoPtr = &frameInfoNull; /* all parameters set to default */
-
+
headerSize = 7; /* basic header size (no option) including magic number */
blockInfoSize = 4; /* basic blockInfo size (no option) for one block */
-
- blockSize = LZ4F_getBlockSize(frameInfoPtr->maxBlockSizeID);
+
+ blockSize = LZ4F_getBlockSize(frameInfoPtr->blockSizeID);
nbBlocks = (srcSize + (blockSize-1)) / blockSize;
blockInfoSize *= nbBlocks; /* total block info size */
-
+
frameSuffixSize = 4; /* basic frameSuffixSize (no option) */
if (frameInfoPtr->contentChecksumFlag == contentChecksumEnabled) frameSuffixSize += 4;
-
+
totalBound = headerSize + srcSize + blockInfoSize + frameSuffixSize;
- if (totalBound < srcSize) return ERROR_srcSize_tooLarge; /* overflow error */
-
+ if (totalBound < srcSize) return -ERROR_srcSize_tooLarge; /* overflow error */
+
return totalBound;
}
@@ -222,27 +214,27 @@ size_t LZ4F_compressFrame(void* dstBuffer, size_t dstMaxSize, const void* srcBuf
{
const LZ4F_frameInfo_t frameInfoNull = { 0 };
const LZ4F_frameInfo_t* const frameInfoPtr = (preferencesPtr==NULL) ? &frameInfoNull : &(preferencesPtr->frameInfo);
- LZ4F_compressionContext_t cctx;
+ LZ4F_compressionContext_t cctx = NULL;
LZ4F_errorCode_t errorCode;
BYTE* const dstStart = (BYTE*) dstBuffer;
BYTE* dstPtr = dstStart;
- size_t blockSize = LZ4F_getBlockSize(frameInfoPtr->maxBlockSizeID);
+ size_t blockSize = LZ4F_getBlockSize(frameInfoPtr->blockSizeID);
unsigned nbBlocks = (srcSize + (blockSize-1)) / blockSize;
unsigned blockNb;
const BYTE* srcPtr = (const BYTE*) srcBuffer;
const size_t dstBlockSize = LZ4F_compressBound(blockSize, frameInfoPtr);
-
+
if (dstMaxSize < LZ4F_compressFrameBound(srcSize, frameInfoPtr))
- return ERROR_maxDstSize_tooSmall;
-
+ return -ERROR_dstMaxSize_tooSmall;
+
errorCode = LZ4F_createCompressionContext(&cctx, LZ4F_VERSION, preferencesPtr);
if (LZ4F_isError(errorCode)) return errorCode;
-
+
errorCode = LZ4F_compressBegin(cctx, dstBuffer, dstMaxSize); /* write header */
if (LZ4F_isError(errorCode)) return errorCode;
dstPtr += errorCode; /* header size */
-
+
for (blockNb=1; blockNb<nbBlocks; blockNb++)
{
errorCode = LZ4F_compress(cctx, dstPtr, dstBlockSize, srcPtr, blockSize, NULL);
@@ -250,13 +242,16 @@ size_t LZ4F_compressFrame(void* dstBuffer, size_t dstMaxSize, const void* srcBuf
srcPtr += blockSize;
dstPtr += errorCode;
}
-
+
/* last block */
- blockSize = srcSize % blockSize;
- errorCode = LZ4F_compress(cctx, dstPtr, dstBlockSize, srcPtr, blockSize, NULL);
- if (LZ4F_isError(errorCode)) return errorCode;
- dstPtr += errorCode;
-
+ {
+ size_t lastBlockSize = srcSize % blockSize;
+ if (lastBlockSize==0) lastBlockSize = blockSize;
+ errorCode = LZ4F_compress(cctx, dstPtr, dstBlockSize, srcPtr, lastBlockSize, NULL);
+ if (LZ4F_isError(errorCode)) return errorCode;
+ dstPtr += errorCode;
+ }
+
errorCode = LZ4F_compressEnd(cctx, dstPtr, dstMaxSize, NULL); /* flush last block, and generate suffix */
if (LZ4F_isError(errorCode)) return errorCode;
dstPtr += errorCode;
@@ -266,3 +261,295 @@ size_t LZ4F_compressFrame(void* dstBuffer, size_t dstMaxSize, const void* srcBuf
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 LZ4F_preferences_t structure is optional : you can provide NULL as argument, all preferences will then be set to default.
+ * The function will provide a pointer to a fully allocated LZ4F_compressionContext_t object.
+ * If the result LZ4F_errorCode_t is not zero, 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, int version, const LZ4F_preferences_t* preferencesPtr)
+{
+ const LZ4F_preferences_t prefNull = { 0 };
+ LZ4F_cctx_internal_t* cctxPtr;
+
+ if (preferencesPtr == NULL) preferencesPtr = &prefNull;
+
+ cctxPtr = malloc(sizeof(LZ4F_cctx_internal_t));
+ if (cctxPtr==NULL) return -ERROR_allocation_failed;
+
+ cctxPtr->prefs = *preferencesPtr; /* equivalent to memcpy() */
+ cctxPtr->version = version;
+ cctxPtr->cStage = 0; /* Next stage : write header */
+ if (cctxPtr->prefs.frameInfo.blockSizeID == 0) cctxPtr->prefs.frameInfo.blockSizeID = LZ4F_BLOCKSIZEID_DEFAULT;
+ cctxPtr->maxBlockSize = LZ4F_getBlockSize(cctxPtr->prefs.frameInfo.blockSizeID);
+ cctxPtr->tmpInputBuffer = malloc(cctxPtr->maxBlockSize);
+ if (cctxPtr->tmpInputBuffer == NULL) return -ERROR_allocation_failed;
+ cctxPtr->tmpInputFilled = 0;
+ XXH32_resetState(&(cctxPtr->xxh), 0);
+
+ *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;
+
+ free(cctxPtr->tmpInputBuffer);
+ free(LZ4F_compressionContext);
+
+ return OK_NoError;
+}
+
+
+/* LZ4F_compressBegin() :
+ * will write the frame header into dstBuffer.
+ * dstBuffer must be large enough to accomodate a header (dstMaxSize). Maximum header size is LZ4F_MAXHEADERFRAME_SIZE(19) 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)
+{
+ LZ4F_cctx_internal_t* cctxPtr = (LZ4F_cctx_internal_t*)compressionContext;
+ BYTE* const dstStart = (BYTE*)dstBuffer;
+ BYTE* dstPtr = dstStart;
+ BYTE* headerStart;
+
+ if (dstMaxSize < LZ4F_MAXHEADERFRAME_SIZE) return -ERROR_dstMaxSize_tooSmall;
+ if (cctxPtr->cStage != 0) return -ERROR_GENERIC;
+
+ /* Magic Number */
+ LZ4F_writeLE32(dstPtr, LZ4F_MAGICNUMBER);
+ dstPtr += 4;
+ headerStart = dstPtr;
+
+ /* FLG Byte */
+ *dstPtr = (1 & _2BITS) << 6; /* Version('01') */
+ *dstPtr |= (1 & _1BIT ) << 5; /* Blocks independents */
+ *dstPtr |= (1 & _1BIT ) << 2; /* Stream checksum */
+ dstPtr++;
+ /* BD Byte */
+ *dstPtr = (char)((cctxPtr->prefs.frameInfo.blockSizeID & _3BITS) << 4);
+ dstPtr++;
+ /* CRC Byte */
+ *dstPtr = LZ4F_headerChecksum(headerStart, 2);
+ dstPtr++;
+
+ cctxPtr->cStage = 1; /* header written */
+
+ 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_frameInfo_t* frameInfoPtr)
+{
+ blockSizeID_t bid = (frameInfoPtr==NULL) ? LZ4F_BLOCKSIZEID_DEFAULT : frameInfoPtr->blockSizeID;
+ size_t blockSize = LZ4F_getBlockSize(bid);
+ size_t vSrcSize = srcSize + (blockSize-1); /* worst case : tmp buffer almost filled */
+ unsigned nbBlocks = vSrcSize / blockSize;
+ size_t blockInfo = 4; /* default, without block CRC option */
+
+ return (blockSize + blockInfo) * nbBlocks;
+}
+
+/* LZ4F_getMaxSrcSize() : gives max allowed srcSize given dstMaxSize to handle worst case situations.
+ * You can use dstMaxSize==0 to know the "natural" srcSize instead (block size).
+ * The LZ4F_frameInfo_t structure is optional :
+ * you can provide NULL as argument, all preferences will then be set to default.
+ * */
+size_t LZ4F_getMaxSrcSize(size_t dstMaxSize, const LZ4F_frameInfo_t* frameInfoPtr)
+{
+ blockSizeID_t bid = (frameInfoPtr==NULL) ? LZ4F_BLOCKSIZEID_DEFAULT : frameInfoPtr->blockSizeID;
+ size_t blockSize = LZ4F_getBlockSize(bid);
+ size_t worstCBlockSize = blockSize + 4; /* default, with no block CRC option */
+ unsigned nbBlocks = dstMaxSize / worstCBlockSize;
+ size_t maxSrcSize = nbBlocks * blockSize;
+
+ if (dstMaxSize == 0) return blockSize;
+ if (nbBlocks == 0) return -ERROR_dstMaxSize_tooSmall; /* can't even fit one block */
+
+ return maxSrcSize;
+}
+
+
+/* LZ4F_compress()
+ * You can then call LZ4F_compress() 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.
+ * You can get the minimum value of dstMaxSize by using LZ4F_compressBound()
+ * Conversely, given a fixed dstMaxSize value, you can know the maximum srcSize authorized using LZ4F_getMaxSrcSize()
+ * If this condition is not respected, LZ4F_compress() will fail (result is an errorCode)
+ * 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 is just stored within compressionContext for a future block to complete)
+ * The function outputs an error code if it fails (can be tested using LZ4F_isError())
+ */
+size_t LZ4F_compress(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;
+
+
+ if (cctxPtr->cStage != 1) return -ERROR_GENERIC;
+ if (dstMaxSize < LZ4F_compressBound(srcSize, &(cctxPtr->prefs.frameInfo))) return -ERROR_dstMaxSize_tooSmall;
+ if (compressOptionsPtr == NULL) compressOptionsPtr = &cOptionsNull;
+
+ /* complete tmp buffer */
+ if (cctxPtr->tmpInputFilled > 0)
+ {
+ size_t sizeToCopy = blockSize - cctxPtr->tmpInputFilled;
+ if (sizeToCopy > srcSize)
+ {
+ /* add to tmp buffer */
+ memcpy(cctxPtr->tmpInputBuffer + cctxPtr->tmpInputFilled, srcBuffer, srcSize);
+ srcPtr = srcEnd;
+ cctxPtr->tmpInputFilled += srcSize;
+ }
+ else
+ {
+ BYTE* cSizePtr = dstPtr;
+ U32 cSize;
+ memcpy(cctxPtr->tmpInputBuffer + cctxPtr->tmpInputFilled, srcBuffer, sizeToCopy);
+ srcPtr += sizeToCopy;
+ dstPtr += 4; /* space for cSizePtr */
+ cSize = (U32)LZ4_compress_limitedOutput((const char*)cctxPtr->tmpInputBuffer, (char*)dstPtr, (int)(blockSize), (int)(blockSize-1));
+ dstPtr += cSize;
+ LZ4F_writeLE32(cSizePtr, cSize);
+ if (cSize == 0) /* compression failed */
+ {
+ cSize = blockSize + LZ4F_BLOCKUNCOMPRESSED_FLAG;
+ LZ4F_writeLE32(cSizePtr, cSize);
+ memcpy(dstPtr, cctxPtr->tmpInputBuffer, blockSize);
+ dstPtr += blockSize;
+ }
+ cctxPtr->tmpInputFilled = 0;
+ }
+ }
+
+ while ((size_t)(srcEnd - srcPtr) >= blockSize)
+ {
+ /* compress one block */
+ BYTE* cSizePtr = dstPtr;
+ U32 cSize;
+ dstPtr += 4; /* space for cSizePtr */
+ cSize = (U32)LZ4_compress_limitedOutput((const char*)srcPtr, (char*)dstPtr, (int)(blockSize), (int)(blockSize-1));
+ dstPtr += cSize;
+ LZ4F_writeLE32(cSizePtr, cSize);
+ if (cSize == 0) /* compression failed */
+ {
+ cSize = blockSize + LZ4F_BLOCKUNCOMPRESSED_FLAG;
+ LZ4F_writeLE32(cSizePtr, cSize);
+ memcpy(dstPtr, srcPtr, blockSize);
+ dstPtr += blockSize;
+ }
+ srcPtr += blockSize;
+ }
+
+ if (srcPtr < srcEnd)
+ {
+ /* fill tmp buffer */
+ size_t sizeToCopy = srcEnd - srcPtr;
+ memcpy(cctxPtr->tmpInputBuffer, srcPtr, sizeToCopy);
+ cctxPtr->tmpInputFilled = 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;
+
+
+ if (cctxPtr->tmpInputFilled == 0) return 0; /* nothing to flush */
+ if (cctxPtr->cStage != 1) return -ERROR_GENERIC;
+ if (dstMaxSize < LZ4F_compressBound(1, &(cctxPtr->prefs.frameInfo))) return -ERROR_dstMaxSize_tooSmall;
+ if (compressOptionsPtr == NULL) compressOptionsPtr = &cOptionsNull;
+
+ {
+ BYTE* cSizePtr = dstPtr;
+ U32 cSize;
+ dstPtr += 4; /* space for cSizePtr */
+ cSize = (U32)LZ4_compress_limitedOutput((const char*)cctxPtr->tmpInputBuffer, (char*)dstPtr, (int)(cctxPtr->tmpInputFilled), (int)(cctxPtr->tmpInputFilled-1));
+ dstPtr += cSize;
+ LZ4F_writeLE32(cSizePtr, cSize);
+ if (cSize == 0) /* compression failed */
+ {
+ cSize = cctxPtr->tmpInputFilled + LZ4F_BLOCKUNCOMPRESSED_FLAG;
+ LZ4F_writeLE32(cSizePtr, cSize);
+ memcpy(dstPtr, cctxPtr->tmpInputBuffer, cctxPtr->tmpInputFilled);
+ dstPtr += cctxPtr->tmpInputFilled;
+ }
+ cctxPtr->tmpInputFilled = 0;
+ }
+
+ 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_intermediateDigest(&(cctxPtr->xxh));
+ LZ4F_writeLE32(dstPtr, xxh);
+ dstPtr+=4; /* content Checksum */
+ }
+
+ cctxPtr->cStage = 0; /* state is now re-usable (with identical preferences) */
+
+ return dstPtr - dstStart;
+}