Merge pull request #1543 from shakeelrao/dev
Provide a function to estimate decompressed size.
This commit is contained in:
@@ -433,6 +433,88 @@ static size_t ZSTD_decodeFrameHeader(ZSTD_DCtx* dctx, const void* src, size_t he
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains the compressed frame size and an upper-bound for the decompressed frame size.
|
||||
* Note: before using `compressedSize` you must check for errors using ZSTD_isError().
|
||||
* similarly, before using `decompressedBound`, you must check for errors using:
|
||||
* `decompressedBound` != ZSTD_CONTENTSIZE_UNKNOWN
|
||||
*/
|
||||
typedef struct {
|
||||
size_t compressedSize;
|
||||
unsigned long long decompressedBound;
|
||||
} ZSTD_frameSizeInfo;
|
||||
|
||||
static ZSTD_frameSizeInfo ZSTD_errorFrameSizeInfo(size_t ret)
|
||||
{
|
||||
ZSTD_frameSizeInfo frameSizeInfo;
|
||||
frameSizeInfo.compressedSize = ret;
|
||||
frameSizeInfo.decompressedBound = ZSTD_CONTENTSIZE_UNKNOWN;
|
||||
return frameSizeInfo;
|
||||
}
|
||||
|
||||
static ZSTD_frameSizeInfo ZSTD_findFrameSizeInfo(const void* src, size_t srcSize)
|
||||
{
|
||||
ZSTD_frameSizeInfo frameSizeInfo;
|
||||
memset(&frameSizeInfo, 0, sizeof(ZSTD_frameSizeInfo));
|
||||
|
||||
#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1)
|
||||
if (ZSTD_isLegacy(src, srcSize))
|
||||
return ZSTD_errorFrameSizeInfo(ZSTD_findFrameCompressedSizeLegacy(src, srcSize));
|
||||
#endif
|
||||
|
||||
if ((srcSize >= ZSTD_SKIPPABLEHEADERSIZE)
|
||||
&& (MEM_readLE32(src) & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) {
|
||||
frameSizeInfo.compressedSize = readSkippableFrameSize(src, srcSize);
|
||||
return frameSizeInfo;
|
||||
} else {
|
||||
const BYTE* ip = (const BYTE*)src;
|
||||
const BYTE* const ipstart = ip;
|
||||
size_t remainingSize = srcSize;
|
||||
size_t nbBlocks = 0;
|
||||
ZSTD_frameHeader zfh;
|
||||
|
||||
/* Extract Frame Header */
|
||||
{ size_t const ret = ZSTD_getFrameHeader(&zfh, src, srcSize);
|
||||
if (ZSTD_isError(ret))
|
||||
return ZSTD_errorFrameSizeInfo(ret);
|
||||
if (ret > 0)
|
||||
return ZSTD_errorFrameSizeInfo(ERROR(srcSize_wrong));
|
||||
}
|
||||
|
||||
ip += zfh.headerSize;
|
||||
remainingSize -= zfh.headerSize;
|
||||
|
||||
/* Iterate over each block */
|
||||
while (1) {
|
||||
blockProperties_t blockProperties;
|
||||
size_t const cBlockSize = ZSTD_getcBlockSize(ip, remainingSize, &blockProperties);
|
||||
if (ZSTD_isError(cBlockSize))
|
||||
return ZSTD_errorFrameSizeInfo(cBlockSize);
|
||||
|
||||
if (ZSTD_blockHeaderSize + cBlockSize > remainingSize)
|
||||
return ZSTD_errorFrameSizeInfo(ERROR(srcSize_wrong));
|
||||
|
||||
ip += ZSTD_blockHeaderSize + cBlockSize;
|
||||
remainingSize -= ZSTD_blockHeaderSize + cBlockSize;
|
||||
nbBlocks++;
|
||||
|
||||
if (blockProperties.lastBlock) break;
|
||||
}
|
||||
|
||||
/* Final frame content checksum */
|
||||
if (zfh.checksumFlag) {
|
||||
if (remainingSize < 4)
|
||||
return ZSTD_errorFrameSizeInfo(ERROR(srcSize_wrong));
|
||||
ip += 4;
|
||||
}
|
||||
|
||||
frameSizeInfo.compressedSize = ip - ipstart;
|
||||
frameSizeInfo.decompressedBound = (zfh.frameContentSize != ZSTD_CONTENTSIZE_UNKNOWN)
|
||||
? zfh.frameContentSize
|
||||
: nbBlocks * zfh.blockSizeMax;
|
||||
return frameSizeInfo;
|
||||
}
|
||||
}
|
||||
|
||||
/** ZSTD_findFrameCompressedSize() :
|
||||
* compatible with legacy mode
|
||||
@@ -441,53 +523,34 @@ static size_t ZSTD_decodeFrameHeader(ZSTD_DCtx* dctx, const void* src, size_t he
|
||||
* @return : the compressed size of the frame starting at `src` */
|
||||
size_t ZSTD_findFrameCompressedSize(const void *src, size_t srcSize)
|
||||
{
|
||||
#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1)
|
||||
if (ZSTD_isLegacy(src, srcSize))
|
||||
return ZSTD_findFrameCompressedSizeLegacy(src, srcSize);
|
||||
#endif
|
||||
if ( (srcSize >= ZSTD_SKIPPABLEHEADERSIZE)
|
||||
&& (MEM_readLE32(src) & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START ) {
|
||||
return readSkippableFrameSize(src, srcSize);
|
||||
} else {
|
||||
const BYTE* ip = (const BYTE*)src;
|
||||
const BYTE* const ipstart = ip;
|
||||
size_t remainingSize = srcSize;
|
||||
ZSTD_frameHeader zfh;
|
||||
|
||||
/* Extract Frame Header */
|
||||
{ size_t const ret = ZSTD_getFrameHeader(&zfh, src, srcSize);
|
||||
if (ZSTD_isError(ret)) return ret;
|
||||
RETURN_ERROR_IF(ret > 0, srcSize_wrong);
|
||||
}
|
||||
|
||||
ip += zfh.headerSize;
|
||||
remainingSize -= zfh.headerSize;
|
||||
|
||||
/* Loop on each block */
|
||||
while (1) {
|
||||
blockProperties_t blockProperties;
|
||||
size_t const cBlockSize = ZSTD_getcBlockSize(ip, remainingSize, &blockProperties);
|
||||
if (ZSTD_isError(cBlockSize)) return cBlockSize;
|
||||
|
||||
RETURN_ERROR_IF(ZSTD_blockHeaderSize + cBlockSize > remainingSize,
|
||||
srcSize_wrong);
|
||||
|
||||
ip += ZSTD_blockHeaderSize + cBlockSize;
|
||||
remainingSize -= ZSTD_blockHeaderSize + cBlockSize;
|
||||
|
||||
if (blockProperties.lastBlock) break;
|
||||
}
|
||||
|
||||
if (zfh.checksumFlag) { /* Final frame content checksum */
|
||||
RETURN_ERROR_IF(remainingSize < 4, srcSize_wrong);
|
||||
ip += 4;
|
||||
}
|
||||
|
||||
return ip - ipstart;
|
||||
}
|
||||
ZSTD_frameSizeInfo const frameSizeInfo = ZSTD_findFrameSizeInfo(src, srcSize);
|
||||
return frameSizeInfo.compressedSize;
|
||||
}
|
||||
|
||||
|
||||
/** ZSTD_decompressBound() :
|
||||
* currently incompatible with legacy mode
|
||||
* `src` must point to the start of a ZSTD frame or a skippeable frame
|
||||
* `srcSize` must be at least as large as the frame contained
|
||||
* @return : the maximum decompressed size of the compressed source
|
||||
*/
|
||||
unsigned long long ZSTD_decompressBound(const void* src, size_t srcSize)
|
||||
{
|
||||
unsigned long long bound = 0;
|
||||
/* Iterate over each frame */
|
||||
while (srcSize > 0) {
|
||||
ZSTD_frameSizeInfo const frameSizeInfo = ZSTD_findFrameSizeInfo(src, srcSize);
|
||||
size_t const compressedSize = frameSizeInfo.compressedSize;
|
||||
unsigned long long const decompressedBound = frameSizeInfo.decompressedBound;
|
||||
if (ZSTD_isError(compressedSize) || decompressedBound == ZSTD_CONTENTSIZE_ERROR)
|
||||
return ZSTD_CONTENTSIZE_ERROR;
|
||||
src = (const BYTE*)src + compressedSize;
|
||||
srcSize -= compressedSize;
|
||||
bound += decompressedBound;
|
||||
}
|
||||
return bound;
|
||||
}
|
||||
|
||||
|
||||
/*-*************************************************************
|
||||
* Frame decoding
|
||||
|
||||
+17
-2
@@ -1081,9 +1081,9 @@ typedef enum {
|
||||
***************************************/
|
||||
|
||||
/*! ZSTD_findDecompressedSize() :
|
||||
* `src` should point the start of a series of ZSTD encoded and/or skippable frames
|
||||
* `src` should point to the start of a series of ZSTD encoded and/or skippable frames
|
||||
* `srcSize` must be the _exact_ size of this series
|
||||
* (i.e. there should be a frame boundary exactly at `srcSize` bytes after `src`)
|
||||
* (i.e. there should be a frame boundary at `src + srcSize`)
|
||||
* @return : - decompressed size of all data in all successive frames
|
||||
* - if the decompressed size cannot be determined: ZSTD_CONTENTSIZE_UNKNOWN
|
||||
* - if an error occurred: ZSTD_CONTENTSIZE_ERROR
|
||||
@@ -1103,6 +1103,21 @@ typedef enum {
|
||||
* however it does mean that all frame data must be present and valid. */
|
||||
ZSTDLIB_API unsigned long long ZSTD_findDecompressedSize(const void* src, size_t srcSize);
|
||||
|
||||
/** ZSTD_decompressBound() :
|
||||
* `src` should point to the start of a series of ZSTD encoded and/or skippable frames
|
||||
* `srcSize` must be the _exact_ size of this series
|
||||
* (i.e. there should be a frame boundary at `src + srcSize`)
|
||||
* @return : - upper-bound for the decompressed size of all data in all successive frames
|
||||
* - if an error occured: ZSTD_CONTENTSIZE_ERROR
|
||||
*
|
||||
* note 1 : an error can occur if `src` contains a legacy frame or an invalid/incorrectly formatted frame.
|
||||
* note 2 : the upper-bound is exact when the decompressed size field is available in every ZSTD encoded frame of `src`.
|
||||
* in this case, `ZSTD_findDecompressedSize` and `ZSTD_decompressBound` return the same value.
|
||||
* note 3 : when the decompressed size field isn't available, the upper-bound for that frame is calculated by:
|
||||
* upper-bound = # blocks * min(128 KB, Window_Size)
|
||||
*/
|
||||
ZSTDLIB_API unsigned long long ZSTD_decompressBound(const void* src, size_t srcSice);
|
||||
|
||||
/*! ZSTD_frameHeaderSize() :
|
||||
* srcSize must be >= ZSTD_FRAMEHEADERSIZE_PREFIX.
|
||||
* @return : size of the Frame Header,
|
||||
|
||||
@@ -376,6 +376,13 @@ static int basicUnitTests(U32 seed, double compressibility)
|
||||
}
|
||||
DISPLAYLEVEL(3, "OK \n");
|
||||
|
||||
DISPLAYLEVEL(3, "test%3i : tight ZSTD_decompressBound test : ", testNb++);
|
||||
{
|
||||
unsigned long long bound = ZSTD_decompressBound(compressedBuffer, cSize);
|
||||
if (bound != CNBuffSize) goto _output_error;
|
||||
}
|
||||
DISPLAYLEVEL(3, "OK \n");
|
||||
|
||||
DISPLAYLEVEL(3, "test%3i : decompress %u bytes : ", testNb++, (unsigned)CNBuffSize);
|
||||
{ size_t const r = ZSTD_decompress(decodedBuffer, CNBuffSize, compressedBuffer, cSize);
|
||||
if (r != CNBuffSize) goto _output_error; }
|
||||
@@ -431,6 +438,27 @@ static int basicUnitTests(U32 seed, double compressibility)
|
||||
if (ZSTD_getErrorCode(r) != ZSTD_error_srcSize_wrong) goto _output_error; }
|
||||
DISPLAYLEVEL(3, "OK \n");
|
||||
|
||||
DISPLAYLEVEL(3, "test%3i : ZSTD_decompressBound test with content size missing : ", testNb++);
|
||||
{ /* create compressed buffer with content size missing */
|
||||
ZSTD_CCtx* cctx = ZSTD_createCCtx();
|
||||
CHECK_Z( ZSTD_CCtx_setParameter(cctx, ZSTD_c_contentSizeFlag, 0) );
|
||||
CHECKPLUS(r, ZSTD_compress2(cctx,
|
||||
compressedBuffer, compressedBufferSize,
|
||||
CNBuffer, CNBuffSize),
|
||||
cSize=r );
|
||||
ZSTD_freeCCtx(cctx);
|
||||
}
|
||||
{ /* ensure frame content size is missing */
|
||||
ZSTD_frameHeader zfh;
|
||||
size_t const ret = ZSTD_getFrameHeader(&zfh, compressedBuffer, compressedBufferSize);
|
||||
if (ret != 0 || zfh.frameContentSize != ZSTD_CONTENTSIZE_UNKNOWN) goto _output_error;
|
||||
}
|
||||
{ /* ensure CNBuffSize <= decompressBound */
|
||||
unsigned long long const bound = ZSTD_decompressBound(compressedBuffer, compressedBufferSize);
|
||||
if (CNBuffSize > bound) goto _output_error;
|
||||
}
|
||||
DISPLAYLEVEL(3, "OK \n");
|
||||
|
||||
DISPLAYLEVEL(3, "test%3d : check CCtx size after compressing empty input : ", testNb++);
|
||||
{ ZSTD_CCtx* const cctx = ZSTD_createCCtx();
|
||||
size_t const r = ZSTD_compressCCtx(cctx, compressedBuffer, compressedBufferSize, NULL, 0, 19);
|
||||
@@ -901,6 +929,11 @@ static int basicUnitTests(U32 seed, double compressibility)
|
||||
if (r != CNBuffSize / 2) goto _output_error; }
|
||||
DISPLAYLEVEL(3, "OK \n");
|
||||
|
||||
DISPLAYLEVEL(3, "test%3i : get tight decompressed bound of multiple frames : ", testNb++);
|
||||
{ unsigned long long const bound = ZSTD_decompressBound(compressedBuffer, cSize);
|
||||
if (bound != CNBuffSize / 2) goto _output_error; }
|
||||
DISPLAYLEVEL(3, "OK \n");
|
||||
|
||||
DISPLAYLEVEL(3, "test%3i : decompress multiple frames : ", testNb++);
|
||||
{ CHECK_V(r, ZSTD_decompress(decodedBuffer, CNBuffSize, compressedBuffer, cSize));
|
||||
if (r != CNBuffSize / 2) goto _output_error; }
|
||||
|
||||
@@ -31,6 +31,7 @@ static const void *symbols[] = {
|
||||
&ZSTD_getFrameContentSize,
|
||||
&ZSTD_maxCLevel,
|
||||
&ZSTD_compressBound,
|
||||
&ZSTD_decompressBound,
|
||||
&ZSTD_isError,
|
||||
&ZSTD_getErrorName,
|
||||
&ZSTD_createCCtx,
|
||||
|
||||
Reference in New Issue
Block a user