Compress-LZ4Frame
view release on metacpan or search on metacpan
/* not enough input to fully decode frame header */
if (srcPtr != dctx->header)
memcpy(dctx->header, srcPtr, srcSize);
dctx->tmpInSize = srcSize;
dctx->tmpInTarget = frameHeaderSize;
dctx->dStage = dstage_storeFrameHeader;
return srcSize;
}
{ U32 const BD = srcPtr[5];
blockSizeID = (BD>>4) & _3BITS;
/* validate */
if (((BD>>7)&_1BIT) != 0) return err0r(LZ4F_ERROR_reservedFlag_set); /* Reserved bit */
if (blockSizeID < 4) return err0r(LZ4F_ERROR_maxBlockSize_invalid); /* 4-7 only supported values for the time being */
if (((BD>>0)&_4BITS) != 0) return err0r(LZ4F_ERROR_reservedFlag_set); /* Reserved bits */
}
/* check header */
{ BYTE const HC = LZ4F_headerChecksum(srcPtr+4, frameHeaderSize-5);
if (HC != srcPtr[frameHeaderSize-1])
return err0r(LZ4F_ERROR_headerChecksum_invalid);
}
/* save */
dctx->frameInfo.blockMode = (LZ4F_blockMode_t)blockMode;
dctx->frameInfo.blockChecksumFlag = (LZ4F_blockChecksum_t)blockChecksumFlag;
dctx->frameInfo.contentChecksumFlag = (LZ4F_contentChecksum_t)contentChecksumFlag;
dctx->frameInfo.blockSizeID = (LZ4F_blockSizeID_t)blockSizeID;
dctx->maxBlockSize = LZ4F_getBlockSize(blockSizeID);
if (contentSizeFlag)
dctx->frameRemainingSize =
dctx->frameInfo.contentSize = LZ4F_readLE64(srcPtr+6);
if (dictIDFlag)
dctx->frameInfo.dictID = LZ4F_readLE32(srcPtr + frameHeaderSize - 5);
dctx->dStage = dstage_init;
return frameHeaderSize;
}
/*! 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,
const void* srcBuffer, size_t* srcSizePtr)
{
if (dctx->dStage > dstage_storeFrameHeader) { /* assumption : dstage_* header enum at beginning of range */
/* 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 err0r(LZ4F_ERROR_frameDecoding_alreadyStarted);
} else {
size_t decodeResult;
size_t const hSize = LZ4F_headerSize(srcBuffer, *srcSizePtr);
if (LZ4F_isError(hSize)) { *srcSizePtr=0; return hSize; }
if (*srcSizePtr < hSize) {
*srcSizePtr=0;
return err0r(LZ4F_ERROR_frameHeader_incomplete);
}
decodeResult = LZ4F_decodeHeader(dctx, srcBuffer, hSize);
if (LZ4F_isError(decodeResult)) {
*srcSizePtr = 0;
} else {
*srcSizePtr = decodeResult;
decodeResult = BHSize; /* block header size */
}
*frameInfoPtr = dctx->frameInfo;
return decodeResult;
} }
}
/* LZ4F_updateDict() :
* only used for LZ4F_blockLinked mode */
static void LZ4F_updateDict(LZ4F_dctx* dctx, const BYTE* dstPtr, size_t dstSize, const BYTE* dstPtr0, unsigned withinTmp)
{
if (dctx->dictSize==0)
dctx->dict = (const BYTE*)dstPtr; /* priority to dictionary continuity */
if (dctx->dict + dctx->dictSize == dstPtr) { /* dictionary continuity */
dctx->dictSize += dstSize;
return;
}
if (dstPtr - dstPtr0 + dstSize >= 64 KB) { /* dstBuffer large enough to become dictionary */
dctx->dict = (const BYTE*)dstPtr0;
dctx->dictSize = dstPtr - dstPtr0 + dstSize;
return;
}
if ((withinTmp) && (dctx->dict == dctx->tmpOutBuffer)) {
/* assumption : 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 = 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;
memcpy(dctx->tmpOutBuffer + preserveSize - copySize, oldDictEnd - copySize, copySize);
dctx->dict = dctx->tmpOutBuffer;
dctx->dictSize = preserveSize + dctx->tmpOutStart + dstSize;
return;
}
if (dctx->dict == dctx->tmpOutBuffer) { /* copy dst into tmp to complete dict */
if (dctx->dictSize + dstSize > dctx->maxBufferSize) { /* tmp buffer not large enough */
size_t const preserveSize = 64 KB - dstSize; /* note : dstSize < 64 KB */
memcpy(dctx->tmpOutBuffer, dctx->dict + dctx->dictSize - preserveSize, preserveSize);
dctx->dictSize = preserveSize;
}
memcpy(dctx->tmpOutBuffer + dctx->dictSize, dstPtr, dstSize);
dctx->dictSize += dstSize;
return;
}
/* join dict & dest into tmp */
{ size_t preserveSize = 64 KB - dstSize; /* note : dstSize < 64 KB */
if (preserveSize > dctx->dictSize) preserveSize = dctx->dictSize;
memcpy(dctx->tmpOutBuffer, dctx->dict + dctx->dictSize - preserveSize, preserveSize);
memcpy(dctx->tmpOutBuffer + preserveSize, dstPtr, dstSize);
dctx->dict = dctx->tmpOutBuffer;
dctx->dictSize = preserveSize + dstSize;
}
}
/*! LZ4F_decompress() :
* Call this function repetitively to regenerate compressed data in srcBuffer.
* The function will attempt to decode up to *srcSizePtr bytes from srcBuffer
* into dstBuffer of capacity *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 number of bytes read is < number of bytes provided, then decompression operation is not complete.
* Remaining data will have to be presented again in a subsequent invocation.
*
* The function result is an hint of the better srcSize to use for next call to LZ4F_decompress.
* Schematically, it's the size of the current (or remaining) compressed block + header of next block.
* Respecting the hint provides a small boost to performance, since it allows less buffer shuffling.
* Note that this is just a hint, and it's always possible to any srcSize value.
* When a frame is fully decoded, @return will be 0.
* If decompression failed, @return is an error code which can be tested using LZ4F_isError().
*/
size_t LZ4F_decompress(LZ4F_dctx* dctx,
void* dstBuffer, size_t* dstSizePtr,
const void* srcBuffer, size_t* srcSizePtr,
const LZ4F_decompressOptions_t* decompressOptionsPtr)
{
LZ4F_decompressOptions_t optionsNull;
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;
memset(&optionsNull, 0, sizeof(optionsNull));
if (decompressOptionsPtr==NULL) decompressOptionsPtr = &optionsNull;
*srcSizePtr = 0;
*dstSizePtr = 0;
/* behaves as a state machine */
while (doAnotherStage) {
switch(dctx->dStage)
{
case dstage_getFrameHeader:
if ((size_t)(srcEnd-srcPtr) >= maxFHSize) { /* enough to decode - shortcut */
size_t const hSize = LZ4F_decodeHeader(dctx, srcPtr, srcEnd-srcPtr); /* will update dStage appropriately */
if (LZ4F_isError(hSize)) return hSize;
srcPtr += hSize;
break;
}
dctx->tmpInSize = 0;
if (srcEnd-srcPtr == 0) return minFHSize; /* 0-size input */
dctx->tmpInTarget = minFHSize; /* minimum to attempt decode */
dctx->dStage = dstage_storeFrameHeader;
/* fall-through */
case dstage_storeFrameHeader:
{ size_t const sizeToCopy = MIN(dctx->tmpInTarget - dctx->tmpInSize, (size_t)(srcEnd - srcPtr));
memcpy(dctx->header + dctx->tmpInSize, srcPtr, sizeToCopy);
dctx->tmpInSize += sizeToCopy;
srcPtr += sizeToCopy;
}
if (dctx->tmpInSize < dctx->tmpInTarget) {
nextSrcSizeHint = (dctx->tmpInTarget - dctx->tmpInSize) + BHSize; /* rest of header + nextBlockHeader */
doAnotherStage = 0; /* not enough src data, ask for some more */
break;
}
{ size_t const hSize = LZ4F_decodeHeader(dctx, dctx->header, dctx->tmpInTarget); /* will update dStage appropriately */
if (LZ4F_isError(hSize)) return hSize;
}
break;
srcPtr += 4;
} else {
size_t const stillToCopy = 4 - dctx->tmpInSize;
size_t const sizeToCopy = MIN(stillToCopy, (size_t)(srcEnd-srcPtr));
memcpy(dctx->header + dctx->tmpInSize, srcPtr, sizeToCopy);
dctx->tmpInSize += sizeToCopy;
srcPtr += sizeToCopy;
if (dctx->tmpInSize < 4) { /* all input consumed */
doAnotherStage = 0;
break;
}
crcSrc = dctx->header;
}
{ U32 const readCRC = LZ4F_readLE32(crcSrc);
U32 const calcCRC = XXH32_digest(&dctx->blockChecksum);
if (readCRC != calcCRC)
return err0r(LZ4F_ERROR_blockChecksum_invalid);
}
}
dctx->dStage = dstage_getBlockHeader; /* new block */
break;
case dstage_getCBlock:
if ((size_t)(srcEnd-srcPtr) < dctx->tmpInTarget) {
dctx->tmpInSize = 0;
dctx->dStage = dstage_storeCBlock;
break;
}
/* input large enough to read full block directly */
selectedIn = srcPtr;
srcPtr += dctx->tmpInTarget;
if (0) /* jump over next block */
case dstage_storeCBlock:
{ size_t const wantedData = dctx->tmpInTarget - dctx->tmpInSize;
size_t const inputLeft = (size_t)(srcEnd-srcPtr);
size_t const sizeToCopy = MIN(wantedData, inputLeft);
memcpy(dctx->tmpIn + dctx->tmpInSize, srcPtr, sizeToCopy);
dctx->tmpInSize += sizeToCopy;
srcPtr += sizeToCopy;
if (dctx->tmpInSize < dctx->tmpInTarget) { /* need more input */
nextSrcSizeHint = (dctx->tmpInTarget - dctx->tmpInSize) + BHSize;
doAnotherStage=0;
break;
}
selectedIn = dctx->tmpIn;
}
/* At this stage, input is large enough to decode a block */
if (dctx->frameInfo.blockChecksumFlag) {
dctx->tmpInTarget -= 4;
assert(selectedIn != NULL); /* selectedIn is defined at this stage (either srcPtr, or dctx->tmpIn) */
{ U32 const readBlockCrc = LZ4F_readLE32(selectedIn + dctx->tmpInTarget);
U32 const calcBlockCrc = XXH32(selectedIn, dctx->tmpInTarget, 0);
if (readBlockCrc != calcBlockCrc)
return err0r(LZ4F_ERROR_blockChecksum_invalid);
} }
if ((size_t)(dstEnd-dstPtr) >= dctx->maxBlockSize) {
/* enough capacity in `dst` to decompress directly there */
int const decodedSize = LZ4_decompress_safe_usingDict(
(const char*)selectedIn, (char*)dstPtr,
(int)dctx->tmpInTarget, (int)dctx->maxBlockSize,
(const char*)dctx->dict, (int)dctx->dictSize);
if (decodedSize < 0) return err0r(LZ4F_ERROR_GENERIC); /* decompression failed */
if (dctx->frameInfo.contentChecksumFlag)
XXH32_update(&(dctx->xxh), dstPtr, decodedSize);
if (dctx->frameInfo.contentSize)
dctx->frameRemainingSize -= decodedSize;
/* dictionary management */
if (dctx->frameInfo.blockMode==LZ4F_blockLinked)
LZ4F_updateDict(dctx, dstPtr, decodedSize, dstStart, 0);
dstPtr += decodedSize;
dctx->dStage = dstage_getBlockHeader;
break;
}
/* not enough place into dst : decode into tmpOut */
/* ensure enough place for tmpOut */
if (dctx->frameInfo.blockMode == LZ4F_blockLinked) {
if (dctx->dict == dctx->tmpOutBuffer) {
if (dctx->dictSize > 128 KB) {
memcpy(dctx->tmpOutBuffer, dctx->dict + dctx->dictSize - 64 KB, 64 KB);
dctx->dictSize = 64 KB;
}
dctx->tmpOut = dctx->tmpOutBuffer + dctx->dictSize;
} else { /* dict not within tmp */
size_t const reservedDictSpace = MIN(dctx->dictSize, 64 KB);
dctx->tmpOut = dctx->tmpOutBuffer + reservedDictSpace;
}
}
/* Decode block */
{ int const decodedSize = LZ4_decompress_safe_usingDict(
(const char*)selectedIn, (char*)dctx->tmpOut,
(int)dctx->tmpInTarget, (int)dctx->maxBlockSize,
(const char*)dctx->dict, (int)dctx->dictSize);
if (decodedSize < 0) /* decompression failed */
return err0r(LZ4F_ERROR_decompressionFailed);
if (dctx->frameInfo.contentChecksumFlag)
XXH32_update(&(dctx->xxh), dctx->tmpOut, decodedSize);
if (dctx->frameInfo.contentSize)
dctx->frameRemainingSize -= decodedSize;
dctx->tmpOutSize = decodedSize;
dctx->tmpOutStart = 0;
dctx->dStage = dstage_flushOut;
}
/* fall-through */
case dstage_flushOut: /* flush decoded data from tmpOut to dstBuffer */
{ size_t const sizeToCopy = MIN(dctx->tmpOutSize - dctx->tmpOutStart, (size_t)(dstEnd-dstPtr));
memcpy(dstPtr, dctx->tmpOut + dctx->tmpOutStart, sizeToCopy);
/* dictionary management */
if (dctx->frameInfo.blockMode==LZ4F_blockLinked)
LZ4F_updateDict(dctx, dstPtr, sizeToCopy, dstStart, 1);
dctx->tmpOutStart += sizeToCopy;
dstPtr += sizeToCopy;
if (dctx->tmpOutStart == dctx->tmpOutSize) { /* all flushed */
dctx->dStage = dstage_getBlockHeader; /* get next block */
break;
}
nextSrcSizeHint = BHSize;
doAnotherStage = 0; /* still some data to flush */
break;
}
case dstage_getSuffix:
if (dctx->frameRemainingSize)
return err0r(LZ4F_ERROR_frameSize_wrong); /* incorrect frame size decoded */
if (!dctx->frameInfo.contentChecksumFlag) { /* no checksum, frame is completed */
nextSrcSizeHint = 0;
LZ4F_resetDecompressionContext(dctx);
doAnotherStage = 0;
break;
}
if ((srcEnd - srcPtr) < 4) { /* not enough size for entire CRC */
dctx->tmpInSize = 0;
dctx->dStage = dstage_storeSuffix;
} else {
selectedIn = srcPtr;
srcPtr += 4;
}
if (dctx->dStage == dstage_storeSuffix) /* can be skipped */
case dstage_storeSuffix:
{ size_t const remainingInput = (size_t)(srcEnd - srcPtr);
size_t const wantedData = 4 - dctx->tmpInSize;
size_t const sizeToCopy = MIN(wantedData, remainingInput);
memcpy(dctx->tmpIn + dctx->tmpInSize, srcPtr, sizeToCopy);
srcPtr += sizeToCopy;
dctx->tmpInSize += sizeToCopy;
if (dctx->tmpInSize < 4) { /* not enough input to read complete suffix */
nextSrcSizeHint = 4 - dctx->tmpInSize;
doAnotherStage=0;
break;
}
selectedIn = dctx->tmpIn;
} /* if (dctx->dStage == dstage_storeSuffix) */
/* case dstage_checkSuffix: */ /* no direct call, avoid scan-build warning */
{ U32 const readCRC = LZ4F_readLE32(selectedIn);
U32 const resultCRC = XXH32_digest(&(dctx->xxh));
if (readCRC != resultCRC)
return err0r(LZ4F_ERROR_contentChecksum_invalid);
nextSrcSizeHint = 0;
LZ4F_resetDecompressionContext(dctx);
doAnotherStage = 0;
break;
}
case dstage_getSFrameSize:
if ((srcEnd - srcPtr) >= 4) {
selectedIn = srcPtr;
srcPtr += 4;
} else {
/* not enough input to read cBlockSize field */
dctx->tmpInSize = 4;
dctx->tmpInTarget = 8;
dctx->dStage = dstage_storeSFrameSize;
}
if (dctx->dStage == dstage_storeSFrameSize)
case dstage_storeSFrameSize:
{
size_t const sizeToCopy = MIN(dctx->tmpInTarget - dctx->tmpInSize,
(size_t)(srcEnd - srcPtr) );
memcpy(dctx->header + dctx->tmpInSize, srcPtr, sizeToCopy);
srcPtr += sizeToCopy;
dctx->tmpInSize += sizeToCopy;
( run in 0.517 second using v1.01-cache-2.11-cpan-39bf76dae61 )