/* * LZ4 auto-framing library * Copyright (C) 2011-2016, 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 homepage : http://www.lz4.org * - LZ4 source repository : https://github.com/lz4/lz4
*/
/* LZ4F is a stand-alone API to create LZ4-compressed Frames * in full conformance with specification v1.6.1 . * This library rely upon memory management capabilities (malloc, free) * provided either by <stdlib.h>, * or redirected towards another library of user's choice * (see Memory Routines below).
*/
/*-************************************ * Memory routines
**************************************/ /* * User may redirect invocations of * malloc(), calloc() and free() * towards another library or solution of their choice * by modifying below section.
**/
staticvoid* LZ4F_calloc(size_t s, LZ4F_CustomMem cmem)
{ /* custom calloc defined : use it */ if (cmem.customCalloc != NULL) { return cmem.customCalloc(cmem.opaqueState, s);
} /* nothing defined : use default <stdlib.h>'s calloc() */ if (cmem.customAlloc == NULL) { return ALLOC_AND_ZERO(s);
} /* only custom alloc defined : use it, and combine it with memset() */
{ void* const p = cmem.customAlloc(cmem.opaqueState, s); if (p != NULL) MEM_INIT(p, 0, s); return p;
} }
staticvoid* LZ4F_malloc(size_t s, LZ4F_CustomMem cmem)
{ /* custom malloc defined : use it */ if (cmem.customAlloc != NULL) { return cmem.customAlloc(cmem.opaqueState, s);
} /* nothing defined : use default <stdlib.h>'s malloc() */ return ALLOC(s);
}
staticvoid LZ4F_free(void* p, LZ4F_CustomMem cmem)
{ if (p == NULL) return; if (cmem.customFree != NULL) { /* custom allocation defined : use it */
cmem.customFree(cmem.opaqueState, p); return;
} /* nothing defined : use default <stdlib.h>'s free() */
FREEMEM(p);
}
static LZ4F_errorCode_t LZ4F_returnErrorCode(LZ4F_errorCodes code)
{ /* A compilation error here means sizeof(ptrdiff_t) is not large enough */
LZ4F_STATIC_ASSERT(sizeof(ptrdiff_t) >= sizeof(size_t)); return (LZ4F_errorCode_t)-(ptrdiff_t)code;
}
/*! LZ4F_compressFrame_usingCDict() : * Compress srcBuffer using a dictionary, in a single step. * cdict can be NULL, in which case, no dictionary is used. * dstBuffer MUST be >= LZ4F_compressFrameBound(srcSize, preferencesPtr). * The LZ4F_preferences_t structure is optional : you may provide NULL as argument, * however, it's the only way to provide a dictID, so it's not recommended. * @return : number of bytes written into dstBuffer, * or an error code if it fails (can be tested using LZ4F_isError())
*/
size_t LZ4F_compressFrame_usingCDict(LZ4F_cctx* cctx, void* dstBuffer, size_t dstCapacity, constvoid* srcBuffer, size_t srcSize, const LZ4F_CDict* cdict, const LZ4F_preferences_t* preferencesPtr)
{
LZ4F_preferences_t prefs;
LZ4F_compressOptions_t options;
BYTE* const dstStart = (BYTE*) dstBuffer;
BYTE* dstPtr = dstStart;
BYTE* const dstEnd = dstStart + dstCapacity;
DEBUGLOG(4, "LZ4F_compressFrame_usingCDict (srcSize=%u)", (unsigned)srcSize); if (preferencesPtr!=NULL)
prefs = *preferencesPtr; else
MEM_INIT(&prefs, 0, sizeof(prefs)); if (prefs.frameInfo.contentSize != 0)
prefs.frameInfo.contentSize = (U64)srcSize; /* auto-correct content size if selected (!=0) */
prefs.frameInfo.blockSizeID = LZ4F_optimalBSID(prefs.frameInfo.blockSizeID, srcSize);
prefs.autoFlush = 1; if (srcSize <= LZ4F_getBlockSize(prefs.frameInfo.blockSizeID))
prefs.frameInfo.blockMode = LZ4F_blockIndependent; /* only one block => no need for inter-block link */
/*! LZ4F_compressFrame() : * Compress an entire srcBuffer into a valid LZ4 frame, in a single step. * dstBuffer MUST be >= LZ4F_compressFrameBound(srcSize, preferencesPtr). * The LZ4F_preferences_t structure is optional : you can provide NULL as argument. All preferences will be set to default. * @return : number of bytes written into dstBuffer. * or an error code if it fails (can be tested using LZ4F_isError())
*/
size_t LZ4F_compressFrame(void* dstBuffer, size_t dstCapacity, constvoid* srcBuffer, size_t srcSize, const LZ4F_preferences_t* preferencesPtr)
{
size_t result; #if (LZ4F_HEAPMODE)
LZ4F_cctx_t* cctxPtr;
result = LZ4F_createCompressionContext(&cctxPtr, LZ4F_VERSION);
FORWARD_IF_ERROR(result); #else
LZ4F_cctx_t cctx;
LZ4_stream_t lz4ctx;
LZ4F_cctx_t* const cctxPtr = &cctx;
MEM_INIT(&cctx, 0, sizeof(cctx));
cctx.version = LZ4F_VERSION;
cctx.maxBufferSize = 5 MB; /* mess with real buffer size to prevent dynamic allocation; works only because autoflush==1 & stableSrc==1 */ if ( preferencesPtr == NULL
|| preferencesPtr->compressionLevel < LZ4HC_CLEVEL_MIN ) {
LZ4_initStream(&lz4ctx, sizeof(lz4ctx));
cctxPtr->lz4CtxPtr = &lz4ctx;
cctxPtr->lz4CtxAlloc = 1;
cctxPtr->lz4CtxType = ctxFast;
} #endif
DEBUGLOG(4, "LZ4F_compressFrame");
result = LZ4F_compressFrame_usingCDict(cctxPtr, dstBuffer, dstCapacity,
srcBuffer, srcSize,
NULL, preferencesPtr);
struct LZ4F_CDict_s {
LZ4F_CustomMem cmem; void* dictContent;
LZ4_stream_t* fastCtx;
LZ4_streamHC_t* HCCtx;
}; /* typedef'd to LZ4F_CDict within lz4frame_static.h */
LZ4F_CDict*
LZ4F_createCDict_advanced(LZ4F_CustomMem cmem, constvoid* dictBuffer, size_t dictSize)
{ constchar* dictStart = (constchar*)dictBuffer;
LZ4F_CDict* const cdict = (LZ4F_CDict*)LZ4F_malloc(sizeof(*cdict), cmem);
DEBUGLOG(4, "LZ4F_createCDict_advanced"); if (!cdict) return NULL;
cdict->cmem = cmem; if (dictSize > 64 KB) {
dictStart += dictSize - 64 KB;
dictSize = 64 KB;
}
cdict->dictContent = LZ4F_malloc(dictSize, cmem); /* note: using @cmem to allocate => can't use default create */
cdict->fastCtx = (LZ4_stream_t*)LZ4F_malloc(sizeof(LZ4_stream_t), cmem);
cdict->HCCtx = (LZ4_streamHC_t*)LZ4F_malloc(sizeof(LZ4_streamHC_t), cmem); if (!cdict->dictContent || !cdict->fastCtx || !cdict->HCCtx) {
LZ4F_freeCDict(cdict); return NULL;
}
memcpy(cdict->dictContent, dictStart, dictSize);
LZ4_initStream(cdict->fastCtx, sizeof(LZ4_stream_t));
LZ4_loadDictSlow(cdict->fastCtx, (constchar*)cdict->dictContent, (int)dictSize);
LZ4_initStreamHC(cdict->HCCtx, sizeof(LZ4_streamHC_t)); /* note: we don't know at this point which compression level is going to be used
* as a consequence, HCCtx is created for the more common HC mode */
LZ4_setCompressionLevel(cdict->HCCtx, LZ4HC_CLEVEL_DEFAULT);
LZ4_loadDictHC(cdict->HCCtx, (constchar*)cdict->dictContent, (int)dictSize); return cdict;
}
/*! LZ4F_createCDict() : * When compressing multiple messages / blocks with the same dictionary, it's recommended to load it just once. * LZ4F_createCDict() will create a digested dictionary, ready to start future compression operations without startup delay. * LZ4F_CDict can be created once and shared by multiple threads concurrently, since its usage is read-only. * @dictBuffer can be released after LZ4F_CDict creation, since its content is copied within CDict
* @return : digested dictionary for compression, or NULL if failed */
LZ4F_CDict* LZ4F_createCDict(constvoid* dictBuffer, size_t dictSize)
{
DEBUGLOG(4, "LZ4F_createCDict"); return LZ4F_createCDict_advanced(LZ4F_defaultCMem, dictBuffer, dictSize);
}
void LZ4F_freeCDict(LZ4F_CDict* cdict)
{ if (cdict==NULL) return; /* support free on NULL */
LZ4F_free(cdict->dictContent, cdict->cmem);
LZ4F_free(cdict->fastCtx, cdict->cmem);
LZ4F_free(cdict->HCCtx, cdict->cmem);
LZ4F_free(cdict, cdict->cmem);
}
/*! 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 incompatible 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_cctx** LZ4F_compressionContextPtr, unsigned version)
{
assert(LZ4F_compressionContextPtr != NULL); /* considered a violation of narrow contract */ /* in case it nonetheless happen in production */
RETURN_ERROR_IF(LZ4F_compressionContextPtr == NULL, parameter_null);
LZ4F_errorCode_t LZ4F_freeCompressionContext(LZ4F_cctx* cctxPtr)
{ if (cctxPtr != NULL) { /* support free on NULL */
LZ4F_free(cctxPtr->lz4CtxPtr, cctxPtr->cmem); /* note: LZ4_streamHC_t and LZ4_stream_t are simple POD types */
LZ4F_free(cctxPtr->tmpBuff, cctxPtr->cmem);
LZ4F_free(cctxPtr, cctxPtr->cmem);
} return LZ4F_OK_NoError;
}
/** * This function prepares the internal LZ4(HC) stream for a new compression, * resetting the context and attaching the dictionary, if there is one. * * It needs to be called at the beginning of each independent compression * stream (i.e., at the beginning of a frame in blockLinked mode, or at the * beginning of each block in blockIndependent mode).
*/ staticvoid LZ4F_initStream(void* ctx, const LZ4F_CDict* cdict, int level,
LZ4F_blockMode_t blockMode) { if (level < LZ4HC_CLEVEL_MIN) { if (cdict || blockMode == LZ4F_blockLinked) { /* In these cases, we will call LZ4_compress_fast_continue(), * which needs an already reset context. Otherwise, we'll call a * one-shot API. The non-continued APIs internally perform their own * resets at the beginning of their calls, where they know what * tableType they need the context to be in. So in that case this
* would be misguided / wasted work. */
LZ4_resetStream_fast((LZ4_stream_t*)ctx); if (cdict)
LZ4_attach_dictionary((LZ4_stream_t*)ctx, cdict->fastCtx);
} /* In these cases, we'll call a one-shot API. * The non-continued APIs internally perform their own resets * at the beginning of their calls, where they know * which tableType they need the context to be in.
* Therefore, a reset here would be wasted work. */
} else {
LZ4_resetStreamHC_fast((LZ4_streamHC_t*)ctx, level); if (cdict)
LZ4_attach_HC_dictionary((LZ4_streamHC_t*)ctx, cdict->HCCtx);
}
}
staticint ctxTypeID_to_size(int ctxTypeID) { switch(ctxTypeID) { case 1: return LZ4_sizeofState(); case 2: return LZ4_sizeofStateHC(); default: return 0;
}
}
/* LZ4F_compressBound() : * @return minimum capacity of dstBuffer for a given srcSize to handle worst case scenario. * LZ4F_preferences_t structure is optional : if NULL, preferences will be set to cover worst case scenario. * This function cannot fail.
*/
size_t LZ4F_compressBound(size_t srcSize, const LZ4F_preferences_t* preferencesPtr)
{ if (preferencesPtr && preferencesPtr->autoFlush) { return LZ4F_compressBound_internal(srcSize, preferencesPtr, 0);
} return LZ4F_compressBound_internal(srcSize, preferencesPtr, (size_t)-1);
}
typedefint (*compressFunc_t)(void* ctx, constchar* src, char* dst, int srcSize, int dstSize, int level, const LZ4F_CDict* cdict);
/*! LZ4F_compressUpdateImpl() : * LZ4F_compressUpdate() can be called repetitively to compress as much data as necessary. * When successful, the function always entirely consumes @srcBuffer. * src data is either buffered or compressed into @dstBuffer. * If the block compression does not match the compression of the previous block, the old data is flushed * and operations continue with the new compression mode. * @dstCapacity MUST be >= LZ4F_compressBound(srcSize, preferencesPtr) when block compression is turned on. * @compressOptionsPtr is optional : provide NULL to mean "default". * @return : the number of bytes written into dstBuffer. It can be zero, meaning input data was just buffered. * or an error code if it fails (which can be tested using LZ4F_isError()) * After an error, the state is left in a UB state, and must be re-initialized.
*/ static size_t LZ4F_compressUpdateImpl(LZ4F_cctx* cctxPtr, void* dstBuffer, size_t dstCapacity, constvoid* srcBuffer, size_t srcSize, const LZ4F_compressOptions_t* compressOptionsPtr,
LZ4F_BlockCompressMode_e blockCompression)
{
size_t const 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 const compress = LZ4F_selectCompression(cctxPtr->prefs.frameInfo.blockMode, cctxPtr->prefs.compressionLevel, blockCompression);
size_t bytesWritten;
DEBUGLOG(4, "LZ4F_compressUpdate (srcSize=%zu)", srcSize);
RETURN_ERROR_IF(cctxPtr->cStage != 1, compressionState_uninitialized); /* state must be initialized and waiting for next block */ if (dstCapacity < LZ4F_compressBound_internal(srcSize, &(cctxPtr->prefs), cctxPtr->tmpInSize))
RETURN_ERROR(dstMaxSize_tooSmall);
if (blockCompression == LZ4B_UNCOMPRESSED && dstCapacity < srcSize)
RETURN_ERROR(dstMaxSize_tooSmall);
/* flush currently written block, to continue with new block compression */ if (cctxPtr->blockCompressMode != blockCompression) {
bytesWritten = LZ4F_flush(cctxPtr, dstBuffer, dstCapacity, compressOptionsPtr);
dstPtr += bytesWritten;
cctxPtr->blockCompressMode = blockCompression;
}
if (compressOptionsPtr == NULL) compressOptionsPtr = &k_cOptionsNull;
/* complete tmp buffer */ if (cctxPtr->tmpInSize > 0) { /* some data already within tmp buffer */
size_t const sizeToCopy = blockSize - cctxPtr->tmpInSize;
assert(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;
/* preserve dictionary within @tmpBuff whenever necessary */ if ((cctxPtr->prefs.frameInfo.blockMode==LZ4F_blockLinked) && (lastBlockCompressed==fromSrcBuffer)) { /* linked blocks are only supported in compressed mode, see LZ4F_uncompressedUpdate */
assert(blockCompression == LZ4B_COMPRESSED); if (compressOptionsPtr->stableSrc) {
cctxPtr->tmpIn = cctxPtr->tmpBuff; /* src is stable : dictionary remains in src across invocations */
} else { intconst realDictSize = LZ4F_localSaveDict(cctxPtr);
assert(0 <= realDictSize && realDictSize <= 64 KB);
cctxPtr->tmpIn = cctxPtr->tmpBuff + realDictSize;
}
}
/* keep tmpIn within limits */ if (!(cctxPtr->prefs.autoFlush) /* no autoflush : there may be some data left within internal buffer */
&& (cctxPtr->tmpIn + blockSize) > (cctxPtr->tmpBuff + cctxPtr->maxBufferSize) ) /* not enough room to store next block */
{ /* only preserve 64KB within internal buffer. Ensures there is enough room for next block.
* note: this situation necessarily implies lastBlockCompressed==fromTmpBuffer */ intconst realDictSize = LZ4F_localSaveDict(cctxPtr);
cctxPtr->tmpIn = cctxPtr->tmpBuff + realDictSize;
assert((cctxPtr->tmpIn + blockSize) <= (cctxPtr->tmpBuff + cctxPtr->maxBufferSize));
}
/* some input data left, necessarily < blockSize */ if (srcPtr < srcEnd) { /* fill tmp buffer */
size_t const sizeToCopy = (size_t)(srcEnd - srcPtr);
memcpy(cctxPtr->tmpIn, srcPtr, sizeToCopy);
cctxPtr->tmpInSize = sizeToCopy;
}
if (cctxPtr->prefs.frameInfo.contentChecksumFlag == LZ4F_contentChecksumEnabled)
(void)XXH32_update(&(cctxPtr->xxh), srcBuffer, srcSize);
/*! LZ4F_compressUpdate() : * LZ4F_compressUpdate() can be called repetitively to compress as much data as necessary. * When successful, the function always entirely consumes @srcBuffer. * src data is either buffered or compressed into @dstBuffer. * If previously an uncompressed block was written, buffered data is flushed * before appending compressed data is continued. * @dstCapacity MUST be >= LZ4F_compressBound(srcSize, preferencesPtr). * @compressOptionsPtr is optional : provide NULL to mean "default". * @return : the number of bytes written into dstBuffer. It can be zero, meaning input data was just buffered. * or an error code if it fails (which can be tested using LZ4F_isError()) * After an error, the state is left in a UB state, and must be re-initialized.
*/
size_t LZ4F_compressUpdate(LZ4F_cctx* cctxPtr, void* dstBuffer, size_t dstCapacity, constvoid* srcBuffer, size_t srcSize, const LZ4F_compressOptions_t* compressOptionsPtr)
{ return LZ4F_compressUpdateImpl(cctxPtr,
dstBuffer, dstCapacity,
srcBuffer, srcSize,
compressOptionsPtr, LZ4B_COMPRESSED);
}
/*! LZ4F_uncompressedUpdate() : * Same as LZ4F_compressUpdate(), but requests blocks to be sent uncompressed. * This symbol is only supported when LZ4F_blockIndependent is used * @dstCapacity MUST be >= LZ4F_compressBound(srcSize, preferencesPtr). * @compressOptionsPtr is optional : provide NULL to mean "default". * @return : the number of bytes written into dstBuffer. It can be zero, meaning input data was just buffered. * or an error code if it fails (which can be tested using LZ4F_isError()) * After an error, the state is left in a UB state, and must be re-initialized.
*/
size_t LZ4F_uncompressedUpdate(LZ4F_cctx* cctxPtr, void* dstBuffer, size_t dstCapacity, constvoid* srcBuffer, size_t srcSize, const LZ4F_compressOptions_t* compressOptionsPtr)
{ return LZ4F_compressUpdateImpl(cctxPtr,
dstBuffer, dstCapacity,
srcBuffer, srcSize,
compressOptionsPtr, LZ4B_UNCOMPRESSED);
}
/*! LZ4F_flush() : * When compressed data must be sent immediately, without waiting for a block to be filled, * invoke LZ4_flush(), which will immediately compress any remaining data stored within LZ4F_cctx. * 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 LZ4F_cctx. * The function outputs an error code if it fails (can be tested using LZ4F_isError()) * LZ4F_compressOptions_t* is optional. NULL is a valid argument.
*/
size_t LZ4F_flush(LZ4F_cctx* cctxPtr, void* dstBuffer, size_t dstCapacity, const LZ4F_compressOptions_t* compressOptionsPtr)
{
BYTE* const dstStart = (BYTE*)dstBuffer;
BYTE* dstPtr = dstStart;
compressFunc_t compress;
if (cctxPtr->tmpInSize == 0) return 0; /* nothing to flush */
RETURN_ERROR_IF(cctxPtr->cStage != 1, compressionState_uninitialized);
RETURN_ERROR_IF(dstCapacity < (cctxPtr->tmpInSize + BHSize + BFSize), dstMaxSize_tooSmall);
(void)compressOptionsPtr; /* not useful (yet) */
/* select compression function */
compress = LZ4F_selectCompression(cctxPtr->prefs.frameInfo.blockMode, cctxPtr->prefs.compressionLevel, cctxPtr->blockCompressMode);
/*! 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 an (optional) checksum. * LZ4F_compressOptions_t structure is optional : you can provide NULL as argument. * @return: the number of bytes written into dstBuffer (necessarily >= 4 (endMark size)) * or an error code if it fails (can be tested using LZ4F_isError()) * The context can then be used again to compress a new frame, starting with LZ4F_compressBegin().
*/
size_t LZ4F_compressEnd(LZ4F_cctx* cctxPtr, void* dstBuffer, size_t dstCapacity, const LZ4F_compressOptions_t* compressOptionsPtr)
{
BYTE* const dstStart = (BYTE*)dstBuffer;
BYTE* dstPtr = dstStart;
/*! LZ4F_createDecompressionContext() : * Create a decompressionContext object, which will track all decompression operations. * Provides a pointer to a fully allocated and initialized LZ4F_decompressionContext object. * Object can later be released using LZ4F_freeDecompressionContext(). * @return : if != 0, there was an error during context creation.
*/
LZ4F_errorCode_t
LZ4F_createDecompressionContext(LZ4F_dctx** LZ4F_decompressionContextPtr, unsignedversionNumber)
{
assert(LZ4F_decompressionContextPtr != NULL); /* violation of narrow contract */
RETURN_ERROR_IF(LZ4F_decompressionContextPtr == NULL, parameter_null); /* in case it nonetheless happen in production */
/*! LZ4F_headerSize() : * @return : size of frame header * or an error code, which can be tested using LZ4F_isError()
*/
size_t LZ4F_headerSize(constvoid* src, size_t srcSize)
{
RETURN_ERROR_IF(src == NULL, srcPtr_wrong);
/* minimal srcSize to determine header size */ if (srcSize < LZ4F_MIN_SIZE_TO_KNOW_HEADER_LENGTH)
RETURN_ERROR(frameHeader_incomplete);
/* special case : skippable frames */ if ((LZ4F_readLE32(src) & 0xFFFFFFF0U) == LZ4F_MAGIC_SKIPPABLE_START) return 8;
/* control magic number */ #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION if (LZ4F_readLE32(src) != LZ4F_MAGICNUMBER)
RETURN_ERROR(frameType_unknown); #endif
/*! LZ4F_getFrameInfo() : * This function extracts frame parameters (max blockSize, frame checksum, etc.). * Usage is optional. Objective is to provide relevant information for allocation purposes. * This function works in 2 situations : * - At the beginning of a new frame, in which case it will decode this information from `srcBuffer`, and start the decoding process. * Amount of input data provided must be large enough to successfully decode the frame header. * A header size is variable, but is guaranteed to be <= LZ4F_HEADER_SIZE_MAX bytes. It's possible to provide more input data than this minimum. * - After decoding has been started. In which case, no input is read, frame parameters are extracted from dctx. * The number of bytes consumed from srcBuffer will be updated within *srcSizePtr (necessarily <= original value). * Decompression must resume from (srcBuffer + *srcSizePtr). * @return : an hint about how many srcSize bytes LZ4F_decompress() expects for next call, * or an error code which can be tested using LZ4F_isError() * note 1 : in case of error, dctx is not modified. Decoding operations can resume from where they stopped. * note 2 : frame parameters are *copied into* an already allocated LZ4F_frameInfo_t structure.
*/
LZ4F_errorCode_t LZ4F_getFrameInfo(LZ4F_dctx* dctx,
LZ4F_frameInfo_t* frameInfoPtr, constvoid* srcBuffer, size_t* srcSizePtr)
{
LZ4F_STATIC_ASSERT(dstage_getFrameHeader < dstage_storeFrameHeader); if (dctx->dStage > dstage_storeFrameHeader) { /* frameInfo already decoded */
size_t o=0, i=0;
*srcSizePtr = 0;
*frameInfoPtr = dctx->frameInfo; /* returns : recommended nb of bytes for LZ4F_decompress() */ return LZ4F_decompress(dctx, NULL, &o, NULL, &i, NULL);
} else { if (dctx->dStage == dstage_storeFrameHeader) { /* frame decoding already started, in the middle of header => automatic fail */
*srcSizePtr = 0;
RETURN_ERROR(frameDecoding_alreadyStarted);
} else {
size_t const hSize = LZ4F_headerSize(srcBuffer, *srcSizePtr); if (LZ4F_isError(hSize)) { *srcSizePtr=0; return hSize; } if (*srcSizePtr < hSize) {
*srcSizePtr=0;
RETURN_ERROR(frameHeader_incomplete);
}
/* LZ4F_updateDict() : * only used for LZ4F_blockLinked mode * Condition : @dstPtr != NULL
*/ staticvoid LZ4F_updateDict(LZ4F_dctx* dctx, const BYTE* dstPtr, size_t dstSize, const BYTE* dstBufferStart, unsigned withinTmp)
{
assert(dstPtr != NULL); if (dctx->dictSize==0) dctx->dict = (const BYTE*)dstPtr; /* will lead to prefix mode */
assert(dctx->dict != NULL);
if (dctx->dict + dctx->dictSize == dstPtr) { /* prefix mode, everything within dstBuffer */
dctx->dictSize += dstSize; return;
}
assert(dstPtr >= dstBufferStart); if ((size_t)(dstPtr - dstBufferStart) + dstSize >= 64 KB) { /* history in dstBuffer becomes large enough to become dictionary */
dctx->dict = (const BYTE*)dstBufferStart;
dctx->dictSize = (size_t)(dstPtr - dstBufferStart) + dstSize; return;
}
assert(dstSize < 64 KB); /* if dstSize >= 64 KB, dictionary would be set into dstBuffer directly */
/* dstBuffer does not contain whole useful history (64 KB), so it must be saved within tmpOutBuffer */
assert(dctx->tmpOutBuffer != NULL);
if (withinTmp && (dctx->dict == dctx->tmpOutBuffer)) { /* continue history within tmpOutBuffer */ /* withinTmp expectation : content of [dstPtr,dstSize] is same as [dict+dictSize,dstSize], so we just extend it */
assert(dctx->dict + dctx->dictSize == dctx->tmpOut + dctx->tmpOutStart);
dctx->dictSize += dstSize; return;
}
if (withinTmp) { /* copy relevant dict portion in front of tmpOut within tmpOutBuffer */
size_t const preserveSize = (size_t)(dctx->tmpOut - dctx->tmpOutBuffer);
size_t copySize = 64 KB - dctx->tmpOutSize; const BYTE* const oldDictEnd = dctx->dict + dctx->dictSize - dctx->tmpOutStart; if (dctx->tmpOutSize > 64 KB) copySize = 0; if (copySize > preserveSize) copySize = preserveSize;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.