Merge pull request #1390 from facebook/nullAsOutput

support decompressing an empty frame into NULL
This commit is contained in:
Yann Collet 2018-10-26 14:43:16 -07:00 committed by GitHub
commit 7d4960a5e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 73 additions and 22 deletions

View File

@ -404,9 +404,9 @@ unsigned long long ZSTD_findDecompressedSize(const void* src, size_t srcSize)
} }
/** ZSTD_getDecompressedSize() : /** ZSTD_getDecompressedSize() :
* compatible with legacy mode * compatible with legacy mode
* @return : decompressed size if known, 0 otherwise * @return : decompressed size if known, 0 otherwise
note : 0 can mean any of the following : note : 0 can mean any of the following :
- frame content is empty - frame content is empty
- decompressed size field is not present in frame header - decompressed size field is not present in frame header
- frame header unknown / not supported - frame header unknown / not supported
@ -420,8 +420,8 @@ unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize)
/** ZSTD_decodeFrameHeader() : /** ZSTD_decodeFrameHeader() :
* `headerSize` must be the size provided by ZSTD_frameHeaderSize(). * `headerSize` must be the size provided by ZSTD_frameHeaderSize().
* @return : 0 if success, or an error code, which can be tested using ZSTD_isError() */ * @return : 0 if success, or an error code, which can be tested using ZSTD_isError() */
static size_t ZSTD_decodeFrameHeader(ZSTD_DCtx* dctx, const void* src, size_t headerSize) static size_t ZSTD_decodeFrameHeader(ZSTD_DCtx* dctx, const void* src, size_t headerSize)
{ {
size_t const result = ZSTD_getFrameHeader_advanced(&(dctx->fParams), src, headerSize, dctx->format); size_t const result = ZSTD_getFrameHeader_advanced(&(dctx->fParams), src, headerSize, dctx->format);
@ -439,7 +439,7 @@ static size_t ZSTD_decodeFrameHeader(ZSTD_DCtx* dctx, const void* src, size_t he
***************************************************************/ ***************************************************************/
/*! ZSTD_getcBlockSize() : /*! ZSTD_getcBlockSize() :
* Provides the size of compressed block from block header `src` */ * Provides the size of compressed block from block header `src`. */
size_t ZSTD_getcBlockSize(const void* src, size_t srcSize, size_t ZSTD_getcBlockSize(const void* src, size_t srcSize,
blockProperties_t* bpPtr) blockProperties_t* bpPtr)
{ {
@ -459,9 +459,10 @@ size_t ZSTD_getcBlockSize(const void* src, size_t srcSize,
static size_t ZSTD_copyRawBlock(void* dst, size_t dstCapacity, static size_t ZSTD_copyRawBlock(void* dst, size_t dstCapacity,
const void* src, size_t srcSize) const void* src, size_t srcSize)
{ {
if (dst==NULL) return ERROR(dstSize_tooSmall); DEBUGLOG(5, "ZSTD_copyRawBlock");
if (dst == NULL) dstCapacity = 0; /* better safe than sorry */
if (srcSize > dstCapacity) return ERROR(dstSize_tooSmall); if (srcSize > dstCapacity) return ERROR(dstSize_tooSmall);
memcpy(dst, src, srcSize); if (dst) memcpy(dst, src, srcSize);
return srcSize; return srcSize;
} }
@ -1761,7 +1762,9 @@ size_t ZSTD_findFrameCompressedSize(const void *src, size_t srcSize)
} }
/*! ZSTD_decompressFrame() : /*! ZSTD_decompressFrame() :
* @dctx must be properly initialized */ * @dctx must be properly initialized
* will update *srcPtr and *srcSizePtr,
* to make *srcPtr progress by one frame. */
static size_t ZSTD_decompressFrame(ZSTD_DCtx* dctx, static size_t ZSTD_decompressFrame(ZSTD_DCtx* dctx,
void* dst, size_t dstCapacity, void* dst, size_t dstCapacity,
const void** srcPtr, size_t *srcSizePtr) const void** srcPtr, size_t *srcSizePtr)
@ -1770,31 +1773,33 @@ static size_t ZSTD_decompressFrame(ZSTD_DCtx* dctx,
BYTE* const ostart = (BYTE* const)dst; BYTE* const ostart = (BYTE* const)dst;
BYTE* const oend = ostart + dstCapacity; BYTE* const oend = ostart + dstCapacity;
BYTE* op = ostart; BYTE* op = ostart;
size_t remainingSize = *srcSizePtr; size_t remainingSrcSize = *srcSizePtr;
DEBUGLOG(4, "ZSTD_decompressFrame (srcSize:%i)", (int)*srcSizePtr);
/* check */ /* check */
if (remainingSize < ZSTD_frameHeaderSize_min+ZSTD_blockHeaderSize) if (remainingSrcSize < ZSTD_frameHeaderSize_min+ZSTD_blockHeaderSize)
return ERROR(srcSize_wrong); return ERROR(srcSize_wrong);
/* Frame Header */ /* Frame Header */
{ size_t const frameHeaderSize = ZSTD_frameHeaderSize(ip, ZSTD_frameHeaderSize_prefix); { size_t const frameHeaderSize = ZSTD_frameHeaderSize(ip, ZSTD_frameHeaderSize_prefix);
if (ZSTD_isError(frameHeaderSize)) return frameHeaderSize; if (ZSTD_isError(frameHeaderSize)) return frameHeaderSize;
if (remainingSize < frameHeaderSize+ZSTD_blockHeaderSize) if (remainingSrcSize < frameHeaderSize+ZSTD_blockHeaderSize)
return ERROR(srcSize_wrong); return ERROR(srcSize_wrong);
CHECK_F( ZSTD_decodeFrameHeader(dctx, ip, frameHeaderSize) ); CHECK_F( ZSTD_decodeFrameHeader(dctx, ip, frameHeaderSize) );
ip += frameHeaderSize; remainingSize -= frameHeaderSize; ip += frameHeaderSize; remainingSrcSize -= frameHeaderSize;
} }
/* Loop on each block */ /* Loop on each block */
while (1) { while (1) {
size_t decodedSize; size_t decodedSize;
blockProperties_t blockProperties; blockProperties_t blockProperties;
size_t const cBlockSize = ZSTD_getcBlockSize(ip, remainingSize, &blockProperties); size_t const cBlockSize = ZSTD_getcBlockSize(ip, remainingSrcSize, &blockProperties);
if (ZSTD_isError(cBlockSize)) return cBlockSize; if (ZSTD_isError(cBlockSize)) return cBlockSize;
ip += ZSTD_blockHeaderSize; ip += ZSTD_blockHeaderSize;
remainingSize -= ZSTD_blockHeaderSize; remainingSrcSize -= ZSTD_blockHeaderSize;
if (cBlockSize > remainingSize) return ERROR(srcSize_wrong); if (cBlockSize > remainingSrcSize) return ERROR(srcSize_wrong);
switch(blockProperties.blockType) switch(blockProperties.blockType)
{ {
@ -1817,7 +1822,7 @@ static size_t ZSTD_decompressFrame(ZSTD_DCtx* dctx,
XXH64_update(&dctx->xxhState, op, decodedSize); XXH64_update(&dctx->xxhState, op, decodedSize);
op += decodedSize; op += decodedSize;
ip += cBlockSize; ip += cBlockSize;
remainingSize -= cBlockSize; remainingSrcSize -= cBlockSize;
if (blockProperties.lastBlock) break; if (blockProperties.lastBlock) break;
} }
@ -1828,16 +1833,16 @@ static size_t ZSTD_decompressFrame(ZSTD_DCtx* dctx,
if (dctx->fParams.checksumFlag) { /* Frame content checksum verification */ if (dctx->fParams.checksumFlag) { /* Frame content checksum verification */
U32 const checkCalc = (U32)XXH64_digest(&dctx->xxhState); U32 const checkCalc = (U32)XXH64_digest(&dctx->xxhState);
U32 checkRead; U32 checkRead;
if (remainingSize<4) return ERROR(checksum_wrong); if (remainingSrcSize<4) return ERROR(checksum_wrong);
checkRead = MEM_readLE32(ip); checkRead = MEM_readLE32(ip);
if (checkRead != checkCalc) return ERROR(checksum_wrong); if (checkRead != checkCalc) return ERROR(checksum_wrong);
ip += 4; ip += 4;
remainingSize -= 4; remainingSrcSize -= 4;
} }
/* Allow caller to get size read */ /* Allow caller to get size read */
*srcPtr = ip; *srcPtr = ip;
*srcSizePtr = remainingSize; *srcSizePtr = remainingSrcSize;
return op-ostart; return op-ostart;
} }
@ -1869,7 +1874,9 @@ static size_t ZSTD_decompressMultiFrame(ZSTD_DCtx* dctx,
if (dctx->staticSize) return ERROR(memory_allocation); if (dctx->staticSize) return ERROR(memory_allocation);
decodedSize = ZSTD_decompressLegacy(dst, dstCapacity, src, frameSize, dict, dictSize); decodedSize = ZSTD_decompressLegacy(dst, dstCapacity, src, frameSize, dict, dictSize);
if (ZSTD_isError(decodedSize)) return decodedSize;
assert(decodedSize <=- dstCapacity);
dst = (BYTE*)dst + decodedSize; dst = (BYTE*)dst + decodedSize;
dstCapacity -= decodedSize; dstCapacity -= decodedSize;
@ -1922,7 +1929,7 @@ static size_t ZSTD_decompressMultiFrame(ZSTD_DCtx* dctx,
return ERROR(srcSize_wrong); return ERROR(srcSize_wrong);
} }
if (ZSTD_isError(res)) return res; if (ZSTD_isError(res)) return res;
/* no need to bound check, ZSTD_decompressFrame already has */ assert(res <= dstCapacity);
dst = (BYTE*)dst + res; dst = (BYTE*)dst + res;
dstCapacity -= res; dstCapacity -= res;
} }

View File

@ -411,11 +411,55 @@ static int basicUnitTests(U32 seed, double compressibility)
DISPLAYLEVEL(3, "OK \n"); DISPLAYLEVEL(3, "OK \n");
DISPLAYLEVEL(3, "test%3d : check CCtx size after compressing empty input : ", testNb++); DISPLAYLEVEL(3, "test%3d : check CCtx size after compressing empty input : ", testNb++);
{ ZSTD_CCtx* cctx = ZSTD_createCCtx(); { ZSTD_CCtx* const cctx = ZSTD_createCCtx();
size_t const r = ZSTD_compressCCtx(cctx, compressedBuffer, compressedBufferSize, NULL, 0, 19); size_t const r = ZSTD_compressCCtx(cctx, compressedBuffer, compressedBufferSize, NULL, 0, 19);
if (ZSTD_isError(r)) goto _output_error; if (ZSTD_isError(r)) goto _output_error;
if (ZSTD_sizeof_CCtx(cctx) > (1U << 20)) goto _output_error; if (ZSTD_sizeof_CCtx(cctx) > (1U << 20)) goto _output_error;
ZSTD_freeCCtx(cctx); ZSTD_freeCCtx(cctx);
cSize = r;
}
DISPLAYLEVEL(3, "OK \n");
DISPLAYLEVEL(3, "test%3d : decompress empty frame into NULL : ", testNb++);
{ size_t const r = ZSTD_decompress(NULL, 0, compressedBuffer, cSize);
if (ZSTD_isError(r)) goto _output_error;
if (r != 0) goto _output_error;
}
{ ZSTD_CCtx* const cctx = ZSTD_createCCtx();
ZSTD_outBuffer output;
if (cctx==NULL) goto _output_error;
output.dst = compressedBuffer;
output.size = compressedBufferSize;
output.pos = 0;
CHECK_Z( ZSTD_initCStream(cctx, 1) ); /* content size unknown */
CHECK_Z( ZSTD_flushStream(cctx, &output) ); /* ensure no possibility to "concatenate" and determine the content size */
CHECK_Z( ZSTD_endStream(cctx, &output) );
ZSTD_freeCCtx(cctx);
/* single scan decompression */
{ size_t const r = ZSTD_decompress(NULL, 0, compressedBuffer, output.pos);
if (ZSTD_isError(r)) goto _output_error;
if (r != 0) goto _output_error;
}
/* streaming decompression */
{ ZSTD_DCtx* const dstream = ZSTD_createDStream();
ZSTD_inBuffer dinput;
ZSTD_outBuffer doutput;
size_t ipos;
if (dstream==NULL) goto _output_error;
dinput.src = compressedBuffer;
dinput.size = 0;
dinput.pos = 0;
doutput.dst = NULL;
doutput.size = 0;
doutput.pos = 0;
CHECK_Z ( ZSTD_initDStream(dstream) );
for (ipos=1; ipos<=output.pos; ipos++) {
dinput.size = ipos;
CHECK_Z ( ZSTD_decompressStream(dstream, &doutput, &dinput) );
}
if (doutput.pos != 0) goto _output_error;
ZSTD_freeDStream(dstream);
}
} }
DISPLAYLEVEL(3, "OK \n"); DISPLAYLEVEL(3, "OK \n");