From 409f816267b00e2307fabc59cc6ddffcc605a1ec Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sun, 19 Apr 2015 15:23:53 +0100 Subject: Updated LZ4F_getFrameInfo() behavior, related to uncomplete frame header decoding attempts --- lib/lz4frame.c | 82 ++++++++++++++++++++++++++++----------------------- lib/lz4frame.h | 20 ++++++------- lib/lz4frame_static.h | 16 +++++----- programs/frametest.c | 31 +++++++++++++++++++ 4 files changed, 94 insertions(+), 55 deletions(-) diff --git a/lib/lz4frame.c b/lib/lz4frame.c index 6f53897..7fc1314 100644 --- a/lib/lz4frame.c +++ b/lib/lz4frame.c @@ -100,10 +100,11 @@ typedef unsigned long long U64; #define LZ4F_MAGIC_SKIPPABLE_START 0x184D2A50U #define LZ4F_MAGICNUMBER 0x184D2204U #define LZ4F_BLOCKUNCOMPRESSED_FLAG 0x80000000U -#define LZ4F_MAXHEADERFRAME_SIZE 15 #define LZ4F_BLOCKSIZEID_DEFAULT LZ4F_max64KB -static const size_t minFHSize = 5; +static const size_t minFHSize = 7; +static const size_t maxFHSize = 15; +static const size_t BHSize = 4; static const U32 minHClevel = 3; /************************************** @@ -263,7 +264,7 @@ size_t LZ4F_compressFrameBound(size_t srcSize, const LZ4F_preferences_t* prefere prefs.frameInfo.blockSizeID = LZ4F_optimalBSID(prefs.frameInfo.blockSizeID, srcSize); prefs.autoFlush = 1; - headerSize = 15; /* header size, including magic number and frame content size*/ + headerSize = maxFHSize; /* header size, including magic number and frame content size*/ streamSize = LZ4F_compressBound(srcSize, &prefs); return headerSize + streamSize; @@ -398,7 +399,7 @@ size_t LZ4F_compressBegin(LZ4F_compressionContext_t compressionContext, void* ds BYTE* headerStart; size_t requiredBuffSize; - if (dstMaxSize < LZ4F_MAXHEADERFRAME_SIZE) return (size_t)-LZ4F_ERROR_dstMaxSize_tooSmall; + if (dstMaxSize < maxFHSize) return (size_t)-LZ4F_ERROR_dstMaxSize_tooSmall; if (cctxPtr->cStage != 0) return (size_t)-LZ4F_ERROR_GENERIC; memset(&prefNull, 0, sizeof(prefNull)); if (preferencesPtr == NULL) preferencesPtr = &prefNull; @@ -789,8 +790,9 @@ LZ4F_errorCode_t LZ4F_freeDecompressionContext(LZ4F_decompressionContext_t LZ4F_ typedef enum { dstage_getHeader=0, dstage_storeHeader, dstage_getCBlockSize, dstage_storeCBlockSize, dstage_copyDirect, - dstage_getCBlock, dstage_storeCBlock, dstage_decodeCBlock, - dstage_decodeCBlock_intoDst, dstage_decodeCBlock_intoTmp, dstage_flushOut, + dstage_getCBlock, dstage_storeCBlock, + dstage_decodeCBlock, dstage_decodeCBlock_intoDst, + dstage_decodeCBlock_intoTmp, dstage_flushOut, dstage_getSuffix, dstage_storeSuffix, dstage_getSFrameSize, dstage_storeSFrameSize, dstage_skipSkippable @@ -802,6 +804,7 @@ typedef enum { dstage_getHeader=0, dstage_storeHeader, or an error code (testable with LZ4F_isError()) output : set internal values of dctx, such as dctxPtr->frameInfo and dctxPtr->dStage. + input : srcVoidPtr points at the **beginning of the frame** */ static size_t LZ4F_decodeHeader(LZ4F_dctx_internal_t* dctxPtr, const void* srcVoidPtr, size_t srcSize) { @@ -812,10 +815,10 @@ static size_t LZ4F_decodeHeader(LZ4F_dctx_internal_t* dctxPtr, const void* srcVo const BYTE* srcPtr = (const BYTE*)srcVoidPtr; /* need to decode header to get frameInfo */ - if (srcSize < minFHSize) return (size_t)-LZ4F_ERROR_GENERIC; /* minimal header size */ + if (srcSize < minFHSize) return (size_t)-LZ4F_ERROR_frameHeader_incomplete; /* minimal frame header size */ memset(&(dctxPtr->frameInfo), 0, sizeof(dctxPtr->frameInfo)); - /* skippable frames */ + /* special case : skippable frames */ if ((LZ4F_readLE32(srcPtr) & 0xFFFFFFF0U) == LZ4F_MAGIC_SKIPPABLE_START) { dctxPtr->frameInfo.frameType = LZ4F_skippableFrame; @@ -846,10 +849,11 @@ static size_t LZ4F_decodeHeader(LZ4F_dctx_internal_t* dctxPtr, const void* srcVo contentChecksumFlag = (FLG>>2) & _1BIT; /* Frame Header Size */ - frameHeaderSize = contentSizeFlag ? 15 : 7; + frameHeaderSize = contentSizeFlag ? maxFHSize : minFHSize; if (srcSize < frameHeaderSize) { + /* not enough input to fully decode frame header */ if (srcPtr != dctxPtr->header) memcpy(dctxPtr->header, srcPtr, srcSize); dctxPtr->tmpInSize = srcSize; @@ -920,28 +924,32 @@ static size_t LZ4F_decodeHeader(LZ4F_dctx_internal_t* dctxPtr, const void* srcVo * 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_errorCode_t LZ4F_getFrameInfo(LZ4F_decompressionContext_t dCtx, LZ4F_frameInfo_t* frameInfoPtr, + const void* srcBuffer, size_t* srcSizePtr) { - LZ4F_dctx_internal_t* dctxPtr = (LZ4F_dctx_internal_t*)decompressionContext; + LZ4F_dctx_internal_t* dctxPtr = (LZ4F_dctx_internal_t*)dCtx; - if (dctxPtr->dStage == dstage_getHeader) + if (dctxPtr->dStage > dstage_storeHeader) /* note : requires dstage_* header related to be at beginning of enum */ { - size_t frameHeaderSize = LZ4F_decodeHeader(dctxPtr, srcBuffer, *srcSizePtr); - if (LZ4F_isError(frameHeaderSize)) return frameHeaderSize; - *srcSizePtr = frameHeaderSize; /* nb Bytes consumed */ - *frameInfoPtr = dctxPtr->frameInfo; /* copy into */ - dctxPtr->srcExpect = NULL; - return 4; /* nextSrcSizeHint : 4 == block header size */ + size_t o=0, i=0; + /* frameInfo already decoded */ + *srcSizePtr = 0; + *frameInfoPtr = dctxPtr->frameInfo; + return LZ4F_decompress(dCtx, NULL, &o, NULL, &i, NULL); + } + else + { + size_t o=0; + size_t nextSrcSize = LZ4F_decompress(dCtx, NULL, &o, srcBuffer, srcSizePtr, NULL); + if (dctxPtr->dStage <= dstage_storeHeader) /* note : requires dstage_* header related to be at beginning of enum */ + return (size_t)-LZ4F_ERROR_frameHeader_incomplete; + *frameInfoPtr = dctxPtr->frameInfo; + return nextSrcSize; } - - /* frameInfo already decoded */ - *srcSizePtr = 0; - *frameInfoPtr = dctxPtr->frameInfo; - return 0; } -/* redirector, with common prototype */ +/* trivial redirector, for common prototype */ static int LZ4F_decompress_safe (const char* source, char* dest, int compressedSize, int maxDecompressedSize, const char* dictStart, int dictSize) { (void)dictStart; (void)dictSize; @@ -1071,7 +1079,7 @@ size_t LZ4F_decompress(LZ4F_decompressionContext_t decompressionContext, case dstage_getHeader: { - if (srcEnd-srcPtr >= 7) + if ((size_t)(srcEnd-srcPtr) >= maxFHSize) /* enough to decode - shortcut */ { LZ4F_errorCode_t errorCode = LZ4F_decodeHeader(dctxPtr, srcPtr, srcEnd-srcPtr); if (LZ4F_isError(errorCode)) return errorCode; @@ -1079,7 +1087,7 @@ size_t LZ4F_decompress(LZ4F_decompressionContext_t decompressionContext, break; } dctxPtr->tmpInSize = 0; - dctxPtr->tmpInTarget = 7; + dctxPtr->tmpInTarget = minFHSize; /* minimum to attempt decode */ dctxPtr->dStage = dstage_storeHeader; } @@ -1092,7 +1100,7 @@ size_t LZ4F_decompress(LZ4F_decompressionContext_t decompressionContext, srcPtr += sizeToCopy; if (dctxPtr->tmpInSize < dctxPtr->tmpInTarget) { - nextSrcSizeHint = (dctxPtr->tmpInTarget - dctxPtr->tmpInSize) + 4; + nextSrcSizeHint = (dctxPtr->tmpInTarget - dctxPtr->tmpInSize) + BHSize; /* rest of header + nextBlockHeader */ doAnotherStage = 0; /* not enough src data, ask for some more */ break; } @@ -1105,10 +1113,10 @@ size_t LZ4F_decompress(LZ4F_decompressionContext_t decompressionContext, case dstage_getCBlockSize: { - if ((srcEnd - srcPtr) >= 4) + if ((size_t)(srcEnd - srcPtr) >= BHSize) { selectedIn = srcPtr; - srcPtr += 4; + srcPtr += BHSize; } else { @@ -1121,15 +1129,15 @@ size_t LZ4F_decompress(LZ4F_decompressionContext_t decompressionContext, if (dctxPtr->dStage == dstage_storeCBlockSize) case dstage_storeCBlockSize: { - size_t sizeToCopy = 4 - dctxPtr->tmpInSize; + size_t sizeToCopy = BHSize - 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 */ + if (dctxPtr->tmpInSize < BHSize) /* not enough input to get full cBlockSize; wait for more */ { - nextSrcSizeHint = 4 - dctxPtr->tmpInSize; - doAnotherStage=0; + nextSrcSizeHint = BHSize - dctxPtr->tmpInSize; + doAnotherStage = 0; break; } selectedIn = dctxPtr->tmpIn; @@ -1153,7 +1161,7 @@ size_t LZ4F_decompress(LZ4F_decompressionContext_t decompressionContext, dctxPtr->dStage = dstage_getCBlock; if (dstPtr==dstEnd) { - nextSrcSizeHint = nextCBlockSize + 4; + nextSrcSizeHint = nextCBlockSize + BHSize; doAnotherStage = 0; } break; @@ -1180,7 +1188,7 @@ size_t LZ4F_decompress(LZ4F_decompressionContext_t decompressionContext, break; } dctxPtr->tmpInTarget -= sizeToCopy; /* still need to copy more */ - nextSrcSizeHint = dctxPtr->tmpInTarget + 4; + nextSrcSizeHint = dctxPtr->tmpInTarget + BHSize; doAnotherStage = 0; break; } @@ -1208,7 +1216,7 @@ size_t LZ4F_decompress(LZ4F_decompressionContext_t decompressionContext, srcPtr += sizeToCopy; if (dctxPtr->tmpInSize < dctxPtr->tmpInTarget) /* need more input */ { - nextSrcSizeHint = (dctxPtr->tmpInTarget - dctxPtr->tmpInSize) + 4; + nextSrcSizeHint = (dctxPtr->tmpInTarget - dctxPtr->tmpInSize) + BHSize; doAnotherStage=0; break; } @@ -1311,7 +1319,7 @@ size_t LZ4F_decompress(LZ4F_decompressionContext_t decompressionContext, dctxPtr->dStage = dstage_getCBlockSize; break; } - nextSrcSizeHint = 4; + nextSrcSizeHint = BHSize; doAnotherStage = 0; /* still some data to flush */ break; } diff --git a/lib/lz4frame.h b/lib/lz4frame.h index 9c5ff37..e871046 100644 --- a/lib/lz4frame.h +++ b/lib/lz4frame.h @@ -33,7 +33,7 @@ */ /* LZ4F is a stand-alone API to create LZ4-compressed frames - * fully conformant to specification v1.4.1. + * fully conformant to specification v1.5.1. * All related operations, including memory management, are handled by the library. * You don't need lz4.h when using lz4frame.h. * */ @@ -254,18 +254,16 @@ size_t LZ4F_getFrameInfo(LZ4F_decompressionContext_t dctx, LZ4F_frameInfo_t* frameInfoPtr, const void* srcBuffer, size_t* srcSizePtr); /* 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. - * The function will work only if srcBuffer points at the beginning of the frame, - * and *srcSizePtr is large enough to decode the whole header (typically, between 7 & 15 bytes). - * The result is copied into an LZ4F_frameInfo_t structure, which is pointed by frameInfoPtr, and must be already allocated. - * LZ4F_getFrameInfo() can also be used *after* starting decompression, on a valid LZ4F_decompressionContext_t. + * This function decodes frame header information (such as max blockSize, frame checksum, etc.). + * Its usage is optional : you can start by calling directly LZ4F_decompress() instead. + * The objective is to extract frame header information, typically for allocation purposes. + * LZ4F_getFrameInfo() can also be used anytime *after* starting decompression, on any valid LZ4F_decompressionContext_t. + * The result is *copied* into an existing LZ4F_frameInfo_t structure which must be already allocated. * The number of bytes read from srcBuffer will be provided within *srcSizePtr (necessarily <= original value). - * It is basically the frame header size. - * You are expected to resume decompression from where it stopped (srcBuffer + *srcSizePtr) * The function result is an hint of how many srcSize bytes LZ4F_decompress() expects for next call, - * or an error code which can be tested using LZ4F_isError(). + * or an error code which can be tested using LZ4F_isError() + * (typically, when there is not enough src bytes to fully decode the frame header) + * You are expected to resume decompression from where it stopped (srcBuffer + *srcSizePtr) */ size_t LZ4F_decompress(LZ4F_decompressionContext_t dctx, diff --git a/lib/lz4frame_static.h b/lib/lz4frame_static.h index 25afed4..0d90975 100644 --- a/lib/lz4frame_static.h +++ b/lib/lz4frame_static.h @@ -40,10 +40,18 @@ extern "C" { #endif /* lz4frame_static.h should be used solely in the context of static linking. + * It contains definitions which may still change overtime. + * Never use it in the context of DLL linking. * */ /************************************** +* Includes +**************************************/ +#include "lz4frame.h" + + +/************************************** * Error management * ************************************/ #define LZ4F_LIST_ERRORS(ITEM) \ @@ -53,7 +61,7 @@ extern "C" { ITEM(ERROR_headerVersion_wrong) ITEM(ERROR_blockChecksum_unsupported) ITEM(ERROR_reservedFlag_set) \ ITEM(ERROR_allocation_failed) \ ITEM(ERROR_srcSize_tooLarge) ITEM(ERROR_dstMaxSize_tooSmall) \ - ITEM(ERROR_frameType_unknown) ITEM(ERROR_frameSize_wrong) \ + ITEM(ERROR_frameHeader_incomplete) ITEM(ERROR_frameType_unknown) ITEM(ERROR_frameSize_wrong) \ ITEM(ERROR_srcPtr_wrong) \ ITEM(ERROR_decompressionFailed) \ ITEM(ERROR_headerChecksum_invalid) ITEM(ERROR_contentChecksum_invalid) \ @@ -68,12 +76,6 @@ extern "C" { typedef enum { LZ4F_LIST_ERRORS(LZ4F_GENERATE_ENUM) } LZ4F_errorCodes; /* enum is exposed, to handle specific errors; compare function result to -enum value */ -/************************************** - Includes -**************************************/ -#include "lz4frame.h" - - #if defined (__cplusplus) } #endif diff --git a/programs/frametest.c b/programs/frametest.c index 6adf408..ed131d2 100644 --- a/programs/frametest.c +++ b/programs/frametest.c @@ -277,6 +277,37 @@ int basicTests(U32 seed, double compressibility) if (crcDest != crcOrig) goto _output_error; DISPLAYLEVEL(3, "Regenerated %i bytes \n", (int)decodedBufferSize); + DISPLAYLEVEL(4, "Reusing decompression context \n"); + { + size_t oSize = 0; + size_t iSize = 0; + LZ4F_frameInfo_t fi; + + DISPLAYLEVEL(3, "Start by feeding 0 bytes, to get next input size : "); + errorCode = LZ4F_decompress(dCtx, NULL, &oSize, ip, &iSize, NULL); + if (LZ4F_isError(errorCode)) goto _output_error; + DISPLAYLEVEL(3, " %u \n", (unsigned)errorCode); + + DISPLAYLEVEL(3, "get FrameInfo on null input : "); + errorCode = LZ4F_getFrameInfo(dCtx, &fi, ip, &iSize); + if (errorCode != (size_t)-LZ4F_ERROR_frameHeader_incomplete) goto _output_error; + DISPLAYLEVEL(3, " correctly failed : %s \n", LZ4F_getErrorName(errorCode)); + + DISPLAYLEVEL(3, "get FrameInfo on not enough input : "); + iSize = 6; + errorCode = LZ4F_getFrameInfo(dCtx, &fi, ip, &iSize); + if (errorCode != (size_t)-LZ4F_ERROR_frameHeader_incomplete) goto _output_error; + DISPLAYLEVEL(3, " correctly failed : %s \n", LZ4F_getErrorName(errorCode)); + ip += iSize; + + DISPLAYLEVEL(3, "get FrameInfo on enough input : "); + iSize = 15 - iSize; + errorCode = LZ4F_getFrameInfo(dCtx, &fi, ip, &iSize); + if (LZ4F_isError(errorCode)) goto _output_error; + DISPLAYLEVEL(3, " correctly decoded \n"); + ip += iSize; + } + DISPLAYLEVEL(3, "Byte after byte : \n"); while (ip < iend) { -- cgit v0.12