From 6d8778d8e7af50f0a7055d239df796e34f188239 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Mon, 9 Oct 2017 16:44:31 -0700 Subject: [PATCH 001/145] [doc] Add images for release notes --- doc/images/linux-4.7-12-compress.png | Bin 0 -> 93913 bytes doc/images/linux-4.7-12-decompress.png | Bin 0 -> 56215 bytes doc/images/linux-git-compress.png | Bin 0 -> 105620 bytes doc/images/linux-git-decompress.png | Bin 0 -> 54333 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/images/linux-4.7-12-compress.png create mode 100644 doc/images/linux-4.7-12-decompress.png create mode 100644 doc/images/linux-git-compress.png create mode 100644 doc/images/linux-git-decompress.png diff --git a/doc/images/linux-4.7-12-compress.png b/doc/images/linux-4.7-12-compress.png new file mode 100644 index 0000000000000000000000000000000000000000..71f3381bdef8fb1e0d811513c588b03dfb03d0a2 GIT binary patch literal 93913 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 literal 0 HcmV?d00001 diff --git a/doc/images/linux-4.7-12-decompress.png b/doc/images/linux-4.7-12-decompress.png new file mode 100644 index 0000000000000000000000000000000000000000..c99ff9809d540440e09e1a88a9bbb75bfb52af7e GIT binary patch literal 56215 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 literal 0 HcmV?d00001 diff --git a/doc/images/linux-git-compress.png b/doc/images/linux-git-compress.png new file mode 100644 index 0000000000000000000000000000000000000000..3fa0e88f4f169b1f23c43c189097af7be1d08729 GIT binary patch literal 105620 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%0DoN$)29@ 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 literal 0 HcmV?d00001 diff --git a/doc/images/linux-git-decompress.png b/doc/images/linux-git-decompress.png new file mode 100644 index 0000000000000000000000000000000000000000..279ba7cf289f084c44721499825fac351a45d5d9 GIT binary patch literal 54333 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 literal 0 HcmV?d00001 From a9e5705077604130f2b77142190978928a0b2bc4 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 13 Oct 2017 02:36:16 -0700 Subject: [PATCH 002/145] minor code formatting added a trace during sequence encoding --- lib/compress/zstd_compress.c | 169 +++++++++++++++++++---------------- lib/compress/zstd_compress.h | 2 +- 2 files changed, 93 insertions(+), 78 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 2c46c79f1..e072fe30e 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1238,7 +1238,7 @@ static size_t ZSTD_compressLiterals (ZSTD_entropyCTables_t * entropy, ostart[4] = (BYTE)(cLitSize >> 10); break; } - default: /* not possible : lhSize is {3,4,5} */ + default: /* not possible : lhSize is {3,4,5} */ assert(0); } return lhSize+cLitSize; @@ -1273,7 +1273,8 @@ typedef enum { ZSTD_defaultAllowed = 1 } ZSTD_defaultPolicy_e; -MEM_STATIC symbolEncodingType_e ZSTD_selectEncodingType( +MEM_STATIC +symbolEncodingType_e ZSTD_selectEncodingType( FSE_repeat* repeatMode, size_t const mostFrequent, size_t nbSeq, U32 defaultNormLog, ZSTD_defaultPolicy_e const isDefaultAllowed) { @@ -1288,10 +1289,12 @@ MEM_STATIC symbolEncodingType_e ZSTD_selectEncodingType( *repeatMode = FSE_repeat_check; return set_rle; } - if (isDefaultAllowed && (*repeatMode == FSE_repeat_valid) && (nbSeq < MAX_SEQ_FOR_STATIC_FSE)) { + if ( isDefaultAllowed + && (*repeatMode == FSE_repeat_valid) && (nbSeq < MAX_SEQ_FOR_STATIC_FSE)) { return set_repeat; } - if (isDefaultAllowed && ((nbSeq < MIN_SEQ_FOR_DYNAMIC_FSE) || (mostFrequent < (nbSeq >> (defaultNormLog-1))))) { + if ( isDefaultAllowed + && ((nbSeq < MIN_SEQ_FOR_DYNAMIC_FSE) || (mostFrequent < (nbSeq >> (defaultNormLog-1)))) ) { *repeatMode = FSE_repeat_valid; return set_basic; } @@ -1299,7 +1302,8 @@ MEM_STATIC symbolEncodingType_e ZSTD_selectEncodingType( return set_compressed; } -MEM_STATIC size_t ZSTD_buildCTable(void* dst, size_t dstCapacity, +MEM_STATIC +size_t ZSTD_buildCTable(void* dst, size_t dstCapacity, FSE_CTable* CTable, U32 FSELog, symbolEncodingType_e type, U32* count, U32 max, BYTE const* codeTable, size_t nbSeq, @@ -1317,7 +1321,7 @@ MEM_STATIC size_t ZSTD_buildCTable(void* dst, size_t dstCapacity, case set_repeat: return 0; case set_basic: - CHECK_F(FSE_buildCTable_wksp(CTable, defaultNorm, defaultMax, defaultNormLog, workspace, workspaceSize)); + CHECK_F(FSE_buildCTable_wksp(CTable, defaultNorm, defaultMax, defaultNormLog, workspace, workspaceSize)); /* note : could be pre-calculated */ return 0; case set_compressed: { S16 norm[MaxSeq + 1]; @@ -1339,11 +1343,13 @@ MEM_STATIC size_t ZSTD_buildCTable(void* dst, size_t dstCapacity, } } -MEM_STATIC size_t ZSTD_encodeSequences(void* dst, size_t dstCapacity, - FSE_CTable const* CTable_MatchLength, BYTE const* mlCodeTable, - FSE_CTable const* CTable_OffsetBits, BYTE const* ofCodeTable, - FSE_CTable const* CTable_LitLength, BYTE const* llCodeTable, - seqDef const* sequences, size_t nbSeq, int longOffsets) +MEM_STATIC +size_t ZSTD_encodeSequences( + void* dst, size_t dstCapacity, + FSE_CTable const* CTable_MatchLength, BYTE const* mlCodeTable, + FSE_CTable const* CTable_OffsetBits, BYTE const* ofCodeTable, + FSE_CTable const* CTable_LitLength, BYTE const* llCodeTable, + seqDef const* sequences, size_t nbSeq, int longOffsets) { BIT_CStream_t blockStream; FSE_CState_t stateMatchLength; @@ -1380,8 +1386,12 @@ MEM_STATIC size_t ZSTD_encodeSequences(void* dst, size_t dstCapacity, BYTE const ofCode = ofCodeTable[n]; BYTE const mlCode = mlCodeTable[n]; U32 const llBits = LL_bits[llCode]; - U32 const ofBits = ofCode; /* 32b*/ /* 64b*/ + U32 const ofBits = ofCode; U32 const mlBits = ML_bits[mlCode]; + DEBUGLOG(6, "encoding: litlen:%u - matchlen:%u - offCode:%u", + sequences[n].litLength, + sequences[n].matchLength + MINMATCH, + sequences[n].offset); /* 32b*/ /* 64b*/ /* (7)*/ /* (7)*/ FSE_encodeSymbol(&blockStream, &stateOffsetBits, ofCode); /* 15 */ /* 15 */ FSE_encodeSymbol(&blockStream, &stateMatchLength, mlCode); /* 24 */ /* 24 */ @@ -1462,7 +1472,7 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, /* convert length/distances into codes */ ZSTD_seqToCodes(seqStorePtr); - /* CTable for Literal Lengths */ + /* build CTable for Literal Lengths */ { U32 max = MaxLL; size_t const mostFrequent = FSE_countFast_wksp(count, &max, llCodeTable, nbSeq, entropy->workspace); LLtype = ZSTD_selectEncodingType(&entropy->litlength_repeatMode, mostFrequent, nbSeq, LL_defaultNormLog, ZSTD_defaultAllowed); @@ -1472,11 +1482,11 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, if (ZSTD_isError(countSize)) return countSize; op += countSize; } } - /* CTable for Offsets */ + /* build CTable for Offsets */ { U32 max = MaxOff; size_t const mostFrequent = FSE_countFast_wksp(count, &max, ofCodeTable, nbSeq, entropy->workspace); /* We can only use the basic table if max <= DefaultMaxOff, otherwise the offsets are too large */ - ZSTD_defaultPolicy_e const defaultPolicy = max <= DefaultMaxOff ? ZSTD_defaultAllowed : ZSTD_defaultDisallowed; + ZSTD_defaultPolicy_e const defaultPolicy = (max <= DefaultMaxOff) ? ZSTD_defaultAllowed : ZSTD_defaultDisallowed; Offtype = ZSTD_selectEncodingType(&entropy->offcode_repeatMode, mostFrequent, nbSeq, OF_defaultNormLog, defaultPolicy); { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_OffsetBits, OffFSELog, (symbolEncodingType_e)Offtype, count, max, ofCodeTable, nbSeq, OF_defaultNorm, OF_defaultNormLog, DefaultMaxOff, @@ -1484,7 +1494,7 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, if (ZSTD_isError(countSize)) return countSize; op += countSize; } } - /* CTable for MatchLengths */ + /* build CTable for MatchLengths */ { U32 max = MaxML; size_t const mostFrequent = FSE_countFast_wksp(count, &max, mlCodeTable, nbSeq, entropy->workspace); MLtype = ZSTD_selectEncodingType(&entropy->matchlength_repeatMode, mostFrequent, nbSeq, ML_defaultNormLog, ZSTD_defaultAllowed); @@ -1497,13 +1507,15 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, *seqHead = (BYTE)((LLtype<<6) + (Offtype<<4) + (MLtype<<2)); - { size_t const streamSize = ZSTD_encodeSequences(op, oend - op, - CTable_MatchLength, mlCodeTable, - CTable_OffsetBits, ofCodeTable, - CTable_LitLength, llCodeTable, - sequences, nbSeq, longOffsets); - if (ZSTD_isError(streamSize)) return streamSize; - op += streamSize; + { size_t const bitstreamSize = ZSTD_encodeSequences( + op, oend - op, + CTable_MatchLength, mlCodeTable, + CTable_OffsetBits, ofCodeTable, + CTable_LitLength, llCodeTable, + sequences, nbSeq, + longOffsets); + if (ZSTD_isError(bitstreamSize)) return bitstreamSize; + op += bitstreamSize; } return op - ostart; @@ -1517,27 +1529,27 @@ MEM_STATIC size_t ZSTD_compressSequences(seqStore_t* seqStorePtr, { size_t const cSize = ZSTD_compressSequences_internal(seqStorePtr, entropy, cParams, dst, dstCapacity); - size_t const minGain = ZSTD_minGain(srcSize); - size_t const maxCSize = srcSize - minGain; /* If the srcSize <= dstCapacity, then there is enough space to write a * raw uncompressed block. Since we ran out of space, the block must not * be compressible, so fall back to a raw uncompressed block. */ - int const uncompressibleError = cSize == ERROR(dstSize_tooSmall) && srcSize <= dstCapacity; - + int const uncompressibleError = (cSize == ERROR(dstSize_tooSmall)) && (srcSize <= dstCapacity); if (ZSTD_isError(cSize) && !uncompressibleError) return cSize; + /* Check compressibility */ - if (cSize >= maxCSize || uncompressibleError) { - entropy->hufCTable_repeatMode = HUF_repeat_none; - entropy->offcode_repeatMode = FSE_repeat_none; - entropy->matchlength_repeatMode = FSE_repeat_none; - entropy->litlength_repeatMode = FSE_repeat_none; - return 0; - } + { size_t const minGain = ZSTD_minGain(srcSize); /* note : fixed formula, maybe should depend on compression level, or strategy */ + size_t const maxCSize = srcSize - minGain; + if (cSize >= maxCSize || uncompressibleError) { + entropy->hufCTable_repeatMode = HUF_repeat_none; + entropy->offcode_repeatMode = FSE_repeat_none; + entropy->matchlength_repeatMode = FSE_repeat_none; + entropy->litlength_repeatMode = FSE_repeat_none; + return 0; /* block not compressed */ + } } assert(!ZSTD_isError(cSize)); - /* confirm repcodes */ + /* block is compressed => confirm repcodes in history */ { int i; for (i=0; irep[i] = seqStorePtr->repToConfirm[i]; } return cSize; } @@ -1559,9 +1571,9 @@ ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, int extDict ZSTD_compressBlock_btopt_extDict, ZSTD_compressBlock_btultra_extDict } }; ZSTD_STATIC_ASSERT((unsigned)ZSTD_fast == 1); + assert((U32)strat >= (U32)ZSTD_fast); assert((U32)strat <= (U32)ZSTD_btultra); - return blockCompressor[extDict!=0][(U32)strat]; } @@ -1574,28 +1586,28 @@ static void ZSTD_storeLastLiterals(seqStore_t* seqStorePtr, static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, void* dst, size_t dstCapacity, const void* src, size_t srcSize) { - const BYTE* const base = zc->base; - const BYTE* const istart = (const BYTE*)src; - const U32 current = (U32)(istart-base); - size_t lastLLSize; - const BYTE* anchor; - U32 const extDict = zc->lowLimit < zc->dictLimit; - const ZSTD_blockCompressor blockCompressor = - zc->appliedParams.ldmParams.enableLdm - ? (extDict ? ZSTD_compressBlock_ldm_extDict : ZSTD_compressBlock_ldm) - : ZSTD_selectBlockCompressor(zc->appliedParams.cParams.strategy, extDict); - - if (srcSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1) return 0; /* don't even attempt compression below a certain srcSize */ + if (srcSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1) + return 0; /* don't even attempt compression below a certain srcSize */ ZSTD_resetSeqStore(&(zc->seqStore)); - if (current > zc->nextToUpdate + 384) - zc->nextToUpdate = current - MIN(192, (U32)(current - zc->nextToUpdate - 384)); /* limited update after finding a very long match */ - - lastLLSize = blockCompressor(zc, src, srcSize); - - /* Last literals */ - anchor = (const BYTE*)src + srcSize - lastLLSize; - ZSTD_storeLastLiterals(&zc->seqStore, anchor, lastLLSize); + /* limited update after a very long match */ + { const BYTE* const base = zc->base; + const BYTE* const istart = (const BYTE*)src; + const U32 current = (U32)(istart-base); + if (current > zc->nextToUpdate + 384) + zc->nextToUpdate = current - MIN(192, (U32)(current - zc->nextToUpdate - 384)); + } + /* find and store sequences */ + { U32 const extDict = zc->lowLimit < zc->dictLimit; + const ZSTD_blockCompressor blockCompressor = + zc->appliedParams.ldmParams.enableLdm + ? (extDict ? ZSTD_compressBlock_ldm_extDict : ZSTD_compressBlock_ldm) + : ZSTD_selectBlockCompressor(zc->appliedParams.cParams.strategy, extDict); + size_t const lastLLSize = blockCompressor(zc, src, srcSize); + const BYTE* const anchor = (const BYTE*)src + srcSize - lastLLSize; + ZSTD_storeLastLiterals(&zc->seqStore, anchor, lastLLSize); + } + /* encode */ return ZSTD_compressSequences(&zc->seqStore, zc->entropy, &zc->appliedParams.cParams, dst, dstCapacity, srcSize); } @@ -1618,13 +1630,13 @@ static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx, BYTE* const ostart = (BYTE*)dst; BYTE* op = ostart; U32 const maxDist = (U32)1 << cctx->appliedParams.cParams.windowLog; + assert(cctx->appliedParams.cParams.windowLog <= 31); if (cctx->appliedParams.fParams.checksumFlag && srcSize) XXH64_update(&cctx->xxhState, src, srcSize); while (remaining) { U32 const lastBlock = lastFrameChunk & (blockSize >= remaining); - size_t cSize; if (dstCapacity < ZSTD_blockHeaderSize + MIN_CBLOCK_SIZE) return ERROR(dstSize_tooSmall); /* not enough space to store compressed block */ @@ -1666,34 +1678,37 @@ static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx, else cctx->nextToUpdate -= correction; DEBUGLOG(4, "Correction of 0x%x bytes to lowLimit=0x%x\n", correction, cctx->lowLimit); } - + /* enforce maxDist */ if ((U32)(ip+blockSize - cctx->base) > cctx->loadedDictEnd + maxDist) { - /* enforce maxDist */ U32 const newLowLimit = (U32)(ip+blockSize - cctx->base) - maxDist; if (cctx->lowLimit < newLowLimit) cctx->lowLimit = newLowLimit; if (cctx->dictLimit < cctx->lowLimit) cctx->dictLimit = cctx->lowLimit; } - cSize = ZSTD_compressBlock_internal(cctx, op+ZSTD_blockHeaderSize, dstCapacity-ZSTD_blockHeaderSize, ip, blockSize); - if (ZSTD_isError(cSize)) return cSize; + { size_t cSize = ZSTD_compressBlock_internal(cctx, + op+ZSTD_blockHeaderSize, dstCapacity-ZSTD_blockHeaderSize, + ip, blockSize); + if (ZSTD_isError(cSize)) return cSize; - if (cSize == 0) { /* block is not compressible */ - U32 const cBlockHeader24 = lastBlock + (((U32)bt_raw)<<1) + (U32)(blockSize << 3); - if (blockSize + ZSTD_blockHeaderSize > dstCapacity) return ERROR(dstSize_tooSmall); - MEM_writeLE32(op, cBlockHeader24); /* no pb, 4th byte will be overwritten */ - memcpy(op + ZSTD_blockHeaderSize, ip, blockSize); - cSize = ZSTD_blockHeaderSize+blockSize; - } else { - U32 const cBlockHeader24 = lastBlock + (((U32)bt_compressed)<<1) + (U32)(cSize << 3); - MEM_writeLE24(op, cBlockHeader24); - cSize += ZSTD_blockHeaderSize; - } + if (cSize == 0) { /* block is not compressible */ + U32 const cBlockHeader24 = lastBlock + (((U32)bt_raw)<<1) + (U32)(blockSize << 3); + if (blockSize + ZSTD_blockHeaderSize > dstCapacity) return ERROR(dstSize_tooSmall); + MEM_writeLE32(op, cBlockHeader24); /* 4th byte will be overwritten */ + memcpy(op + ZSTD_blockHeaderSize, ip, blockSize); + cSize = ZSTD_blockHeaderSize + blockSize; + } else { + U32 const cBlockHeader24 = lastBlock + (((U32)bt_compressed)<<1) + (U32)(cSize << 3); + MEM_writeLE24(op, cBlockHeader24); + cSize += ZSTD_blockHeaderSize; + } - remaining -= blockSize; - dstCapacity -= cSize; - ip += blockSize; - op += cSize; - } + ip += blockSize; + assert(remaining >= blockSize); + remaining -= blockSize; + op += cSize; + assert(dstCapacity >= cSize); + dstCapacity -= cSize; + } } if (lastFrameChunk && (op>ostart)) cctx->stage = ZSTDcs_ending; return op-ostart; diff --git a/lib/compress/zstd_compress.h b/lib/compress/zstd_compress.h index 94606edc9..fe98da78d 100644 --- a/lib/compress/zstd_compress.h +++ b/lib/compress/zstd_compress.h @@ -127,7 +127,7 @@ MEM_STATIC void ZSTD_storeSeq(seqStore_t* seqStorePtr, size_t litLength, const v #if defined(ZSTD_DEBUG) && (ZSTD_DEBUG >= 6) static const BYTE* g_start = NULL; U32 const pos = (U32)((const BYTE*)literals - g_start); - if (g_start==NULL) g_start = (const BYTE*)literals; + if (g_start==NULL) g_start = (const BYTE*)literals; /* note : index only works for compression within a single segment */ if ((pos > 0) && (pos < 1000000000)) DEBUGLOG(6, "Cpos %6u :%5u literals & match %3u bytes at distance %6u", pos, (U32)litLength, (U32)matchCode+MINMATCH, (U32)offsetCode); From 24ac2dbd2a56dccbfe52293ac98c3cdd8eb0c0c0 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 13 Oct 2017 12:47:00 -0700 Subject: [PATCH 003/145] Fix invalid use of dictionary offcode table Fixes #888. --- lib/compress/zstd_compress.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index e072fe30e..3f1a7cdba 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1536,6 +1536,12 @@ MEM_STATIC size_t ZSTD_compressSequences(seqStore_t* seqStorePtr, int const uncompressibleError = (cSize == ERROR(dstSize_tooSmall)) && (srcSize <= dstCapacity); if (ZSTD_isError(cSize) && !uncompressibleError) return cSize; + /* We check that dictionaries have offset codes available for the first + * block. After the first block, the offcode table might not have large + * enough codes to represent the offsets in the data. + */ + if (entropy->offcode_repeatMode == FSE_repeat_valid) + entropy->offcode_repeatMode = FSE_repeat_check; /* Check compressibility */ { size_t const minGain = ZSTD_minGain(srcSize); /* note : fixed formula, maybe should depend on compression level, or strategy */ From ced6e6189cf02b2de9bfcd7191f77cf9ff07f345 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 13 Oct 2017 14:09:17 -0700 Subject: [PATCH 004/145] Add DEBUGLOG() that prints FSE encoding types --- lib/compress/zstd_compress.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 3f1a7cdba..188b8e717 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1282,6 +1282,7 @@ symbolEncodingType_e ZSTD_selectEncodingType( #define MAX_SEQ_FOR_STATIC_FSE 1000 ZSTD_STATIC_ASSERT(ZSTD_defaultDisallowed == 0 && ZSTD_defaultAllowed != 0); if ((mostFrequent == nbSeq) && (!isDefaultAllowed || nbSeq > 2)) { + DEBUGLOG(5, "Selected set_rle"); /* Prefer set_basic over set_rle when there are 2 or less symbols, * since RLE uses 1 byte, but set_basic uses 5-6 bits per symbol. * If basic encoding isn't possible, always choose RLE. @@ -1291,13 +1292,16 @@ symbolEncodingType_e ZSTD_selectEncodingType( } if ( isDefaultAllowed && (*repeatMode == FSE_repeat_valid) && (nbSeq < MAX_SEQ_FOR_STATIC_FSE)) { + DEBUGLOG(5, "Selected set_repeat"); return set_repeat; } if ( isDefaultAllowed && ((nbSeq < MIN_SEQ_FOR_DYNAMIC_FSE) || (mostFrequent < (nbSeq >> (defaultNormLog-1)))) ) { + DEBUGLOG(5, "Selected set_basic"); *repeatMode = FSE_repeat_valid; return set_basic; } + DEBUGLOG(5, "Selected set_compressed"); *repeatMode = FSE_repeat_check; return set_compressed; } @@ -1475,6 +1479,7 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, /* build CTable for Literal Lengths */ { U32 max = MaxLL; size_t const mostFrequent = FSE_countFast_wksp(count, &max, llCodeTable, nbSeq, entropy->workspace); + DEBUGLOG(5, "Building LL table"); LLtype = ZSTD_selectEncodingType(&entropy->litlength_repeatMode, mostFrequent, nbSeq, LL_defaultNormLog, ZSTD_defaultAllowed); { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_LitLength, LLFSELog, (symbolEncodingType_e)LLtype, count, max, llCodeTable, nbSeq, LL_defaultNorm, LL_defaultNormLog, MaxLL, @@ -1487,6 +1492,7 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, size_t const mostFrequent = FSE_countFast_wksp(count, &max, ofCodeTable, nbSeq, entropy->workspace); /* We can only use the basic table if max <= DefaultMaxOff, otherwise the offsets are too large */ ZSTD_defaultPolicy_e const defaultPolicy = (max <= DefaultMaxOff) ? ZSTD_defaultAllowed : ZSTD_defaultDisallowed; + DEBUGLOG(5, "Building OF table"); Offtype = ZSTD_selectEncodingType(&entropy->offcode_repeatMode, mostFrequent, nbSeq, OF_defaultNormLog, defaultPolicy); { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_OffsetBits, OffFSELog, (symbolEncodingType_e)Offtype, count, max, ofCodeTable, nbSeq, OF_defaultNorm, OF_defaultNormLog, DefaultMaxOff, @@ -1497,6 +1503,7 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, /* build CTable for MatchLengths */ { U32 max = MaxML; size_t const mostFrequent = FSE_countFast_wksp(count, &max, mlCodeTable, nbSeq, entropy->workspace); + DEBUGLOG(5, "Building ML table"); MLtype = ZSTD_selectEncodingType(&entropy->matchlength_repeatMode, mostFrequent, nbSeq, ML_defaultNormLog, ZSTD_defaultAllowed); { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_MatchLength, MLFSELog, (symbolEncodingType_e)MLtype, count, max, mlCodeTable, nbSeq, ML_defaultNorm, ML_defaultNormLog, MaxML, From dd18d73e7ef1b204bf514095fa8db7f9d6dfdc7b Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 13 Oct 2017 16:32:18 -0700 Subject: [PATCH 005/145] fileio: content size is enabled by default --- lib/compress/zstd_compress.c | 4 ++-- programs/fileio.c | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index e072fe30e..eb6f43d8c 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -391,7 +391,7 @@ size_t ZSTD_CCtxParam_setParameter( case ZSTD_p_contentSizeFlag : /* Content size written in frame header _when known_ (default:1) */ - DEBUGLOG(5, "set content size flag = %u", (value>0)); + DEBUGLOG(4, "set content size flag = %u", (value>0)); params->fParams.contentSizeFlag = value > 0; return 0; @@ -401,7 +401,7 @@ size_t ZSTD_CCtxParam_setParameter( return 0; case ZSTD_p_dictIDFlag : /* When applicable, dictionary's dictID is provided in frame header (default:1) */ - DEBUGLOG(5, "set dictIDFlag = %u", (value>0)); + DEBUGLOG(4, "set dictIDFlag = %u", (value>0)); params->fParams.noDictIDFlag = (value == 0); return 0; diff --git a/programs/fileio.c b/programs/fileio.c index 70158f930..427f251b9 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -439,6 +439,7 @@ static cRess_t FIO_createCResources(const char* dictFileName, int cLevel, #ifdef ZSTD_NEWAPI { /* frame parameters */ + CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_contentSizeFlag, 1) ); CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_dictIDFlag, g_dictIDFlag) ); CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_checksumFlag, g_checksumFlag) ); (void)srcSize; From fb4451664125919e0870b256797f826bad576efc Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 13 Oct 2017 17:39:13 -0700 Subject: [PATCH 006/145] ensure fParams.contentSizeFlag starts at 1 such default was failing for ZSTD_compressBegin/ZSTD_compressContinue fixed too --- lib/compress/zstd_compress.c | 19 +++++++++++-------- lib/zstd.h | 8 ++++---- tests/fullbench.c | 2 +- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index eb6f43d8c..b42e6996f 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -78,6 +78,7 @@ ZSTD_CCtx* ZSTD_createCCtx_advanced(ZSTD_customMem customMem) if (!cctx) return NULL; cctx->customMem = customMem; cctx->requestedParams.compressionLevel = ZSTD_CLEVEL_DEFAULT; + cctx->requestedParams.fParams.contentSizeFlag = 1; ZSTD_STATIC_ASSERT(zcss_init==0); ZSTD_STATIC_ASSERT(ZSTD_CONTENTSIZE_UNKNOWN==(0ULL - 1)); return cctx; @@ -1002,7 +1003,7 @@ void ZSTD_invalidateRepCodes(ZSTD_CCtx* cctx) { * The "context", in this case, refers to the hash and chain tables, entropy * tables, and dictionary offsets. * Only works during stage ZSTDcs_init (i.e. after creation, but before first call to ZSTD_compressContinue()). - * pledgedSrcSize=0 means "empty" if fParams.contentSizeFlag=1 + * pledgedSrcSize=0 means "empty". * @return : 0, or an error code */ static size_t ZSTD_copyCCtx_internal(ZSTD_CCtx* dstCCtx, const ZSTD_CCtx* srcCCtx, @@ -1059,7 +1060,8 @@ size_t ZSTD_copyCCtx(ZSTD_CCtx* dstCCtx, const ZSTD_CCtx* srcCCtx, unsigned long ZSTD_frameParameters fParams = { 1 /*content*/, 0 /*checksum*/, 0 /*noDictID*/ }; ZSTD_buffered_policy_e const zbuff = (ZSTD_buffered_policy_e)(srcCCtx->inBuffSize>0); ZSTD_STATIC_ASSERT((U32)ZSTDb_buffered==1); - fParams.contentSizeFlag = pledgedSrcSize>0; + if (pledgedSrcSize==0) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN; + fParams.contentSizeFlag = (pledgedSrcSize != ZSTD_CONTENTSIZE_UNKNOWN); return ZSTD_copyCCtx_internal(dstCCtx, srcCCtx, fParams, pledgedSrcSize, zbuff); } @@ -2077,7 +2079,7 @@ size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t di ZSTD_CCtx_params const cctxParams = ZSTD_assignParamsToCCtxParams(cctx->requestedParams, params); return ZSTD_compressBegin_internal(cctx, dict, dictSize, ZSTD_dm_auto, NULL, - cctxParams, 0, ZSTDb_not_buffered); + cctxParams, ZSTD_CONTENTSIZE_UNKNOWN, ZSTDb_not_buffered); } size_t ZSTD_compressBegin(ZSTD_CCtx* cctx, int compressionLevel) @@ -2797,18 +2799,18 @@ size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, } DEBUGLOG(4, "call ZSTDMT_initCStream_internal as nbThreads=%u", params.nbThreads); CHECK_F( ZSTDMT_initCStream_internal( - cctx->mtctx, - prefixDict.dict, prefixDict.dictSize, ZSTD_dm_rawContent, - cctx->cdict, params, cctx->pledgedSrcSizePlusOne-1) ); + cctx->mtctx, + prefixDict.dict, prefixDict.dictSize, ZSTD_dm_rawContent, + cctx->cdict, params, cctx->pledgedSrcSizePlusOne-1) ); cctx->streamStage = zcss_load; cctx->appliedParams.nbThreads = params.nbThreads; } else #endif - { - CHECK_F( ZSTD_resetCStream_internal( + { CHECK_F( ZSTD_resetCStream_internal( cctx, prefixDict.dict, prefixDict.dictSize, prefixDict.dictMode, cctx->cdict, params, cctx->pledgedSrcSizePlusOne-1) ); + assert(cctx->streamStage == zcss_load); } } /* compression stage */ @@ -3034,5 +3036,6 @@ ZSTD_parameters ZSTD_getParams(int compressionLevel, unsigned long long srcSizeH ZSTD_compressionParameters const cParams = ZSTD_getCParams(compressionLevel, srcSizeHint, dictSize); memset(¶ms, 0, sizeof(params)); params.cParams = cParams; + params.fParams.contentSizeFlag = 1; return params; } diff --git a/lib/zstd.h b/lib/zstd.h index 2194a3b23..7fafdbfc3 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -1000,11 +1000,11 @@ typedef enum { * Special: value 0 means "do not change strategy". */ /* frame parameters */ - ZSTD_p_contentSizeFlag=200, /* Content size is written into frame header _whenever known_ (default:1) - * note that content size must be known at the beginning, - * it is sent using ZSTD_CCtx_setPledgedSrcSize() */ + ZSTD_p_contentSizeFlag=200, /* Content size will be written into frame header _whenever known_ (default:1) + * Content size must be known at the beginning of compression, + * it is provided using ZSTD_CCtx_setPledgedSrcSize() */ ZSTD_p_checksumFlag, /* A 32-bits checksum of content is written at end of frame (default:0) */ - ZSTD_p_dictIDFlag, /* When applicable, dictID of dictionary is provided in frame header (default:1) */ + ZSTD_p_dictIDFlag, /* When applicable, dictionary's ID is written into frame header (default:1) */ /* multi-threading parameters */ ZSTD_p_nbThreads=400, /* Select how many threads a compression job can spawn (default:1) diff --git a/tests/fullbench.c b/tests/fullbench.c index db00ce217..3f497b034 100644 --- a/tests/fullbench.c +++ b/tests/fullbench.c @@ -233,7 +233,7 @@ static ZSTD_CCtx* g_zcc = NULL; size_t local_ZSTD_compressContinue(void* dst, size_t dstCapacity, void* buff2, const void* src, size_t srcSize) { (void)buff2; - ZSTD_compressBegin(g_zcc, 1); + ZSTD_compressBegin(g_zcc, 1 /* compressionLevel */); return ZSTD_compressEnd(g_zcc, dst, dstCapacity, src, srcSize); } From 3c1e3f8ec90a09558bda9b860fc1cbfdd322919c Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 13 Oct 2017 18:32:06 -0700 Subject: [PATCH 007/145] contentSizeFlag enabled by default would also fail for streaming and MT operations fixed --- lib/compress/zstd_compress.c | 14 ++++++++------ lib/compress/zstdmt_compress.c | 34 +++++++++++++++++----------------- tests/zstreamtest.c | 5 +++-- 3 files changed, 28 insertions(+), 25 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index b42e6996f..49b532b94 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2053,6 +2053,8 @@ size_t ZSTD_compressBegin_advanced_internal( ZSTD_CCtx_params params, unsigned long long pledgedSrcSize) { + DEBUGLOG(4, "ZSTD_compressBegin_advanced_internal"); + DEBUGLOG(4, "contentSizeFlag : %u", params.fParams.contentSizeFlag); /* compression parameters verification and optimization */ CHECK_F( ZSTD_checkCParams(params.cParams) ); return ZSTD_compressBegin_internal(cctx, dict, dictSize, dictMode, NULL, @@ -2281,7 +2283,7 @@ ZSTD_CDict* ZSTD_createCDict_advanced(const void* dictBuffer, size_t dictSize, ZSTD_dictMode_e dictMode, ZSTD_compressionParameters cParams, ZSTD_customMem customMem) { - DEBUGLOG(5, "ZSTD_createCDict_advanced, mode %u", (U32)dictMode); + DEBUGLOG(4, "ZSTD_createCDict_advanced, mode %u", (U32)dictMode); if (!customMem.customAlloc ^ !customMem.customFree) return NULL; { ZSTD_CDict* const cdict = (ZSTD_CDict*)ZSTD_malloc(sizeof(ZSTD_CDict), customMem); @@ -2356,9 +2358,9 @@ ZSTD_CDict* ZSTD_initStaticCDict(void* workspace, size_t workspaceSize, + cctxSize; ZSTD_CDict* const cdict = (ZSTD_CDict*) workspace; void* ptr; - DEBUGLOG(5, "(size_t)workspace & 7 : %u", (U32)(size_t)workspace & 7); + DEBUGLOG(4, "(size_t)workspace & 7 : %u", (U32)(size_t)workspace & 7); if ((size_t)workspace & 7) return NULL; /* 8-aligned */ - DEBUGLOG(5, "(workspaceSize < neededSize) : (%u < %u) => %u", + DEBUGLOG(4, "(workspaceSize < neededSize) : (%u < %u) => %u", (U32)workspaceSize, (U32)neededSize, (U32)(workspaceSize < neededSize)); if (workspaceSize < neededSize) return NULL; @@ -2394,7 +2396,7 @@ size_t ZSTD_compressBegin_usingCDict_advanced( { ZSTD_CCtx_params params = cctx->requestedParams; params.cParams = ZSTD_getCParamsFromCDict(cdict); params.fParams = fParams; - DEBUGLOG(5, "ZSTD_compressBegin_usingCDict_advanced"); + DEBUGLOG(4, "ZSTD_compressBegin_usingCDict_advanced"); return ZSTD_compressBegin_internal(cctx, NULL, 0, ZSTD_dm_auto, cdict, @@ -2409,7 +2411,7 @@ size_t ZSTD_compressBegin_usingCDict_advanced( size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict) { ZSTD_frameParameters const fParams = { 0 /*content*/, 0 /*checksum*/, 0 /*noDictID*/ }; - DEBUGLOG(5, "ZSTD_compressBegin_usingCDict : dictIDFlag == %u", !fParams.noDictIDFlag); + DEBUGLOG(4, "ZSTD_compressBegin_usingCDict : dictIDFlag == %u", !fParams.noDictIDFlag); return ZSTD_compressBegin_usingCDict_advanced(cctx, cdict, fParams, 0); } @@ -2585,7 +2587,7 @@ size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t di ZSTD_parameters const params = ZSTD_getParams(compressionLevel, 0, dictSize); ZSTD_CCtx_params const cctxParams = ZSTD_assignParamsToCCtxParams(zcs->requestedParams, params); - return ZSTD_initCStream_internal(zcs, dict, dictSize, NULL, cctxParams, 0); + return ZSTD_initCStream_internal(zcs, dict, dictSize, NULL, cctxParams, ZSTD_CONTENTSIZE_UNKNOWN); } size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 7831cd3bd..17c69c9df 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -327,6 +327,7 @@ void ZSTDMT_compressChunk(void* jobDescription) ZSTD_CCtx* cctx = ZSTDMT_getCCtx(job->cctxPool); const void* const src = (const char*)job->srcStart + job->dictSize; buffer_t dstBuff = job->dstBuff; + DEBUGLOG(5, "ZSTDMT_compressChunk") DEBUGLOG(5, "job (first:%u) (last:%u) : dictSize %u, srcSize %u", job->firstChunk, job->lastChunk, (U32)job->dictSize, (U32)job->srcSize); @@ -344,22 +345,22 @@ void ZSTDMT_compressChunk(void* jobDescription) job->dstBuff = dstBuff; } - if (job->cdict) { /* should only happen for first segment */ + if (job->cdict) { size_t const initError = ZSTD_compressBegin_usingCDict_advanced(cctx, job->cdict, job->params.fParams, job->fullFrameSize); - DEBUGLOG(5, "using CDict"); + DEBUGLOG(4, "ZSTDMT_compressChunk, using CDict"); + assert(job->firstChunk); /* should only happen for first segment */ if (ZSTD_isError(initError)) { job->cSize = initError; goto _endJob; } } else { /* srcStart points at reloaded section */ - if (!job->firstChunk) job->params.fParams.contentSizeFlag = 0; /* ensure no srcSize control */ - { ZSTD_CCtx_params jobParams = job->params; - size_t const forceWindowError = - ZSTD_CCtxParam_setParameter(&jobParams, ZSTD_p_forceMaxWindow, !job->firstChunk); - /* Force loading dictionary in "content-only" mode (no header analysis) */ - size_t const initError = ZSTD_compressBegin_advanced_internal(cctx, job->srcStart, job->dictSize, ZSTD_dm_rawContent, jobParams, job->fullFrameSize); - if (ZSTD_isError(initError) || ZSTD_isError(forceWindowError)) { - job->cSize = initError; - goto _endJob; - } - } } + ZSTD_CCtx_params jobParams = job->params; + size_t const forceWindowError = ZSTD_CCtxParam_setParameter(&jobParams, ZSTD_p_forceMaxWindow, !job->firstChunk); + U64 const pledgedSrcSize = job->firstChunk ? job->fullFrameSize : ZSTD_CONTENTSIZE_UNKNOWN; + /* load dictionary in "content-only" mode (no header analysis) */ + size_t const initError = ZSTD_compressBegin_advanced_internal(cctx, job->srcStart, job->dictSize, ZSTD_dm_rawContent, jobParams, pledgedSrcSize); + if (ZSTD_isError(initError) || ZSTD_isError(forceWindowError)) { + job->cSize = initError; + goto _endJob; + } + } if (!job->firstChunk) { /* flush and overwrite frame header when it's not first segment */ size_t const hSize = ZSTD_compressContinue(cctx, dstBuff.start, dstBuff.size, src, 0); if (ZSTD_isError(hSize)) { job->cSize = hSize; goto _endJob; } @@ -367,7 +368,7 @@ void ZSTDMT_compressChunk(void* jobDescription) } DEBUGLOG(5, "Compressing : "); - DEBUG_PRINTHEX(4, job->srcStart, 12); + DEBUG_PRINTHEX(6, job->srcStart, 12); job->cSize = (job->lastChunk) ? ZSTD_compressEnd (cctx, dstBuff.start, dstBuff.size, src, job->srcSize) : ZSTD_compressContinue(cctx, dstBuff.start, dstBuff.size, src, job->srcSize); @@ -610,6 +611,7 @@ static size_t ZSTDMT_compress_advanced_internal( assert(jobParams.nbThreads == 0); assert(mtctx->cctxPool->totalCCtx == params.nbThreads); + DEBUGLOG(4, "ZSTDMT_compress_advanced_internal"); DEBUGLOG(4, "nbChunks : %2u (chunkSize : %u bytes) ", nbChunks, (U32)avgChunkSize); if (nbChunks==1) { /* fallback to single-thread mode */ ZSTD_CCtx* const cctx = mtctx->cctxPool->cctx[0]; @@ -641,7 +643,7 @@ static size_t ZSTDMT_compress_advanced_internal( mtctx->jobs[u].srcStart = srcStart + frameStartPos - dictSize; mtctx->jobs[u].dictSize = dictSize; mtctx->jobs[u].srcSize = chunkSize; - mtctx->jobs[u].cdict = mtctx->nextJobID==0 ? cdict : NULL; + mtctx->jobs[u].cdict = (u==0) ? cdict : NULL; mtctx->jobs[u].fullFrameSize = srcSize; mtctx->jobs[u].params = jobParams; /* do not calculate checksum within sections, but write it in header for first section */ @@ -777,7 +779,6 @@ size_t ZSTDMT_initCStream_internal( zcs->params = params; zcs->frameContentSize = pledgedSrcSize; if (dict) { - DEBUGLOG(4,"cdictLocal: %08X", (U32)(size_t)zcs->cdictLocal); ZSTD_freeCDict(zcs->cdictLocal); zcs->cdictLocal = ZSTD_createCDict_advanced(dict, dictSize, ZSTD_dlm_byCopy, dictMode, /* note : a loadPrefix becomes an internal CDict */ @@ -785,7 +786,6 @@ size_t ZSTDMT_initCStream_internal( zcs->cdict = zcs->cdictLocal; if (zcs->cdictLocal == NULL) return ERROR(memory_allocation); } else { - DEBUGLOG(4,"cdictLocal: %08X", (U32)(size_t)zcs->cdictLocal); ZSTD_freeCDict(zcs->cdictLocal); zcs->cdictLocal = NULL; zcs->cdict = cdict; diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index 8b4c83694..36eede293 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -190,9 +190,10 @@ static int basicUnitTests(U32 seed, double compressibility, ZSTD_customMem custo /* Basic compression test */ DISPLAYLEVEL(3, "test%3i : compress %u bytes : ", testNb++, COMPRESSIBLE_NOISE_LENGTH); - CHECK_Z( ZSTD_initCStream_usingDict(zc, CNBuffer, dictSize, 1) ); + CHECK_Z( ZSTD_initCStream_usingDict(zc, CNBuffer, dictSize, 1 /* cLevel */) ); outBuff.dst = (char*)(compressedBuffer)+cSize; - outBuff.size = compressedBufferSize; + assert(compressedBufferSize > cSize); + outBuff.size = compressedBufferSize - cSize; outBuff.pos = 0; inBuff.src = CNBuffer; inBuff.size = CNBufferSize; From 213ef3b510b909b7dd95dc472e680ebea25bc450 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 13 Oct 2017 19:01:58 -0700 Subject: [PATCH 008/145] fixed ZSTD_initCStream_advanced() behavior, which depends on contentSizeFlag, and a stream fuzzer test, which was incorrect (relied on 0 being unconditionnally transformed into `ZSTD_CONTENTSIZE_UNKNOWN`) --- lib/compress/zstd_compress.c | 16 ++++++++++++---- lib/zstd.h | 2 +- tests/zstreamtest.c | 5 +++-- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 49b532b94..4ceba415c 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -822,9 +822,8 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, ZSTD_compResetPolicy_e const crp, ZSTD_buffered_policy_e const zbuff) { - DEBUGLOG(4, "ZSTD_resetCCtx_internal"); + DEBUGLOG(4, "ZSTD_resetCCtx_internal: pledgedSrcSize=%u", (U32)pledgedSrcSize); assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams))); - DEBUGLOG(4, "pledgedSrcSize: %u", (U32)pledgedSrcSize); if (crp == ZSTDcrp_continue) { if (ZSTD_equivalentParams(params, zc->appliedParams)) { @@ -2500,12 +2499,15 @@ static size_t ZSTD_resetCStream_internal(ZSTD_CStream* zcs, return 0; /* ready to go */ } +/* ZSTD_resetCStream(): + * pledgedSrcSize == 0 means "unknown" */ size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pledgedSrcSize) { ZSTD_CCtx_params params = zcs->requestedParams; - params.fParams.contentSizeFlag = (pledgedSrcSize > 0); + DEBUGLOG(4, "ZSTD_resetCStream: pledgedSrcSize = %u", (U32)pledgedSrcSize); + if (pledgedSrcSize==0) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN; + params.fParams.contentSizeFlag = 1; params.cParams = ZSTD_getCParamsFromCCtxParams(params, pledgedSrcSize, 0); - DEBUGLOG(4, "ZSTD_resetCStream"); return ZSTD_resetCStream_internal(zcs, NULL, 0, ZSTD_dm_auto, zcs->cdict, params, pledgedSrcSize); } @@ -2572,13 +2574,19 @@ size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict) return ZSTD_initCStream_usingCDict_advanced(zcs, cdict, fParams, 0); /* note : will check that cdict != NULL */ } +/* ZSTD_initCStream_advanced() : + * pledgedSrcSize is optional: it can be 0, which means "unknown" if contentSizeFlag is set, or "unknown" if contentSizeFlag is not set. */ size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, const void* dict, size_t dictSize, ZSTD_parameters params, unsigned long long pledgedSrcSize) { ZSTD_CCtx_params const cctxParams = ZSTD_assignParamsToCCtxParams(zcs->requestedParams, params); + DEBUGLOG(4, "ZSTD_initCStream_advanced: pledgedSrcSize=%u, flag=%u", + (U32)pledgedSrcSize, params.fParams.contentSizeFlag); CHECK_F( ZSTD_checkCParams(params.cParams) ); + if ((pledgedSrcSize==0) && (params.fParams.contentSizeFlag==0)) + pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN; return ZSTD_initCStream_internal(zcs, dict, dictSize, NULL, cctxParams, pledgedSrcSize); } diff --git a/lib/zstd.h b/lib/zstd.h index 7fafdbfc3..2866866be 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -734,7 +734,7 @@ ZSTDLIB_API ZSTD_CStream* ZSTD_initStaticCStream(void* workspace, size_t workspa ZSTDLIB_API size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize); /**< pledgedSrcSize must be correct, a size of 0 means unknown. for a frame size of 0 use initCStream_advanced */ ZSTDLIB_API size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel); /**< creates of an internal CDict (incompatible with static CCtx), except if dict == NULL or dictSize < 8, in which case no dict is used. Note: dict is loaded with ZSTD_dm_auto (treated as a full zstd dictionary if it begins with ZSTD_MAGIC_DICTIONARY, else as raw content) and ZSTD_dlm_byCopy.*/ ZSTDLIB_API size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, const void* dict, size_t dictSize, - ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize is optional and can be 0 (meaning unknown). note: if the contentSizeFlag is set, pledgedSrcSize == 0 means the source size is actually 0. dict is loaded with ZSTD_dm_auto and ZSTD_dlm_byCopy. */ + ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize is optional: it can be 0, which means "unknown" if contentSizeFlag is set, or "unknown" if contentSizeFlag is not set. dict is loaded with ZSTD_dm_auto and ZSTD_dlm_byCopy. */ ZSTDLIB_API size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict); /**< note : cdict will just be referenced, and must outlive compression session */ ZSTDLIB_API size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs, const ZSTD_CDict* cdict, ZSTD_frameParameters fParams, unsigned long long pledgedSrcSize); /**< same as ZSTD_initCStream_usingCDict(), with control over frame parameters */ diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index 36eede293..3aea22709 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -559,7 +559,7 @@ static int basicUnitTests(U32 seed, double compressibility, ZSTD_customMem custo DISPLAYLEVEL(3, "test%3i : ZSTD_initCStream_advanced with pledgedSrcSize=0 and dict : ", testNb++); { ZSTD_parameters params = ZSTD_getParams(5, 0, 0); params.fParams.contentSizeFlag = 1; - CHECK_Z( ZSTD_initCStream_advanced(zc, dictionary.start, dictionary.filled, params, 0) ); + CHECK_Z( ZSTD_initCStream_advanced(zc, dictionary.start, dictionary.filled, params, 0 /* pledgedSrcSize==0 means "empty" when params.fParams.contentSizeFlag is set */) ); } /* cstream advanced shall write content size = 0 */ inBuff.src = CNBuffer; inBuff.size = 0; @@ -833,10 +833,11 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, double compres { size_t const dictStart = FUZ_rand(&lseed) % (srcBufferSize - dictSize); dict = srcBuffer + dictStart; } - { U64 const pledgedSrcSize = (FUZ_rand(&lseed) & 3) ? 0 : maxTestSize; + { U64 const pledgedSrcSize = (FUZ_rand(&lseed) & 3) ? ZSTD_CONTENTSIZE_UNKNOWN : maxTestSize; ZSTD_parameters params = ZSTD_getParams(cLevel, pledgedSrcSize, dictSize); params.fParams.checksumFlag = FUZ_rand(&lseed) & 1; params.fParams.noDictIDFlag = FUZ_rand(&lseed) & 1; + params.fParams.contentSizeFlag = FUZ_rand(&lseed) & 1; CHECK_Z ( ZSTD_initCStream_advanced(zc, dict, dictSize, params, pledgedSrcSize) ); } } From beb9b4b398cb4a9b98f48a49a1c95cfbd030527c Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 13 Oct 2017 19:09:30 -0700 Subject: [PATCH 009/145] fixed ZSTDMT_initCStream() when contentSizeFlag==1 by default and a wrong test in zstreamtest --mt --- lib/compress/zstdmt_compress.c | 2 +- tests/zstreamtest.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 17c69c9df..197a2c49e 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -852,7 +852,7 @@ size_t ZSTDMT_initCStream(ZSTDMT_CCtx* zcs, int compressionLevel) { ZSTD_CCtx_params cctxParams = zcs->params; cctxParams.cParams = params.cParams; cctxParams.fParams = params.fParams; - return ZSTDMT_initCStream_internal(zcs, NULL, 0, ZSTD_dm_auto, NULL, cctxParams, 0); + return ZSTDMT_initCStream_internal(zcs, NULL, 0, ZSTD_dm_auto, NULL, cctxParams, ZSTD_CONTENTSIZE_UNKNOWN); } diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index 3aea22709..38320e8c4 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -1088,13 +1088,13 @@ static int fuzzerTests_MT(U32 seed, U32 nbTests, unsigned startTest, double comp { size_t const dictStart = FUZ_rand(&lseed) % (srcBufferSize - dictSize); dict = srcBuffer + dictStart; } - { U64 const pledgedSrcSize = (FUZ_rand(&lseed) & 3) ? 0 : maxTestSize; + { U64 const pledgedSrcSize = (FUZ_rand(&lseed) & 3) ? ZSTD_CONTENTSIZE_UNKNOWN : maxTestSize; ZSTD_parameters params = ZSTD_getParams(cLevel, pledgedSrcSize, dictSize); DISPLAYLEVEL(5, "Init with windowLog = %u and pledgedSrcSize = %u \n", params.cParams.windowLog, (U32)pledgedSrcSize); params.fParams.checksumFlag = FUZ_rand(&lseed) & 1; params.fParams.noDictIDFlag = FUZ_rand(&lseed) & 1; - params.fParams.contentSizeFlag = pledgedSrcSize>0; + params.fParams.contentSizeFlag = FUZ_rand(&lseed) & 1; DISPLAYLEVEL(5, "checksumFlag : %u \n", params.fParams.checksumFlag); CHECK_Z( ZSTDMT_initCStream_advanced(zc, dict, dictSize, params, pledgedSrcSize) ); CHECK_Z( ZSTDMT_setMTCtxParameter(zc, ZSTDMT_p_overlapSectionLog, FUZ_rand(&lseed) % 12) ); From 43914f6a206989432d68bb6abe336e1a662e913d Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 13 Oct 2017 23:47:01 -0700 Subject: [PATCH 010/145] add test for presence of decompressed size field in header previous test was "display" only, it wouldn't trigger an error in case of missing header. --- tests/playTests.sh | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/playTests.sh b/tests/playTests.sh index bc021648c..013182aeb 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -600,7 +600,7 @@ $ECHO "\n**** zstd --list/-l single frame tests ****" ./datagen > tmp3 $ZSTD tmp* $ZSTD -l *.zst -$ZSTD -lv *.zst +$ZSTD -lv *.zst | grep "Decompressed Size:" # check that decompressed size is present in header $ZSTD --list *.zst $ZSTD --list -v *.zst @@ -609,8 +609,6 @@ cat tmp1.zst tmp2.zst > tmp12.zst cat tmp12.zst tmp3.zst > tmp123.zst $ZSTD -l *.zst $ZSTD -lv *.zst -$ZSTD --list *.zst -$ZSTD --list -v *.zst $ECHO "\n**** zstd --list/-l error detection tests ****" ! $ZSTD -l tmp1 tmp1.zst @@ -627,9 +625,9 @@ $ZSTD -lv tmp5.zst ! $ZSTD -lv tmp5* $ECHO "\n**** zstd --list/-l test with no content size field ****" -./datagen -g1MB | $ZSTD > tmp6.zst +./datagen -g513K | $ZSTD > tmp6.zst $ZSTD -l tmp6.zst -$ZSTD -lv tmp6.zst +! $ZSTD -lv tmp6.zst | grep "Decompressed Size:" # must NOT be present in header $ECHO "\n**** zstd --list/-l test with no checksum ****" $ZSTD -f --no-check tmp1 From 9ef32b3cf1cbf5276f3e6dd0484de1ddd258b4a4 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 14 Oct 2017 00:02:32 -0700 Subject: [PATCH 011/145] minor : zstd -l -v display each file name --- programs/fileio.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/programs/fileio.c b/programs/fileio.c index 427f251b9..01e901ed8 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -1927,7 +1927,7 @@ static int getFileInfo(fileInfo_t* info, const char* inFileName){ return detectError; } -static void displayInfo(const char* inFileName, fileInfo_t* info, int displayLevel){ +static void displayInfo(const char* inFileName, const fileInfo_t* info, int displayLevel){ unsigned const unit = info->compressedSize < (1 MB) ? (1 KB) : (1 MB); const char* const unitStr = info->compressedSize < (1 MB) ? "KB" : "MB"; double const windowSizeUnit = (double)info->windowSize / unit; @@ -1950,8 +1950,10 @@ static void displayInfo(const char* inFileName, fileInfo_t* info, int displayLev checkString, inFileName); } } else { + DISPLAYOUT("%s \n", inFileName); DISPLAYOUT("# Zstandard Frames: %d\n", info->numActualFrames); - DISPLAYOUT("# Skippable Frames: %d\n", info->numSkippableFrames); + if (info->numSkippableFrames) + DISPLAYOUT("# Skippable Frames: %d\n", info->numSkippableFrames); DISPLAYOUT("Window Size: %.2f %2s (%llu B)\n", windowSizeUnit, unitStr, (unsigned long long)info->windowSize); @@ -1983,7 +1985,6 @@ static fileInfo_t FIO_addFInfo(fileInfo_t fi1, fileInfo_t fi2) } static int FIO_listFile(fileInfo_t* total, const char* inFileName, int displayLevel){ - /* initialize info to avoid warnings */ fileInfo_t info; memset(&info, 0, sizeof(info)); { int const error = getFileInfo(&info, inFileName); @@ -2023,7 +2024,7 @@ int FIO_listMultipleFiles(unsigned numFiles, const char** filenameTable, int dis for (u=0; u 1 && displayLevel <= 2) { + if (numFiles > 1 && displayLevel <= 2) { /* display total */ unsigned const unit = total.compressedSize < (1 MB) ? (1 KB) : (1 MB); const char* const unitStr = total.compressedSize < (1 MB) ? "KB" : "MB"; double const compressedSizeUnit = (double)total.compressedSize / unit; @@ -2043,8 +2044,7 @@ int FIO_listMultipleFiles(unsigned numFiles, const char** filenameTable, int dis total.numSkippableFrames, compressedSizeUnit, unitStr, decompressedSizeUnit, unitStr, ratio, checkString, total.nbFiles); - } - } + } } return error; } } From 5eed8e7a5538be8fc39ab94d1a61e161bbc348b9 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 14 Oct 2017 00:32:06 -0700 Subject: [PATCH 012/145] changed API comments to invite using macro ZSTD_CONTENTSIZE_UNKNOWN to mean "pledgedSrcSize is not known at init time" instead of `0`. Note that, a few prototypes created and documented with `0` to mean "unknown" still interpret "0" as unknown, to avoid breaking 3rd party applications which depend on this behavior. But this value is no longer recommended to mean "unknown". In some future version, it might be possible to switch "0" to mean "empty", as is already the case for several prototypes. The advantage is that pledgedSrcSize field would have same behavior accross entire API, making it easier to reason about. Note that all concerned prototypes belong to the "experimental" API section. srcSize is controlled at end of compression, so if someone uses "0" to mean "unknown" while it effectively means "empty", this is immediately caught by the compression function, which generates an error code : ZSTD_ERROR_srcSize_wrong --- doc/zstd_manual.html | 26 ++++++++++++++------------ lib/zstd.h | 18 ++++++++++-------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html index b4720adac..7ef86a23d 100644 --- a/doc/zstd_manual.html +++ b/doc/zstd_manual.html @@ -631,10 +631,10 @@ size_t ZSTD_estimateDDictSize(size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMet

Advanced Streaming compression functions

ZSTD_CStream* ZSTD_createCStream_advanced(ZSTD_customMem customMem);
 ZSTD_CStream* ZSTD_initStaticCStream(void* workspace, size_t workspaceSize);    /**< same as ZSTD_initStaticCCtx() */
-size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize);   /**< pledgedSrcSize must be correct, a size of 0 means unknown.  for a frame size of 0 use initCStream_advanced */
+size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize);   /**< pledgedSrcSize must be correct. If it is not known at init time, use ZSTD_CONTENTSIZE_UNKNOWN. Note that, for compatibility with older programs, a size of 0 is interepreted as "unknown". But it may change in some future version to mean "empty". */
 size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel); /**< creates of an internal CDict (incompatible with static CCtx), except if dict == NULL or dictSize < 8, in which case no dict is used. Note: dict is loaded with ZSTD_dm_auto (treated as a full zstd dictionary if it begins with ZSTD_MAGIC_DICTIONARY, else as raw content) and ZSTD_dlm_byCopy.*/
 size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, const void* dict, size_t dictSize,
-                                             ZSTD_parameters params, unsigned long long pledgedSrcSize);  /**< pledgedSrcSize is optional and can be 0 (meaning unknown). note: if the contentSizeFlag is set, pledgedSrcSize == 0 means the source size is actually 0. dict is loaded with ZSTD_dm_auto and ZSTD_dlm_byCopy. */
+                                             ZSTD_parameters params, unsigned long long pledgedSrcSize);  /**< pledgedSrcSize : If srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN. dict is loaded with ZSTD_dm_auto and ZSTD_dlm_byCopy. */
 size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict);  /**< note : cdict will just be referenced, and must outlive compression session */
 size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs, const ZSTD_CDict* cdict, ZSTD_frameParameters fParams, unsigned long long pledgedSrcSize);  /**< same as ZSTD_initCStream_usingCDict(), with control over frame parameters */
 

@@ -642,9 +642,11 @@ size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs, const ZSTD_CDict*

start a new compression job, using same parameters from previous job. This is typically useful to skip dictionary loading stage, since it will re-use it in-place.. Note that zcs must be init at least once before using ZSTD_resetCStream(). - pledgedSrcSize==0 means "srcSize unknown". + If pledgedSrcSize is not known at reset time, use macro ZSTD_CONTENTSIZE_UNKNOWN. If pledgedSrcSize > 0, its value must be correct, as it will be written in header, and controlled at the end. - @return : 0, or an error code (which can be tested using ZSTD_isError()) + For the time being, pledgedSrcSize==0 is interpreted as "srcSize unknown" for compatibility with older programs, + but it may change to mean "empty" in some future version, so prefer using macro ZSTD_CONTENTSIZE_UNKNOWN. + @return : 0, or an error code (which can be tested using ZSTD_isError())


Advanced Streaming decompression functions


-

Helper functions

#define ZSTD_COMPRESSBOUND(srcSize)   ((srcSize) + ((srcSize)>>8) + (((srcSize) < 128 KB) ? ((128 KB - (srcSize)) >> 11) /* margin, from 64 to 0 */ : 0))  /* this formula ensures that bound(A) + bound(B) <= bound(A+B) as long as A and B >= 128 KB */
+

Helper functions

#define ZSTD_COMPRESSBOUND(srcSize)   ((srcSize) + ((srcSize)>>8) + (((srcSize) < (128<<10)) ? (((128<<10) - (srcSize)) >> 11) /* margin, from 64 to 0 */ : 0))  /* this formula ensures that bound(A) + bound(B) <= bound(A+B) as long as A and B >= 128 KB */
 size_t      ZSTD_compressBound(size_t srcSize); /*!< maximum compressed size in worst case scenario */
 unsigned    ZSTD_isError(size_t code);          /*!< tells if a `size_t` function result is an error code */
 const char* ZSTD_getErrorName(size_t code);     /*!< provides readable string from an error code */
@@ -906,7 +906,8 @@ size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long
 
size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, unsigned value);
 

Set one compression parameter, selected by enum ZSTD_cParameter. Note : when `value` is an enum, cast it to unsigned for proper type checking. - @result : 0, or an error code (which can be tested with ZSTD_isError()). + @result : informational value (typically, the one being set, possibly corrected), + or an error code (which can be tested with ZSTD_isError()).


size_t ZSTD_CCtx_setPledgedSrcSize(ZSTD_CCtx* cctx, unsigned long long pledgedSrcSize);
diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c
index b9a2cf85f..1defcb8fe 100644
--- a/lib/compress/zstd_opt.c
+++ b/lib/compress/zstd_opt.c
@@ -606,7 +606,8 @@ size_t ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx,
                         } else {
                             if (optLevel==0) break;  /* gets ~+10% speed for about -0.01 ratio loss */
                         }
-        }   }   }   }
+            }   }   }
+        }  /* for (cur = 1; cur <= last_pos; cur++) */
 
         best_mlen = opt[last_pos].mlen;
         best_off = opt[last_pos].off;
@@ -661,7 +662,7 @@ _shortestPath:   /* cur, last_pos, best_mlen, best_off have to be set */
                 anchor = ip;
         }   }
         ZSTD_setLog2Prices(optStatePtr);
-    }   /* for (cur=0; cur < last_pos; ) */
+    }   while (ip < ilimit)
 
     /* Save reps for next block */
     { int i; for (i=0; irepToConfirm[i] = rep[i]; }

From f8d5c478afe143ffff63ac67c3ade593974efa27 Mon Sep 17 00:00:00 2001
From: Yann Collet 
Date: Tue, 21 Nov 2017 10:36:14 -0800
Subject: [PATCH 075/145] fixed comment, reported by @gyscos

---
 lib/compress/zstd_opt.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c
index 1defcb8fe..66a7da19c 100644
--- a/lib/compress/zstd_opt.c
+++ b/lib/compress/zstd_opt.c
@@ -662,7 +662,7 @@ _shortestPath:   /* cur, last_pos, best_mlen, best_off have to be set */
                 anchor = ip;
         }   }
         ZSTD_setLog2Prices(optStatePtr);
-    }   while (ip < ilimit)
+    }   /* while (ip < ilimit) */
 
     /* Save reps for next block */
     { int i; for (i=0; irepToConfirm[i] = rep[i]; }

From 31a191b17895d115b35fa4c254a9b99b67bbe58a Mon Sep 17 00:00:00 2001
From: Scott Baker 
Date: Wed, 22 Nov 2017 20:29:46 -0800
Subject: [PATCH 076/145] Include information about the benchmark
 output/methodology

Addresses #930
---
 programs/zstd.1.md | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/programs/zstd.1.md b/programs/zstd.1.md
index eea685489..9e4c5d305 100644
--- a/programs/zstd.1.md
+++ b/programs/zstd.1.md
@@ -258,6 +258,9 @@ BENCHMARK
 * `--priority=rt`:
     set process priority to real-time
 
+**Output Format:** CompressionLevel#Filename : IntputSize -> OutputSize (CompressionRatio), CompressionSpeed, DecompressionSpeed
+
+**Methodology:** For both compression and decompression speed, the entire input is compressed/decompressed in-memory to measure speed. A run lasts at least 1 sec, so when files are small, they are compressed/decompressed several times per run, in order to improve measurement accuracy.
 
 ADVANCED COMPRESSION OPTIONS
 ----------------------------

From c857ee850ad6bd830b44fcf54692478e1206da53 Mon Sep 17 00:00:00 2001
From: Yann Collet 
Date: Fri, 24 Nov 2017 16:44:28 -0800
Subject: [PATCH 077/145] minor update

---
 programs/platform.h | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/programs/platform.h b/programs/platform.h
index a4d7850fd..81dee23f9 100644
--- a/programs/platform.h
+++ b/programs/platform.h
@@ -21,10 +21,10 @@ extern "C" {
 *  Compiler Options
 ****************************************/
 #if defined(_MSC_VER)
-#  define _CRT_SECURE_NO_WARNINGS   /* Disable Visual Studio warning messages for fopen, strncpy, strerror */
-#  define _CRT_SECURE_NO_DEPRECATE  /* VS2005 - must be declared before  and  */
-#  if (_MSC_VER <= 1800)            /* (1800 = Visual Studio 2013) */
-#    define snprintf sprintf_s      /* snprintf unsupported by Visual <= 2013 */
+#  define _CRT_SECURE_NO_WARNINGS    /* Disable Visual Studio warning messages for fopen, strncpy, strerror */
+#  if (_MSC_VER <= 1800)             /* 1800 == Visual Studio 2013 */
+#    define _CRT_SECURE_NO_DEPRECATE /* VS2005 - must be declared before  and  */
+#    define snprintf sprintf_s       /* snprintf unsupported by Visual <= 2013 */
 #  endif
 #endif
 
@@ -117,7 +117,7 @@ static __inline int IS_CONSOLE(FILE* stdStream)
 
 
 /******************************
-*  OS-specific Includes
+*  OS-specific IO behaviors
 ******************************/
 #if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(_WIN32)
 #  include    /* _O_BINARY */
@@ -125,7 +125,7 @@ static __inline int IS_CONSOLE(FILE* stdStream)
 #  if !defined(__DJGPP__)
 #    include  /* DeviceIoControl, HANDLE, FSCTL_SET_SPARSE */
 #    include  /* FSCTL_SET_SPARSE */
-#    define SET_BINARY_MODE(file) { int unused=_setmode(_fileno(file), _O_BINARY); (void)unused; }
+#    define SET_BINARY_MODE(file) { int const unused=_setmode(_fileno(file), _O_BINARY); (void)unused; }
 #    define SET_SPARSE_FILE_MODE(file) { DWORD dw; DeviceIoControl((HANDLE) _get_osfhandle(_fileno(file)), FSCTL_SET_SPARSE, 0, 0, 0, 0, &dw, 0); }
 #  else
 #    define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)

From 2fd765498a750f009404feedd26f8e8f60f83a1e Mon Sep 17 00:00:00 2001
From: Yann Collet 
Date: Fri, 24 Nov 2017 17:20:54 -0800
Subject: [PATCH 078/145] updated man page

following patch #931 by @scottchiefbaker
---
 programs/zstd.1 | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/programs/zstd.1 b/programs/zstd.1
index 8187c7403..eb53e2c33 100644
--- a/programs/zstd.1
+++ b/programs/zstd.1
@@ -1,5 +1,5 @@
 .
-.TH "ZSTD" "1" "September 2017" "zstd 1.3.1" "User Commands"
+.TH "ZSTD" "1" "November 2017" "zstd 1.3.2" "User Commands"
 .
 .SH "NAME"
 \fBzstd\fR \- zstd, zstdmt, unzstd, zstdcat \- Compress or decompress \.zst files
@@ -60,11 +60,11 @@ In most places where an integer argument is expected, an optional suffix is supp
 .
 .TP
 \fBKiB\fR
-Multiply the integer by 1,024 (2\e \fBKi\fR, \fBK\fR, and \fBKB\fR are accepted as synonyms for \fBKiB\fR\.
+Multiply the integer by 1,024 (2^10)\. \fBKi\fR, \fBK\fR, and \fBKB\fR are accepted as synonyms for \fBKiB\fR\.
 .
 .TP
 \fBMiB\fR
-Multiply the integer by 1,048,576 (2\e \fBMi\fR, \fBM\fR, and \fBMB\fR are accepted as synonyms for \fBMiB\fR\.
+Multiply the integer by 1,048,576 (2^20)\. \fBMi\fR, \fBM\fR, and \fBMB\fR are accepted as synonyms for \fBMiB\fR\.
 .
 .SS "Operation mode"
 If multiple operation mode options are given, the last one takes effect\.
@@ -261,6 +261,12 @@ cut file(s) into independent blocks of size # (default: no block)
 \fB\-\-priority=rt\fR
 set process priority to real\-time
 .
+.P
+\fBOutput Format:\fR CompressionLevel#Filename : IntputSize \-> OutputSize (CompressionRatio), CompressionSpeed, DecompressionSpeed
+.
+.P
+\fBMethodology:\fR For both compression and decompression speed, the entire input is compressed/decompressed in\-memory to measure speed\. A run lasts at least 1 sec, so when files are small, they are compressed/decompressed several times per run, in order to improve measurement accuracy\.
+.
 .SH "ADVANCED COMPRESSION OPTIONS"
 .
 .SS "\-\-zstd[=options]:"

From bd88f633acda0de4f9eaef6b0914c3b569a34724 Mon Sep 17 00:00:00 2001
From: Yann Collet 
Date: Mon, 27 Nov 2017 12:15:23 -0800
Subject: [PATCH 079/145] zstreamtest : in `-T#s`, s considered a suffix
 meaning "seconds"

avoid unintentionnally triggering `seedset`,
so that seed gets automatically determined when not set.
---
 doc/zstd_manual.html | 62 +++++++++++++++++++++++---------------------
 lib/zstd.h           | 17 +++++++-----
 tests/zstreamtest.c  | 31 +++++++++++-----------
 3 files changed, 58 insertions(+), 52 deletions(-)

diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html
index e50f46661..872dbf637 100644
--- a/doc/zstd_manual.html
+++ b/doc/zstd_manual.html
@@ -19,16 +19,17 @@
 
  • Streaming decompression - HowTo
  • START OF ADVANCED AND EXPERIMENTAL FUNCTIONS
  • Advanced types
  • -
  • Frame size functions
  • -
  • Context memory usage
  • -
  • Advanced compression functions
  • -
  • Advanced decompression functions
  • -
  • Advanced streaming functions
  • -
  • Buffer-less and synchronous inner streaming functions
  • -
  • Buffer-less streaming compression (synchronous mode)
  • -
  • Buffer-less streaming decompression (synchronous mode)
  • -
  • New advanced API (experimental)
  • -
  • Block level API
  • +
  • Custom memory allocation functions
  • +
  • Frame size functions
  • +
  • Context memory usage
  • +
  • Advanced compression functions
  • +
  • Advanced decompression functions
  • +
  • Advanced streaming functions
  • +
  • Buffer-less and synchronous inner streaming functions
  • +
  • Buffer-less streaming compression (synchronous mode)
  • +
  • Buffer-less streaming decompression (synchronous mode)
  • +
  • New advanced API (experimental)
  • +
  • Block level API

  • Introduction

    @@ -346,17 +347,15 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB
         ZSTD_frameParameters fParams;
     } ZSTD_parameters;
     

    -

    Custom memory allocation functions

    typedef void* (*ZSTD_allocFunction) (void* opaque, size_t size);
    -typedef void  (*ZSTD_freeFunction) (void* opaque, void* address);
    -typedef struct { ZSTD_allocFunction customAlloc; ZSTD_freeFunction customFree; void* opaque; } ZSTD_customMem;
    -/* use this constant to defer to stdlib's functions */
    -static const ZSTD_customMem ZSTD_defaultCMem = { NULL, NULL, NULL };
    -

    -

    Frame size functions

    
    +

    Custom memory allocation functions

    
    +
    +
    typedef struct { ZSTD_allocFunction customAlloc; ZSTD_freeFunction customFree; void* opaque; } ZSTD_customMem;
    +

    +

    Frame size functions

    
     
     
    size_t ZSTD_findFrameCompressedSize(const void* src, size_t srcSize);
     

    `src` should point to the start of a ZSTD encoded frame or skippable frame - `srcSize` must be at least as large as the frame + `srcSize` must be >= first frame size @return : the compressed size of the first frame starting at `src`, suitable to pass to `ZSTD_decompress` or similar, or an error code if input is invalid @@ -391,7 +390,7 @@ static const ZSTD_customMem ZSTD_defaultCMem = { NULL, NULL, NULL }; @return : size of the Frame Header


    -

    Context memory usage

    
    +

    Context memory usage

    
     
     
    size_t ZSTD_sizeof_CCtx(const ZSTD_CCtx* cctx);
     size_t ZSTD_sizeof_DCtx(const ZSTD_DCtx* dctx);
    @@ -450,7 +449,7 @@ size_t ZSTD_estimateDDictSize(size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMet
      
     


    -

    Advanced compression functions

    
    +

    Advanced compression functions

    
     
     
    ZSTD_CCtx* ZSTD_createCCtx_advanced(ZSTD_customMem customMem);
     

    Create a ZSTD compression context using external alloc and free functions @@ -462,7 +461,8 @@ size_t ZSTD_estimateDDictSize(size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMet It must outlive context usage. workspaceSize: Use ZSTD_estimateCCtxSize() or ZSTD_estimateCStreamSize() to determine how large workspace must be to support scenario. - @return : pointer to ZSTD_CCtx*, or NULL if error (size too small) + @return : pointer to ZSTD_CCtx* (same address as workspace, but different type), + or NULL if error (typically size too small) Note : zstd will never resize nor malloc() when using a static cctx. If it needs more memory than available, it will simply error out. Note 2 : there is no corresponding "free" function. @@ -505,7 +505,8 @@ size_t ZSTD_estimateDDictSize(size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMet to determine how large workspace must be. cParams : use ZSTD_getCParams() to transform a compression level into its relevants cParams. - @return : pointer to ZSTD_CDict*, or NULL if error (size too small) + @return : pointer to ZSTD_CDict* (same address as workspace, but different type), + or NULL if error (typically, size too small). Note : there is no corresponding "free" function. Since workspace was allocated externally, it must be freed externally. @@ -545,7 +546,7 @@ size_t ZSTD_estimateDDictSize(size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMet

    Same as ZSTD_compress_usingCDict(), with fine-tune control over frame parameters


    -

    Advanced decompression functions

    
    +

    Advanced decompression functions

    
     
     
    unsigned ZSTD_isFrame(const void* buffer, size_t size);
     

    Tells if the content of `buffer` starts with a valid Frame Identifier. @@ -564,7 +565,8 @@ size_t ZSTD_estimateDDictSize(size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMet It must outlive context usage. workspaceSize: Use ZSTD_estimateDCtxSize() or ZSTD_estimateDStreamSize() to determine how large workspace must be to support scenario. - @return : pointer to ZSTD_DCtx*, or NULL if error (size too small) + @return : pointer to ZSTD_DCtx* (same address as workspace, but different type), + or NULL if error (typically size too small) Note : zstd will never resize nor malloc() when using a static dctx. If it needs more memory than available, it will simply error out. Note 2 : static dctx is incompatible with legacy support @@ -627,7 +629,7 @@ size_t ZSTD_estimateDDictSize(size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMet When identifying the exact failure cause, it's possible to use ZSTD_getFrameHeader(), which will provide a more precise error code.


    -

    Advanced streaming functions

    
    +

    Advanced streaming functions

    
     
     

    Advanced Streaming compression functions

    ZSTD_CStream* ZSTD_createCStream_advanced(ZSTD_customMem customMem);
     ZSTD_CStream* ZSTD_initStaticCStream(void* workspace, size_t workspaceSize);    /**< same as ZSTD_initStaticCCtx() */
    @@ -657,14 +659,14 @@ size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t di
     size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* zds, const ZSTD_DDict* ddict);  /**< note : ddict is referenced, it must outlive decompression session */
     size_t ZSTD_resetDStream(ZSTD_DStream* zds);  /**< re-use decompression parameters from previous init; saves dictionary loading */
     

    -

    Buffer-less and synchronous inner streaming functions

    +

    Buffer-less and synchronous inner streaming functions

       This is an advanced API, giving full control over buffer management, for users which need direct control over memory.
       But it's also a complex one, with several restrictions, documented below.
       Prefer normal streaming API for an easier experience.
      
     
    -

    Buffer-less streaming compression (synchronous mode)

    +

    Buffer-less streaming compression (synchronous mode)

       A ZSTD_CCtx object is required to track streaming operations.
       Use ZSTD_createCCtx() / ZSTD_freeCCtx() to manage resource.
       ZSTD_CCtx object can be re-used multiple times within successive compression operations.
    @@ -700,7 +702,7 @@ size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict);
     size_t ZSTD_compressBegin_usingCDict_advanced(ZSTD_CCtx* const cctx, const ZSTD_CDict* const cdict, ZSTD_frameParameters const fParams, unsigned long long const pledgedSrcSize);   /* compression parameters are already set within cdict. pledgedSrcSize must be correct. If srcSize is not known, use macro ZSTD_CONTENTSIZE_UNKNOWN */
     size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, unsigned long long pledgedSrcSize); /**<  note: if pledgedSrcSize is not known, use ZSTD_CONTENTSIZE_UNKNOWN */
     

    -

    Buffer-less streaming decompression (synchronous mode)

    +

    Buffer-less streaming decompression (synchronous mode)

       A ZSTD_DCtx object is required to track streaming operations.
       Use ZSTD_createDCtx() / ZSTD_freeDCtx() to manage it.
       A ZSTD_DCtx object can be re-used multiple times.
    @@ -786,7 +788,7 @@ size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long
     

    typedef enum { ZSTDnit_frameHeader, ZSTDnit_blockHeader, ZSTDnit_block, ZSTDnit_lastBlock, ZSTDnit_checksum, ZSTDnit_skippableFrame } ZSTD_nextInputType_e;
     

    -

    New advanced API (experimental)

    
    +

    New advanced API (experimental)

    
     
     
    typedef enum {
         /* Question : should we have a format ZSTD_f_auto ?
    @@ -1169,7 +1171,7 @@ size_t ZSTD_DCtx_refPrefix_advanced(ZSTD_DCtx* dctx, const void* prefix, size_t
      
     


    -

    Block level API

    
    +

    Block level API

    
     
     

    Frame metadata cost is typically ~18 bytes, which can be non-negligible for very small blocks (< 100 bytes). User will have to take in charge required information to regenerate data, such as compressed and content sizes. diff --git a/lib/zstd.h b/lib/zstd.h index 919d1e2e0..fe37cec8b 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -432,12 +432,12 @@ typedef struct { typedef struct ZSTD_CCtx_params_s ZSTD_CCtx_params; -/*= Custom memory allocation functions */ +/*--- Custom memory allocation functions ---*/ typedef void* (*ZSTD_allocFunction) (void* opaque, size_t size); typedef void (*ZSTD_freeFunction) (void* opaque, void* address); typedef struct { ZSTD_allocFunction customAlloc; ZSTD_freeFunction customFree; void* opaque; } ZSTD_customMem; /* use this constant to defer to stdlib's functions */ -static const ZSTD_customMem ZSTD_defaultCMem = { NULL, NULL, NULL }; +static ZSTD_customMem const ZSTD_defaultCMem = { NULL, NULL, NULL }; /*************************************** @@ -446,7 +446,7 @@ static const ZSTD_customMem ZSTD_defaultCMem = { NULL, NULL, NULL }; /*! ZSTD_findFrameCompressedSize() : * `src` should point to the start of a ZSTD encoded frame or skippable frame - * `srcSize` must be at least as large as the frame + * `srcSize` must be >= first frame size * @return : the compressed size of the first frame starting at `src`, * suitable to pass to `ZSTD_decompress` or similar, * or an error code if input is invalid */ @@ -557,7 +557,8 @@ ZSTDLIB_API ZSTD_CCtx* ZSTD_createCCtx_advanced(ZSTD_customMem customMem); * It must outlive context usage. * workspaceSize: Use ZSTD_estimateCCtxSize() or ZSTD_estimateCStreamSize() * to determine how large workspace must be to support scenario. - * @return : pointer to ZSTD_CCtx*, or NULL if error (size too small) + * @return : pointer to ZSTD_CCtx* (same address as workspace, but different type), + * or NULL if error (typically size too small) * Note : zstd will never resize nor malloc() when using a static cctx. * If it needs more memory than available, it will simply error out. * Note 2 : there is no corresponding "free" function. @@ -587,7 +588,7 @@ ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_advanced(const void* dict, size_t dictS ZSTD_compressionParameters cParams, ZSTD_customMem customMem); -/*! ZSTD_initStaticCDict_advanced() : +/*! ZSTD_initStaticCDict() : * Generate a digested dictionary in provided memory area. * workspace: The memory area to emplace the dictionary into. * Provided pointer must 8-bytes aligned. @@ -596,7 +597,8 @@ ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_advanced(const void* dict, size_t dictS * to determine how large workspace must be. * cParams : use ZSTD_getCParams() to transform a compression level * into its relevants cParams. - * @return : pointer to ZSTD_CDict*, or NULL if error (size too small) + * @return : pointer to ZSTD_CDict* (same address as workspace, but different type), + * or NULL if error (typically, size too small). * Note : there is no corresponding "free" function. * Since workspace was allocated externally, it must be freed externally. */ @@ -660,7 +662,8 @@ ZSTDLIB_API ZSTD_DCtx* ZSTD_createDCtx_advanced(ZSTD_customMem customMem); * It must outlive context usage. * workspaceSize: Use ZSTD_estimateDCtxSize() or ZSTD_estimateDStreamSize() * to determine how large workspace must be to support scenario. - * @return : pointer to ZSTD_DCtx*, or NULL if error (size too small) + * @return : pointer to ZSTD_DCtx* (same address as workspace, but different type), + * or NULL if error (typically size too small) * Note : zstd will never resize nor malloc() when using a static dctx. * If it needs more memory than available, it will simply error out. * Note 2 : static dctx is incompatible with legacy support diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index 4cdd6235b..b8c437a41 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -209,7 +209,7 @@ static size_t SEQ_generateRoundTrip(ZSTD_CCtx* cctx, ZSTD_DCtx* dctx, return 0; } -static int basicUnitTests(U32 seed, double compressibility, ZSTD_customMem customMem) +static int basicUnitTests(U32 seed, double compressibility) { size_t const CNBufferSize = COMPRESSIBLE_NOISE_LENGTH; void* CNBuffer = malloc(CNBufferSize); @@ -221,8 +221,8 @@ static int basicUnitTests(U32 seed, double compressibility, ZSTD_customMem custo size_t cSize; int testResult = 0; U32 testNb = 1; - ZSTD_CStream* zc = ZSTD_createCStream_advanced(customMem); - ZSTD_DStream* zd = ZSTD_createDStream_advanced(customMem); + ZSTD_CStream* zc = ZSTD_createCStream(); + ZSTD_DStream* zd = ZSTD_createDStream(); ZSTDMT_CCtx* mtctx = ZSTDMT_createCCtx(2); ZSTD_inBuffer inBuff, inBuff2; @@ -431,7 +431,7 @@ static int basicUnitTests(U32 seed, double compressibility, ZSTD_customMem custo /* Complex context re-use scenario */ DISPLAYLEVEL(3, "test%3i : context re-use : ", testNb++); ZSTD_freeCStream(zc); - zc = ZSTD_createCStream_advanced(customMem); + zc = ZSTD_createCStream(); if (zc==NULL) goto _output_error; /* memory allocation issue */ /* use 1 */ { size_t const inSize = 513; @@ -548,7 +548,7 @@ static int basicUnitTests(U32 seed, double compressibility, ZSTD_customMem custo DISPLAYLEVEL(3, "test%3i : ZSTD_initCStream_usingCDict_advanced with masked dictID : ", testNb++); { ZSTD_compressionParameters const cParams = ZSTD_getCParams(1, CNBufferSize, dictionary.filled); ZSTD_frameParameters const fParams = { 1 /* contentSize */, 1 /* checksum */, 1 /* noDictID */}; - ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(dictionary.start, dictionary.filled, ZSTD_dlm_byRef, ZSTD_dm_auto, cParams, customMem); + ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(dictionary.start, dictionary.filled, ZSTD_dlm_byRef, ZSTD_dm_auto, cParams, ZSTD_defaultCMem); size_t const initError = ZSTD_initCStream_usingCDict_advanced(zc, cdict, fParams, CNBufferSize); if (ZSTD_isError(initError)) goto _output_error; cSize = 0; @@ -1702,24 +1702,23 @@ typedef enum { simple_api, mt_api, advanced_api } e_api; int main(int argc, const char** argv) { - U32 seed=0; - int seedset=0; - int argNb; + U32 seed = 0; + int seedset = 0; int nbTests = nbTestsDefault; int testNb = 0; int proba = FUZ_COMPRESSIBILITY_DEFAULT; - int result=0; + int result = 0; int mainPause = 0; int bigTests = (sizeof(size_t) == 8); e_api selected_api = simple_api; const char* const programName = argv[0]; - ZSTD_customMem const customNULL = ZSTD_defaultCMem; U32 useOpaqueAPI = 0; + int argNb; /* Check command line */ for(argNb=1; argNb='0') && (*argument<='9')) { seed *= 10; seed += *argument - '0'; @@ -1827,7 +1828,7 @@ int main(int argc, const char** argv) if (nbTests<=0) nbTests=1; if (testNb==0) { - result = basicUnitTests(0, ((double)proba) / 100, customNULL); /* constant seed for predictability */ + result = basicUnitTests(0, ((double)proba) / 100); /* constant seed for predictability */ } if (!result) { From 743b23878eaa5c71c019a70327d52d95c6016b62 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 27 Nov 2017 13:47:35 -0800 Subject: [PATCH 080/145] install: changed variable MANDIR into MAN1DIR MANDIR still exists, and is now the parent of MAN1DIR --- programs/Makefile | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/programs/Makefile b/programs/Makefile index e1c5f50b6..37788db39 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -65,6 +65,7 @@ endif else endif +# Sort files in alphabetical order for reproducible builds ZSTDLIB_FILES := $(sort $(wildcard $(ZSTD_FILES)) $(wildcard $(ZSTDLEGACY_FILES)) $(wildcard $(ZDICT_FILES))) # Define *.exe as extension for Windows systems @@ -262,9 +263,10 @@ mandir ?= $(datarootdir)/man man1dir ?= $(mandir)/man1 ifneq (,$(filter $(shell uname),OpenBSD FreeBSD NetBSD DragonFly SunOS)) -MANDIR ?= $(PREFIX)/man/man1 +MANDIR ?= $(PREFIX)/man +MAN1DIR ?= $(MANDIR)/man1 else -MANDIR ?= $(man1dir) +MAN1DIR ?= $(man1dir) endif ifneq (,$(filter $(shell uname),SunOS)) @@ -281,7 +283,7 @@ INSTALL_MAN ?= $(INSTALL_DATA) .PHONY: install install: zstd @echo Installing binaries - @$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR)/ $(DESTDIR)$(MANDIR)/ + @$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR)/ $(DESTDIR)$(MAN1DIR)/ @$(INSTALL_PROGRAM) zstd $(DESTDIR)$(BINDIR)/zstd @ln -sf zstd $(DESTDIR)$(BINDIR)/zstdcat @ln -sf zstd $(DESTDIR)$(BINDIR)/unzstd @@ -289,9 +291,9 @@ install: zstd @$(INSTALL_SCRIPT) zstdless $(DESTDIR)$(BINDIR)/zstdless @$(INSTALL_SCRIPT) zstdgrep $(DESTDIR)$(BINDIR)/zstdgrep @echo Installing man pages - @$(INSTALL_MAN) zstd.1 $(DESTDIR)$(MANDIR)/zstd.1 - @ln -sf zstd.1 $(DESTDIR)$(MANDIR)/zstdcat.1 - @ln -sf zstd.1 $(DESTDIR)$(MANDIR)/unzstd.1 + @$(INSTALL_MAN) zstd.1 $(DESTDIR)$(MAN1DIR)/zstd.1 + @ln -sf zstd.1 $(DESTDIR)$(MAN1DIR)/zstdcat.1 + @ln -sf zstd.1 $(DESTDIR)$(MAN1DIR)/unzstd.1 @echo zstd installation completed .PHONY: uninstall @@ -301,9 +303,9 @@ uninstall: @$(RM) $(DESTDIR)$(BINDIR)/zstdcat @$(RM) $(DESTDIR)$(BINDIR)/unzstd @$(RM) $(DESTDIR)$(BINDIR)/zstd - @$(RM) $(DESTDIR)$(MANDIR)/zstdcat.1 - @$(RM) $(DESTDIR)$(MANDIR)/unzstd.1 - @$(RM) $(DESTDIR)$(MANDIR)/zstd.1 + @$(RM) $(DESTDIR)$(MAN1DIR)/zstdcat.1 + @$(RM) $(DESTDIR)$(MAN1DIR)/unzstd.1 + @$(RM) $(DESTDIR)$(MAN1DIR)/zstd.1 @echo zstd programs successfully uninstalled endif From e9d1987fd7bbeb669fe23a6f396159b4579c9d21 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 27 Nov 2017 17:01:51 -0800 Subject: [PATCH 081/145] btopt: minor speed optimization matchPrice is always right at beginning --- lib/compress/zstd_opt.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 66a7da19c..459fe6571 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -523,14 +523,11 @@ size_t ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx, U32 const offset = matches[matchNb].off; U32 const end = matches[matchNb].len; repcodes_t const repHistory = ZSTD_updateRep(rep, offset, ll0); - while (pos <= end) { + for ( ; pos <= end ; pos++) { U32 const matchPrice = ZSTD_getPrice(optStatePtr, litlen, anchor, offset, pos, optLevel); - if (pos > last_pos || matchPrice < opt[pos].price) { - DEBUGLOG(7, "rPos:%u => set initial price : %u", - pos, matchPrice); - SET_PRICE(pos, pos, offset, litlen, matchPrice, repHistory); /* note : macro modifies last_pos */ - } - pos++; + DEBUGLOG(7, "rPos:%u => set initial price : %u", + pos, matchPrice); + SET_PRICE(pos, pos, offset, litlen, matchPrice, repHistory); /* note : macro modifies last_pos */ } } } } From eee87cd6f20522d34db862ec9ef000572c26164a Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 27 Nov 2017 17:13:59 -0800 Subject: [PATCH 082/145] btopt: minor refactor : removed one SET_PRICE() macro invocation direct assignment makes operation cleaner. Also allows some (very minor) optimization (non-measurable) --- lib/compress/zstd_opt.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 459fe6571..c1ebea4d9 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -517,18 +517,28 @@ size_t ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx, } } /* set prices for first matches starting position == 0 */ - { U32 pos = minMatch; + { U32 pos; U32 matchNb; + for (pos = 0; pos < minMatch; pos++) { + opt[pos].mlen = 1; + opt[pos].price = ZSTD_MAX_PRICE; + } for (matchNb = 0; matchNb < nbMatches; matchNb++) { U32 const offset = matches[matchNb].off; U32 const end = matches[matchNb].len; repcodes_t const repHistory = ZSTD_updateRep(rep, offset, ll0); - for ( ; pos <= end ; pos++) { + for ( ; pos <= end ; pos++ ) { U32 const matchPrice = ZSTD_getPrice(optStatePtr, litlen, anchor, offset, pos, optLevel); DEBUGLOG(7, "rPos:%u => set initial price : %u", pos, matchPrice); - SET_PRICE(pos, pos, offset, litlen, matchPrice, repHistory); /* note : macro modifies last_pos */ - } } } + opt[pos].mlen = pos; + opt[pos].off = offset; + opt[pos].litlen = litlen; + opt[pos].price = matchPrice; + memcpy(opt[pos].rep, &repHistory, sizeof(repHistory)); + } } + last_pos = pos-1; + } } /* check further positions */ From 62c746dcf90c9f69f404df08533795d72083361d Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Tue, 28 Nov 2017 12:06:48 -0500 Subject: [PATCH 083/145] Add Test on LZ4 Format Input Buffer Overrun --- tests/Makefile | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/Makefile b/tests/Makefile index 11d55a75d..f944fd5a8 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -374,9 +374,9 @@ test-pool: poolTests test-lz4: ZSTD = LD_LIBRARY_PATH=/usr/local/lib $(PRGDIR)/zstd test-lz4: ZSTD_LZ4 = LD_LIBRARY_PATH=/usr/local/lib ./lz4 test-lz4: ZSTD_UNLZ4 = LD_LIBRARY_PATH=/usr/local/lib ./unlz4 -test-lz4: zstd decodecorpus - ln -s $(PRGDIR)/zstd lz4 - ln -s $(PRGDIR)/zstd unlz4 +test-lz4: zstd decodecorpus datagen + [ -f lz4 ] || ln -s $(PRGDIR)/zstd lz4 + [ -f unlz4 ] || ln -s $(PRGDIR)/zstd unlz4 ./decodecorpus -ptmp # lz4 -> zstd @@ -402,6 +402,8 @@ test-lz4: zstd decodecorpus $(ZSTD) -d | \ cmp - tmp + ./datagen -g384KB | $(ZSTD) --format=lz4 | $(ZSTD) -d > /dev/null + rm tmp lz4 unlz4 endif From baff9dd15eb65eaf54f0dee9ee5fadc9ce16d6ab Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Tue, 17 Oct 2017 01:19:29 -0400 Subject: [PATCH 084/145] Fix LZ4 Compression Buffer Overflow Fixes issue where, when `zstd --format=lz4` is fed an input larger than 128KB, the read overruns the input buffer. This changes Zstd to use LZ4 with chained 64KB blocks. This is technically a breaking change in that some third party LZ4 implementations may not support linked blocks. However, progress should not be allowed to be stopped by such petty concerns as backwards compatibility! --- programs/fileio.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/programs/fileio.c b/programs/fileio.c index 8bee6086c..eb004f916 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -616,11 +616,16 @@ static unsigned long long FIO_compressLzmaFrame(cRess_t* ress, #endif #ifdef ZSTD_LZ4COMPRESS +#if LZ4_VERSION_NUMBER <= 10600 +#define LZ4F_blockLinked blockLinked +#define LZ4F_max64KB max64KB +#endif static int FIO_LZ4_GetBlockSize_FromBlockId (int id) { return (1 << (8 + (2 * id))); } static unsigned long long FIO_compressLz4Frame(cRess_t* ress, const char* srcFileName, U64 const srcFileSize, int compressionLevel, U64* readsize) { + const size_t blockSize = FIO_LZ4_GetBlockSize_FromBlockId(LZ4F_max64KB); unsigned long long inFileSize = 0, outFileSize = 0; LZ4F_preferences_t prefs; @@ -632,21 +637,19 @@ static unsigned long long FIO_compressLz4Frame(cRess_t* ress, memset(&prefs, 0, sizeof(prefs)); -#if LZ4_VERSION_NUMBER <= 10600 -#define LZ4F_blockIndependent blockIndependent -#define LZ4F_max4MB max4MB -#endif + assert(blockSize <= ress->srcBufferSize); + assert(LZ4F_compressBound(blockSize) <= ress->dstBufferSize); prefs.autoFlush = 1; prefs.compressionLevel = compressionLevel; - prefs.frameInfo.blockMode = LZ4F_blockIndependent; /* stick to defaults for lz4 cli */ - prefs.frameInfo.blockSizeID = LZ4F_max4MB; + prefs.frameInfo.blockMode = LZ4F_blockLinked; + prefs.frameInfo.blockSizeID = LZ4F_max64KB; prefs.frameInfo.contentChecksumFlag = (contentChecksum_t)g_checksumFlag; #if LZ4_VERSION_NUMBER >= 10600 prefs.frameInfo.contentSize = (srcFileSize==UTIL_FILESIZE_UNKNOWN) ? 0 : srcFileSize; #endif - { size_t blockSize = FIO_LZ4_GetBlockSize_FromBlockId(LZ4F_max4MB); + { size_t readSize; size_t headerSize = LZ4F_compressBegin(ctx, ress->dstBuffer, ress->dstBufferSize, &prefs); if (LZ4F_isError(headerSize)) From 03f30d9dcbb8c20fc239d4a10b5df377ea8dc532 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 28 Nov 2017 12:14:46 -0800 Subject: [PATCH 085/145] separate rawLiterals, fullLiterals and match costs removed one SET_PRICE() macro invocation --- lib/compress/zstd_opt.c | 120 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 110 insertions(+), 10 deletions(-) diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index c1ebea4d9..83696f64d 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -96,6 +96,55 @@ static void ZSTD_rescaleFreqs(optState_t* optPtr, const BYTE* src, size_t srcSiz } +static U32 ZSTD_rawLiteralsCost(const optState_t* const optPtr, + const BYTE* const literals, U32 const litLength) +{ + if (optPtr->staticPrices) return (litLength*6); /* 6 bit per literal - no statistic used */ + if (litLength == 0) return 0; + + /* literals */ + { U32 u; + U32 cost = litLength * optPtr->log2litSum; + for (u=0; u < litLength; u++) + cost -= ZSTD_highbit32(optPtr->litFreq[literals[u]]+1); + return cost; + } +} + +static U32 ZSTD_litLengthPrice(const optState_t* const optPtr, U32 const litLength) +{ + if (optPtr->staticPrices) return ZSTD_highbit32((U32)litLength+1); + + /* literal Length */ + { U32 const llCode = ZSTD_LLcode(litLength); + U32 const price = LL_bits[llCode] + optPtr->log2litLengthSum - ZSTD_highbit32(optPtr->litLengthFreq[llCode]+1); + return price; + } +} + +static U32 ZSTD_fullLiteralsCost(const optState_t* const optPtr, + const BYTE* const literals, U32 const litLength) +{ + return ZSTD_rawLiteralsCost(optPtr, literals, litLength) + ZSTD_litLengthPrice(optPtr, litLength); +} + +static U32 ZSTD_updateRawLiteralsCost(const optState_t* const optPtr, + U32 const previousCost, + const BYTE* const literals, U32 const litLength) +{ + return previousCost + ZSTD_rawLiteralsCost(optPtr, literals, litLength); +} + +static U32 ZSTD_newLiteralsCost(const optState_t* const optPtr, + U32 const previousCost, BYTE const newLiteral) +{ + return previousCost + + optPtr->log2litSum - ZSTD_highbit32(optPtr->litFreq[newLiteral]+1); +} + + + + static U32 ZSTD_getLiteralPrice(optState_t* const optPtr, U32 const litLength, const BYTE* const literals) { @@ -139,10 +188,10 @@ static U32 ZSTD_getLiteralPrice(optState_t* const optPtr, } -/* ZSTD_getPrice() : +/* ZSTD_getMatchPrice() : * optLevel: when <2, favors small offset for decompression speed (improved cache efficiency) */ -FORCE_INLINE_TEMPLATE U32 ZSTD_getPrice(optState_t* optPtr, - U32 const litLength, const BYTE* const literals, U32 const offset, U32 const matchLength, +FORCE_INLINE_TEMPLATE U32 ZSTD_getMatchPrice(optState_t* optPtr, + U32 const offset, U32 const matchLength, int const optLevel) { U32 price; @@ -151,7 +200,7 @@ FORCE_INLINE_TEMPLATE U32 ZSTD_getPrice(optState_t* optPtr, assert(matchLength >= MINMATCH); if (optPtr->staticPrices) /* fixed scheme, do not use statistics */ - return ZSTD_getLiteralPrice(optPtr, litLength, literals) + ZSTD_highbit32((U32)mlBase+1) + 16 + offCode; + return ZSTD_highbit32((U32)mlBase+1) + 16 + offCode; price = offCode + optPtr->log2offCodeSum - ZSTD_highbit32(optPtr->offCodeFreq[offCode]+1); if ((optLevel<2) /*static*/ && offCode >= 20) price += (offCode-19)*2; /* handicap for long distance offsets, favor decompression speed */ @@ -161,7 +210,18 @@ FORCE_INLINE_TEMPLATE U32 ZSTD_getPrice(optState_t* optPtr, price += ML_bits[mlCode] + optPtr->log2matchLengthSum - ZSTD_highbit32(optPtr->matchLengthFreq[mlCode]+1); } - price += ZSTD_getLiteralPrice(optPtr, litLength, literals) + optPtr->factor; + price += + optPtr->factor; + DEBUGLOG(8, "ZSTD_getMatchPrice(ml:%u) = %u", matchLength, price); + return price; +} + +/* ZSTD_getPrice() : + * optLevel: when <2, favors small offset for decompression speed (improved cache efficiency) */ +FORCE_INLINE_TEMPLATE U32 ZSTD_getPrice(optState_t* optPtr, + U32 const litLength, const BYTE* const literals, U32 const offset, U32 const matchLength, + int const optLevel) +{ + U32 const price = ZSTD_getLiteralPrice(optPtr, litLength, literals) + ZSTD_getMatchPrice(optPtr, offset, matchLength, optLevel); DEBUGLOG(8, "ZSTD_getPrice(ll:%u, ml:%u) = %u", litLength, matchLength, price); return price; } @@ -454,6 +514,39 @@ repcodes_t ZSTD_updateRep(U32 const rep[3], U32 const offset, U32 const ll0) memcpy(opt[pos].rep, &rep_, sizeof(rep_)); \ } +typedef struct { + const BYTE* anchor; + U32 litlen; + U32 rawLitCost; +} cachedLiteralPrice_t; + +static U32 ZSTD_updateCachedLPrice(const optState_t* const optStatePtr, + cachedLiteralPrice_t* const cachedLitPrice, + const BYTE* const anchor, U32 const litlen) +{ + U32 startCost; + U32 remainingLength; + const BYTE* startPosition; + + if (anchor == cachedLitPrice->anchor) { + startCost = cachedLitPrice->rawLitCost; + startPosition = anchor + cachedLitPrice->litlen; + assert(litlen >= cachedLitPrice->litlen); + remainingLength = litlen - cachedLitPrice->litlen; + } else { + startCost = 0; + startPosition = anchor; + remainingLength = litlen; + } + + { U32 const rawLitCost = startCost + ZSTD_rawLiteralsCost(optStatePtr, startPosition, remainingLength); + cachedLitPrice->anchor = anchor; + cachedLitPrice->litlen = litlen; + cachedLitPrice->rawLitCost = rawLitCost; + return rawLitCost + ZSTD_litLengthPrice(optStatePtr, litlen); + } +} + FORCE_INLINE_TEMPLATE size_t ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx, const void* src, size_t srcSize, @@ -476,6 +569,7 @@ size_t ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx, ZSTD_optimal_t* const opt = optStatePtr->priceTable; ZSTD_match_t* const matches = optStatePtr->matchTable; + cachedLiteralPrice_t cachedLitPrice; U32 rep[ZSTD_REP_NUM]; /* init */ @@ -484,6 +578,7 @@ size_t ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx, ZSTD_rescaleFreqs(optStatePtr, (const BYTE*)src, srcSize); ip += (ip==prefixStart); { int i; for (i=0; irep[i]; } + memset(&cachedLitPrice, 0, sizeof(cachedLitPrice)); /* Match Loop */ while (ip < ilimit) { @@ -517,7 +612,8 @@ size_t ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx, } } /* set prices for first matches starting position == 0 */ - { U32 pos; + { U32 const literalsPrice = ZSTD_updateCachedLPrice(optStatePtr, &cachedLitPrice, anchor, litlen); + U32 pos; U32 matchNb; for (pos = 0; pos < minMatch; pos++) { opt[pos].mlen = 1; @@ -528,7 +624,7 @@ size_t ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx, U32 const end = matches[matchNb].len; repcodes_t const repHistory = ZSTD_updateRep(rep, offset, ll0); for ( ; pos <= end ; pos++ ) { - U32 const matchPrice = ZSTD_getPrice(optStatePtr, litlen, anchor, offset, pos, optLevel); + U32 const matchPrice = literalsPrice + ZSTD_getMatchPrice(optStatePtr, offset, pos, optLevel); DEBUGLOG(7, "rPos:%u => set initial price : %u", pos, matchPrice); opt[pos].mlen = pos; @@ -550,14 +646,18 @@ size_t ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx, { U32 const litlen = (opt[cur-1].mlen == 1) ? opt[cur-1].litlen + 1 : 1; U32 price; if (cur > litlen) { - price = opt[cur - litlen].price + ZSTD_getLiteralPrice(optStatePtr, litlen, inr-litlen); + price = opt[cur - litlen].price + ZSTD_fullLiteralsCost(optStatePtr, inr-litlen, litlen); } else { - price = ZSTD_getLiteralPrice(optStatePtr, litlen, anchor); + price = ZSTD_updateCachedLPrice(optStatePtr, &cachedLitPrice, anchor, litlen); } if (price <= opt[cur].price) { DEBUGLOG(7, "rPos:%u : better price (%u<%u) using literal", cur, price, opt[cur].price); - SET_PRICE(cur, 1/*mlen*/, 0/*offset*/, litlen, price, opt[cur-1].rep); + opt[cur].mlen = 1; + opt[cur].off = 0; + opt[cur].litlen = litlen; + opt[cur].price = price; + memcpy(opt[cur].rep, opt[cur-1].rep, sizeof(opt[cur].rep)); } } /* last match must start at a minimum distance of 8 from oend */ From b71405dc51cd6fb05ea2417ac6ed0516ef24d2f0 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 28 Nov 2017 12:32:24 -0800 Subject: [PATCH 086/145] removed a bunch of code related to cached literal price optState was used both to evaluate price and to cache cost of previously calculated literals. This created a strong dependency, forcing parser to request cost in a strict order. This limitation is forbids future parser with skipping capabilities. After this patch, caching literals price still exists, but is now explicit, in a stack structure. --- lib/compress/zstd_compress_internal.h | 3 - lib/compress/zstd_opt.c | 115 +++++--------------------- 2 files changed, 22 insertions(+), 96 deletions(-) diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 4fd55c438..e33ec4086 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -93,9 +93,6 @@ typedef struct { U32 factor; /* fixed cost added when calculating ZSTD_getPrice() (but why ? seems to favor less sequences) */ /* end : updated by ZSTD_setLog2Prices */ U32 staticPrices; /* prices follow a pre-defined cost structure, statistics are irrelevant */ - U32 cachedPrice; - U32 cachedLitLength; - const BYTE* cachedLiterals; } optState_t; typedef struct { diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 83696f64d..fe52fed58 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -32,10 +32,9 @@ static void ZSTD_setLog2Prices(optState_t* optPtr) } -static void ZSTD_rescaleFreqs(optState_t* optPtr, const BYTE* src, size_t srcSize) +static void ZSTD_rescaleFreqs(optState_t* const optPtr, + const BYTE* const src, size_t const srcSize) { - optPtr->cachedLiterals = NULL; - optPtr->cachedPrice = optPtr->cachedLitLength = 0; optPtr->staticPrices = 0; if (optPtr->litLengthSum == 0) { /* first init */ @@ -97,7 +96,7 @@ static void ZSTD_rescaleFreqs(optState_t* optPtr, const BYTE* src, size_t srcSiz static U32 ZSTD_rawLiteralsCost(const optState_t* const optPtr, - const BYTE* const literals, U32 const litLength) + const BYTE* const literals, U32 const litLength) { if (optPtr->staticPrices) return (litLength*6); /* 6 bit per literal - no statistic used */ if (litLength == 0) return 0; @@ -128,69 +127,10 @@ static U32 ZSTD_fullLiteralsCost(const optState_t* const optPtr, return ZSTD_rawLiteralsCost(optPtr, literals, litLength) + ZSTD_litLengthPrice(optPtr, litLength); } -static U32 ZSTD_updateRawLiteralsCost(const optState_t* const optPtr, - U32 const previousCost, - const BYTE* const literals, U32 const litLength) -{ - return previousCost + ZSTD_rawLiteralsCost(optPtr, literals, litLength); -} - -static U32 ZSTD_newLiteralsCost(const optState_t* const optPtr, - U32 const previousCost, BYTE const newLiteral) -{ - return previousCost - + optPtr->log2litSum - ZSTD_highbit32(optPtr->litFreq[newLiteral]+1); -} - - - - -static U32 ZSTD_getLiteralPrice(optState_t* const optPtr, - U32 const litLength, const BYTE* const literals) -{ - U32 price; - - if (optPtr->staticPrices) - return ZSTD_highbit32((U32)litLength+1) + (litLength*6); /* 6 bit per literal - no statistic used */ - - if (litLength == 0) - return optPtr->log2litLengthSum - ZSTD_highbit32(optPtr->litLengthFreq[0]+1); - - /* literals */ - if (optPtr->cachedLiterals == literals) { - U32 u; - U32 const additional = litLength - optPtr->cachedLitLength; - const BYTE* const literals2 = optPtr->cachedLiterals + optPtr->cachedLitLength; - price = optPtr->cachedPrice + additional * optPtr->log2litSum; - for (u=0; u < additional; u++) - price -= ZSTD_highbit32(optPtr->litFreq[literals2[u]]+1); - optPtr->cachedPrice = price; - optPtr->cachedLitLength = litLength; - } else { - U32 u; - price = litLength * optPtr->log2litSum; - for (u=0; u < litLength; u++) - price -= ZSTD_highbit32(optPtr->litFreq[literals[u]]+1); - - if (litLength >= 12) { - optPtr->cachedLiterals = literals; - optPtr->cachedPrice = price; - optPtr->cachedLitLength = litLength; - } - } - - /* literal Length */ - { U32 const llCode = ZSTD_LLcode(litLength); - price += LL_bits[llCode] + optPtr->log2litLengthSum - ZSTD_highbit32(optPtr->litLengthFreq[llCode]+1); - } - - return price; -} - /* ZSTD_getMatchPrice() : * optLevel: when <2, favors small offset for decompression speed (improved cache efficiency) */ -FORCE_INLINE_TEMPLATE U32 ZSTD_getMatchPrice(optState_t* optPtr, +FORCE_INLINE_TEMPLATE U32 ZSTD_getMatchPrice(const optState_t* const optPtr, U32 const offset, U32 const matchLength, int const optLevel) { @@ -210,24 +150,14 @@ FORCE_INLINE_TEMPLATE U32 ZSTD_getMatchPrice(optState_t* optPtr, price += ML_bits[mlCode] + optPtr->log2matchLengthSum - ZSTD_highbit32(optPtr->matchLengthFreq[mlCode]+1); } - price += + optPtr->factor; + price += optPtr->factor; DEBUGLOG(8, "ZSTD_getMatchPrice(ml:%u) = %u", matchLength, price); return price; } -/* ZSTD_getPrice() : - * optLevel: when <2, favors small offset for decompression speed (improved cache efficiency) */ -FORCE_INLINE_TEMPLATE U32 ZSTD_getPrice(optState_t* optPtr, - U32 const litLength, const BYTE* const literals, U32 const offset, U32 const matchLength, - int const optLevel) -{ - U32 const price = ZSTD_getLiteralPrice(optPtr, litLength, literals) + ZSTD_getMatchPrice(optPtr, offset, matchLength, optLevel); - DEBUGLOG(8, "ZSTD_getPrice(ll:%u, ml:%u) = %u", litLength, matchLength, price); - return price; -} - - -static void ZSTD_updateStats(optState_t* optPtr, U32 litLength, const BYTE* literals, U32 offsetCode, U32 matchLength) +static void ZSTD_updateStats(optState_t* const optPtr, + U32 litLength, const BYTE* literals, + U32 offsetCode, U32 matchLength) { /* literals */ { U32 u; @@ -275,13 +205,13 @@ MEM_STATIC U32 ZSTD_readMINMATCH(const void* memPtr, U32 length) /* Update hashTable3 up to ip (excluded) Assumption : always within prefix (i.e. not within extDict) */ -static U32 ZSTD_insertAndFindFirstIndexHash3 (ZSTD_CCtx* zc, const BYTE* ip) +static U32 ZSTD_insertAndFindFirstIndexHash3 (ZSTD_CCtx* const cctx, const BYTE* const ip) { - U32* const hashTable3 = zc->hashTable3; - U32 const hashLog3 = zc->hashLog3; - const BYTE* const base = zc->base; - U32 idx = zc->nextToUpdate3; - U32 const target = zc->nextToUpdate3 = (U32)(ip - base); + U32* const hashTable3 = cctx->hashTable3; + U32 const hashLog3 = cctx->hashLog3; + const BYTE* const base = cctx->base; + U32 idx = cctx->nextToUpdate3; + U32 const target = cctx->nextToUpdate3 = (U32)(ip - base); size_t const hash3 = ZSTD_hash3Ptr(ip, hashLog3); while(idx < target) { @@ -298,11 +228,11 @@ static U32 ZSTD_insertAndFindFirstIndexHash3 (ZSTD_CCtx* zc, const BYTE* ip) ***************************************/ FORCE_INLINE_TEMPLATE U32 ZSTD_insertBtAndGetAllMatches ( - ZSTD_CCtx* zc, - const BYTE* const ip, const BYTE* const iLimit, const int extDict, - U32 nbCompares, U32 const mls, U32 const sufficient_len, - U32 rep[ZSTD_REP_NUM], U32 const ll0, - ZSTD_match_t* matches, const U32 lengthToBeat) + ZSTD_CCtx* zc, + const BYTE* const ip, const BYTE* const iLimit, int const extDict, + U32 nbCompares, U32 const mls, U32 const sufficient_len, + U32 rep[ZSTD_REP_NUM], U32 const ll0, + ZSTD_match_t* matches, const U32 lengthToBeat) { const BYTE* const base = zc->base; U32 const current = (U32)(ip-base); @@ -671,12 +601,11 @@ size_t ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx, { U32 const ll0 = (opt[cur].mlen != 1); U32 const litlen = (opt[cur].mlen == 1) ? opt[cur].litlen : 0; - U32 const basePrice = (cur > litlen) ? opt[cur-litlen].price : 0; - const BYTE* const baseLiterals = ip + cur - litlen; + U32 const previousPrice = (cur > litlen) ? opt[cur-litlen].price : 0; + U32 const basePrice = previousPrice + ZSTD_fullLiteralsCost(optStatePtr, inr-litlen, litlen); U32 const nbMatches = ZSTD_BtGetAllMatches(ctx, inr, iend, extDict, maxSearches, mls, sufficient_len, opt[cur].rep, ll0, matches, minMatch); U32 matchNb; if (!nbMatches) continue; - assert(baseLiterals >= prefixStart); { U32 const maxML = matches[nbMatches-1].len; DEBUGLOG(7, "rPos:%u, found %u matches, of maxLength=%u", @@ -704,7 +633,7 @@ size_t ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx, for (mlen = lastML; mlen >= startML; mlen--) { U32 const pos = cur + mlen; - U32 const price = basePrice + ZSTD_getPrice(optStatePtr, litlen, baseLiterals, offset, mlen, optLevel); + U32 const price = basePrice + ZSTD_getMatchPrice(optStatePtr, offset, mlen, optLevel); if ((pos > last_pos) || (price < opt[pos].price)) { DEBUGLOG(7, "rPos:%u => new better price (%u<%u)", From 0a0a21293436a5f19e3fc79b1e4d69dc3acabc1b Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 28 Nov 2017 14:07:03 -0800 Subject: [PATCH 087/145] zstd_opt: changed cost formula There was a flaw in the formula which compared literal cost with match cost : at a given position, a non-null literal suite is going to be part of next sequence, while if position ends a previous match, to immediately start another match, next sequence will have a litlength of zero. A litlength of zero has a non-null cost. It follows that literals cost should be compared to match cost + litlength==0. Not doing so gave a structural advantage to matches, which would be selected more often. I believe that's what led to the creation of the strange heuristic which added a complex cost to matches. The heuristic was actually compensating. It was probably created through multiple trials, settling for best outcome on a given scenario (I suspect silesia.tar). The problem with this heuristic is that it's hard to understand, and unfortunately, any future change in the parser would impact the way it should be calculated and its effects. The "proper" formula makes it possible to remove this heuristic. Now, the problem is : in a head to head comparison, it's sometimes better, sometimes worse. Note that all differences are small (< 0.01 ratio). In general, the newer formula is better for smaller files (for example, calgary.tar and enwik7). I suspect that's because starting statistics are pretty poor (another area of improvement). However, for silesia.tar specifically, it's worse at level 22 (while being better at level 17, so even compression level has an impact ...). It's a pity that zstd -22 gets worse on silesia.tar. That being said, I like that the new code gets rid of strange variables, which were introducing complexity for any future evolution (faster variants being in mind). Therefore, in spite of this detrimental side effect, I tend to be in favor of it. --- lib/common/zstd_internal.h | 6 +- lib/compress/zstd_compress_internal.h | 4 +- lib/compress/zstd_opt.c | 92 +++++++++++++++++++-------- programs/bench.c | 2 +- 4 files changed, 71 insertions(+), 33 deletions(-) diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h index 01d343da6..2f8124825 100644 --- a/lib/common/zstd_internal.h +++ b/lib/common/zstd_internal.h @@ -253,16 +253,14 @@ MEM_STATIC U32 ZSTD_highbit32(U32 val) /* compress, dictBuilder, decodeCorpus # elif defined(__GNUC__) && (__GNUC__ >= 3) /* GCC Intrinsic */ return 31 - __builtin_clz(val); # else /* Software version */ - static const int DeBruijnClz[32] = { 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 }; + static const U32 DeBruijnClz[32] = { 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 }; U32 v = val; - int r; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; - r = DeBruijnClz[(U32)(v * 0x07C4ACDDU) >> 27]; - return r; + return DeBruijnClz[(v * 0x07C4ACDDU) >> 27]; # endif } } diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index e33ec4086..1308541f4 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -64,7 +64,7 @@ typedef struct { } ZSTD_match_t; typedef struct { - U32 price; + int price; U32 off; U32 mlen; U32 litlen; @@ -83,14 +83,12 @@ typedef struct { U32 litSum; /* nb of literals */ U32 litLengthSum; /* nb of litLength codes */ U32 matchLengthSum; /* nb of matchLength codes */ - U32 matchSum; /* a strange argument used in calculating `factor` */ U32 offCodeSum; /* nb of offset codes */ /* begin updated by ZSTD_setLog2Prices */ U32 log2litSum; /* pow2 to compare log2(litfreq) to */ U32 log2litLengthSum; /* pow2 to compare log2(llfreq) to */ U32 log2matchLengthSum; /* pow2 to compare log2(mlfreq) to */ U32 log2offCodeSum; /* pow2 to compare log2(offreq) to */ - U32 factor; /* fixed cost added when calculating ZSTD_getPrice() (but why ? seems to favor less sequences) */ /* end : updated by ZSTD_setLog2Prices */ U32 staticPrices; /* prices follow a pre-defined cost structure, statistics are irrelevant */ } optState_t; diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index fe52fed58..6218d3682 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -27,8 +27,6 @@ static void ZSTD_setLog2Prices(optState_t* optPtr) optPtr->log2litLengthSum = ZSTD_highbit32(optPtr->litLengthSum+1); optPtr->log2matchLengthSum = ZSTD_highbit32(optPtr->matchLengthSum+1); optPtr->log2offCodeSum = ZSTD_highbit32(optPtr->offCodeSum+1); - optPtr->factor = 1 + ((optPtr->litSum>>5) / optPtr->litLengthSum) - + ((optPtr->litSum<<1) / (optPtr->litSum + optPtr->matchSum)); /* => {0,1}, == (optPtr->matchSum <= optPtr->litSum) */ } @@ -58,7 +56,6 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, for (u=0; u<=MaxML; u++) optPtr->matchLengthFreq[u] = 1; optPtr->matchLengthSum = MaxML+1; - optPtr->matchSum = (ZSTD_LITFREQ_ADD << Litbits); for (u=0; u<=MaxOff; u++) optPtr->offCodeFreq[u] = 1; optPtr->offCodeSum = (MaxOff+1); @@ -77,13 +74,10 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, optPtr->litLengthSum += optPtr->litLengthFreq[u]; } optPtr->matchLengthSum = 0; - optPtr->matchSum = 0; for (u=0; u<=MaxML; u++) { optPtr->matchLengthFreq[u] = 1 + (optPtr->matchLengthFreq[u]>>ZSTD_FREQ_DIV); optPtr->matchLengthSum += optPtr->matchLengthFreq[u]; - optPtr->matchSum += optPtr->matchLengthFreq[u] * (u + 3); } - optPtr->matchSum *= ZSTD_LITFREQ_ADD; optPtr->offCodeSum = 0; for (u=0; u<=MaxOff; u++) { optPtr->offCodeFreq[u] = 1 + (optPtr->offCodeFreq[u]>>ZSTD_FREQ_DIV); @@ -95,8 +89,8 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, } -static U32 ZSTD_rawLiteralsCost(const optState_t* const optPtr, - const BYTE* const literals, U32 const litLength) +static U32 ZSTD_rawLiteralsCost(const BYTE* const literals, U32 const litLength, + const optState_t* const optPtr) { if (optPtr->staticPrices) return (litLength*6); /* 6 bit per literal - no statistic used */ if (litLength == 0) return 0; @@ -110,7 +104,7 @@ static U32 ZSTD_rawLiteralsCost(const optState_t* const optPtr, } } -static U32 ZSTD_litLengthPrice(const optState_t* const optPtr, U32 const litLength) +static U32 ZSTD_litLengthPrice(U32 const litLength, const optState_t* const optPtr) { if (optPtr->staticPrices) return ZSTD_highbit32((U32)litLength+1); @@ -121,17 +115,44 @@ static U32 ZSTD_litLengthPrice(const optState_t* const optPtr, U32 const litLeng } } -static U32 ZSTD_fullLiteralsCost(const optState_t* const optPtr, - const BYTE* const literals, U32 const litLength) +static U32 ZSTD_fullLiteralsCost(const BYTE* const literals, U32 const litLength, + const optState_t* const optPtr) { - return ZSTD_rawLiteralsCost(optPtr, literals, litLength) + ZSTD_litLengthPrice(optPtr, litLength); + return ZSTD_rawLiteralsCost(literals, litLength, optPtr) + + ZSTD_litLengthPrice(litLength, optPtr); +} + +static int ZSTD_litLengthContribution(U32 const litLength, const optState_t* const optPtr) +{ + if (optPtr->staticPrices) return ZSTD_highbit32(litLength+1); + + /* literal Length */ + { U32 const llCode = ZSTD_LLcode(litLength); + int const contribution = LL_bits[llCode] + + ZSTD_highbit32(optPtr->litLengthFreq[0]+1) + - ZSTD_highbit32(optPtr->litLengthFreq[llCode]+1); +#if 1 + return contribution; +#else + return MAX(0, contribution); /* sometimes better, sometimes not ... */ +#endif + } +} + +static int ZSTD_literalsContribution(const BYTE* const literals, U32 const litLength, + const optState_t* const optPtr) +{ + int const contribution = ZSTD_rawLiteralsCost(literals, litLength, optPtr) + + ZSTD_litLengthContribution(litLength, optPtr); + return contribution; } /* ZSTD_getMatchPrice() : * optLevel: when <2, favors small offset for decompression speed (improved cache efficiency) */ -FORCE_INLINE_TEMPLATE U32 ZSTD_getMatchPrice(const optState_t* const optPtr, +FORCE_INLINE_TEMPLATE U32 ZSTD_getMatchPrice( U32 const offset, U32 const matchLength, + const optState_t* const optPtr, int const optLevel) { U32 price; @@ -150,7 +171,7 @@ FORCE_INLINE_TEMPLATE U32 ZSTD_getMatchPrice(const optState_t* const optPtr, price += ML_bits[mlCode] + optPtr->log2matchLengthSum - ZSTD_highbit32(optPtr->matchLengthFreq[mlCode]+1); } - price += optPtr->factor; + //price += optPtr->factor; DEBUGLOG(8, "ZSTD_getMatchPrice(ml:%u) = %u", matchLength, price); return price; } @@ -450,9 +471,10 @@ typedef struct { U32 rawLitCost; } cachedLiteralPrice_t; -static U32 ZSTD_updateCachedLPrice(const optState_t* const optStatePtr, +static U32 ZSTD_rawLiteralsCost_cached( cachedLiteralPrice_t* const cachedLitPrice, - const BYTE* const anchor, U32 const litlen) + const BYTE* const anchor, U32 const litlen, + const optState_t* const optStatePtr) { U32 startCost; U32 remainingLength; @@ -469,14 +491,33 @@ static U32 ZSTD_updateCachedLPrice(const optState_t* const optStatePtr, remainingLength = litlen; } - { U32 const rawLitCost = startCost + ZSTD_rawLiteralsCost(optStatePtr, startPosition, remainingLength); + { U32 const rawLitCost = startCost + ZSTD_rawLiteralsCost(startPosition, remainingLength, optStatePtr); cachedLitPrice->anchor = anchor; cachedLitPrice->litlen = litlen; cachedLitPrice->rawLitCost = rawLitCost; - return rawLitCost + ZSTD_litLengthPrice(optStatePtr, litlen); + return rawLitCost; } } +static U32 ZSTD_fullLiteralsCost_cached( + cachedLiteralPrice_t* const cachedLitPrice, + const BYTE* const anchor, U32 const litlen, + const optState_t* const optStatePtr) +{ + return ZSTD_rawLiteralsCost_cached(cachedLitPrice, anchor, litlen, optStatePtr) + + ZSTD_litLengthPrice(litlen, optStatePtr); +} + +static int ZSTD_literalsContribution_cached( + cachedLiteralPrice_t* const cachedLitPrice, + const BYTE* const anchor, U32 const litlen, + const optState_t* const optStatePtr) +{ + int const contribution = ZSTD_rawLiteralsCost_cached(cachedLitPrice, anchor, litlen, optStatePtr) + + ZSTD_litLengthContribution(litlen, optStatePtr); + return contribution; +} + FORCE_INLINE_TEMPLATE size_t ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx, const void* src, size_t srcSize, @@ -542,7 +583,7 @@ size_t ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx, } } /* set prices for first matches starting position == 0 */ - { U32 const literalsPrice = ZSTD_updateCachedLPrice(optStatePtr, &cachedLitPrice, anchor, litlen); + { U32 const literalsPrice = ZSTD_fullLiteralsCost_cached(&cachedLitPrice, anchor, litlen, optStatePtr); U32 pos; U32 matchNb; for (pos = 0; pos < minMatch; pos++) { @@ -554,7 +595,7 @@ size_t ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx, U32 const end = matches[matchNb].len; repcodes_t const repHistory = ZSTD_updateRep(rep, offset, ll0); for ( ; pos <= end ; pos++ ) { - U32 const matchPrice = literalsPrice + ZSTD_getMatchPrice(optStatePtr, offset, pos, optLevel); + U32 const matchPrice = literalsPrice + ZSTD_getMatchPrice(offset, pos, optStatePtr, optLevel); DEBUGLOG(7, "rPos:%u => set initial price : %u", pos, matchPrice); opt[pos].mlen = pos; @@ -574,12 +615,13 @@ size_t ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx, /* Fix current position with one literal if cheaper */ { U32 const litlen = (opt[cur-1].mlen == 1) ? opt[cur-1].litlen + 1 : 1; - U32 price; + int price; /* note : contribution can be negative */ if (cur > litlen) { - price = opt[cur - litlen].price + ZSTD_fullLiteralsCost(optStatePtr, inr-litlen, litlen); + price = opt[cur - litlen].price + ZSTD_literalsContribution(inr-litlen, litlen, optStatePtr); } else { - price = ZSTD_updateCachedLPrice(optStatePtr, &cachedLitPrice, anchor, litlen); + price = ZSTD_literalsContribution_cached(&cachedLitPrice, anchor, litlen, optStatePtr); } + assert(price < 1000000000); /* overflow check */ if (price <= opt[cur].price) { DEBUGLOG(7, "rPos:%u : better price (%u<%u) using literal", cur, price, opt[cur].price); @@ -602,7 +644,7 @@ size_t ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx, { U32 const ll0 = (opt[cur].mlen != 1); U32 const litlen = (opt[cur].mlen == 1) ? opt[cur].litlen : 0; U32 const previousPrice = (cur > litlen) ? opt[cur-litlen].price : 0; - U32 const basePrice = previousPrice + ZSTD_fullLiteralsCost(optStatePtr, inr-litlen, litlen); + U32 const basePrice = previousPrice + ZSTD_fullLiteralsCost(inr-litlen, litlen, optStatePtr); U32 const nbMatches = ZSTD_BtGetAllMatches(ctx, inr, iend, extDict, maxSearches, mls, sufficient_len, opt[cur].rep, ll0, matches, minMatch); U32 matchNb; if (!nbMatches) continue; @@ -633,7 +675,7 @@ size_t ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx, for (mlen = lastML; mlen >= startML; mlen--) { U32 const pos = cur + mlen; - U32 const price = basePrice + ZSTD_getMatchPrice(optStatePtr, offset, mlen, optLevel); + int const price = basePrice + ZSTD_getMatchPrice(offset, mlen, optStatePtr, optLevel); if ((pos > last_pos) || (price < opt[pos].price)) { DEBUGLOG(7, "rPos:%u => new better price (%u<%u)", diff --git a/programs/bench.c b/programs/bench.c index 1df8aeb0d..2f30c65ac 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -191,7 +191,7 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, const char* displayName, int cLevel, const size_t* fileSizes, U32 nbFiles, const void* dictBuffer, size_t dictBufferSize, - const ZSTD_compressionParameters* comprParams) + const ZSTD_compressionParameters* const comprParams) { size_t const blockSize = ((g_blockSize>=32 && !g_decodeOnly) ? g_blockSize : srcSize) + (!srcSize) /* avoid div by 0 */ ; U32 const maxNbBlocks = (U32) ((srcSize + (blockSize-1)) / blockSize) + nbFiles; From f5747afe0fffa5fb1c993c5366d6a6c7f73783a0 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 28 Nov 2017 15:02:48 -0800 Subject: [PATCH 088/145] add integer overflow trapping to debug builds doc: https://gcc.gnu.org/onlinedocs/gcc-4.0.2/gcc/Code-Gen-Options.html --- tests/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Makefile b/tests/Makefile index 11d55a75d..89a109c78 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -32,7 +32,7 @@ CFLAGS += -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \ -Wstrict-aliasing=1 -Wswitch-enum -Wdeclaration-after-statement \ -Wstrict-prototypes -Wundef -Wformat-security \ -Wvla -Wformat=2 -Winit-self -Wfloat-equal -Wwrite-strings \ - -Wredundant-decls + -Wredundant-decls -ftrapv CFLAGS += $(DEBUGFLAGS) $(MOREFLAGS) FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) From bc42bc3b1dcfb0d9bcffa4e4710ac0dd42a3ec27 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 28 Nov 2017 16:08:43 -0800 Subject: [PATCH 089/145] removed one invocation of SET_PRICE() macro --- lib/compress/zstd_opt.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 6218d3682..d44b5be58 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -680,7 +680,12 @@ size_t ZSTD_compressBlock_opt_generic(ZSTD_CCtx* ctx, if ((pos > last_pos) || (price < opt[pos].price)) { DEBUGLOG(7, "rPos:%u => new better price (%u<%u)", pos, price, opt[pos].price); - SET_PRICE(pos, mlen, offset, litlen, price, repHistory); /* note : macro modifies last_pos */ + while (last_pos < pos) { opt[last_pos+1].price = ZSTD_MAX_PRICE; last_pos++; } + opt[pos].mlen = mlen; + opt[pos].off = offset; + opt[pos].litlen = litlen; + opt[pos].price = price; + memcpy(opt[pos].rep, &repHistory, sizeof(repHistory)); } else { if (optLevel==0) break; /* gets ~+10% speed for about -0.01 ratio loss */ } From f98ee994c4569de52d60b70bc1e2dae75e0c9b90 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 29 Nov 2017 15:19:00 -0800 Subject: [PATCH 090/145] zstd_opt: added comments, as requested by @terrelln --- lib/compress/zstd_opt.c | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index d44b5be58..7171ff537 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -89,6 +89,9 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, } +/* ZSTD_rawLiteralsCost() : + * cost of literals (only) in given segment (which length can be null) + * does not include cost of literalLength symbol */ static U32 ZSTD_rawLiteralsCost(const BYTE* const literals, U32 const litLength, const optState_t* const optPtr) { @@ -104,6 +107,8 @@ static U32 ZSTD_rawLiteralsCost(const BYTE* const literals, U32 const litLength, } } +/* ZSTD_litLengthPrice() : + * cost of literalLength symbol */ static U32 ZSTD_litLengthPrice(U32 const litLength, const optState_t* const optPtr) { if (optPtr->staticPrices) return ZSTD_highbit32((U32)litLength+1); @@ -115,6 +120,9 @@ static U32 ZSTD_litLengthPrice(U32 const litLength, const optState_t* const optP } } +/* ZSTD_litLengthPrice() : + * cost of the literal part of a sequence, + * including literals themselves, and literalLength symbol */ static U32 ZSTD_fullLiteralsCost(const BYTE* const literals, U32 const litLength, const optState_t* const optPtr) { @@ -122,6 +130,10 @@ static U32 ZSTD_fullLiteralsCost(const BYTE* const literals, U32 const litLength + ZSTD_litLengthPrice(litLength, optPtr); } +/* ZSTD_litLengthContribution() : + * @return ( cost(litlength) - cost(0) ) + * this value can then be added to rawLiteralsCost() + * to provide a cost which is directly comparable to a match ending at same position */ static int ZSTD_litLengthContribution(U32 const litLength, const optState_t* const optPtr) { if (optPtr->staticPrices) return ZSTD_highbit32(litLength+1); @@ -139,6 +151,10 @@ static int ZSTD_litLengthContribution(U32 const litLength, const optState_t* con } } +/* ZSTD_literalsContribution() : + * creates a fake cost for the literals part of a sequence + * which can be compared to the ending cost of a match + * should a new match start at this position */ static int ZSTD_literalsContribution(const BYTE* const literals, U32 const litLength, const optState_t* const optPtr) { @@ -147,8 +163,9 @@ static int ZSTD_literalsContribution(const BYTE* const literals, U32 const litLe return contribution; } - /* ZSTD_getMatchPrice() : + * Provides the cost of the match part (offset + matchLength) of a sequence + * Must be combined with ZSTD_fullLiteralsCost() to get the full cost of a sequence. * optLevel: when <2, favors small offset for decompression speed (improved cache efficiency) */ FORCE_INLINE_TEMPLATE U32 ZSTD_getMatchPrice( U32 const offset, U32 const matchLength, @@ -171,14 +188,13 @@ FORCE_INLINE_TEMPLATE U32 ZSTD_getMatchPrice( price += ML_bits[mlCode] + optPtr->log2matchLengthSum - ZSTD_highbit32(optPtr->matchLengthFreq[mlCode]+1); } - //price += optPtr->factor; DEBUGLOG(8, "ZSTD_getMatchPrice(ml:%u) = %u", matchLength, price); return price; } static void ZSTD_updateStats(optState_t* const optPtr, - U32 litLength, const BYTE* literals, - U32 offsetCode, U32 matchLength) + U32 litLength, const BYTE* literals, + U32 offsetCode, U32 matchLength) { /* literals */ { U32 u; @@ -209,7 +225,9 @@ static void ZSTD_updateStats(optState_t* const optPtr, } -/* function safe only for comparisons */ +/* ZSTD_readMINMATCH() : + * function safe only for comparisons + * assumption : memPtr must be at least 4 bytes before end of buffer */ MEM_STATIC U32 ZSTD_readMINMATCH(const void* memPtr, U32 length) { switch (length) @@ -454,16 +472,6 @@ repcodes_t ZSTD_updateRep(U32 const rep[3], U32 const offset, U32 const ll0) return newReps; } -/* update opt[pos] and last_pos */ -#define SET_PRICE(pos, mlen_, offset_, litlen_, price_, rep_) \ - { \ - while (last_pos < pos) { opt[last_pos+1].price = ZSTD_MAX_PRICE; last_pos++; } \ - opt[pos].mlen = mlen_; \ - opt[pos].off = offset_; \ - opt[pos].litlen = litlen_; \ - opt[pos].price = price_; \ - memcpy(opt[pos].rep, &rep_, sizeof(rep_)); \ - } typedef struct { const BYTE* anchor; From 998a93b784798a6cab9d4a536a207f863ea3bcf3 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 29 Nov 2017 16:13:05 -0800 Subject: [PATCH 091/145] simplified ZSTD_CCtx_setParametersUsingCCtxParams() Any ZSTD_CCtx_setParameter() shall just write the requested parameter, without further action. Any action shall be taken at parameter application only (during init). It makes it possible to just copy CCtxParams from external container to internal state, and get rid of the more complex code which was trying to compensate for missing actions. --- lib/compress/zstd_compress.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 858eaca30..e6e0c0210 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -271,11 +271,11 @@ size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, unsigned v case ZSTD_p_forceMaxWindow : /* Force back-references to remain < windowSize, * even when referencing into Dictionary content * default : 0 when using a CDict, 1 when using a Prefix */ - cctx->loadedDictEnd = 0; + cctx->loadedDictEnd = 0; /* ? */ return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value); case ZSTD_p_nbThreads: - if (value > 1 && cctx->staticSize) { + if ((value > 1) && cctx->staticSize) { return ERROR(parameter_unsupported); /* MT not compatible with static alloc */ } return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value); @@ -465,14 +465,17 @@ size_t ZSTD_CCtxParam_setParameter( * The multithreading parameters jobSize and overlapSizeLog are set only if * nbThreads > 1. * - * Pledged srcSize is treated as unknown. + * pledgedSrcSize is considered unknown */ size_t ZSTD_CCtx_setParametersUsingCCtxParams( - ZSTD_CCtx* cctx, const ZSTD_CCtx_params* params) + ZSTD_CCtx* const cctx, const ZSTD_CCtx_params* const params) { if (cctx->streamStage != zcss_init) return ERROR(stage_wrong); if (cctx->cdict) return ERROR(stage_wrong); +#if 1 + cctx->requestedParams = *params; +#else /* Assume the compression and frame parameters are validated */ cctx->requestedParams.cParams = params->cParams; cctx->requestedParams.fParams = params->fParams; @@ -492,6 +495,7 @@ size_t ZSTD_CCtx_setParametersUsingCCtxParams( /* Copy long distance matching parameters */ cctx->requestedParams.ldmParams = params->ldmParams; +#endif /* customMem is used only for create/free params and can be ignored */ return 0; @@ -2858,6 +2862,7 @@ size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, if (cctx->mtctx == NULL) return ERROR(memory_allocation); } DEBUGLOG(4, "call ZSTDMT_initCStream_internal as nbThreads=%u", params.nbThreads); + DEBUGLOG(2, "params.windowLog = %u", params.cParams.windowLog); CHECK_F( ZSTDMT_initCStream_internal( cctx->mtctx, prefixDict.dict, prefixDict.dictSize, ZSTD_dm_rawContent, From d3c59edac9e0bf544b7414e5442448f7a67b55ff Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 29 Nov 2017 16:42:20 -0800 Subject: [PATCH 092/145] removed long-range-mode tests from `zstreamtest --no-big-tests` --- lib/compress/zstd_compress.c | 3 ++- lib/compress/zstdmt_compress.c | 2 +- tests/zstreamtest.c | 33 +++++++++++++++++++-------------- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index e6e0c0210..dd12b171b 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -241,6 +241,7 @@ static ZSTD_CCtx_params ZSTD_assignParamsToCCtxParams( size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, unsigned value) { + DEBUGLOG(4, "ZSTD_CCtx_setParameter"); if (cctx->streamStage != zcss_init) return ERROR(stage_wrong); switch(param) @@ -320,6 +321,7 @@ size_t ZSTD_CCtxParam_setParameter( return CCtxParams->compressionLevel; case ZSTD_p_windowLog : + DEBUGLOG(4, "setting windowLog=%u", value); if (value) { /* 0 : does not change current windowLog */ CLAMPCHECK(value, ZSTD_WINDOWLOG_MIN, ZSTD_WINDOWLOG_MAX); ZSTD_cLevelToCCtxParams(CCtxParams); @@ -2862,7 +2864,6 @@ size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, if (cctx->mtctx == NULL) return ERROR(memory_allocation); } DEBUGLOG(4, "call ZSTDMT_initCStream_internal as nbThreads=%u", params.nbThreads); - DEBUGLOG(2, "params.windowLog = %u", params.cParams.windowLog); CHECK_F( ZSTDMT_initCStream_internal( cctx->mtctx, prefixDict.dict, prefixDict.dictSize, ZSTD_dm_rawContent, diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 2926d3b48..ce813dccc 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -140,7 +140,7 @@ static size_t ZSTDMT_sizeof_bufferPool(ZSTDMT_bufferPool* bufPool) return poolSize + totalBufferSize; } -static void ZSTDMT_setBufferSize(ZSTDMT_bufferPool* bufPool, size_t bSize) +static void ZSTDMT_setBufferSize(ZSTDMT_bufferPool* const bufPool, size_t const bSize) { ZSTD_pthread_mutex_lock(&bufPool->poolMutex); DEBUGLOG(4, "ZSTDMT_setBufferSize: bSize = %u", (U32)bSize); diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index b8c437a41..0a09cfa15 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -1486,43 +1486,47 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double cParams = ZSTD_adjustCParams(cParams, 0, 0); if (FUZ_rand(&lseed) & 1) { + DISPLAYLEVEL(5, "t%u: windowLog : %u \n", testNb, cParams.windowLog); CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_windowLog, cParams.windowLog, useOpaqueAPI) ); assert(cParams.windowLog >= ZSTD_WINDOWLOG_MIN); /* guaranteed by ZSTD_adjustCParams() */ windowLogMalus = (cParams.windowLog - ZSTD_WINDOWLOG_MIN) / 5; - DISPLAYLEVEL(5, "t%u: windowLog : %u \n", testNb, cParams.windowLog); } if (FUZ_rand(&lseed) & 1) { - CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_hashLog, cParams.hashLog, useOpaqueAPI) ); DISPLAYLEVEL(5, "t%u: hashLog : %u \n", testNb, cParams.hashLog); + CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_hashLog, cParams.hashLog, useOpaqueAPI) ); } if (FUZ_rand(&lseed) & 1) { - CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_chainLog, cParams.chainLog, useOpaqueAPI) ); DISPLAYLEVEL(5, "t%u: chainLog : %u \n", testNb, cParams.chainLog); + CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_chainLog, cParams.chainLog, useOpaqueAPI) ); } if (FUZ_rand(&lseed) & 1) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_searchLog, cParams.searchLog, useOpaqueAPI) ); if (FUZ_rand(&lseed) & 1) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_minMatch, cParams.searchLength, useOpaqueAPI) ); if (FUZ_rand(&lseed) & 1) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_targetLength, cParams.targetLength, useOpaqueAPI) ); /* mess with long distance matching parameters */ - if (FUZ_rand(&lseed) & 1) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_enableLongDistanceMatching, FUZ_rand(&lseed) & 63, useOpaqueAPI) ); - if (FUZ_rand(&lseed) & 3) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_ldmHashLog, FUZ_randomClampedLength(&lseed, ZSTD_HASHLOG_MIN, 23), useOpaqueAPI) ); - if (FUZ_rand(&lseed) & 3) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_ldmMinMatch, FUZ_randomClampedLength(&lseed, ZSTD_LDM_MINMATCH_MIN, ZSTD_LDM_MINMATCH_MAX), useOpaqueAPI) ); - if (FUZ_rand(&lseed) & 3) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_ldmBucketSizeLog, FUZ_randomClampedLength(&lseed, 0, ZSTD_LDM_BUCKETSIZELOG_MAX), useOpaqueAPI) ); - if (FUZ_rand(&lseed) & 3) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_ldmHashEveryLog, FUZ_randomClampedLength(&lseed, 0, ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN), useOpaqueAPI) ); + if (bigTests) { + if (FUZ_rand(&lseed) & 1) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_enableLongDistanceMatching, FUZ_rand(&lseed) & 63, useOpaqueAPI) ); + if (FUZ_rand(&lseed) & 3) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_ldmHashLog, FUZ_randomClampedLength(&lseed, ZSTD_HASHLOG_MIN, 23), useOpaqueAPI) ); + if (FUZ_rand(&lseed) & 3) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_ldmMinMatch, FUZ_randomClampedLength(&lseed, ZSTD_LDM_MINMATCH_MIN, ZSTD_LDM_MINMATCH_MAX), useOpaqueAPI) ); + if (FUZ_rand(&lseed) & 3) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_ldmBucketSizeLog, FUZ_randomClampedLength(&lseed, 0, ZSTD_LDM_BUCKETSIZELOG_MAX), useOpaqueAPI) ); + if (FUZ_rand(&lseed) & 3) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_ldmHashEveryLog, FUZ_randomClampedLength(&lseed, 0, ZSTD_WINDOWLOG_MAX - ZSTD_HASHLOG_MIN), useOpaqueAPI) ); + } /* mess with frame parameters */ if (FUZ_rand(&lseed) & 1) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_checksumFlag, FUZ_rand(&lseed) & 1, useOpaqueAPI) ); if (FUZ_rand(&lseed) & 1) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_dictIDFlag, FUZ_rand(&lseed) & 1, useOpaqueAPI) ); if (FUZ_rand(&lseed) & 1) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_contentSizeFlag, FUZ_rand(&lseed) & 1, useOpaqueAPI) ); - if (FUZ_rand(&lseed) & 1) CHECK_Z( ZSTD_CCtx_setPledgedSrcSize(zc, pledgedSrcSize) ); - DISPLAYLEVEL(5, "t%u: pledgedSrcSize : %u \n", testNb, (U32)pledgedSrcSize); + if (FUZ_rand(&lseed) & 1) { + DISPLAYLEVEL(5, "t%u: pledgedSrcSize : %u \n", testNb, (U32)pledgedSrcSize); + CHECK_Z( ZSTD_CCtx_setPledgedSrcSize(zc, pledgedSrcSize) ); + } /* multi-threading parameters */ { U32 const nbThreadsCandidate = (FUZ_rand(&lseed) & 4) + 1; U32 const nbThreadsAdjusted = (windowLogMalus < nbThreadsCandidate) ? nbThreadsCandidate - windowLogMalus : 1; U32 const nbThreads = MIN(nbThreadsAdjusted, nbThreadsMax); - CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_nbThreads, nbThreads, useOpaqueAPI) ); DISPLAYLEVEL(5, "t%u: nbThreads : %u \n", testNb, nbThreads); + CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_nbThreads, nbThreads, useOpaqueAPI) ); if (nbThreads > 1) { U32 const jobLog = FUZ_rand(&lseed) % (testLog+1); CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_overlapSizeLog, FUZ_rand(&lseed) % 10, useOpaqueAPI) ); @@ -1530,10 +1534,11 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double } } - if (FUZ_rand(&lseed) & 1) CHECK_Z (setCCtxParameter(zc, cctxParams, ZSTD_p_forceMaxWindow, FUZ_rand(&lseed) & 1, useOpaqueAPI) ); + if (FUZ_rand(&lseed) & 1) CHECK_Z( setCCtxParameter(zc, cctxParams, ZSTD_p_forceMaxWindow, FUZ_rand(&lseed) & 1, useOpaqueAPI) ); /* Apply parameters */ if (useOpaqueAPI) { + DISPLAYLEVEL(6," t%u: applying CCtxParams \n", testNb); CHECK_Z (ZSTD_CCtx_setParametersUsingCCtxParams(zc, cctxParams) ); } @@ -1573,8 +1578,8 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double outBuff.size = outBuff.pos + dstBuffSize; CHECK_Z( ZSTD_compress_generic(zc, &outBuff, &inBuff, flush) ); - DISPLAYLEVEL(6, "compress consumed %u bytes (total : %u) \n", - (U32)inBuff.pos, (U32)(totalTestSize + inBuff.pos)); + DISPLAYLEVEL(6, "t%u: compress consumed %u bytes (total : %u) \n", + testNb, (U32)inBuff.pos, (U32)(totalTestSize + inBuff.pos)); XXH64_update(&xxhState, srcBuffer+srcStart, inBuff.pos); memcpy(copyBuffer+totalTestSize, srcBuffer+srcStart, inBuff.pos); From 78290874a5e91e9d9c95ffea67592caa8425ba50 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 29 Nov 2017 17:01:14 -0800 Subject: [PATCH 093/145] fixed Visual warning on minor interface discrepancy --- lib/compress/zstd_compress.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index dd12b171b..bc2d8a576 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -470,7 +470,7 @@ size_t ZSTD_CCtxParam_setParameter( * pledgedSrcSize is considered unknown */ size_t ZSTD_CCtx_setParametersUsingCCtxParams( - ZSTD_CCtx* const cctx, const ZSTD_CCtx_params* const params) + ZSTD_CCtx* cctx, const ZSTD_CCtx_params* params) { if (cctx->streamStage != zcss_init) return ERROR(stage_wrong); if (cctx->cdict) return ERROR(stage_wrong); From 9a2f6f477ba8cefa0040bb0323203aadde1e7c32 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Wed, 29 Nov 2017 19:11:12 -0800 Subject: [PATCH 094/145] Use util.h for timing --- programs/bench.c | 15 +++++++------- programs/dibio.c | 16 +++++++-------- programs/fileio.c | 12 ++++++----- programs/util.h | 4 ++++ tests/decodecorpus.c | 21 +++++++++----------- tests/fuzzer.c | 28 ++++++++++++-------------- tests/paramgrill.c | 47 +++++++++++++++++++++----------------------- tests/zbufftest.c | 35 +++++++++++++++------------------ tests/zstreamtest.c | 43 +++++++++++++++++++--------------------- 9 files changed, 106 insertions(+), 115 deletions(-) diff --git a/programs/bench.c b/programs/bench.c index 1df8aeb0d..384b982f4 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -34,7 +34,6 @@ #include /* malloc, free */ #include /* memset */ #include /* fprintf, fopen */ -#include /* clock_t, clock, CLOCKS_PER_SEC */ #include "mem.h" #define ZSTD_STATIC_LINKING_ONLY @@ -72,12 +71,14 @@ static U32 g_compressibilityDefault = 50; #define DISPLAYLEVEL(l, ...) if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } static int g_displayLevel = 2; /* 0 : no display; 1: errors; 2 : + result + interaction + warnings; 3 : + progression; 4 : + information */ -#define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \ - if ((clock() - g_time > refreshRate) || (g_displayLevel>=4)) \ - { g_time = clock(); DISPLAY(__VA_ARGS__); \ - if (g_displayLevel>=4) fflush(stderr); } } -static const clock_t refreshRate = CLOCKS_PER_SEC * 15 / 100; -static clock_t g_time = 0; +#define SEC_TO_MICRO 1000000 +static const U64 g_refreshRate = SEC_TO_MICRO / 6; +static UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER; + +#define DISPLAYUPDATE(l, ...) { if (g_displayLevel>=l) { \ + if ((UTIL_clockSpanMicro(g_displayClock) > g_refreshRate) || (g_displayLevel>=4)) \ + { g_displayClock = UTIL_getTime(); DISPLAY(__VA_ARGS__); \ + if (g_displayLevel>=4) fflush(stderr); } } } /* ************************************* diff --git a/programs/dibio.c b/programs/dibio.c index dea3ec44f..9cc5264d2 100644 --- a/programs/dibio.c +++ b/programs/dibio.c @@ -26,7 +26,6 @@ #include /* malloc, free */ #include /* memset */ #include /* fprintf, fopen, ftello64 */ -#include /* clock_t, clock, CLOCKS_PER_SEC */ #include /* errno */ #include "mem.h" /* read */ @@ -55,15 +54,14 @@ static const size_t g_maxMemory = (sizeof(size_t) == 4) ? (2 GB - 64 MB) : ((siz #define DISPLAY(...) fprintf(stderr, __VA_ARGS__) #define DISPLAYLEVEL(l, ...) if (displayLevel>=l) { DISPLAY(__VA_ARGS__); } -#define DISPLAYUPDATE(l, ...) if (displayLevel>=l) { \ - if ((DIB_clockSpan(g_time) > refreshRate) || (displayLevel>=4)) \ - { g_time = clock(); DISPLAY(__VA_ARGS__); \ - if (displayLevel>=4) fflush(stderr); } } -static const clock_t refreshRate = CLOCKS_PER_SEC * 2 / 10; -static clock_t g_time = 0; - -static clock_t DIB_clockSpan(clock_t nPrevious) { return clock() - nPrevious; } +#define SEC_TO_MICRO 1000000 +static const U64 g_refreshRate = SEC_TO_MICRO / 6; +static UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER; +#define DISPLAYUPDATE(l, ...) { if (displayLevel>=l) { \ + if ((UTIL_clockSpanMicro(g_displayClock) > g_refreshRate) || (displayLevel>=4)) \ + { g_displayClock = UTIL_getTime(); DISPLAY(__VA_ARGS__); \ + if (displayLevel>=4) fflush(stderr); } } } /*-************************************* * Exceptions diff --git a/programs/fileio.c b/programs/fileio.c index eb004f916..0ea1ad152 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -29,7 +29,6 @@ #include /* fprintf, fopen, fread, _fileno, stdin, stdout */ #include /* malloc, free */ #include /* strcmp, strlen */ -#include /* clock */ #include /* errno */ #if defined (_MSC_VER) @@ -40,6 +39,7 @@ #include "bitstream.h" #include "mem.h" #include "fileio.h" +#include "util.h" #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_magicNumber, ZSTD_frameHeaderSize_max */ #include "zstd.h" #if defined(ZSTD_GZCOMPRESS) || defined(ZSTD_GZDECOMPRESS) @@ -81,12 +81,14 @@ static int g_displayLevel = 2; /* 0 : no display; 1: errors; 2: + result + interaction + warnings; 3: + progression; 4: + information */ void FIO_setNotificationLevel(unsigned level) { g_displayLevel=level; } +#define SEC_TO_MICRO 1000000 +static const U64 g_refreshRate = SEC_TO_MICRO / 6; +static UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER; + #define DISPLAYUPDATE(l, ...) { if (g_displayLevel>=l) { \ - if ((clock() - g_time > refreshRate) || (g_displayLevel>=4)) \ - { g_time = clock(); DISPLAY(__VA_ARGS__); \ + if ((UTIL_clockSpanMicro(g_displayClock) > g_refreshRate) || (g_displayLevel>=4)) \ + { g_displayClock = UTIL_getTime(); DISPLAY(__VA_ARGS__); \ if (g_displayLevel>=4) fflush(stderr); } } } -static const clock_t refreshRate = CLOCKS_PER_SEC * 15 / 100; -static clock_t g_time = 0; #undef MIN /* in case it would be already defined */ #define MIN(a,b) ((a) < (b) ? (a) : (b)) diff --git a/programs/util.h b/programs/util.h index e44d7459d..41155694e 100644 --- a/programs/util.h +++ b/programs/util.h @@ -118,6 +118,7 @@ static int g_utilDisplayLevel; * Time functions ******************************************/ #if defined(_WIN32) /* Windows */ + #define UTIL_TIME_INITIALIZER { { 0, 0 } } typedef LARGE_INTEGER UTIL_time_t; UTIL_STATIC UTIL_time_t UTIL_getTime(void) { UTIL_time_t x; QueryPerformanceCounter(&x); return x; } UTIL_STATIC U64 UTIL_getSpanTimeMicro(UTIL_time_t clockStart, UTIL_time_t clockEnd) @@ -144,6 +145,7 @@ static int g_utilDisplayLevel; } #elif defined(__APPLE__) && defined(__MACH__) #include + #define UTIL_TIME_INITIALIZER 0 typedef U64 UTIL_time_t; UTIL_STATIC UTIL_time_t UTIL_getTime(void) { return mach_absolute_time(); } UTIL_STATIC U64 UTIL_getSpanTimeMicro(UTIL_time_t clockStart, UTIL_time_t clockEnd) @@ -168,6 +170,7 @@ static int g_utilDisplayLevel; } #elif (PLATFORM_POSIX_VERSION >= 200112L) #include + #define UTIL_TIME_INITIALIZER { 0, 0 } typedef struct timespec UTIL_freq_t; typedef struct timespec UTIL_time_t; UTIL_STATIC UTIL_time_t UTIL_getTime(void) @@ -207,6 +210,7 @@ static int g_utilDisplayLevel; } #else /* relies on standard C (note : clock_t measurements can be wrong when using multi-threading) */ typedef clock_t UTIL_time_t; + #define UTIL_TIME_INITIALIZER 0 UTIL_STATIC UTIL_time_t UTIL_getTime(void) { return clock(); } UTIL_STATIC U64 UTIL_getSpanTimeMicro(UTIL_time_t clockStart, UTIL_time_t clockEnd) { return 1000000ULL * (clockEnd - clockStart) / CLOCKS_PER_SEC; } UTIL_STATIC U64 UTIL_getSpanTimeNano(UTIL_time_t clockStart, UTIL_time_t clockEnd) { return 1000000000ULL * (clockEnd - clockStart) / CLOCKS_PER_SEC; } diff --git a/tests/decodecorpus.c b/tests/decodecorpus.c index e697e519c..7d7b64664 100644 --- a/tests/decodecorpus.c +++ b/tests/decodecorpus.c @@ -14,8 +14,8 @@ #include #include #include -#include +#include "util.h" #include "zstd.h" #include "zstd_internal.h" #include "mem.h" @@ -49,20 +49,17 @@ static U32 g_displayLevel = 2; #define DISPLAYUPDATE(...) \ do { \ - if ((clockSpan(g_displayClock) > g_refreshRate) || \ + if ((UTIL_clockSpanMicro(g_displayClock) > g_refreshRate) || \ (g_displayLevel >= 4)) { \ - g_displayClock = clock(); \ + g_displayClock = UTIL_getTime(); \ DISPLAY(__VA_ARGS__); \ if (g_displayLevel >= 4) fflush(stderr); \ } \ } while (0) -static const clock_t g_refreshRate = CLOCKS_PER_SEC / 6; -static clock_t g_displayClock = 0; -static clock_t clockSpan(clock_t cStart) -{ - return clock() - cStart; /* works even when overflow; max span ~ 30mn */ -} +#define SEC_TO_MICRO 1000000 +static const U64 g_refreshRate = SEC_TO_MICRO / 6; +static UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER; #define CHECKERR(code) \ do { \ @@ -1531,14 +1528,14 @@ static int runTestMode(U32 seed, unsigned numFiles, unsigned const testDurationS { unsigned fnum; - clock_t const startClock = clock(); - clock_t const maxClockSpan = testDurationS * CLOCKS_PER_SEC; + UTIL_time_t const startClock = UTIL_getTime(); + U64 const maxClockSpan = testDurationS * SEC_TO_MICRO; if (numFiles == 0 && !testDurationS) numFiles = 1; DISPLAY("seed: %u\n", seed); - for (fnum = 0; fnum < numFiles || clockSpan(startClock) < maxClockSpan; fnum++) { + for (fnum = 0; fnum < numFiles || UTIL_clockSpanMicro(startClock) < maxClockSpan; fnum++) { if (fnum < numFiles) DISPLAYUPDATE("\r%u/%u ", fnum, numFiles); else diff --git a/tests/fuzzer.c b/tests/fuzzer.c index be9dc3e06..980c36176 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -25,7 +25,6 @@ #include /* free */ #include /* fgets, sscanf */ #include /* strcmp */ -#include /* clock_t */ #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_compressContinue, ZSTD_compressBlock */ #include "zstd.h" /* ZSTD_VERSION_STRING */ #include "zstd_errors.h" /* ZSTD_getErrorCode */ @@ -36,6 +35,7 @@ #include "mem.h" #define XXH_STATIC_LINKING_ONLY #include "xxhash.h" /* XXH64 */ +#include "util.h" /*-************************************ @@ -56,25 +56,23 @@ static const U32 nbTestsDefault = 30000; #define DISPLAYLEVEL(l, ...) if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } static U32 g_displayLevel = 2; -#define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \ - if ((FUZ_clockSpan(g_displayClock) > g_refreshRate) || (g_displayLevel>=4)) \ - { g_displayClock = clock(); DISPLAY(__VA_ARGS__); \ - if (g_displayLevel>=4) fflush(stdout); } } -static const clock_t g_refreshRate = CLOCKS_PER_SEC / 6; -static clock_t g_displayClock = 0; +#define SEC_TO_MICRO 1000000 +static const U64 g_refreshRate = SEC_TO_MICRO / 6; +static UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER; +#define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \ + if ((UTIL_clockSpanMicro(g_displayClock) > g_refreshRate) || (g_displayLevel>=4)) \ + { g_displayClock = UTIL_getTime(); DISPLAY(__VA_ARGS__); \ + if (g_displayLevel>=4) fflush(stderr); } } /*-******************************************************* * Fuzzer functions *********************************************************/ +#undef MIN +#undef MAX #define MIN(a,b) ((a)<(b)?(a):(b)) #define MAX(a,b) ((a)>(b)?(a):(b)) -static clock_t FUZ_clockSpan(clock_t cStart) -{ - return clock() - cStart; /* works even when overflow; max span ~ 30mn */ -} - #define FUZ_rotl32(x,r) ((x << r) | (x >> (32 - r))) static unsigned FUZ_rand(unsigned* src) { @@ -1208,8 +1206,8 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, U32 const maxD U32 result = 0; U32 testNb = 0; U32 coreSeed = seed, lseed = 0; - clock_t const startClock = clock(); - clock_t const maxClockSpan = maxDurationS * CLOCKS_PER_SEC; + UTIL_time_t const startClock = UTIL_getTime(); + U64 const maxClockSpan = maxDurationS * SEC_TO_MICRO; int const cLevelLimiter = bigTests ? 3 : 2; /* allocation */ @@ -1234,7 +1232,7 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, U32 const maxD for (testNb=1; testNb < startTest; testNb++) FUZ_rand(&coreSeed); /* main test loop */ - for ( ; (testNb <= nbTests) || (FUZ_clockSpan(startClock) < maxClockSpan); testNb++ ) { + for ( ; (testNb <= nbTests) || (UTIL_clockSpanMicro(startClock) < maxClockSpan); testNb++ ) { size_t sampleSize, maxTestSize, totalTestSize; size_t cSize, totalCSize, totalGenSize; U64 crcOrig; diff --git a/tests/paramgrill.c b/tests/paramgrill.c index 081a284e4..e258932da 100644 --- a/tests/paramgrill.c +++ b/tests/paramgrill.c @@ -17,13 +17,14 @@ #include /* fprintf, fopen, ftello64 */ #include /* strcmp */ #include /* log */ -#include /* clock_t */ +#include #include "mem.h" #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_parameters, ZSTD_estimateCCtxSize */ #include "zstd.h" #include "datagen.h" #include "xxhash.h" +#include "util.h" /*-************************************ @@ -38,8 +39,9 @@ #define MB *(1<<20) #define GB *(1ULL<<30) +#define SEC_TO_MICRO 1000000 #define NBLOOPS 2 -#define TIMELOOP (2 * CLOCKS_PER_SEC) +#define TIMELOOP (2 * SEC_TO_MICRO) #define NB_LEVELS_TRACKED 30 @@ -49,8 +51,8 @@ static const size_t maxMemory = (sizeof(size_t)==4) ? (2 GB - 64 MB) : (size_t static const size_t sampleSize = 10000000; static const double g_grillDuration_s = 90000; /* about 24 hours */ -static const clock_t g_maxParamTime = 15 * CLOCKS_PER_SEC; -static const clock_t g_maxVariationTime = 60 * CLOCKS_PER_SEC; +static const U64 g_maxParamTime = 15 * SEC_TO_MICRO; +static const U64 g_maxVariationTime = 60 * SEC_TO_MICRO; static const int g_maxNbVariations = 64; @@ -88,13 +90,9 @@ void BMK_SetNbIterations(int nbLoops) * Private functions *********************************************************/ -/* works even if overflow ; max span ~ 30 mn */ -static clock_t BMK_clockSpan(clock_t cStart) { return clock() - cStart; } - /* accuracy in seconds only, span can be multiple years */ static double BMK_timeSpan(time_t tStart) { return difftime(time(NULL), tStart); } - static size_t BMK_findMaxMem(U64 requiredMem) { size_t const step = 64 MB; @@ -221,7 +219,7 @@ static size_t BMK_benchParam(BMK_result_t* resultPtr, size_t cSize = 0; double fastestC = 100000000., fastestD = 100000000.; double ratio = 0.; - clock_t const benchStart = clock(); + UTIL_time_t const benchStart = UTIL_getTime(); DISPLAY("\r%79s\r", ""); memset(¶ms, 0, sizeof(params)); @@ -229,9 +227,10 @@ static size_t BMK_benchParam(BMK_result_t* resultPtr, for (loopNb = 1; loopNb <= g_nbIterations; loopNb++) { int nbLoops; U32 blockNb; - clock_t roundStart, roundClock; + UTIL_time_t roundStart; + U64 roundClock; - { clock_t const benchTime = BMK_clockSpan(benchStart); + { U64 const benchTime = UTIL_clockSpanMicro(benchStart); if (benchTime > g_maxParamTime) break; } /* Compression */ @@ -239,10 +238,9 @@ static size_t BMK_benchParam(BMK_result_t* resultPtr, memset(compressedBuffer, 0xE5, maxCompressedSize); nbLoops = 0; - roundStart = clock(); - while (clock() == roundStart); - roundStart = clock(); - while (BMK_clockSpan(roundStart) < TIMELOOP) { + UTIL_waitForNextTick(); + roundStart = UTIL_getTime(); + while (UTIL_clockSpanMicro(roundStart) < TIMELOOP) { for (blockNb=0; blockNb", loopNb, name, (U32)srcSize); DISPLAY(" %9u (%4.3f),%7.1f MB/s", (U32)cSize, ratio, (double)srcSize / fastestC / 1000000.); @@ -269,17 +267,16 @@ static size_t BMK_benchParam(BMK_result_t* resultPtr, memset(resultBuffer, 0xD6, srcSize); nbLoops = 0; - roundStart = clock(); - while (clock() == roundStart); - roundStart = clock(); - for ( ; BMK_clockSpan(roundStart) < TIMELOOP; nbLoops++) { + UTIL_waitForNextTick(); + roundStart = UTIL_getTime(); + for ( ; UTIL_clockSpanMicro(roundStart) < TIMELOOP; nbLoops++) { for (blockNb=0; blockNb ", loopNb, name, (U32)srcSize); DISPLAY("%9u (%4.3f),%7.1f MB/s, ", (U32)cSize, ratio, (double)srcSize / fastestC / 1000000.); @@ -521,9 +518,9 @@ static void playAround(FILE* f, winnerInfo_t* winners, ZSTD_CCtx* ctx) { int nbVariations = 0; - clock_t const clockStart = clock(); + UTIL_time_t const clockStart = UTIL_getTime(); - while (BMK_clockSpan(clockStart) < g_maxVariationTime) { + while (UTIL_clockSpanMicro(clockStart) < g_maxVariationTime) { ZSTD_compressionParameters p = params; if (nbVariations++ > g_maxNbVariations) break; diff --git a/tests/zbufftest.c b/tests/zbufftest.c index ce5e5518a..f770c8395 100644 --- a/tests/zbufftest.c +++ b/tests/zbufftest.c @@ -24,7 +24,6 @@ **************************************/ #include /* free */ #include /* fgets, sscanf */ -#include /* clock_t, clock() */ #include /* strcmp */ #include "mem.h" #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_maxCLevel */ @@ -34,6 +33,7 @@ #include "datagen.h" /* RDG_genBuffer */ #define XXH_STATIC_LINKING_ONLY #include "xxhash.h" /* XXH64_* */ +#include "util.h" /*-************************************ @@ -58,26 +58,25 @@ static const U32 prime2 = 2246822519U; #define DISPLAYLEVEL(l, ...) if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } static U32 g_displayLevel = 2; -#define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \ - if ((FUZ_GetClockSpan(g_displayClock) > g_refreshRate) || (g_displayLevel>=4)) \ - { g_displayClock = clock(); DISPLAY(__VA_ARGS__); \ - if (g_displayLevel>=4) fflush(stderr); } } -static const clock_t g_refreshRate = CLOCKS_PER_SEC * 15 / 100; -static clock_t g_displayClock = 0; +#define SEC_TO_MICRO 1000000 +static const U64 g_refreshRate = SEC_TO_MICRO / 6; +static UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER; -static clock_t g_clockTime = 0; +#define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \ + if ((UTIL_clockSpanMicro(g_displayClock) > g_refreshRate) || (g_displayLevel>=4)) \ + { g_displayClock = UTIL_getTime(); DISPLAY(__VA_ARGS__); \ + if (g_displayLevel>=4) fflush(stderr); } } + +static U64 g_clockTime = 0; /*-******************************************************* * Fuzzer functions *********************************************************/ +#undef MIN +#undef MAX +#define MIN(a,b) ((a)<(b)?(a):(b)) #define MAX(a,b) ((a)>(b)?(a):(b)) - -static clock_t FUZ_GetClockSpan(clock_t clockStart) -{ - return clock() - clockStart; /* works even when overflow. Max span ~ 30 mn */ -} - /*! FUZ_rand() : @return : a 27 bits random value, from a 32-bits `seed`. `seed` is also modified */ @@ -256,8 +255,6 @@ static size_t FUZ_randomLength(U32* seed, U32 maxLog) return FUZ_rLogLength(seed, logLength); } -#define MIN(a,b) ( (a) < (b) ? (a) : (b) ) - #define CHECK(cond, ...) if (cond) { DISPLAY("Error => "); DISPLAY(__VA_ARGS__); \ DISPLAY(" (seed %u, test nb %u) \n", seed, testNb); goto _output_error; } @@ -278,7 +275,7 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, double compres U32 coreSeed = seed; ZBUFF_CCtx* zc; ZBUFF_DCtx* zd; - clock_t startClock = clock(); + UTIL_time_t startClock = UTIL_getTime(); /* allocations */ zc = ZBUFF_createCCtx(); @@ -308,7 +305,7 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, double compres FUZ_rand(&coreSeed); /* test loop */ - for ( ; (testNb <= nbTests) || (FUZ_GetClockSpan(startClock) < g_clockTime) ; testNb++ ) { + for ( ; (testNb <= nbTests) || (UTIL_clockSpanMicro(startClock) < g_clockTime) ; testNb++ ) { U32 lseed; const BYTE* srcBuffer; const BYTE* dict; @@ -548,7 +545,7 @@ int main(int argc, const char** argv) } if (*argument=='m') g_clockTime *=60, argument++; if (*argument=='n') argument++; - g_clockTime *= CLOCKS_PER_SEC; + g_clockTime *= SEC_TO_MICRO; break; case 's': diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index b8c437a41..aad9cba71 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -24,7 +24,6 @@ **************************************/ #include /* free */ #include /* fgets, sscanf */ -#include /* clock_t, clock() */ #include /* strcmp */ #include /* assert */ #include "mem.h" @@ -37,6 +36,7 @@ #define XXH_STATIC_LINKING_ONLY /* XXH64_state_t */ #include "xxhash.h" /* XXH64_* */ #include "seqgen.h" +#include "util.h" /*-************************************ @@ -62,26 +62,25 @@ static const U32 prime32 = 2654435761U; if (g_displayLevel>=4) fflush(stderr); } static U32 g_displayLevel = 2; -#define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \ - if ((FUZ_GetClockSpan(g_displayClock) > g_refreshRate) || (g_displayLevel>=4)) \ - { g_displayClock = clock(); DISPLAY(__VA_ARGS__); \ - if (g_displayLevel>=4) fflush(stderr); } } -static const clock_t g_refreshRate = CLOCKS_PER_SEC / 6; -static clock_t g_displayClock = 0; +#define SEC_TO_MICRO 1000000 +static const U64 g_refreshRate = SEC_TO_MICRO / 6; +static UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER; -static clock_t g_clockTime = 0; +#define DISPLAYUPDATE(l, ...) if (g_displayLevel>=l) { \ + if ((UTIL_clockSpanMicro(g_displayClock) > g_refreshRate) || (g_displayLevel>=4)) \ + { g_displayClock = UTIL_getTime(); DISPLAY(__VA_ARGS__); \ + if (g_displayLevel>=4) fflush(stderr); } } + +static U64 g_clockTime = 0; /*-******************************************************* * Fuzzer functions *********************************************************/ +#undef MIN +#undef MAX +#define MIN(a,b) ((a)<(b)?(a):(b)) #define MAX(a,b) ((a)>(b)?(a):(b)) - -static clock_t FUZ_GetClockSpan(clock_t clockStart) -{ - return clock() - clockStart; /* works even when overflow. Max span ~ 30 mn */ -} - /*! FUZ_rand() : @return : a 27 bits random value, from a 32-bits `seed`. `seed` is also modified */ @@ -815,8 +814,6 @@ static size_t FUZ_randomLength(U32* seed, U32 maxLog) return FUZ_rLogLength(seed, logLength); } -#define MIN(a,b) ( (a) < (b) ? (a) : (b) ) - /* Return value in range minVal <= v <= maxVal */ static U32 FUZ_randomClampedLength(U32* seed, U32 minVal, U32 maxVal) { @@ -842,7 +839,7 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, double compres ZSTD_CStream* zc = ZSTD_createCStream(); /* will be re-created sometimes */ ZSTD_DStream* zd = ZSTD_createDStream(); /* will be re-created sometimes */ ZSTD_DStream* const zd_noise = ZSTD_createDStream(); - clock_t const startClock = clock(); + UTIL_time_t const startClock = UTIL_getTime(); const BYTE* dict = NULL; /* can keep same dict on 2 consecutive tests */ size_t dictSize = 0; U32 oldTestLog = 0; @@ -872,7 +869,7 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, double compres FUZ_rand(&coreSeed); /* test loop */ - for ( ; (testNb <= nbTests) || (FUZ_GetClockSpan(startClock) < g_clockTime) ; testNb++ ) { + for ( ; (testNb <= nbTests) || (UTIL_clockSpanMicro(startClock) < g_clockTime) ; testNb++ ) { U32 lseed; const BYTE* srcBuffer; size_t totalTestSize, totalGenSize, cSize; @@ -1092,7 +1089,7 @@ static int fuzzerTests_MT(U32 seed, U32 nbTests, unsigned startTest, double comp ZSTDMT_CCtx* zc = ZSTDMT_createCCtx(nbThreads); /* will be reset sometimes */ ZSTD_DStream* zd = ZSTD_createDStream(); /* will be reset sometimes */ ZSTD_DStream* const zd_noise = ZSTD_createDStream(); - clock_t const startClock = clock(); + UTIL_time_t const startClock = UTIL_getTime(); const BYTE* dict=NULL; /* can keep same dict on 2 consecutive tests */ size_t dictSize = 0; U32 oldTestLog = 0; @@ -1123,7 +1120,7 @@ static int fuzzerTests_MT(U32 seed, U32 nbTests, unsigned startTest, double comp FUZ_rand(&coreSeed); /* test loop */ - for ( ; (testNb <= nbTests) || (FUZ_GetClockSpan(startClock) < g_clockTime) ; testNb++ ) { + for ( ; (testNb <= nbTests) || (UTIL_clockSpanMicro(startClock) < g_clockTime) ; testNb++ ) { U32 lseed; const BYTE* srcBuffer; size_t totalTestSize, totalGenSize, cSize; @@ -1364,7 +1361,7 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double ZSTD_CCtx* zc = ZSTD_createCCtx(); /* will be reset sometimes */ ZSTD_DStream* zd = ZSTD_createDStream(); /* will be reset sometimes */ ZSTD_DStream* const zd_noise = ZSTD_createDStream(); - clock_t const startClock = clock(); + UTIL_time_t const startClock = UTIL_getTime(); const BYTE* dict = NULL; /* can keep same dict on 2 consecutive tests */ size_t dictSize = 0; U32 oldTestLog = 0; @@ -1397,7 +1394,7 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double FUZ_rand(&coreSeed); /* test loop */ - for ( ; (testNb <= nbTests) || (FUZ_GetClockSpan(startClock) < g_clockTime) ; testNb++ ) { + for ( ; (testNb <= nbTests) || (UTIL_clockSpanMicro(startClock) < g_clockTime) ; testNb++ ) { U32 lseed; const BYTE* srcBuffer; size_t totalTestSize, totalGenSize, cSize; @@ -1772,7 +1769,7 @@ int main(int argc, const char** argv) g_clockTime *=60, argument++; if (*argument=='n') argument++; /* -T1mn == -T60 */ } else if (*argument=='s') argument++; /* -T10s == -T10 */ - g_clockTime *= CLOCKS_PER_SEC; + g_clockTime *= SEC_TO_MICRO; break; case 's': /* manually select seed */ From dab8cfa3c73be56ad3424f0299f1bb3740ac104d Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 30 Nov 2017 19:40:53 -0800 Subject: [PATCH 095/145] Combine definitions of SEC_TO_MICRO --- programs/bench.c | 1 - programs/dibio.c | 1 - programs/fileio.c | 1 - programs/util.h | 1 + tests/decodecorpus.c | 1 - tests/fuzzer.c | 1 - tests/paramgrill.c | 1 - tests/zbufftest.c | 1 - tests/zstreamtest.c | 1 - 9 files changed, 1 insertion(+), 8 deletions(-) diff --git a/programs/bench.c b/programs/bench.c index 384b982f4..63b98e532 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -71,7 +71,6 @@ static U32 g_compressibilityDefault = 50; #define DISPLAYLEVEL(l, ...) if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } static int g_displayLevel = 2; /* 0 : no display; 1: errors; 2 : + result + interaction + warnings; 3 : + progression; 4 : + information */ -#define SEC_TO_MICRO 1000000 static const U64 g_refreshRate = SEC_TO_MICRO / 6; static UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER; diff --git a/programs/dibio.c b/programs/dibio.c index 9cc5264d2..112259ddc 100644 --- a/programs/dibio.c +++ b/programs/dibio.c @@ -54,7 +54,6 @@ static const size_t g_maxMemory = (sizeof(size_t) == 4) ? (2 GB - 64 MB) : ((siz #define DISPLAY(...) fprintf(stderr, __VA_ARGS__) #define DISPLAYLEVEL(l, ...) if (displayLevel>=l) { DISPLAY(__VA_ARGS__); } -#define SEC_TO_MICRO 1000000 static const U64 g_refreshRate = SEC_TO_MICRO / 6; static UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER; diff --git a/programs/fileio.c b/programs/fileio.c index 0ea1ad152..c9b6b04e1 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -81,7 +81,6 @@ static int g_displayLevel = 2; /* 0 : no display; 1: errors; 2: + result + interaction + warnings; 3: + progression; 4: + information */ void FIO_setNotificationLevel(unsigned level) { g_displayLevel=level; } -#define SEC_TO_MICRO 1000000 static const U64 g_refreshRate = SEC_TO_MICRO / 6; static UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER; diff --git a/programs/util.h b/programs/util.h index 41155694e..37098f2f7 100644 --- a/programs/util.h +++ b/programs/util.h @@ -216,6 +216,7 @@ static int g_utilDisplayLevel; UTIL_STATIC U64 UTIL_getSpanTimeNano(UTIL_time_t clockStart, UTIL_time_t clockEnd) { return 1000000000ULL * (clockEnd - clockStart) / CLOCKS_PER_SEC; } #endif +#define SEC_TO_MICRO 1000000 /* returns time span in microseconds */ UTIL_STATIC U64 UTIL_clockSpanMicro( UTIL_time_t clockStart ) diff --git a/tests/decodecorpus.c b/tests/decodecorpus.c index 7d7b64664..407653119 100644 --- a/tests/decodecorpus.c +++ b/tests/decodecorpus.c @@ -57,7 +57,6 @@ static U32 g_displayLevel = 2; } \ } while (0) -#define SEC_TO_MICRO 1000000 static const U64 g_refreshRate = SEC_TO_MICRO / 6; static UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER; diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 980c36176..46671a15c 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -56,7 +56,6 @@ static const U32 nbTestsDefault = 30000; #define DISPLAYLEVEL(l, ...) if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } static U32 g_displayLevel = 2; -#define SEC_TO_MICRO 1000000 static const U64 g_refreshRate = SEC_TO_MICRO / 6; static UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER; diff --git a/tests/paramgrill.c b/tests/paramgrill.c index e258932da..ae14aa074 100644 --- a/tests/paramgrill.c +++ b/tests/paramgrill.c @@ -39,7 +39,6 @@ #define MB *(1<<20) #define GB *(1ULL<<30) -#define SEC_TO_MICRO 1000000 #define NBLOOPS 2 #define TIMELOOP (2 * SEC_TO_MICRO) diff --git a/tests/zbufftest.c b/tests/zbufftest.c index f770c8395..9b6f7bad6 100644 --- a/tests/zbufftest.c +++ b/tests/zbufftest.c @@ -58,7 +58,6 @@ static const U32 prime2 = 2246822519U; #define DISPLAYLEVEL(l, ...) if (g_displayLevel>=l) { DISPLAY(__VA_ARGS__); } static U32 g_displayLevel = 2; -#define SEC_TO_MICRO 1000000 static const U64 g_refreshRate = SEC_TO_MICRO / 6; static UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER; diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index aad9cba71..207b2e015 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -62,7 +62,6 @@ static const U32 prime32 = 2654435761U; if (g_displayLevel>=4) fflush(stderr); } static U32 g_displayLevel = 2; -#define SEC_TO_MICRO 1000000 static const U64 g_refreshRate = SEC_TO_MICRO / 6; static UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER; From 453fb5e830356559b8a54a5655219a874a39509e Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 1 Dec 2017 10:30:53 -0800 Subject: [PATCH 096/145] removed -ftrapv from tests/ debug flags -ftrapv is apparently buggy for gcc. versions >= 5 are supposed to work better, but even then, some complaints say it's still flaky when optimizations are enabled. I even saw a post saying it only works if one creates its own signal handler, which would make this flag no longer transparent. on clang, it seems to work correctly. But we would need to add a method to selectively add flags depending on compiler. That's too much troubles for the intended benefit (just catch integer overflows, which we can also do using ubsan). --- tests/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Makefile b/tests/Makefile index 8cc953ec8..f944fd5a8 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -32,7 +32,7 @@ CFLAGS += -Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \ -Wstrict-aliasing=1 -Wswitch-enum -Wdeclaration-after-statement \ -Wstrict-prototypes -Wundef -Wformat-security \ -Wvla -Wformat=2 -Winit-self -Wfloat-equal -Wwrite-strings \ - -Wredundant-decls -ftrapv + -Wredundant-decls CFLAGS += $(DEBUGFLAGS) $(MOREFLAGS) FLAGS = $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) From 4413ed27e450d7154ffbfa7c17b9313c2fd466fa Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 1 Dec 2017 16:27:24 -0800 Subject: [PATCH 097/145] circleci : make libzstd-nomt had its own test --- circle.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index b488c1a7b..7e042d104 100644 --- a/circle.yml +++ b/circle.yml @@ -9,7 +9,7 @@ dependencies: test: override: - ? | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then cc -v; make all && make clean && make -C lib libzstd-nomt && make clean; fi && + if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then cc -v; make all && make clean; fi && if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make gnu90build && make clean; fi : parallel: true @@ -58,6 +58,10 @@ test: if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then true; fi # Could add another test here : parallel: true + - ? | + if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make -C lib libzstd-nomt && make clean; fi + : + parallel: true post: - echo Circle CI tests finished From 519cf3b400b6f87578938ceff3762dc4d85ad07c Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 1 Dec 2017 16:31:40 -0800 Subject: [PATCH 098/145] circleci: add CFLAGS=-O0 environment variable for faster compilation --- circle.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/circle.yml b/circle.yml index 7e042d104..e7f17979c 100644 --- a/circle.yml +++ b/circle.yml @@ -1,3 +1,7 @@ +machine: + environment: + CFLAGS: -O0 # for faster compilation, will preserve warnings + dependencies: override: - sudo dpkg --add-architecture i386 From 363355448b5032d062673cb5faf56a742571d190 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 1 Dec 2017 17:02:55 -0800 Subject: [PATCH 099/145] circleci: removed one test node --- circle.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/circle.yml b/circle.yml index e7f17979c..cc494905c 100644 --- a/circle.yml +++ b/circle.yml @@ -59,11 +59,7 @@ test: parallel: true - ? | if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make -j regressiontest && make clean; fi && - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then true; fi # Could add another test here - : - parallel: true - - ? | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make -C lib libzstd-nomt && make clean; fi + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make -C lib libzstd-nomt && make clean; fi : parallel: true From 30eeb248a8e85dc60030003dee2677af8c6709c6 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 1 Dec 2017 17:22:04 -0800 Subject: [PATCH 100/145] added environment variable through circleci dashboard check if it works better --- circle.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/circle.yml b/circle.yml index cc494905c..478775b70 100644 --- a/circle.yml +++ b/circle.yml @@ -1,7 +1,3 @@ -machine: - environment: - CFLAGS: -O0 # for faster compilation, will preserve warnings - dependencies: override: - sudo dpkg --add-architecture i386 From 74991c2764f32482815569a8efe4ee1e61bdfa71 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 1 Dec 2017 17:28:24 -0800 Subject: [PATCH 101/145] circleci: removed gccX/clang tests does no longer need to install these large packages --- circle.yml | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/circle.yml b/circle.yml index 478775b70..85061faa6 100644 --- a/circle.yml +++ b/circle.yml @@ -3,7 +3,6 @@ dependencies: - sudo dpkg --add-architecture i386 - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test; sudo apt-get -y -qq update - sudo apt-get -y install gcc-powerpc-linux-gnu gcc-arm-linux-gnueabi libc6-dev-armel-cross gcc-aarch64-linux-gnu libc6-dev-arm64-cross - - sudo apt-get -y install libstdc++-7-dev clang gcc g++ gcc-5 gcc-6 gcc-7 zlib1g-dev liblzma-dev - sudo apt-get -y install linux-libc-dev:i386 libc6-dev-i386 test: @@ -25,12 +24,7 @@ test: parallel: true - ? | if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make cxxtest && make clean; fi && - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make gcc5build && make clean; fi - : - parallel: true - - ? | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make gcc6build && make clean; fi && - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make clangbuild && make clean; fi + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make ppc64build && make clean; fi : parallel: true - ? | @@ -43,11 +37,6 @@ test: if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make ppcbuild && make clean; fi : parallel: true - - ? | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make ppc64build && make clean; fi && - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make gcc7build && make clean; fi - : - parallel: true - ? | if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make shortest && make clean; fi && if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make -C tests test-legacy test-longmatch test-symbols && make clean; fi From c6f5bf071cd3e246b3a4dae990489d3294e499eb Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 1 Dec 2017 17:35:19 -0800 Subject: [PATCH 102/145] fixed powerpc test misspelled CFLAGS --- Makefile | 2 +- circle.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 9c2657052..c8f0ed4d4 100644 --- a/Makefile +++ b/Makefile @@ -161,7 +161,7 @@ aarch64build: clean CC=aarch64-linux-gnu-gcc CFLAGS="-Werror" $(MAKE) allzstd ppcbuild: clean - CC=powerpc-linux-gnu-gcc CLAGS="-m32 -Wno-attributes -Werror" $(MAKE) allzstd + CC=powerpc-linux-gnu-gcc CFLAGS="-m32 -Wno-attributes -Werror" $(MAKE) allzstd ppc64build: clean CC=powerpc-linux-gnu-gcc CFLAGS="-m64 -Werror" $(MAKE) allzstd diff --git a/circle.yml b/circle.yml index 85061faa6..3b9facced 100644 --- a/circle.yml +++ b/circle.yml @@ -8,7 +8,7 @@ dependencies: test: override: - ? | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then cc -v; make all && make clean; fi && + if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then cc -v; CFLAGS="-O0 -Werror" make all && make clean; fi && if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make gnu90build && make clean; fi : parallel: true From 71f012e5bfbebe91ca89d9fcc5700a26f0dadc87 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 1 Dec 2017 17:42:46 -0800 Subject: [PATCH 103/145] zstdcli: fixed minor warning when bench module not enabled one variable defined but not used --- programs/zstdcli.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/programs/zstdcli.c b/programs/zstdcli.c index 91af49e24..8b4186207 100644 --- a/programs/zstdcli.c +++ b/programs/zstdcli.c @@ -773,8 +773,9 @@ int main(int argCount, const char* argv[]) BMK_setLdmHashEveryLog(g_ldmHashEveryLog); } BMK_benchFiles(filenameTable, filenameIdx, dictFileName, cLevel, cLevelLast, &compressionParams); +#else + (void)bench_nbSeconds; (void)blockSize; (void)setRealTimePrio; (void)separateFiles; #endif - (void)bench_nbSeconds; (void)blockSize; (void)setRealTimePrio; goto _end; } From 6127592391bfda25154a5167954eda6b38e6efde Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 1 Dec 2017 17:56:16 -0800 Subject: [PATCH 104/145] circleci : removed cmaketest strange error on pthread detection (seems a bug in cmake detection, since `make zstd` works perfectly with `-pthread` on same conf) --- circle.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/circle.yml b/circle.yml index 3b9facced..07f2990dd 100644 --- a/circle.yml +++ b/circle.yml @@ -19,7 +19,7 @@ test: parallel: true - ? | if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make c11build && make clean; fi && - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make cmakebuild && make clean; fi + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make -C lib libzstd-nomt && make clean; fi : parallel: true - ? | @@ -44,7 +44,7 @@ test: parallel: true - ? | if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make -j regressiontest && make clean; fi && - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make -C lib libzstd-nomt && make clean; fi + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then true; fi # can add one more test here : parallel: true From 272c14233032eb02b79f8d2e69364a9e90ed7ae7 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 1 Dec 2017 18:11:34 -0800 Subject: [PATCH 105/145] circleci : balance some tests try to group them by "equivalent" duration since next job will not start as long as previous one is not completed on both VM --- circle.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/circle.yml b/circle.yml index 07f2990dd..9b87ea565 100644 --- a/circle.yml +++ b/circle.yml @@ -19,11 +19,6 @@ test: parallel: true - ? | if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make c11build && make clean; fi && - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make -C lib libzstd-nomt && make clean; fi - : - parallel: true - - ? | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make cxxtest && make clean; fi && if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make ppc64build && make clean; fi : parallel: true @@ -42,6 +37,11 @@ test: if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make -C tests test-legacy test-longmatch test-symbols && make clean; fi : parallel: true + - ? | + if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make cxxtest && make clean; fi && + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make -C lib libzstd-nomt && make clean; fi + : + parallel: true - ? | if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make -j regressiontest && make clean; fi && if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then true; fi # can add one more test here From 6f1377301f7a33f39c9bbd9354730141f627733e Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 1 Dec 2017 18:34:22 -0800 Subject: [PATCH 106/145] circleci: removed -m32 tests installing dependencies costs ~2mn, making it the longest test (test itself was < 15sec) --- circle.yml | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/circle.yml b/circle.yml index 9b87ea565..ed50d59e5 100644 --- a/circle.yml +++ b/circle.yml @@ -3,7 +3,6 @@ dependencies: - sudo dpkg --add-architecture i386 - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test; sudo apt-get -y -qq update - sudo apt-get -y install gcc-powerpc-linux-gnu gcc-arm-linux-gnueabi libc6-dev-armel-cross gcc-aarch64-linux-gnu libc6-dev-arm64-cross - - sudo apt-get -y install linux-libc-dev:i386 libc6-dev-i386 test: override: @@ -23,13 +22,13 @@ test: : parallel: true - ? | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make m32build && make clean; fi && - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make armbuild && make clean; fi + if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make aarch64build && make clean; fi && + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make ppcbuild && make clean; fi : parallel: true - ? | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make aarch64build && make clean; fi && - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make ppcbuild && make clean; fi + if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make -j regressiontest && make clean; fi && + if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make armbuild && make clean; fi : parallel: true - ? | @@ -42,11 +41,6 @@ test: if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then make -C lib libzstd-nomt && make clean; fi : parallel: true - - ? | - if [[ "$CIRCLE_NODE_INDEX" == "0" ]] ; then make -j regressiontest && make clean; fi && - if [[ "$CIRCLE_NODE_TOTAL" < "2" ]] || [[ "$CIRCLE_NODE_INDEX" == "1" ]]; then true; fi # can add one more test here - : - parallel: true post: - echo Circle CI tests finished From 5e1f34b7e4c407f7f733c09c435141d18de2dc18 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 1 Dec 2017 21:17:09 -0800 Subject: [PATCH 107/145] setParameter : no side-effect on setting a compression parameter last such side-effect was modifying cctx->loadedDictEnd on setting forceWindow. It is no a useless operation, so it's removed. No side-effect left when setting a compression parameter. --- lib/compress/zstd_compress.c | 37 ++++------------------------------ lib/compress/zstdmt_compress.c | 2 +- 2 files changed, 5 insertions(+), 34 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index bc2d8a576..8d493852f 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -270,9 +270,8 @@ size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, unsigned v return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value); case ZSTD_p_forceMaxWindow : /* Force back-references to remain < windowSize, - * even when referencing into Dictionary content + * even when referencing into Dictionary content. * default : 0 when using a CDict, 1 when using a Prefix */ - cctx->loadedDictEnd = 0; /* ? */ return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value); case ZSTD_p_nbThreads: @@ -461,13 +460,9 @@ size_t ZSTD_CCtxParam_setParameter( } } -/** - * This function should be updated whenever ZSTD_CCtx_params is updated. - * Parameters are copied manually before the dictionary is loaded. - * The multithreading parameters jobSize and overlapSizeLog are set only if - * nbThreads > 1. - * - * pledgedSrcSize is considered unknown +/** ZSTD_CCtx_setParametersUsingCCtxParams() : + * just applies `params` into `cctx` + * no action is performed, parameters are merely stored. */ size_t ZSTD_CCtx_setParametersUsingCCtxParams( ZSTD_CCtx* cctx, const ZSTD_CCtx_params* params) @@ -475,31 +470,8 @@ size_t ZSTD_CCtx_setParametersUsingCCtxParams( if (cctx->streamStage != zcss_init) return ERROR(stage_wrong); if (cctx->cdict) return ERROR(stage_wrong); -#if 1 cctx->requestedParams = *params; -#else - /* Assume the compression and frame parameters are validated */ - cctx->requestedParams.cParams = params->cParams; - cctx->requestedParams.fParams = params->fParams; - cctx->requestedParams.compressionLevel = params->compressionLevel; - /* Set force window explicitly since it sets cctx->loadedDictEnd */ - CHECK_F( ZSTD_CCtx_setParameter( - cctx, ZSTD_p_forceMaxWindow, params->forceWindow) ); - - /* Set multithreading parameters explicitly */ - CHECK_F( ZSTD_CCtx_setParameter(cctx, ZSTD_p_nbThreads, params->nbThreads) ); - if (params->nbThreads > 1) { - CHECK_F( ZSTD_CCtx_setParameter(cctx, ZSTD_p_jobSize, params->jobSize) ); - CHECK_F( ZSTD_CCtx_setParameter( - cctx, ZSTD_p_overlapSizeLog, params->overlapSizeLog) ); - } - - /* Copy long distance matching parameters */ - cctx->requestedParams.ldmParams = params->ldmParams; -#endif - - /* customMem is used only for create/free params and can be ignored */ return 0; } @@ -2097,7 +2069,6 @@ size_t ZSTD_compressBegin_advanced_internal( unsigned long long pledgedSrcSize) { DEBUGLOG(4, "ZSTD_compressBegin_advanced_internal"); - DEBUGLOG(4, "contentSizeFlag : %u", params.fParams.contentSizeFlag); /* compression parameters verification and optimization */ CHECK_F( ZSTD_checkCParams(params.cParams) ); return ZSTD_compressBegin_internal(cctx, dict, dictSize, dictMode, NULL, diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index ce813dccc..6b6466fe5 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -359,7 +359,7 @@ void ZSTDMT_compressChunk(void* jobDescription) assert(job->firstChunk); /* should only happen for first segment */ if (ZSTD_isError(initError)) { job->cSize = initError; goto _endJob; } } else { /* srcStart points at reloaded section */ - ZSTD_CCtx_params jobParams = job->params; + ZSTD_CCtx_params jobParams = job->params; /* do not modify job->params ! copy it, modify the copy */ size_t const forceWindowError = ZSTD_CCtxParam_setParameter(&jobParams, ZSTD_p_forceMaxWindow, !job->firstChunk); U64 const pledgedSrcSize = job->firstChunk ? job->fullFrameSize : ZSTD_CONTENTSIZE_UNKNOWN; /* load dictionary in "content-only" mode (no header analysis) */ From 55faa5492d5ba593746868991556e593134e6b57 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 4 Dec 2017 11:26:59 -0800 Subject: [PATCH 108/145] fileio: fixed LZ4F invocation from assert() --- programs/fileio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/fileio.c b/programs/fileio.c index c9b6b04e1..101c99b1a 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -639,7 +639,6 @@ static unsigned long long FIO_compressLz4Frame(cRess_t* ress, memset(&prefs, 0, sizeof(prefs)); assert(blockSize <= ress->srcBufferSize); - assert(LZ4F_compressBound(blockSize) <= ress->dstBufferSize); prefs.autoFlush = 1; prefs.compressionLevel = compressionLevel; @@ -649,6 +648,7 @@ static unsigned long long FIO_compressLz4Frame(cRess_t* ress, #if LZ4_VERSION_NUMBER >= 10600 prefs.frameInfo.contentSize = (srcFileSize==UTIL_FILESIZE_UNKNOWN) ? 0 : srcFileSize; #endif + assert(LZ4F_compressBound(blockSize, &prefs) <= ress->dstBufferSize); { size_t readSize; From e46194bbf95aa11b81f3cc1be83f13d3f888421d Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 4 Dec 2017 15:57:01 -0800 Subject: [PATCH 109/145] fix #911 : changed detection macro for clock_gettime() The new macro might be a bit too restrictive. Systems which do not support new test will simply default to 's `clock_t clock()`, suffering lesser benchmark accuracy. Should it matter, the detection macro will have to be upgraded. --- programs/util.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/util.h b/programs/util.h index 37098f2f7..f510bc5e0 100644 --- a/programs/util.h +++ b/programs/util.h @@ -168,7 +168,7 @@ static int g_utilDisplayLevel; } return ((clockEnd - clockStart) * (U64)rate.numer) / ((U64)rate.denom); } -#elif (PLATFORM_POSIX_VERSION >= 200112L) +#elif defined __UCLIBC__ || ((__GLIBC__ == 2 && __GLIBC_MINOR__ >= 17) || __GLIBC__ > 2) #include #define UTIL_TIME_INITIALIZER { 0, 0 } typedef struct timespec UTIL_freq_t; From 00974692380e79ea702e99214c6e7acf672c058e Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 4 Dec 2017 16:02:42 -0800 Subject: [PATCH 110/145] removed a few redundant #include --- programs/util.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/programs/util.h b/programs/util.h index f510bc5e0..38dad2ab4 100644 --- a/programs/util.h +++ b/programs/util.h @@ -34,7 +34,7 @@ extern "C" { # include /* chown, stat */ # include /* utime */ #endif -#include /* time */ +#include /* clock_t, clock, CLOCKS_PER_SEC, nanosleep */ #include #include "mem.h" /* U32, U64 */ @@ -64,7 +64,6 @@ extern "C" { #elif PLATFORM_POSIX_VERSION >= 0 /* Unix-like operating system */ # include # include /* setpriority */ -# include /* clock_t, nanosleep, clock, CLOCKS_PER_SEC */ # if defined(PRIO_PROCESS) # define SET_REALTIME_PRIORITY setpriority(PRIO_PROCESS, 0, -20) # else @@ -169,7 +168,6 @@ static int g_utilDisplayLevel; return ((clockEnd - clockStart) * (U64)rate.numer) / ((U64)rate.denom); } #elif defined __UCLIBC__ || ((__GLIBC__ == 2 && __GLIBC_MINOR__ >= 17) || __GLIBC__ > 2) - #include #define UTIL_TIME_INITIALIZER { 0, 0 } typedef struct timespec UTIL_freq_t; typedef struct timespec UTIL_time_t; From 31293330d0cd3295646b208f5fa05cfa1ad8914c Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 4 Dec 2017 16:31:59 -0800 Subject: [PATCH 111/145] It's still necessary to check PLATFORM_POSIX_VERSION for clock_gettime() glibc/uclibc is not enough --- programs/util.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/util.h b/programs/util.h index 38dad2ab4..af1fa7fca 100644 --- a/programs/util.h +++ b/programs/util.h @@ -167,7 +167,7 @@ static int g_utilDisplayLevel; } return ((clockEnd - clockStart) * (U64)rate.numer) / ((U64)rate.denom); } -#elif defined __UCLIBC__ || ((__GLIBC__ == 2 && __GLIBC_MINOR__ >= 17) || __GLIBC__ > 2) +#elif (PLATFORM_POSIX_VERSION >= 200112L) && (defined __UCLIBC__ || ((__GLIBC__ == 2 && __GLIBC_MINOR__ >= 17) || __GLIBC__ > 2)) #define UTIL_TIME_INITIALIZER { 0, 0 } typedef struct timespec UTIL_freq_t; typedef struct timespec UTIL_time_t; From c173dbd6e766bf894b1e846c82ab7a00fb0e7481 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 4 Dec 2017 17:57:42 -0800 Subject: [PATCH 112/145] no longer supported starting C++17 --- contrib/long_distance_matching/ldm.c | 3 +- lib/common/bitstream.h | 2 +- lib/compress/zstd_compress_internal.h | 2 +- lib/dictBuilder/zdict.c | 2 +- lib/legacy/zstd_v01.c | 2 +- lib/legacy/zstd_v02.c | 75 +------------------------- lib/legacy/zstd_v03.c | 75 +------------------------- lib/legacy/zstd_v04.c | 73 +------------------------ lib/legacy/zstd_v05.c | 74 +------------------------ lib/legacy/zstd_v06.c | 78 +-------------------------- lib/legacy/zstd_v07.c | 78 +-------------------------- 11 files changed, 11 insertions(+), 453 deletions(-) diff --git a/contrib/long_distance_matching/ldm.c b/contrib/long_distance_matching/ldm.c index 4dccd0bfa..37b188b72 100644 --- a/contrib/long_distance_matching/ldm.c +++ b/contrib/long_distance_matching/ldm.c @@ -141,7 +141,7 @@ static LDM_hashEntry *getBucket(const LDM_hashTable *table, const hash_t hash) { return table->entries + (hash << HASH_BUCKET_SIZE_LOG); } -static unsigned ZSTD_NbCommonBytes (register size_t val) { +static unsigned ZSTD_NbCommonBytes (size_t val) { if (MEM_isLittleEndian()) { if (MEM_64bits()) { # if defined(_MSC_VER) && defined(_WIN64) @@ -854,4 +854,3 @@ void LDM_outputConfiguration(void) { printf("HASH_CHAR_OFFSET: %d\n", HASH_CHAR_OFFSET); printf("=====================\n"); } - diff --git a/lib/common/bitstream.h b/lib/common/bitstream.h index 2094823fe..fcf384307 100644 --- a/lib/common/bitstream.h +++ b/lib/common/bitstream.h @@ -167,7 +167,7 @@ MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, unsigned nbBits); /*-************************************************************** * Internal functions ****************************************************************/ -MEM_STATIC unsigned BIT_highbit32 (register U32 val) +MEM_STATIC unsigned BIT_highbit32 (U32 val) { assert(val != 0); { diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 1308541f4..278f2427c 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -268,7 +268,7 @@ MEM_STATIC void ZSTD_storeSeq(seqStore_t* seqStorePtr, size_t litLength, const v /*-************************************* * Match length counter ***************************************/ -static unsigned ZSTD_NbCommonBytes (register size_t val) +static unsigned ZSTD_NbCommonBytes (size_t val) { if (MEM_isLittleEndian()) { if (MEM_64bits()) { diff --git a/lib/dictBuilder/zdict.c b/lib/dictBuilder/zdict.c index 1bb8b0683..e8f454045 100644 --- a/lib/dictBuilder/zdict.c +++ b/lib/dictBuilder/zdict.c @@ -103,7 +103,7 @@ unsigned ZDICT_getDictID(const void* dictBuffer, size_t dictSize) /*-******************************************************** * Dictionary training functions **********************************************************/ -static unsigned ZDICT_NbCommonBytes (register size_t val) +static unsigned ZDICT_NbCommonBytes (size_t val) { if (MEM_isLittleEndian()) { if (MEM_64bits()) { diff --git a/lib/legacy/zstd_v01.c b/lib/legacy/zstd_v01.c index 70003cbed..ae1cb2ce5 100644 --- a/lib/legacy/zstd_v01.c +++ b/lib/legacy/zstd_v01.c @@ -339,7 +339,7 @@ typedef U32 DTable_max_t[FSE_DTABLE_SIZE_U32(FSE_MAX_TABLELOG)]; /**************************************************************** * Internal functions ****************************************************************/ -FORCE_INLINE unsigned FSE_highbit32 (register U32 val) +FORCE_INLINE unsigned FSE_highbit32 (U32 val) { # if defined(_MSC_VER) /* Visual */ unsigned long r; diff --git a/lib/legacy/zstd_v02.c b/lib/legacy/zstd_v02.c index b935a4d18..8bc0eceed 100644 --- a/lib/legacy/zstd_v02.c +++ b/lib/legacy/zstd_v02.c @@ -330,18 +330,6 @@ MEM_STATIC BIT_DStream_status BIT_reloadDStream(BIT_DStream_t* bitD); MEM_STATIC unsigned BIT_endOfDStream(const BIT_DStream_t* bitD); -/* -* Start by invoking BIT_initDStream(). -* A chunk of the bitStream is then stored into a local register. -* Local register size is 64-bits on 64-bits systems, 32-bits on 32-bits systems (size_t). -* You can then retrieve bitFields stored into the local register, **in reverse order**. -* Local register is manually filled from memory by the BIT_reloadDStream() method. -* A reload guarantee a minimum of ((8*sizeof(size_t))-7) bits when its result is BIT_DStream_unfinished. -* Otherwise, it can be less than that, so proceed accordingly. -* Checking if DStream has reached its end can be performed with BIT_endOfDStream() -*/ - - /****************************************** * unsafe API ******************************************/ @@ -353,7 +341,7 @@ MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, unsigned nbBits); /**************************************************************** * Helper functions ****************************************************************/ -MEM_STATIC unsigned BIT_highbit32 (register U32 val) +MEM_STATIC unsigned BIT_highbit32 (U32 val) { # if defined(_MSC_VER) /* Visual */ unsigned long r=0; @@ -427,13 +415,6 @@ MEM_STATIC size_t BIT_initDStream(BIT_DStream_t* bitD, const void* srcBuffer, si return srcSize; } -/*!BIT_lookBits - * Provides next n bits from local register - * local register is not modified (bits are still present for next read/look) - * On 32-bits, maxNbBits==25 - * On 64-bits, maxNbBits==57 - * @return : value extracted - */ MEM_STATIC size_t BIT_lookBits(BIT_DStream_t* bitD, U32 nbBits) { const U32 bitMask = sizeof(bitD->bitContainer)*8 - 1; @@ -453,11 +434,6 @@ MEM_STATIC void BIT_skipBits(BIT_DStream_t* bitD, U32 nbBits) bitD->bitsConsumed += nbBits; } -/*!BIT_readBits - * Read next n bits from local register. - * pay attention to not read more than nbBits contained into local register. - * @return : extracted value. - */ MEM_STATIC size_t BIT_readBits(BIT_DStream_t* bitD, U32 nbBits) { size_t value = BIT_lookBits(bitD, nbBits); @@ -695,55 +671,6 @@ static unsigned char FSE_decodeSymbol(FSE_DState_t* DStatePtr, BIT_DStream_t* bi static unsigned FSE_endOfDState(const FSE_DState_t* DStatePtr); -/* -Let's now decompose FSE_decompress_usingDTable() into its unitary components. -You will decode FSE-encoded symbols from the bitStream, -and also any other bitFields you put in, **in reverse order**. - -You will need a few variables to track your bitStream. They are : - -BIT_DStream_t DStream; // Stream context -FSE_DState_t DState; // State context. Multiple ones are possible -FSE_DTable* DTablePtr; // Decoding table, provided by FSE_buildDTable() - -The first thing to do is to init the bitStream. - errorCode = BIT_initDStream(&DStream, srcBuffer, srcSize); - -You should then retrieve your initial state(s) -(in reverse flushing order if you have several ones) : - errorCode = FSE_initDState(&DState, &DStream, DTablePtr); - -You can then decode your data, symbol after symbol. -For information the maximum number of bits read by FSE_decodeSymbol() is 'tableLog'. -Keep in mind that symbols are decoded in reverse order, like a LIFO stack (last in, first out). - unsigned char symbol = FSE_decodeSymbol(&DState, &DStream); - -You can retrieve any bitfield you eventually stored into the bitStream (in reverse order) -Note : maximum allowed nbBits is 25, for 32-bits compatibility - size_t bitField = BIT_readBits(&DStream, nbBits); - -All above operations only read from local register (which size depends on size_t). -Refueling the register from memory is manually performed by the reload method. - endSignal = FSE_reloadDStream(&DStream); - -BIT_reloadDStream() result tells if there is still some more data to read from DStream. -BIT_DStream_unfinished : there is still some data left into the DStream. -BIT_DStream_endOfBuffer : Dstream reached end of buffer. Its container may no longer be completely filled. -BIT_DStream_completed : Dstream reached its exact end, corresponding in general to decompression completed. -BIT_DStream_tooFar : Dstream went too far. Decompression result is corrupted. - -When reaching end of buffer (BIT_DStream_endOfBuffer), progress slowly, notably if you decode multiple symbols per loop, -to properly detect the exact end of stream. -After each decoded symbol, check if DStream is fully consumed using this simple test : - BIT_reloadDStream(&DStream) >= BIT_DStream_completed - -When it's done, verify decompression is fully completed, by checking both DStream and the relevant states. -Checking if DStream has reached its end is performed by : - BIT_endOfDStream(&DStream); -Check also the states. There might be some symbols left there, if some high probability ones (>50%) are possible. - FSE_endOfDState(&DState); -*/ - /****************************************** * FSE unsafe API diff --git a/lib/legacy/zstd_v03.c b/lib/legacy/zstd_v03.c index 35370dd0c..54445af57 100644 --- a/lib/legacy/zstd_v03.c +++ b/lib/legacy/zstd_v03.c @@ -332,17 +332,6 @@ MEM_STATIC BIT_DStream_status BIT_reloadDStream(BIT_DStream_t* bitD); MEM_STATIC unsigned BIT_endOfDStream(const BIT_DStream_t* bitD); -/* -* Start by invoking BIT_initDStream(). -* A chunk of the bitStream is then stored into a local register. -* Local register size is 64-bits on 64-bits systems, 32-bits on 32-bits systems (size_t). -* You can then retrieve bitFields stored into the local register, **in reverse order**. -* Local register is manually filled from memory by the BIT_reloadDStream() method. -* A reload guarantee a minimum of ((8*sizeof(size_t))-7) bits when its result is BIT_DStream_unfinished. -* Otherwise, it can be less than that, so proceed accordingly. -* Checking if DStream has reached its end can be performed with BIT_endOfDStream() -*/ - /****************************************** * unsafe API @@ -355,7 +344,7 @@ MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, unsigned nbBits); /**************************************************************** * Helper functions ****************************************************************/ -MEM_STATIC unsigned BIT_highbit32 (register U32 val) +MEM_STATIC unsigned BIT_highbit32 (U32 val) { # if defined(_MSC_VER) /* Visual */ unsigned long r=0; @@ -428,14 +417,6 @@ MEM_STATIC size_t BIT_initDStream(BIT_DStream_t* bitD, const void* srcBuffer, si return srcSize; } - -/*!BIT_lookBits - * Provides next n bits from local register - * local register is not modified (bits are still present for next read/look) - * On 32-bits, maxNbBits==25 - * On 64-bits, maxNbBits==57 - * @return : value extracted - */ MEM_STATIC size_t BIT_lookBits(BIT_DStream_t* bitD, U32 nbBits) { const U32 bitMask = sizeof(bitD->bitContainer)*8 - 1; @@ -455,11 +436,6 @@ MEM_STATIC void BIT_skipBits(BIT_DStream_t* bitD, U32 nbBits) bitD->bitsConsumed += nbBits; } -/*!BIT_readBits - * Read next n bits from local register. - * pay attention to not read more than nbBits contained into local register. - * @return : extracted value. - */ MEM_STATIC size_t BIT_readBits(BIT_DStream_t* bitD, U32 nbBits) { size_t value = BIT_lookBits(bitD, nbBits); @@ -697,55 +673,6 @@ static unsigned char FSE_decodeSymbol(FSE_DState_t* DStatePtr, BIT_DStream_t* bi static unsigned FSE_endOfDState(const FSE_DState_t* DStatePtr); -/* -Let's now decompose FSE_decompress_usingDTable() into its unitary components. -You will decode FSE-encoded symbols from the bitStream, -and also any other bitFields you put in, **in reverse order**. - -You will need a few variables to track your bitStream. They are : - -BIT_DStream_t DStream; // Stream context -FSE_DState_t DState; // State context. Multiple ones are possible -FSE_DTable* DTablePtr; // Decoding table, provided by FSE_buildDTable() - -The first thing to do is to init the bitStream. - errorCode = BIT_initDStream(&DStream, srcBuffer, srcSize); - -You should then retrieve your initial state(s) -(in reverse flushing order if you have several ones) : - errorCode = FSE_initDState(&DState, &DStream, DTablePtr); - -You can then decode your data, symbol after symbol. -For information the maximum number of bits read by FSE_decodeSymbol() is 'tableLog'. -Keep in mind that symbols are decoded in reverse order, like a LIFO stack (last in, first out). - unsigned char symbol = FSE_decodeSymbol(&DState, &DStream); - -You can retrieve any bitfield you eventually stored into the bitStream (in reverse order) -Note : maximum allowed nbBits is 25, for 32-bits compatibility - size_t bitField = BIT_readBits(&DStream, nbBits); - -All above operations only read from local register (which size depends on size_t). -Refueling the register from memory is manually performed by the reload method. - endSignal = FSE_reloadDStream(&DStream); - -BIT_reloadDStream() result tells if there is still some more data to read from DStream. -BIT_DStream_unfinished : there is still some data left into the DStream. -BIT_DStream_endOfBuffer : Dstream reached end of buffer. Its container may no longer be completely filled. -BIT_DStream_completed : Dstream reached its exact end, corresponding in general to decompression completed. -BIT_DStream_tooFar : Dstream went too far. Decompression result is corrupted. - -When reaching end of buffer (BIT_DStream_endOfBuffer), progress slowly, notably if you decode multiple symbols per loop, -to properly detect the exact end of stream. -After each decoded symbol, check if DStream is fully consumed using this simple test : - BIT_reloadDStream(&DStream) >= BIT_DStream_completed - -When it's done, verify decompression is fully completed, by checking both DStream and the relevant states. -Checking if DStream has reached its end is performed by : - BIT_endOfDStream(&DStream); -Check also the states. There might be some symbols left there, if some high probability ones (>50%) are possible. - FSE_endOfDState(&DState); -*/ - /****************************************** * FSE unsafe API diff --git a/lib/legacy/zstd_v04.c b/lib/legacy/zstd_v04.c index 1b5f6f3b0..1ff7c95e0 100644 --- a/lib/legacy/zstd_v04.c +++ b/lib/legacy/zstd_v04.c @@ -738,16 +738,6 @@ MEM_STATIC BIT_DStream_status BIT_reloadDStream(BIT_DStream_t* bitD); MEM_STATIC unsigned BIT_endOfDStream(const BIT_DStream_t* bitD); -/* -* Start by invoking BIT_initDStream(). -* A chunk of the bitStream is then stored into a local register. -* Local register size is 64-bits on 64-bits systems, 32-bits on 32-bits systems (size_t). -* You can then retrieve bitFields stored into the local register, **in reverse order**. -* Local register is manually filled from memory by the BIT_reloadDStream() method. -* A reload guarantee a minimum of ((8*sizeof(size_t))-7) bits when its result is BIT_DStream_unfinished. -* Otherwise, it can be less than that, so proceed accordingly. -* Checking if DStream has reached its end can be performed with BIT_endOfDStream() -*/ /****************************************** @@ -761,7 +751,7 @@ MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, unsigned nbBits); /**************************************************************** * Helper functions ****************************************************************/ -MEM_STATIC unsigned BIT_highbit32 (register U32 val) +MEM_STATIC unsigned BIT_highbit32 (U32 val) { # if defined(_MSC_VER) /* Visual */ unsigned long r=0; @@ -834,13 +824,6 @@ MEM_STATIC size_t BIT_initDStream(BIT_DStream_t* bitD, const void* srcBuffer, si return srcSize; } -/*!BIT_lookBits - * Provides next n bits from local register - * local register is not modified (bits are still present for next read/look) - * On 32-bits, maxNbBits==25 - * On 64-bits, maxNbBits==57 - * @return : value extracted - */ MEM_STATIC size_t BIT_lookBits(BIT_DStream_t* bitD, U32 nbBits) { const U32 bitMask = sizeof(bitD->bitContainer)*8 - 1; @@ -860,11 +843,6 @@ MEM_STATIC void BIT_skipBits(BIT_DStream_t* bitD, U32 nbBits) bitD->bitsConsumed += nbBits; } -/*!BIT_readBits - * Read next n bits from local register. - * pay attention to not read more than nbBits contained into local register. - * @return : extracted value. - */ MEM_STATIC size_t BIT_readBits(BIT_DStream_t* bitD, U32 nbBits) { size_t value = BIT_lookBits(bitD, nbBits); @@ -1011,55 +989,6 @@ static unsigned char FSE_decodeSymbol(FSE_DState_t* DStatePtr, BIT_DStream_t* bi static unsigned FSE_endOfDState(const FSE_DState_t* DStatePtr); -/*! -Let's now decompose FSE_decompress_usingDTable() into its unitary components. -You will decode FSE-encoded symbols from the bitStream, -and also any other bitFields you put in, **in reverse order**. - -You will need a few variables to track your bitStream. They are : - -BIT_DStream_t DStream; // Stream context -FSE_DState_t DState; // State context. Multiple ones are possible -FSE_DTable* DTablePtr; // Decoding table, provided by FSE_buildDTable() - -The first thing to do is to init the bitStream. - errorCode = BIT_initDStream(&DStream, srcBuffer, srcSize); - -You should then retrieve your initial state(s) -(in reverse flushing order if you have several ones) : - errorCode = FSE_initDState(&DState, &DStream, DTablePtr); - -You can then decode your data, symbol after symbol. -For information the maximum number of bits read by FSE_decodeSymbol() is 'tableLog'. -Keep in mind that symbols are decoded in reverse order, like a LIFO stack (last in, first out). - unsigned char symbol = FSE_decodeSymbol(&DState, &DStream); - -You can retrieve any bitfield you eventually stored into the bitStream (in reverse order) -Note : maximum allowed nbBits is 25, for 32-bits compatibility - size_t bitField = BIT_readBits(&DStream, nbBits); - -All above operations only read from local register (which size depends on size_t). -Refueling the register from memory is manually performed by the reload method. - endSignal = FSE_reloadDStream(&DStream); - -BIT_reloadDStream() result tells if there is still some more data to read from DStream. -BIT_DStream_unfinished : there is still some data left into the DStream. -BIT_DStream_endOfBuffer : Dstream reached end of buffer. Its container may no longer be completely filled. -BIT_DStream_completed : Dstream reached its exact end, corresponding in general to decompression completed. -BIT_DStream_tooFar : Dstream went too far. Decompression result is corrupted. - -When reaching end of buffer (BIT_DStream_endOfBuffer), progress slowly, notably if you decode multiple symbols per loop, -to properly detect the exact end of stream. -After each decoded symbol, check if DStream is fully consumed using this simple test : - BIT_reloadDStream(&DStream) >= BIT_DStream_completed - -When it's done, verify decompression is fully completed, by checking both DStream and the relevant states. -Checking if DStream has reached its end is performed by : - BIT_endOfDStream(&DStream); -Check also the states. There might be some symbols left there, if some high probability ones (>50%) are possible. - FSE_endOfDState(&DState); -*/ - /* ***************************************** * FSE unsafe API diff --git a/lib/legacy/zstd_v05.c b/lib/legacy/zstd_v05.c index 23188f504..a5e1b1ffc 100644 --- a/lib/legacy/zstd_v05.c +++ b/lib/legacy/zstd_v05.c @@ -736,18 +736,6 @@ MEM_STATIC BITv05_DStream_status BITv05_reloadDStream(BITv05_DStream_t* bitD); MEM_STATIC unsigned BITv05_endOfDStream(const BITv05_DStream_t* bitD); -/*! -* Start by invoking BITv05_initDStream(). -* A chunk of the bitStream is then stored into a local register. -* Local register size is 64-bits on 64-bits systems, 32-bits on 32-bits systems (size_t). -* You can then retrieve bitFields stored into the local register, **in reverse order**. -* Local register is explicitly reloaded from memory by the BITv05_reloadDStream() method. -* A reload guarantee a minimum of ((8*sizeof(size_t))-7) bits when its result is BITv05_DStream_unfinished. -* Otherwise, it can be less than that, so proceed accordingly. -* Checking if DStream has reached its end can be performed with BITv05_endOfDStream() -*/ - - /*-**************************************** * unsafe API ******************************************/ @@ -759,7 +747,7 @@ MEM_STATIC size_t BITv05_readBitsFast(BITv05_DStream_t* bitD, unsigned nbBits); /*-************************************************************** * Helper functions ****************************************************************/ -MEM_STATIC unsigned BITv05_highbit32 (register U32 val) +MEM_STATIC unsigned BITv05_highbit32 (U32 val) { # if defined(_MSC_VER) /* Visual */ unsigned long r=0; @@ -829,13 +817,6 @@ MEM_STATIC size_t BITv05_initDStream(BITv05_DStream_t* bitD, const void* srcBuff return srcSize; } -/*!BITv05_lookBits - * Provides next n bits from local register - * local register is not modified (bits are still present for next read/look) - * On 32-bits, maxNbBits==25 - * On 64-bits, maxNbBits==57 - * @return : value extracted - */ MEM_STATIC size_t BITv05_lookBits(BITv05_DStream_t* bitD, U32 nbBits) { const U32 bitMask = sizeof(bitD->bitContainer)*8 - 1; @@ -855,11 +836,6 @@ MEM_STATIC void BITv05_skipBits(BITv05_DStream_t* bitD, U32 nbBits) bitD->bitsConsumed += nbBits; } -/*!BITv05_readBits - * Read next n bits from local register. - * pay attention to not read more than nbBits contained into local register. - * @return : extracted value. - */ MEM_STATIC size_t BITv05_readBits(BITv05_DStream_t* bitD, U32 nbBits) { size_t value = BITv05_lookBits(bitD, nbBits); @@ -995,54 +971,6 @@ static unsigned char FSEv05_decodeSymbol(FSEv05_DState_t* DStatePtr, BITv05_DStr static unsigned FSEv05_endOfDState(const FSEv05_DState_t* DStatePtr); -/*! -Let's now decompose FSEv05_decompress_usingDTable() into its unitary components. -You will decode FSEv05-encoded symbols from the bitStream, -and also any other bitFields you put in, **in reverse order**. - -You will need a few variables to track your bitStream. They are : - -BITv05_DStream_t DStream; // Stream context -FSEv05_DState_t DState; // State context. Multiple ones are possible -FSEv05_DTable* DTablePtr; // Decoding table, provided by FSEv05_buildDTable() - -The first thing to do is to init the bitStream. - errorCode = BITv05_initDStream(&DStream, srcBuffer, srcSize); - -You should then retrieve your initial state(s) -(in reverse flushing order if you have several ones) : - errorCode = FSEv05_initDState(&DState, &DStream, DTablePtr); - -You can then decode your data, symbol after symbol. -For information the maximum number of bits read by FSEv05_decodeSymbol() is 'tableLog'. -Keep in mind that symbols are decoded in reverse order, like a LIFO stack (last in, first out). - unsigned char symbol = FSEv05_decodeSymbol(&DState, &DStream); - -You can retrieve any bitfield you eventually stored into the bitStream (in reverse order) -Note : maximum allowed nbBits is 25, for 32-bits compatibility - size_t bitField = BITv05_readBits(&DStream, nbBits); - -All above operations only read from local register (which size depends on size_t). -Refueling the register from memory is manually performed by the reload method. - endSignal = FSEv05_reloadDStream(&DStream); - -BITv05_reloadDStream() result tells if there is still some more data to read from DStream. -BITv05_DStream_unfinished : there is still some data left into the DStream. -BITv05_DStream_endOfBuffer : Dstream reached end of buffer. Its container may no longer be completely filled. -BITv05_DStream_completed : Dstream reached its exact end, corresponding in general to decompression completed. -BITv05_DStream_tooFar : Dstream went too far. Decompression result is corrupted. - -When reaching end of buffer (BITv05_DStream_endOfBuffer), progress slowly, notably if you decode multiple symbols per loop, -to properly detect the exact end of stream. -After each decoded symbol, check if DStream is fully consumed using this simple test : - BITv05_reloadDStream(&DStream) >= BITv05_DStream_completed - -When it's done, verify decompression is fully completed, by checking both DStream and the relevant states. -Checking if DStream has reached its end is performed by : - BITv05_endOfDStream(&DStream); -Check also the states. There might be some symbols left there, if some high probability ones (>50%) are possible. - FSEv05_endOfDState(&DState); -*/ /* ***************************************** diff --git a/lib/legacy/zstd_v06.c b/lib/legacy/zstd_v06.c index 62683f994..19116f108 100644 --- a/lib/legacy/zstd_v06.c +++ b/lib/legacy/zstd_v06.c @@ -839,16 +839,6 @@ MEM_STATIC BITv06_DStream_status BITv06_reloadDStream(BITv06_DStream_t* bitD); MEM_STATIC unsigned BITv06_endOfDStream(const BITv06_DStream_t* bitD); -/* Start by invoking BITv06_initDStream(). -* A chunk of the bitStream is then stored into a local register. -* Local register size is 64-bits on 64-bits systems, 32-bits on 32-bits systems (size_t). -* You can then retrieve bitFields stored into the local register, **in reverse order**. -* Local register is explicitly reloaded from memory by the BITv06_reloadDStream() method. -* A reload guarantee a minimum of ((8*sizeof(bitD->bitContainer))-7) bits when its result is BITv06_DStream_unfinished. -* Otherwise, it can be less than that, so proceed accordingly. -* Checking if DStream has reached its end can be performed with BITv06_endOfDStream(). -*/ - /*-**************************************** * unsafe API @@ -861,7 +851,7 @@ MEM_STATIC size_t BITv06_readBitsFast(BITv06_DStream_t* bitD, unsigned nbBits); /*-************************************************************** * Internal functions ****************************************************************/ -MEM_STATIC unsigned BITv06_highbit32 (register U32 val) +MEM_STATIC unsigned BITv06_highbit32 ( U32 val) { # if defined(_MSC_VER) /* Visual */ unsigned long r=0; @@ -929,13 +919,6 @@ MEM_STATIC size_t BITv06_initDStream(BITv06_DStream_t* bitD, const void* srcBuff } -/*! BITv06_lookBits() : - * Provides next n bits from local register. - * local register is not modified. - * On 32-bits, maxNbBits==24. - * On 64-bits, maxNbBits==56. - * @return : value extracted - */ MEM_STATIC size_t BITv06_lookBits(const BITv06_DStream_t* bitD, U32 nbBits) { U32 const bitMask = sizeof(bitD->bitContainer)*8 - 1; @@ -955,11 +938,6 @@ MEM_STATIC void BITv06_skipBits(BITv06_DStream_t* bitD, U32 nbBits) bitD->bitsConsumed += nbBits; } -/*! BITv06_readBits() : - * Read (consume) next n bits from local register and update. - * Pay attention to not read more than nbBits contained into local register. - * @return : extracted value. - */ MEM_STATIC size_t BITv06_readBits(BITv06_DStream_t* bitD, U32 nbBits) { size_t const value = BITv06_lookBits(bitD, nbBits); @@ -976,11 +954,6 @@ MEM_STATIC size_t BITv06_readBitsFast(BITv06_DStream_t* bitD, U32 nbBits) return value; } -/*! BITv06_reloadDStream() : -* Refill `BITv06_DStream_t` from src buffer previously defined (see BITv06_initDStream() ). -* This function is safe, it guarantees it will not read beyond src buffer. -* @return : status of `BITv06_DStream_t` internal register. - if status == unfinished, internal register is filled with >= (sizeof(bitD->bitContainer)*8 - 7) bits */ MEM_STATIC BITv06_DStream_status BITv06_reloadDStream(BITv06_DStream_t* bitD) { if (bitD->bitsConsumed > (sizeof(bitD->bitContainer)*8)) /* should never happen */ @@ -1103,55 +1076,6 @@ static void FSEv06_initDState(FSEv06_DState_t* DStatePtr, BITv06_DStream_t* static unsigned char FSEv06_decodeSymbol(FSEv06_DState_t* DStatePtr, BITv06_DStream_t* bitD); -/*! -Let's now decompose FSEv06_decompress_usingDTable() into its unitary components. -You will decode FSE-encoded symbols from the bitStream, -and also any other bitFields you put in, **in reverse order**. - -You will need a few variables to track your bitStream. They are : - -BITv06_DStream_t DStream; // Stream context -FSEv06_DState_t DState; // State context. Multiple ones are possible -FSEv06_DTable* DTablePtr; // Decoding table, provided by FSEv06_buildDTable() - -The first thing to do is to init the bitStream. - errorCode = BITv06_initDStream(&DStream, srcBuffer, srcSize); - -You should then retrieve your initial state(s) -(in reverse flushing order if you have several ones) : - errorCode = FSEv06_initDState(&DState, &DStream, DTablePtr); - -You can then decode your data, symbol after symbol. -For information the maximum number of bits read by FSEv06_decodeSymbol() is 'tableLog'. -Keep in mind that symbols are decoded in reverse order, like a LIFO stack (last in, first out). - unsigned char symbol = FSEv06_decodeSymbol(&DState, &DStream); - -You can retrieve any bitfield you eventually stored into the bitStream (in reverse order) -Note : maximum allowed nbBits is 25, for 32-bits compatibility - size_t bitField = BITv06_readBits(&DStream, nbBits); - -All above operations only read from local register (which size depends on size_t). -Refueling the register from memory is manually performed by the reload method. - endSignal = FSEv06_reloadDStream(&DStream); - -BITv06_reloadDStream() result tells if there is still some more data to read from DStream. -BITv06_DStream_unfinished : there is still some data left into the DStream. -BITv06_DStream_endOfBuffer : Dstream reached end of buffer. Its container may no longer be completely filled. -BITv06_DStream_completed : Dstream reached its exact end, corresponding in general to decompression completed. -BITv06_DStream_tooFar : Dstream went too far. Decompression result is corrupted. - -When reaching end of buffer (BITv06_DStream_endOfBuffer), progress slowly, notably if you decode multiple symbols per loop, -to properly detect the exact end of stream. -After each decoded symbol, check if DStream is fully consumed using this simple test : - BITv06_reloadDStream(&DStream) >= BITv06_DStream_completed - -When it's done, verify decompression is fully completed, by checking both DStream and the relevant states. -Checking if DStream has reached its end is performed by : - BITv06_endOfDStream(&DStream); -Check also the states. There might be some symbols left there, if some high probability ones (>50%) are possible. - FSEv06_endOfDState(&DState); -*/ - /* ***************************************** * FSE unsafe API diff --git a/lib/legacy/zstd_v07.c b/lib/legacy/zstd_v07.c index aad9b1f65..114ce005b 100644 --- a/lib/legacy/zstd_v07.c +++ b/lib/legacy/zstd_v07.c @@ -511,16 +511,6 @@ MEM_STATIC BITv07_DStream_status BITv07_reloadDStream(BITv07_DStream_t* bitD); MEM_STATIC unsigned BITv07_endOfDStream(const BITv07_DStream_t* bitD); -/* Start by invoking BITv07_initDStream(). -* A chunk of the bitStream is then stored into a local register. -* Local register size is 64-bits on 64-bits systems, 32-bits on 32-bits systems (size_t). -* You can then retrieve bitFields stored into the local register, **in reverse order**. -* Local register is explicitly reloaded from memory by the BITv07_reloadDStream() method. -* A reload guarantee a minimum of ((8*sizeof(bitD->bitContainer))-7) bits when its result is BITv07_DStream_unfinished. -* Otherwise, it can be less than that, so proceed accordingly. -* Checking if DStream has reached its end can be performed with BITv07_endOfDStream(). -*/ - /*-**************************************** * unsafe API @@ -533,7 +523,7 @@ MEM_STATIC size_t BITv07_readBitsFast(BITv07_DStream_t* bitD, unsigned nbBits); /*-************************************************************** * Internal functions ****************************************************************/ -MEM_STATIC unsigned BITv07_highbit32 (register U32 val) +MEM_STATIC unsigned BITv07_highbit32 (U32 val) { # if defined(_MSC_VER) /* Visual */ unsigned long r=0; @@ -599,13 +589,6 @@ MEM_STATIC size_t BITv07_initDStream(BITv07_DStream_t* bitD, const void* srcBuff } -/*! BITv07_lookBits() : - * Provides next n bits from local register. - * local register is not modified. - * On 32-bits, maxNbBits==24. - * On 64-bits, maxNbBits==56. - * @return : value extracted - */ MEM_STATIC size_t BITv07_lookBits(const BITv07_DStream_t* bitD, U32 nbBits) { U32 const bitMask = sizeof(bitD->bitContainer)*8 - 1; @@ -625,11 +608,6 @@ MEM_STATIC void BITv07_skipBits(BITv07_DStream_t* bitD, U32 nbBits) bitD->bitsConsumed += nbBits; } -/*! BITv07_readBits() : - * Read (consume) next n bits from local register and update. - * Pay attention to not read more than nbBits contained into local register. - * @return : extracted value. - */ MEM_STATIC size_t BITv07_readBits(BITv07_DStream_t* bitD, U32 nbBits) { size_t const value = BITv07_lookBits(bitD, nbBits); @@ -646,11 +624,6 @@ MEM_STATIC size_t BITv07_readBitsFast(BITv07_DStream_t* bitD, U32 nbBits) return value; } -/*! BITv07_reloadDStream() : -* Refill `BITv07_DStream_t` from src buffer previously defined (see BITv07_initDStream() ). -* This function is safe, it guarantees it will not read beyond src buffer. -* @return : status of `BITv07_DStream_t` internal register. - if status == unfinished, internal register is filled with >= (sizeof(bitD->bitContainer)*8 - 7) bits */ MEM_STATIC BITv07_DStream_status BITv07_reloadDStream(BITv07_DStream_t* bitD) { if (bitD->bitsConsumed > (sizeof(bitD->bitContainer)*8)) /* should not happen => corruption detected */ @@ -874,55 +847,6 @@ static void FSEv07_initDState(FSEv07_DState_t* DStatePtr, BITv07_DStream_t* static unsigned char FSEv07_decodeSymbol(FSEv07_DState_t* DStatePtr, BITv07_DStream_t* bitD); -/**< -Let's now decompose FSEv07_decompress_usingDTable() into its unitary components. -You will decode FSE-encoded symbols from the bitStream, -and also any other bitFields you put in, **in reverse order**. - -You will need a few variables to track your bitStream. They are : - -BITv07_DStream_t DStream; // Stream context -FSEv07_DState_t DState; // State context. Multiple ones are possible -FSEv07_DTable* DTablePtr; // Decoding table, provided by FSEv07_buildDTable() - -The first thing to do is to init the bitStream. - errorCode = BITv07_initDStream(&DStream, srcBuffer, srcSize); - -You should then retrieve your initial state(s) -(in reverse flushing order if you have several ones) : - errorCode = FSEv07_initDState(&DState, &DStream, DTablePtr); - -You can then decode your data, symbol after symbol. -For information the maximum number of bits read by FSEv07_decodeSymbol() is 'tableLog'. -Keep in mind that symbols are decoded in reverse order, like a LIFO stack (last in, first out). - unsigned char symbol = FSEv07_decodeSymbol(&DState, &DStream); - -You can retrieve any bitfield you eventually stored into the bitStream (in reverse order) -Note : maximum allowed nbBits is 25, for 32-bits compatibility - size_t bitField = BITv07_readBits(&DStream, nbBits); - -All above operations only read from local register (which size depends on size_t). -Refueling the register from memory is manually performed by the reload method. - endSignal = FSEv07_reloadDStream(&DStream); - -BITv07_reloadDStream() result tells if there is still some more data to read from DStream. -BITv07_DStream_unfinished : there is still some data left into the DStream. -BITv07_DStream_endOfBuffer : Dstream reached end of buffer. Its container may no longer be completely filled. -BITv07_DStream_completed : Dstream reached its exact end, corresponding in general to decompression completed. -BITv07_DStream_tooFar : Dstream went too far. Decompression result is corrupted. - -When reaching end of buffer (BITv07_DStream_endOfBuffer), progress slowly, notably if you decode multiple symbols per loop, -to properly detect the exact end of stream. -After each decoded symbol, check if DStream is fully consumed using this simple test : - BITv07_reloadDStream(&DStream) >= BITv07_DStream_completed - -When it's done, verify decompression is fully completed, by checking both DStream and the relevant states. -Checking if DStream has reached its end is performed by : - BITv07_endOfDStream(&DStream); -Check also the states. There might be some symbols left there, if some high probability ones (>50%) are possible. - FSEv07_endOfDState(&DState); -*/ - /* ***************************************** * FSE unsafe API From 3aa2b27a890e9aa4c64b9e52632a6ef4c1418041 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 7 Dec 2017 02:52:50 -0500 Subject: [PATCH 113/145] fix #942 : streaming interface does not compress after ZSTD_initCStream() While the final result is still, technically, a frame, the resulting frame expands initial data instead of compressing it. This is because the streaming API creates a tiny 1-byte buffer for input, because it believes input is empty (0-bytes), because in the past, 0 used to mean "unknown" instead. This patch fixes the issue. Todo : add a test which traps the issue. --- lib/compress/zstd_compress.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 8d493852f..69b0d6402 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2466,6 +2466,7 @@ size_t ZSTD_compress_usingCDict(ZSTD_CCtx* cctx, ZSTD_CStream* ZSTD_createCStream(void) { + DEBUGLOG(3, "ZSTD_createCStream"); return ZSTD_createCStream_advanced(ZSTD_defaultCMem); } @@ -2633,7 +2634,8 @@ size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigne size_t ZSTD_initCStream(ZSTD_CStream* zcs, int compressionLevel) { - return ZSTD_initCStream_srcSize(zcs, compressionLevel, 0); + DEBUGLOG(4, "ZSTD_initCStream"); + return ZSTD_initCStream_srcSize(zcs, compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN); } /*====== Compression ======*/ From 21f7672d43549e1f87b879fb8a14ac1a4fabc266 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 7 Dec 2017 03:06:01 -0500 Subject: [PATCH 114/145] zstreamtest : add test which detects when ZSTD_initCStream() inflate data as in #942, when ZSTD_initCStream() believes data is empty, instead of unknown. --- tests/zstreamtest.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index 920e988b0..e08a96d8f 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -245,13 +245,28 @@ static int basicUnitTests(U32 seed, double compressibility) } dictID = ZDICT_getDictID(dictionary.start, dictionary.filled); + /* Basic compression test */ + DISPLAYLEVEL(3, "test%3i : compress %u bytes : ", testNb++, COMPRESSIBLE_NOISE_LENGTH); + CHECK_Z( ZSTD_initCStream(zc, 1 /* cLevel */) ); + outBuff.dst = (char*)(compressedBuffer); + outBuff.size = compressedBufferSize; + outBuff.pos = 0; + inBuff.src = CNBuffer; + inBuff.size = CNBufferSize; + inBuff.pos = 0; + CHECK_Z( ZSTD_compressStream(zc, &outBuff, &inBuff) ); + if (inBuff.pos != inBuff.size) goto _output_error; /* entire input should be consumed */ + { size_t const r = ZSTD_endStream(zc, &outBuff); + if (r != 0) goto _output_error; } /* error, or some data not flushed */ + DISPLAYLEVEL(3, "OK (%u bytes)\n", (U32)outBuff.pos); + /* generate skippable frame */ MEM_writeLE32(compressedBuffer, ZSTD_MAGIC_SKIPPABLE_START); MEM_writeLE32(((char*)compressedBuffer)+4, (U32)skippableFrameSize); cSize = skippableFrameSize + 8; - /* Basic compression test */ - DISPLAYLEVEL(3, "test%3i : compress %u bytes : ", testNb++, COMPRESSIBLE_NOISE_LENGTH); + /* Basic compression test using dict */ + DISPLAYLEVEL(3, "test%3i : skipframe + compress %u bytes : ", testNb++, COMPRESSIBLE_NOISE_LENGTH); CHECK_Z( ZSTD_initCStream_usingDict(zc, CNBuffer, dictSize, 1 /* cLevel */) ); outBuff.dst = (char*)(compressedBuffer)+cSize; assert(compressedBufferSize > cSize); From c029ee1f0bc464754c899deb2f5f0a2f16f895e4 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 7 Dec 2017 17:13:10 -0500 Subject: [PATCH 115/145] ZSTD_initCStream_srcSize() considers "0" to mean "unknown" to not break existing programs relying on this behavior. Might be changed to mean "empty" in the future. --- lib/compress/zstd_compress.c | 7 +++---- lib/zstd.h | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 69b0d6402..20b095998 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2623,12 +2623,11 @@ size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t di return ZSTD_initCStream_internal(zcs, dict, dictSize, NULL, cctxParams, ZSTD_CONTENTSIZE_UNKNOWN); } -size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize) +size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pss) { - ZSTD_CCtx_params cctxParams; + U64 const pledgedSrcSize = (pss==0) ? ZSTD_CONTENTSIZE_UNKNOWN : pss; /* temporary : 0 interpreted as "unknown" during transition period. Users willing to specify "unknown" **must** use ZSTD_CONTENTSIZE_UNKNOWN. `0` will be interpreted as "empty" in the future */ ZSTD_parameters const params = ZSTD_getParams(compressionLevel, pledgedSrcSize, 0); - cctxParams = ZSTD_assignParamsToCCtxParams(zcs->requestedParams, params); - cctxParams.fParams.contentSizeFlag = (pledgedSrcSize>0); + ZSTD_CCtx_params const cctxParams = ZSTD_assignParamsToCCtxParams(zcs->requestedParams, params); return ZSTD_initCStream_internal(zcs, NULL, 0, NULL, cctxParams, pledgedSrcSize); } diff --git a/lib/zstd.h b/lib/zstd.h index fe37cec8b..76d75e2a0 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -734,7 +734,7 @@ ZSTDLIB_API unsigned ZSTD_getDictID_fromFrame(const void* src, size_t srcSize); /*===== Advanced Streaming compression functions =====*/ ZSTDLIB_API ZSTD_CStream* ZSTD_createCStream_advanced(ZSTD_customMem customMem); ZSTDLIB_API ZSTD_CStream* ZSTD_initStaticCStream(void* workspace, size_t workspaceSize); /**< same as ZSTD_initStaticCCtx() */ -ZSTDLIB_API size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize); /**< pledgedSrcSize must be correct. If it is not known at init time, use ZSTD_CONTENTSIZE_UNKNOWN. Note that, for compatibility with older programs, a size of 0 is interepreted as "unknown". But it may change in some future version to mean "empty". */ +ZSTDLIB_API size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize); /**< pledgedSrcSize must be correct. If it is not known at init time, use ZSTD_CONTENTSIZE_UNKNOWN. Note that, for compatibility with older programs, "0" also disables frame content size field. It may be enabled in the future. */ ZSTDLIB_API size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel); /**< creates of an internal CDict (incompatible with static CCtx), except if dict == NULL or dictSize < 8, in which case no dict is used. Note: dict is loaded with ZSTD_dm_auto (treated as a full zstd dictionary if it begins with ZSTD_MAGIC_DICTIONARY, else as raw content) and ZSTD_dlm_byCopy.*/ ZSTDLIB_API size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, const void* dict, size_t dictSize, ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize : if srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN. dict is loaded with ZSTD_dm_auto and ZSTD_dlm_byCopy. */ From dfc697e967ad736060dc9ec695a7db0d9a805fe3 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 8 Dec 2017 12:16:49 -0500 Subject: [PATCH 116/145] comment clarification --- lib/compress/zstd_compress.c | 8 ++++---- lib/zstd.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 20b095998..3f39875f5 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2599,7 +2599,8 @@ size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict) } /* ZSTD_initCStream_advanced() : - * pledgedSrcSize : if srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN. + * pledgedSrcSize must be correct. + * if srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN. * dict is loaded with default parameters ZSTD_dm_auto and ZSTD_dlm_byCopy. */ size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, const void* dict, size_t dictSize, @@ -2610,9 +2611,8 @@ size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, DEBUGLOG(4, "ZSTD_initCStream_advanced: pledgedSrcSize=%u, flag=%u", (U32)pledgedSrcSize, params.fParams.contentSizeFlag); CHECK_F( ZSTD_checkCParams(params.cParams) ); - if ((pledgedSrcSize==0) && (params.fParams.contentSizeFlag==0)) - pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN; - return ZSTD_initCStream_internal(zcs, dict, dictSize, NULL, cctxParams, pledgedSrcSize); + if ((pledgedSrcSize==0) && (params.fParams.contentSizeFlag==0)) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN; /* for compatibility with older programs relying on this behavior. Users should now specify ZSTD_CONTENTSIZE_UNKNOWN. This line will be removed in the future. */ + return ZSTD_initCStream_internal(zcs, dict, dictSize, NULL /*cdict*/, cctxParams, pledgedSrcSize); } size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel) diff --git a/lib/zstd.h b/lib/zstd.h index 76d75e2a0..15c467535 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -737,9 +737,9 @@ ZSTDLIB_API ZSTD_CStream* ZSTD_initStaticCStream(void* workspace, size_t workspa ZSTDLIB_API size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize); /**< pledgedSrcSize must be correct. If it is not known at init time, use ZSTD_CONTENTSIZE_UNKNOWN. Note that, for compatibility with older programs, "0" also disables frame content size field. It may be enabled in the future. */ ZSTDLIB_API size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel); /**< creates of an internal CDict (incompatible with static CCtx), except if dict == NULL or dictSize < 8, in which case no dict is used. Note: dict is loaded with ZSTD_dm_auto (treated as a full zstd dictionary if it begins with ZSTD_MAGIC_DICTIONARY, else as raw content) and ZSTD_dlm_byCopy.*/ ZSTDLIB_API size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, const void* dict, size_t dictSize, - ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize : if srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN. dict is loaded with ZSTD_dm_auto and ZSTD_dlm_byCopy. */ + ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize must be correct. If srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN. dict is loaded with ZSTD_dm_auto and ZSTD_dlm_byCopy. */ ZSTDLIB_API size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict); /**< note : cdict will just be referenced, and must outlive compression session */ -ZSTDLIB_API size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs, const ZSTD_CDict* cdict, ZSTD_frameParameters fParams, unsigned long long pledgedSrcSize); /**< same as ZSTD_initCStream_usingCDict(), with control over frame parameters */ +ZSTDLIB_API size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs, const ZSTD_CDict* cdict, ZSTD_frameParameters fParams, unsigned long long pledgedSrcSize); /**< same as ZSTD_initCStream_usingCDict(), with control over frame parameters. pledgedSrcSize must be correct. If srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN. */ /*! ZSTD_resetCStream() : * start a new compression job, using same parameters from previous job. From 5098d1fbe2a0b3d0ddb66c0880e3004feac65f3e Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 12 Dec 2017 12:48:31 -0800 Subject: [PATCH 117/145] Created a test case which reliably reproduces bug #944 in zstreamtest. --- lib/compress/zstd_compress.c | 11 ++--- lib/compress/zstdmt_compress.c | 30 ++++++------ lib/decompress/zstd_decompress.c | 19 +++++--- lib/zstd.h | 19 +++++--- tests/zstreamtest.c | 81 +++++++++++++++++++++++++++----- 5 files changed, 114 insertions(+), 46 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 3f39875f5..107291888 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -477,7 +477,7 @@ size_t ZSTD_CCtx_setParametersUsingCCtxParams( ZSTDLIB_API size_t ZSTD_CCtx_setPledgedSrcSize(ZSTD_CCtx* cctx, unsigned long long pledgedSrcSize) { - DEBUGLOG(4, " setting pledgedSrcSize to %u", (U32)pledgedSrcSize); + DEBUGLOG(4, "ZSTD_CCtx_setPledgedSrcSize to %u bytes", (U32)pledgedSrcSize); if (cctx->streamStage != zcss_init) return ERROR(stage_wrong); cctx->pledgedSrcSizePlusOne = pledgedSrcSize+1; return 0; @@ -489,7 +489,7 @@ size_t ZSTD_CCtx_loadDictionary_advanced( { if (cctx->streamStage != zcss_init) return ERROR(stage_wrong); if (cctx->staticSize) return ERROR(memory_allocation); /* no malloc for static CCtx */ - DEBUGLOG(4, "load dictionary of size %u", (U32)dictSize); + DEBUGLOG(4, "ZSTD_CCtx_loadDictionary_advanced (size: %u)", (U32)dictSize); ZSTD_freeCDict(cctx->cdictLocal); /* in case one already exists */ if (dict==NULL || dictSize==0) { /* no dictionary mode */ cctx->cdictLocal = NULL; @@ -2273,7 +2273,7 @@ static size_t ZSTD_initCDict_internal( ZSTD_dictMode_e dictMode, ZSTD_compressionParameters cParams) { - DEBUGLOG(4, "ZSTD_initCDict_internal, mode %u", (U32)dictMode); + DEBUGLOG(3, "ZSTD_initCDict_internal, mode %u", (U32)dictMode); if ((dictLoadMethod == ZSTD_dlm_byRef) || (!dictBuffer) || (!dictSize)) { cdict->dictBuffer = NULL; cdict->dictContent = dictBuffer; @@ -2303,7 +2303,7 @@ ZSTD_CDict* ZSTD_createCDict_advanced(const void* dictBuffer, size_t dictSize, ZSTD_dictMode_e dictMode, ZSTD_compressionParameters cParams, ZSTD_customMem customMem) { - DEBUGLOG(4, "ZSTD_createCDict_advanced, mode %u", (U32)dictMode); + DEBUGLOG(3, "ZSTD_createCDict_advanced, mode %u", (U32)dictMode); if (!customMem.customAlloc ^ !customMem.customFree) return NULL; { ZSTD_CDict* const cdict = (ZSTD_CDict*)ZSTD_malloc(sizeof(ZSTD_CDict), customMem); @@ -2809,7 +2809,7 @@ size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, ZSTD_inBuffer* input, ZSTD_EndDirective endOp) { - DEBUGLOG(5, "ZSTD_compress_generic"); + DEBUGLOG(5, "ZSTD_compress_generic, endOp=%u ", (U32)endOp); /* check conditions */ if (output->pos > output->size) return ERROR(GENERIC); if (input->pos > input->size) return ERROR(GENERIC); @@ -2856,7 +2856,6 @@ size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, #ifdef ZSTD_MULTITHREAD if (cctx->appliedParams.nbThreads > 1) { size_t const flushMin = ZSTDMT_compressStream_generic(cctx->mtctx, output, input, endOp); - DEBUGLOG(5, "ZSTDMT_compressStream_generic result : %u", (U32)flushMin); if ( ZSTD_isError(flushMin) || (endOp == ZSTD_e_end && flushMin == 0) ) { /* compression completed */ ZSTD_startNewCompression(cctx); diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 6b6466fe5..8347cf29d 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -310,7 +310,7 @@ static void ZSTDMT_releaseCCtx(ZSTDMT_CCtxPool* pool, ZSTD_CCtx* cctx) typedef struct { buffer_t src; const void* srcStart; - size_t dictSize; + size_t prefixSize; size_t srcSize; buffer_t dstBuff; size_t cSize; @@ -333,10 +333,10 @@ void ZSTDMT_compressChunk(void* jobDescription) { ZSTDMT_jobDescription* const job = (ZSTDMT_jobDescription*)jobDescription; ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(job->cctxPool); - const void* const src = (const char*)job->srcStart + job->dictSize; + const void* const src = (const char*)job->srcStart + job->prefixSize; buffer_t dstBuff = job->dstBuff; - DEBUGLOG(5, "ZSTDMT_compressChunk: job (first:%u) (last:%u) : dictSize %u, srcSize %u", - job->firstChunk, job->lastChunk, (U32)job->dictSize, (U32)job->srcSize); + DEBUGLOG(5, "ZSTDMT_compressChunk: job (first:%u) (last:%u) : prefixSize %u, srcSize %u ", + job->firstChunk, job->lastChunk, (U32)job->prefixSize, (U32)job->srcSize); if (cctx==NULL) { job->cSize = ERROR(memory_allocation); @@ -356,20 +356,21 @@ void ZSTDMT_compressChunk(void* jobDescription) if (job->cdict) { size_t const initError = ZSTD_compressBegin_usingCDict_advanced(cctx, job->cdict, job->params.fParams, job->fullFrameSize); DEBUGLOG(4, "ZSTDMT_compressChunk: init using CDict"); - assert(job->firstChunk); /* should only happen for first segment */ + assert(job->firstChunk); /* only allowed for first job */ if (ZSTD_isError(initError)) { job->cSize = initError; goto _endJob; } } else { /* srcStart points at reloaded section */ ZSTD_CCtx_params jobParams = job->params; /* do not modify job->params ! copy it, modify the copy */ size_t const forceWindowError = ZSTD_CCtxParam_setParameter(&jobParams, ZSTD_p_forceMaxWindow, !job->firstChunk); U64 const pledgedSrcSize = job->firstChunk ? job->fullFrameSize : ZSTD_CONTENTSIZE_UNKNOWN; /* load dictionary in "content-only" mode (no header analysis) */ - size_t const initError = ZSTD_compressBegin_advanced_internal(cctx, job->srcStart, job->dictSize, ZSTD_dm_rawContent, jobParams, pledgedSrcSize); + size_t const initError = ZSTD_compressBegin_advanced_internal(cctx, job->srcStart, job->prefixSize, ZSTD_dm_rawContent, jobParams, pledgedSrcSize); + DEBUGLOG(5, "ZSTD_compressBegin_advanced_internal called with windowLog = %u ", jobParams.cParams.windowLog); if (ZSTD_isError(initError) || ZSTD_isError(forceWindowError)) { job->cSize = initError; goto _endJob; } } - if (!job->firstChunk) { /* flush and overwrite frame header when it's not first segment */ + if (!job->firstChunk) { /* flush and overwrite frame header when it's not first job */ size_t const hSize = ZSTD_compressContinue(cctx, dstBuff.start, dstBuff.size, src, 0); if (ZSTD_isError(hSize)) { job->cSize = hSize; goto _endJob; } ZSTD_invalidateRepCodes(cctx); @@ -380,9 +381,9 @@ void ZSTDMT_compressChunk(void* jobDescription) job->cSize = (job->lastChunk) ? ZSTD_compressEnd (cctx, dstBuff.start, dstBuff.size, src, job->srcSize) : ZSTD_compressContinue(cctx, dstBuff.start, dstBuff.size, src, job->srcSize); - DEBUGLOG(5, "compressed %u bytes into %u bytes (first:%u) (last:%u)", + DEBUGLOG(5, "compressed %u bytes into %u bytes (first:%u) (last:%u) ", (unsigned)job->srcSize, (unsigned)job->cSize, job->firstChunk, job->lastChunk); - DEBUGLOG(5, "dstBuff.size : %u ; => %s", (U32)dstBuff.size, ZSTD_getErrorName(job->cSize)); + DEBUGLOG(5, "dstBuff.size : %u ; => %s ", (U32)dstBuff.size, ZSTD_getErrorName(job->cSize)); _endJob: ZSTDMT_releaseCCtx(job->cctxPool, cctx); @@ -618,7 +619,7 @@ static size_t ZSTDMT_compress_advanced_internal( size_t const overlapSize = (overlapRLog>=9) ? 0 : (size_t)1 << (params.cParams.windowLog - overlapRLog); unsigned nbChunks = computeNbChunks(srcSize, params.cParams.windowLog, params.nbThreads); size_t const proposedChunkSize = (srcSize + (nbChunks-1)) / nbChunks; - size_t const avgChunkSize = ((proposedChunkSize & 0x1FFFF) < 0x7FFF) ? proposedChunkSize + 0xFFFF : proposedChunkSize; /* avoid too small last block */ + size_t const avgChunkSize = (((proposedChunkSize-1) & 0x1FFFF) < 0x7FFF) ? proposedChunkSize + 0xFFFF : proposedChunkSize; /* avoid too small last block */ const char* const srcStart = (const char*)src; size_t remainingSrcSize = srcSize; unsigned const compressWithinDst = (dstCapacity >= ZSTD_compressBound(srcSize)) ? nbChunks : (unsigned)(dstCapacity / ZSTD_compressBound(avgChunkSize)); /* presumes avgChunkSize >= 256 KB, which should be the case */ @@ -628,7 +629,8 @@ static size_t ZSTDMT_compress_advanced_internal( assert(mtctx->cctxPool->totalCCtx == params.nbThreads); DEBUGLOG(4, "ZSTDMT_compress_advanced_internal"); - DEBUGLOG(4, "nbChunks : %2u (chunkSize : %u bytes) ", nbChunks, (U32)avgChunkSize); + DEBUGLOG(4, "nbChunks : %2u (raw chunkSize : %u bytes; fixed chunkSize: %u) ", + nbChunks, (U32)proposedChunkSize, (U32)avgChunkSize); if (nbChunks==1) { /* fallback to single-thread mode */ ZSTD_CCtx* const cctx = mtctx->cctxPool->cctx[0]; if (cdict) return ZSTD_compress_usingCDict_advanced(cctx, dst, dstCapacity, src, srcSize, cdict, jobParams.fParams); @@ -657,7 +659,7 @@ static size_t ZSTDMT_compress_advanced_internal( mtctx->jobs[u].src = g_nullBuffer; mtctx->jobs[u].srcStart = srcStart + frameStartPos - dictSize; - mtctx->jobs[u].dictSize = dictSize; + mtctx->jobs[u].prefixSize = dictSize; mtctx->jobs[u].srcSize = chunkSize; mtctx->jobs[u].cdict = (u==0) ? cdict : NULL; mtctx->jobs[u].fullFrameSize = srcSize; @@ -888,7 +890,7 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi zcs->jobs[jobID].src = zcs->inBuff.buffer; zcs->jobs[jobID].srcStart = zcs->inBuff.buffer.start; zcs->jobs[jobID].srcSize = srcSize; - zcs->jobs[jobID].dictSize = zcs->dictSize; + zcs->jobs[jobID].prefixSize = zcs->dictSize; assert(zcs->inBuff.filled >= srcSize + zcs->dictSize); zcs->jobs[jobID].params = zcs->params; /* do not calculate checksum within sections, but write it in header for first section */ @@ -1014,7 +1016,7 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx, { size_t const newJobThreshold = mtctx->dictSize + mtctx->targetSectionSize; unsigned forwardInputProgress = 0; - DEBUGLOG(5, "ZSTDMT_compressStream_generic"); + DEBUGLOG(5, "ZSTDMT_compressStream_generic "); assert(output->pos <= output->size); assert(input->pos <= input->size); diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index f0cf967cb..a59d94411 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -2451,14 +2451,16 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB return ZSTD_decompressLegacyStream(zds->legacyContext, legacyVersion, output, input); } #endif - return hSize; /* error */ + return hSize; /* error */ } if (hSize != 0) { /* need more input */ size_t const toLoad = hSize - zds->lhSize; /* if hSize!=0, hSize > zds->lhSize */ - if (toLoad > (size_t)(iend-ip)) { /* not enough input to load full header */ - if (iend-ip > 0) { - memcpy(zds->headerBuffer + zds->lhSize, ip, iend-ip); - zds->lhSize += iend-ip; + size_t const remainingInput = (size_t)(iend-ip); + assert(iend >= ip); + if (toLoad > remainingInput) { /* not enough input to load full header */ + if (remainingInput > 0) { + memcpy(zds->headerBuffer + zds->lhSize, ip, remainingInput); + zds->lhSize += remainingInput; } input->pos = input->size; return (MAX(ZSTD_frameHeaderSize_min, hSize) - zds->lhSize) + ZSTD_blockHeaderSize; /* remaining header bytes + next block header */ @@ -2473,8 +2475,10 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB && (U64)(size_t)(oend-op) >= zds->fParams.frameContentSize) { size_t const cSize = ZSTD_findFrameCompressedSize(istart, iend-istart); if (cSize <= (size_t)(iend-istart)) { + /* shortcut : using single-pass mode */ size_t const decompressedSize = ZSTD_decompress_usingDDict(zds, op, oend-op, istart, cSize, zds->ddict); if (ZSTD_isError(decompressedSize)) return decompressedSize; + DEBUGLOG(4, "shortcut to single-pass ZSTD_decompress_usingDDict()") ip = istart + cSize; op += decompressedSize; zds->expected = 0; @@ -2497,8 +2501,9 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB } /* control buffer memory usage */ - DEBUGLOG(4, "Control max buffer memory usage (max %u KB)", - (U32)(zds->maxWindowSize >> 10)); + DEBUGLOG(4, "Control max memory usage (%u KB <= max %u KB)", + (U32)(zds->fParams.windowSize >>10), + (U32)(zds->maxWindowSize >> 10) ); zds->fParams.windowSize = MAX(zds->fParams.windowSize, 1U << ZSTD_WINDOWLOG_ABSOLUTEMIN); if (zds->fParams.windowSize > zds->maxWindowSize) return ERROR(frameParameter_windowTooLarge); diff --git a/lib/zstd.h b/lib/zstd.h index 15c467535..e4541c3f4 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -1016,7 +1016,8 @@ typedef enum { * More threads improve speed, but also increase memory usage. * Can only receive a value > 1 if ZSTD_MULTITHREAD is enabled. * Special: value 0 means "do not change nbThreads" */ - ZSTD_p_jobSize, /* Size of a compression job. Each compression job is completed in parallel. + ZSTD_p_jobSize, /* Size of a compression job. This value is only enforced in streaming (non-blocking) mode. + * Each compression job is completed in parallel, so indirectly controls the nb of active threads. * 0 means default, which is dynamically determined based on compression parameters. * Job size must be a minimum of overlapSize, or 1 KB, whichever is largest * The minimum size is automatically and transparently enforced */ @@ -1144,13 +1145,19 @@ typedef enum { * - Compression parameters cannot be changed once compression is started. * - outpot->pos must be <= dstCapacity, input->pos must be <= srcSize * - outpot->pos and input->pos will be updated. They are guaranteed to remain below their respective limit. - * - @return provides the minimum amount of data still to flush from internal buffers + * - In single-thread mode (default), function is blocking : it completed its job before returning to caller. + * - In multi-thread mode, function is non-blocking : it just acquires a copy of input, and distribute job to internal worker threads, + * and then immediately returns, just indicating that there is some data remaining to be flushed. + * The function nonetheless guarantees forward progress : it will return only after it reads or write at least 1+ byte. + * - Exception : in multi-threading mode, if the first call requests a ZSTD_e_end directive, it is blocking : it will complete compression before giving back control to caller. + * - @return provides the minimum amount of data remaining to be flushed from internal buffers * or an error code, which can be tested using ZSTD_isError(). - * if @return != 0, flush is not fully completed, there is some data left within internal buffers. - * - after a ZSTD_e_end directive, if internal buffer is not fully flushed, + * if @return != 0, flush is not fully completed, there is still some data left within internal buffers. + * This is useful to determine if a ZSTD_e_flush or ZSTD_e_end directive is completed. + * - after a ZSTD_e_end directive, if internal buffer is not fully flushed (@return != 0), * only ZSTD_e_end or ZSTD_e_flush operations are allowed. - * It is necessary to fully flush internal buffers - * before starting a new compression job, or changing compression parameters. + * Before starting a new compression job, or changing compression parameters, + * it is required to fully flush internal buffers. */ ZSTDLIB_API size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, ZSTD_outBuffer* output, diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index e08a96d8f..6910457ff 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -524,12 +524,12 @@ static int basicUnitTests(U32 seed, double compressibility) { ZSTD_DDict* const ddict = ZSTD_createDDict(dictionary.start, dictionary.filled); size_t const initError = ZSTD_initDStream_usingDDict(zd, ddict); if (ZSTD_isError(initError)) goto _output_error; - inBuff.src = compressedBuffer; - inBuff.size = cSize; - inBuff.pos = 0; outBuff.dst = decodedBuffer; outBuff.size = CNBufferSize; outBuff.pos = 0; + inBuff.src = compressedBuffer; + inBuff.size = cSize; + inBuff.pos = 0; { size_t const r = ZSTD_decompressStream(zd, &outBuff, &inBuff); if (r != 0) goto _output_error; } /* should reach end of frame == 0; otherwise, some data left, or an error */ if (outBuff.pos != CNBufferSize) goto _output_error; /* should regenerate the same amount */ @@ -548,12 +548,12 @@ static int basicUnitTests(U32 seed, double compressibility) DISPLAYLEVEL(3, "test%3i : maxWindowSize < frame requirement : ", testNb++); ZSTD_initDStream_usingDict(zd, CNBuffer, dictSize); CHECK_Z( ZSTD_setDStreamParameter(zd, DStream_p_maxWindowSize, 1000) ); /* too small limit */ - inBuff.src = compressedBuffer; - inBuff.size = cSize; - inBuff.pos = 0; outBuff.dst = decodedBuffer; outBuff.size = CNBufferSize; outBuff.pos = 0; + inBuff.src = compressedBuffer; + inBuff.size = cSize; + inBuff.pos = 0; { size_t const r = ZSTD_decompressStream(zd, &outBuff, &inBuff); if (!ZSTD_isError(r)) goto _output_error; /* must fail : frame requires > 100 bytes */ DISPLAYLEVEL(3, "OK (%s)\n", ZSTD_getErrorName(r)); } @@ -642,12 +642,12 @@ static int basicUnitTests(U32 seed, double compressibility) params.fParams.contentSizeFlag = 1; CHECK_Z( ZSTD_initCStream_advanced(zc, dictionary.start, dictionary.filled, params, 0 /* pledgedSrcSize==0 means "empty" when params.fParams.contentSizeFlag is set */) ); } /* cstream advanced shall write content size = 0 */ - inBuff.src = CNBuffer; - inBuff.size = 0; - inBuff.pos = 0; outBuff.dst = compressedBuffer; outBuff.size = compressedBufferSize; outBuff.pos = 0; + inBuff.src = CNBuffer; + inBuff.size = 0; + inBuff.pos = 0; CHECK_Z( ZSTD_compressStream(zc, &outBuff, &inBuff) ); if (ZSTD_endStream(zc, &outBuff) != 0) goto _output_error; cSize = outBuff.pos; @@ -671,12 +671,12 @@ static int basicUnitTests(U32 seed, double compressibility) if (ZSTD_findDecompressedSize(compressedBuffer, cSize) != 0) goto _output_error; ZSTD_resetCStream(zc, 0); /* resetCStream should treat 0 as unknown */ - inBuff.src = CNBuffer; - inBuff.size = 0; - inBuff.pos = 0; outBuff.dst = compressedBuffer; outBuff.size = compressedBufferSize; outBuff.pos = 0; + inBuff.src = CNBuffer; + inBuff.size = 0; + inBuff.pos = 0; CHECK_Z( ZSTD_compressStream(zc, &outBuff, &inBuff) ); if (ZSTD_endStream(zc, &outBuff) != 0) goto _output_error; cSize = outBuff.pos; @@ -688,7 +688,7 @@ static int basicUnitTests(U32 seed, double compressibility) { ZSTD_parameters const params = ZSTD_getParams(1, 0, 0); CHECK_Z( ZSTDMT_initCStream_advanced(mtctx, CNBuffer, dictSize, params, CNBufferSize) ); } - outBuff.dst = (char*)(compressedBuffer); + outBuff.dst = compressedBuffer; outBuff.size = compressedBufferSize; outBuff.pos = 0; inBuff.src = CNBuffer; @@ -700,6 +700,61 @@ static int basicUnitTests(U32 seed, double compressibility) if (r != 0) goto _output_error; } /* error, or some data not flushed */ DISPLAYLEVEL(3, "OK \n"); + /* Complex multithreading + dictionary test */ + { U32 const nbThreads = 2; + size_t const jobSize = 4 * 1 MB; + size_t const srcSize = jobSize * nbThreads; /* we want each job to have predictable size */ + size_t const segLength = 2 KB; + size_t const offset = 600 KB; /* must be larger than window defined in cdict */ + size_t const start = jobSize + (offset-1); + const BYTE* const srcToCopy = (const BYTE*)CNBuffer + start; + BYTE* const dst = (BYTE*)CNBuffer + start - offset; + DISPLAYLEVEL(3, "test%3i : compress %u bytes with multiple threads + dictionary : ", testNb++, (U32)srcSize); + ZSTD_CCtx_setParameter(zc, ZSTD_p_compressionLevel, 3); + ZSTD_CCtx_setParameter(zc, ZSTD_p_nbThreads, 2); + ZSTD_CCtx_setParameter(zc, ZSTD_p_jobSize, jobSize); + assert(start > offset); + assert(start + segLength < COMPRESSIBLE_NOISE_LENGTH); + memcpy(dst, srcToCopy, segLength); /* create a long repetition at long distance for job 2 */ + outBuff.dst = compressedBuffer; + outBuff.size = compressedBufferSize; + outBuff.pos = 0; + inBuff.src = CNBuffer; + inBuff.size = srcSize; assert(srcSize < COMPRESSIBLE_NOISE_LENGTH); + inBuff.pos = 0; + } + { ZSTD_compressionParameters const cParams = ZSTD_getCParams(1, 4 KB, dictionary.filled); /* intentionnally lies on estimatedSrcSize, to push cdict into targeting a small window size */ + ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(dictionary.start, dictionary.filled, ZSTD_dlm_byRef, ZSTD_dm_fullDict, cParams, ZSTD_defaultCMem); + DISPLAYLEVEL(5, "cParams.windowLog = %u : ", cParams.windowLog); + ZSTD_CCtx_refCDict(zc, cdict); + CHECK_Z( ZSTD_compress_generic(zc, &outBuff, &inBuff, ZSTD_e_end) ); + ZSTD_CCtx_refCDict(zc, NULL); /* do not keep a reference to cdict, as its lifetime ends */ + ZSTD_freeCDict(cdict); + } + if (inBuff.pos != inBuff.size) goto _output_error; /* entire input should be consumed */ + cSize = outBuff.pos; + DISPLAYLEVEL(3, "OK \n"); + + DISPLAYLEVEL(3, "test%3i : decompress large frame created from multiple threads + dictionary : ", testNb++); + { ZSTD_DStream* const dstream = ZSTD_createDCtx(); + ZSTD_frameHeader zfh; + ZSTD_getFrameHeader(&zfh, compressedBuffer, cSize); + DISPLAYLEVEL(5, "frame windowsize = %u : ", (U32)zfh.windowSize); + outBuff.dst = decodedBuffer; + outBuff.size = CNBufferSize; + outBuff.pos = 0; + inBuff.src = compressedBuffer; + inBuff.pos = 0; + CHECK_Z( ZSTD_initDStream_usingDict(dstream, dictionary.start, dictionary.filled) ); + inBuff.size = 1; /* avoid shortcut to single-pass mode */ + CHECK_Z( ZSTD_decompressStream(dstream, &outBuff, &inBuff) ); + inBuff.size = cSize; + CHECK_Z( ZSTD_decompressStream(dstream, &outBuff, &inBuff) ); + if (inBuff.pos != inBuff.size) goto _output_error; /* entire input should be consumed */ + ZSTD_freeDStream(dstream); + } + DISPLAYLEVEL(3, "OK \n"); + DISPLAYLEVEL(3, "test%3i : check dictionary FSE tables can represent every code : ", testNb++); { unsigned const kMaxWindowLog = 24; unsigned value; From 8a104fda05e357d89dc8b263a48aa00ebe5d2603 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 12 Dec 2017 12:51:49 -0800 Subject: [PATCH 118/145] Revert "Created a test case which reliably reproduces bug #944" This reverts commit 5098d1fbe2a0b3d0ddb66c0880e3004feac65f3e. --- lib/compress/zstd_compress.c | 11 +++-- lib/compress/zstdmt_compress.c | 30 ++++++------ lib/decompress/zstd_decompress.c | 19 +++----- lib/zstd.h | 19 +++----- tests/zstreamtest.c | 81 +++++--------------------------- 5 files changed, 46 insertions(+), 114 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 107291888..3f39875f5 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -477,7 +477,7 @@ size_t ZSTD_CCtx_setParametersUsingCCtxParams( ZSTDLIB_API size_t ZSTD_CCtx_setPledgedSrcSize(ZSTD_CCtx* cctx, unsigned long long pledgedSrcSize) { - DEBUGLOG(4, "ZSTD_CCtx_setPledgedSrcSize to %u bytes", (U32)pledgedSrcSize); + DEBUGLOG(4, " setting pledgedSrcSize to %u", (U32)pledgedSrcSize); if (cctx->streamStage != zcss_init) return ERROR(stage_wrong); cctx->pledgedSrcSizePlusOne = pledgedSrcSize+1; return 0; @@ -489,7 +489,7 @@ size_t ZSTD_CCtx_loadDictionary_advanced( { if (cctx->streamStage != zcss_init) return ERROR(stage_wrong); if (cctx->staticSize) return ERROR(memory_allocation); /* no malloc for static CCtx */ - DEBUGLOG(4, "ZSTD_CCtx_loadDictionary_advanced (size: %u)", (U32)dictSize); + DEBUGLOG(4, "load dictionary of size %u", (U32)dictSize); ZSTD_freeCDict(cctx->cdictLocal); /* in case one already exists */ if (dict==NULL || dictSize==0) { /* no dictionary mode */ cctx->cdictLocal = NULL; @@ -2273,7 +2273,7 @@ static size_t ZSTD_initCDict_internal( ZSTD_dictMode_e dictMode, ZSTD_compressionParameters cParams) { - DEBUGLOG(3, "ZSTD_initCDict_internal, mode %u", (U32)dictMode); + DEBUGLOG(4, "ZSTD_initCDict_internal, mode %u", (U32)dictMode); if ((dictLoadMethod == ZSTD_dlm_byRef) || (!dictBuffer) || (!dictSize)) { cdict->dictBuffer = NULL; cdict->dictContent = dictBuffer; @@ -2303,7 +2303,7 @@ ZSTD_CDict* ZSTD_createCDict_advanced(const void* dictBuffer, size_t dictSize, ZSTD_dictMode_e dictMode, ZSTD_compressionParameters cParams, ZSTD_customMem customMem) { - DEBUGLOG(3, "ZSTD_createCDict_advanced, mode %u", (U32)dictMode); + DEBUGLOG(4, "ZSTD_createCDict_advanced, mode %u", (U32)dictMode); if (!customMem.customAlloc ^ !customMem.customFree) return NULL; { ZSTD_CDict* const cdict = (ZSTD_CDict*)ZSTD_malloc(sizeof(ZSTD_CDict), customMem); @@ -2809,7 +2809,7 @@ size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, ZSTD_inBuffer* input, ZSTD_EndDirective endOp) { - DEBUGLOG(5, "ZSTD_compress_generic, endOp=%u ", (U32)endOp); + DEBUGLOG(5, "ZSTD_compress_generic"); /* check conditions */ if (output->pos > output->size) return ERROR(GENERIC); if (input->pos > input->size) return ERROR(GENERIC); @@ -2856,6 +2856,7 @@ size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, #ifdef ZSTD_MULTITHREAD if (cctx->appliedParams.nbThreads > 1) { size_t const flushMin = ZSTDMT_compressStream_generic(cctx->mtctx, output, input, endOp); + DEBUGLOG(5, "ZSTDMT_compressStream_generic result : %u", (U32)flushMin); if ( ZSTD_isError(flushMin) || (endOp == ZSTD_e_end && flushMin == 0) ) { /* compression completed */ ZSTD_startNewCompression(cctx); diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 8347cf29d..6b6466fe5 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -310,7 +310,7 @@ static void ZSTDMT_releaseCCtx(ZSTDMT_CCtxPool* pool, ZSTD_CCtx* cctx) typedef struct { buffer_t src; const void* srcStart; - size_t prefixSize; + size_t dictSize; size_t srcSize; buffer_t dstBuff; size_t cSize; @@ -333,10 +333,10 @@ void ZSTDMT_compressChunk(void* jobDescription) { ZSTDMT_jobDescription* const job = (ZSTDMT_jobDescription*)jobDescription; ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(job->cctxPool); - const void* const src = (const char*)job->srcStart + job->prefixSize; + const void* const src = (const char*)job->srcStart + job->dictSize; buffer_t dstBuff = job->dstBuff; - DEBUGLOG(5, "ZSTDMT_compressChunk: job (first:%u) (last:%u) : prefixSize %u, srcSize %u ", - job->firstChunk, job->lastChunk, (U32)job->prefixSize, (U32)job->srcSize); + DEBUGLOG(5, "ZSTDMT_compressChunk: job (first:%u) (last:%u) : dictSize %u, srcSize %u", + job->firstChunk, job->lastChunk, (U32)job->dictSize, (U32)job->srcSize); if (cctx==NULL) { job->cSize = ERROR(memory_allocation); @@ -356,21 +356,20 @@ void ZSTDMT_compressChunk(void* jobDescription) if (job->cdict) { size_t const initError = ZSTD_compressBegin_usingCDict_advanced(cctx, job->cdict, job->params.fParams, job->fullFrameSize); DEBUGLOG(4, "ZSTDMT_compressChunk: init using CDict"); - assert(job->firstChunk); /* only allowed for first job */ + assert(job->firstChunk); /* should only happen for first segment */ if (ZSTD_isError(initError)) { job->cSize = initError; goto _endJob; } } else { /* srcStart points at reloaded section */ ZSTD_CCtx_params jobParams = job->params; /* do not modify job->params ! copy it, modify the copy */ size_t const forceWindowError = ZSTD_CCtxParam_setParameter(&jobParams, ZSTD_p_forceMaxWindow, !job->firstChunk); U64 const pledgedSrcSize = job->firstChunk ? job->fullFrameSize : ZSTD_CONTENTSIZE_UNKNOWN; /* load dictionary in "content-only" mode (no header analysis) */ - size_t const initError = ZSTD_compressBegin_advanced_internal(cctx, job->srcStart, job->prefixSize, ZSTD_dm_rawContent, jobParams, pledgedSrcSize); - DEBUGLOG(5, "ZSTD_compressBegin_advanced_internal called with windowLog = %u ", jobParams.cParams.windowLog); + size_t const initError = ZSTD_compressBegin_advanced_internal(cctx, job->srcStart, job->dictSize, ZSTD_dm_rawContent, jobParams, pledgedSrcSize); if (ZSTD_isError(initError) || ZSTD_isError(forceWindowError)) { job->cSize = initError; goto _endJob; } } - if (!job->firstChunk) { /* flush and overwrite frame header when it's not first job */ + if (!job->firstChunk) { /* flush and overwrite frame header when it's not first segment */ size_t const hSize = ZSTD_compressContinue(cctx, dstBuff.start, dstBuff.size, src, 0); if (ZSTD_isError(hSize)) { job->cSize = hSize; goto _endJob; } ZSTD_invalidateRepCodes(cctx); @@ -381,9 +380,9 @@ void ZSTDMT_compressChunk(void* jobDescription) job->cSize = (job->lastChunk) ? ZSTD_compressEnd (cctx, dstBuff.start, dstBuff.size, src, job->srcSize) : ZSTD_compressContinue(cctx, dstBuff.start, dstBuff.size, src, job->srcSize); - DEBUGLOG(5, "compressed %u bytes into %u bytes (first:%u) (last:%u) ", + DEBUGLOG(5, "compressed %u bytes into %u bytes (first:%u) (last:%u)", (unsigned)job->srcSize, (unsigned)job->cSize, job->firstChunk, job->lastChunk); - DEBUGLOG(5, "dstBuff.size : %u ; => %s ", (U32)dstBuff.size, ZSTD_getErrorName(job->cSize)); + DEBUGLOG(5, "dstBuff.size : %u ; => %s", (U32)dstBuff.size, ZSTD_getErrorName(job->cSize)); _endJob: ZSTDMT_releaseCCtx(job->cctxPool, cctx); @@ -619,7 +618,7 @@ static size_t ZSTDMT_compress_advanced_internal( size_t const overlapSize = (overlapRLog>=9) ? 0 : (size_t)1 << (params.cParams.windowLog - overlapRLog); unsigned nbChunks = computeNbChunks(srcSize, params.cParams.windowLog, params.nbThreads); size_t const proposedChunkSize = (srcSize + (nbChunks-1)) / nbChunks; - size_t const avgChunkSize = (((proposedChunkSize-1) & 0x1FFFF) < 0x7FFF) ? proposedChunkSize + 0xFFFF : proposedChunkSize; /* avoid too small last block */ + size_t const avgChunkSize = ((proposedChunkSize & 0x1FFFF) < 0x7FFF) ? proposedChunkSize + 0xFFFF : proposedChunkSize; /* avoid too small last block */ const char* const srcStart = (const char*)src; size_t remainingSrcSize = srcSize; unsigned const compressWithinDst = (dstCapacity >= ZSTD_compressBound(srcSize)) ? nbChunks : (unsigned)(dstCapacity / ZSTD_compressBound(avgChunkSize)); /* presumes avgChunkSize >= 256 KB, which should be the case */ @@ -629,8 +628,7 @@ static size_t ZSTDMT_compress_advanced_internal( assert(mtctx->cctxPool->totalCCtx == params.nbThreads); DEBUGLOG(4, "ZSTDMT_compress_advanced_internal"); - DEBUGLOG(4, "nbChunks : %2u (raw chunkSize : %u bytes; fixed chunkSize: %u) ", - nbChunks, (U32)proposedChunkSize, (U32)avgChunkSize); + DEBUGLOG(4, "nbChunks : %2u (chunkSize : %u bytes) ", nbChunks, (U32)avgChunkSize); if (nbChunks==1) { /* fallback to single-thread mode */ ZSTD_CCtx* const cctx = mtctx->cctxPool->cctx[0]; if (cdict) return ZSTD_compress_usingCDict_advanced(cctx, dst, dstCapacity, src, srcSize, cdict, jobParams.fParams); @@ -659,7 +657,7 @@ static size_t ZSTDMT_compress_advanced_internal( mtctx->jobs[u].src = g_nullBuffer; mtctx->jobs[u].srcStart = srcStart + frameStartPos - dictSize; - mtctx->jobs[u].prefixSize = dictSize; + mtctx->jobs[u].dictSize = dictSize; mtctx->jobs[u].srcSize = chunkSize; mtctx->jobs[u].cdict = (u==0) ? cdict : NULL; mtctx->jobs[u].fullFrameSize = srcSize; @@ -890,7 +888,7 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi zcs->jobs[jobID].src = zcs->inBuff.buffer; zcs->jobs[jobID].srcStart = zcs->inBuff.buffer.start; zcs->jobs[jobID].srcSize = srcSize; - zcs->jobs[jobID].prefixSize = zcs->dictSize; + zcs->jobs[jobID].dictSize = zcs->dictSize; assert(zcs->inBuff.filled >= srcSize + zcs->dictSize); zcs->jobs[jobID].params = zcs->params; /* do not calculate checksum within sections, but write it in header for first section */ @@ -1016,7 +1014,7 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx, { size_t const newJobThreshold = mtctx->dictSize + mtctx->targetSectionSize; unsigned forwardInputProgress = 0; - DEBUGLOG(5, "ZSTDMT_compressStream_generic "); + DEBUGLOG(5, "ZSTDMT_compressStream_generic"); assert(output->pos <= output->size); assert(input->pos <= input->size); diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index a59d94411..f0cf967cb 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -2451,16 +2451,14 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB return ZSTD_decompressLegacyStream(zds->legacyContext, legacyVersion, output, input); } #endif - return hSize; /* error */ + return hSize; /* error */ } if (hSize != 0) { /* need more input */ size_t const toLoad = hSize - zds->lhSize; /* if hSize!=0, hSize > zds->lhSize */ - size_t const remainingInput = (size_t)(iend-ip); - assert(iend >= ip); - if (toLoad > remainingInput) { /* not enough input to load full header */ - if (remainingInput > 0) { - memcpy(zds->headerBuffer + zds->lhSize, ip, remainingInput); - zds->lhSize += remainingInput; + if (toLoad > (size_t)(iend-ip)) { /* not enough input to load full header */ + if (iend-ip > 0) { + memcpy(zds->headerBuffer + zds->lhSize, ip, iend-ip); + zds->lhSize += iend-ip; } input->pos = input->size; return (MAX(ZSTD_frameHeaderSize_min, hSize) - zds->lhSize) + ZSTD_blockHeaderSize; /* remaining header bytes + next block header */ @@ -2475,10 +2473,8 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB && (U64)(size_t)(oend-op) >= zds->fParams.frameContentSize) { size_t const cSize = ZSTD_findFrameCompressedSize(istart, iend-istart); if (cSize <= (size_t)(iend-istart)) { - /* shortcut : using single-pass mode */ size_t const decompressedSize = ZSTD_decompress_usingDDict(zds, op, oend-op, istart, cSize, zds->ddict); if (ZSTD_isError(decompressedSize)) return decompressedSize; - DEBUGLOG(4, "shortcut to single-pass ZSTD_decompress_usingDDict()") ip = istart + cSize; op += decompressedSize; zds->expected = 0; @@ -2501,9 +2497,8 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB } /* control buffer memory usage */ - DEBUGLOG(4, "Control max memory usage (%u KB <= max %u KB)", - (U32)(zds->fParams.windowSize >>10), - (U32)(zds->maxWindowSize >> 10) ); + DEBUGLOG(4, "Control max buffer memory usage (max %u KB)", + (U32)(zds->maxWindowSize >> 10)); zds->fParams.windowSize = MAX(zds->fParams.windowSize, 1U << ZSTD_WINDOWLOG_ABSOLUTEMIN); if (zds->fParams.windowSize > zds->maxWindowSize) return ERROR(frameParameter_windowTooLarge); diff --git a/lib/zstd.h b/lib/zstd.h index e4541c3f4..15c467535 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -1016,8 +1016,7 @@ typedef enum { * More threads improve speed, but also increase memory usage. * Can only receive a value > 1 if ZSTD_MULTITHREAD is enabled. * Special: value 0 means "do not change nbThreads" */ - ZSTD_p_jobSize, /* Size of a compression job. This value is only enforced in streaming (non-blocking) mode. - * Each compression job is completed in parallel, so indirectly controls the nb of active threads. + ZSTD_p_jobSize, /* Size of a compression job. Each compression job is completed in parallel. * 0 means default, which is dynamically determined based on compression parameters. * Job size must be a minimum of overlapSize, or 1 KB, whichever is largest * The minimum size is automatically and transparently enforced */ @@ -1145,19 +1144,13 @@ typedef enum { * - Compression parameters cannot be changed once compression is started. * - outpot->pos must be <= dstCapacity, input->pos must be <= srcSize * - outpot->pos and input->pos will be updated. They are guaranteed to remain below their respective limit. - * - In single-thread mode (default), function is blocking : it completed its job before returning to caller. - * - In multi-thread mode, function is non-blocking : it just acquires a copy of input, and distribute job to internal worker threads, - * and then immediately returns, just indicating that there is some data remaining to be flushed. - * The function nonetheless guarantees forward progress : it will return only after it reads or write at least 1+ byte. - * - Exception : in multi-threading mode, if the first call requests a ZSTD_e_end directive, it is blocking : it will complete compression before giving back control to caller. - * - @return provides the minimum amount of data remaining to be flushed from internal buffers + * - @return provides the minimum amount of data still to flush from internal buffers * or an error code, which can be tested using ZSTD_isError(). - * if @return != 0, flush is not fully completed, there is still some data left within internal buffers. - * This is useful to determine if a ZSTD_e_flush or ZSTD_e_end directive is completed. - * - after a ZSTD_e_end directive, if internal buffer is not fully flushed (@return != 0), + * if @return != 0, flush is not fully completed, there is some data left within internal buffers. + * - after a ZSTD_e_end directive, if internal buffer is not fully flushed, * only ZSTD_e_end or ZSTD_e_flush operations are allowed. - * Before starting a new compression job, or changing compression parameters, - * it is required to fully flush internal buffers. + * It is necessary to fully flush internal buffers + * before starting a new compression job, or changing compression parameters. */ ZSTDLIB_API size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, ZSTD_outBuffer* output, diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index 6910457ff..e08a96d8f 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -524,12 +524,12 @@ static int basicUnitTests(U32 seed, double compressibility) { ZSTD_DDict* const ddict = ZSTD_createDDict(dictionary.start, dictionary.filled); size_t const initError = ZSTD_initDStream_usingDDict(zd, ddict); if (ZSTD_isError(initError)) goto _output_error; - outBuff.dst = decodedBuffer; - outBuff.size = CNBufferSize; - outBuff.pos = 0; inBuff.src = compressedBuffer; inBuff.size = cSize; inBuff.pos = 0; + outBuff.dst = decodedBuffer; + outBuff.size = CNBufferSize; + outBuff.pos = 0; { size_t const r = ZSTD_decompressStream(zd, &outBuff, &inBuff); if (r != 0) goto _output_error; } /* should reach end of frame == 0; otherwise, some data left, or an error */ if (outBuff.pos != CNBufferSize) goto _output_error; /* should regenerate the same amount */ @@ -548,12 +548,12 @@ static int basicUnitTests(U32 seed, double compressibility) DISPLAYLEVEL(3, "test%3i : maxWindowSize < frame requirement : ", testNb++); ZSTD_initDStream_usingDict(zd, CNBuffer, dictSize); CHECK_Z( ZSTD_setDStreamParameter(zd, DStream_p_maxWindowSize, 1000) ); /* too small limit */ - outBuff.dst = decodedBuffer; - outBuff.size = CNBufferSize; - outBuff.pos = 0; inBuff.src = compressedBuffer; inBuff.size = cSize; inBuff.pos = 0; + outBuff.dst = decodedBuffer; + outBuff.size = CNBufferSize; + outBuff.pos = 0; { size_t const r = ZSTD_decompressStream(zd, &outBuff, &inBuff); if (!ZSTD_isError(r)) goto _output_error; /* must fail : frame requires > 100 bytes */ DISPLAYLEVEL(3, "OK (%s)\n", ZSTD_getErrorName(r)); } @@ -642,12 +642,12 @@ static int basicUnitTests(U32 seed, double compressibility) params.fParams.contentSizeFlag = 1; CHECK_Z( ZSTD_initCStream_advanced(zc, dictionary.start, dictionary.filled, params, 0 /* pledgedSrcSize==0 means "empty" when params.fParams.contentSizeFlag is set */) ); } /* cstream advanced shall write content size = 0 */ - outBuff.dst = compressedBuffer; - outBuff.size = compressedBufferSize; - outBuff.pos = 0; inBuff.src = CNBuffer; inBuff.size = 0; inBuff.pos = 0; + outBuff.dst = compressedBuffer; + outBuff.size = compressedBufferSize; + outBuff.pos = 0; CHECK_Z( ZSTD_compressStream(zc, &outBuff, &inBuff) ); if (ZSTD_endStream(zc, &outBuff) != 0) goto _output_error; cSize = outBuff.pos; @@ -671,12 +671,12 @@ static int basicUnitTests(U32 seed, double compressibility) if (ZSTD_findDecompressedSize(compressedBuffer, cSize) != 0) goto _output_error; ZSTD_resetCStream(zc, 0); /* resetCStream should treat 0 as unknown */ - outBuff.dst = compressedBuffer; - outBuff.size = compressedBufferSize; - outBuff.pos = 0; inBuff.src = CNBuffer; inBuff.size = 0; inBuff.pos = 0; + outBuff.dst = compressedBuffer; + outBuff.size = compressedBufferSize; + outBuff.pos = 0; CHECK_Z( ZSTD_compressStream(zc, &outBuff, &inBuff) ); if (ZSTD_endStream(zc, &outBuff) != 0) goto _output_error; cSize = outBuff.pos; @@ -688,7 +688,7 @@ static int basicUnitTests(U32 seed, double compressibility) { ZSTD_parameters const params = ZSTD_getParams(1, 0, 0); CHECK_Z( ZSTDMT_initCStream_advanced(mtctx, CNBuffer, dictSize, params, CNBufferSize) ); } - outBuff.dst = compressedBuffer; + outBuff.dst = (char*)(compressedBuffer); outBuff.size = compressedBufferSize; outBuff.pos = 0; inBuff.src = CNBuffer; @@ -700,61 +700,6 @@ static int basicUnitTests(U32 seed, double compressibility) if (r != 0) goto _output_error; } /* error, or some data not flushed */ DISPLAYLEVEL(3, "OK \n"); - /* Complex multithreading + dictionary test */ - { U32 const nbThreads = 2; - size_t const jobSize = 4 * 1 MB; - size_t const srcSize = jobSize * nbThreads; /* we want each job to have predictable size */ - size_t const segLength = 2 KB; - size_t const offset = 600 KB; /* must be larger than window defined in cdict */ - size_t const start = jobSize + (offset-1); - const BYTE* const srcToCopy = (const BYTE*)CNBuffer + start; - BYTE* const dst = (BYTE*)CNBuffer + start - offset; - DISPLAYLEVEL(3, "test%3i : compress %u bytes with multiple threads + dictionary : ", testNb++, (U32)srcSize); - ZSTD_CCtx_setParameter(zc, ZSTD_p_compressionLevel, 3); - ZSTD_CCtx_setParameter(zc, ZSTD_p_nbThreads, 2); - ZSTD_CCtx_setParameter(zc, ZSTD_p_jobSize, jobSize); - assert(start > offset); - assert(start + segLength < COMPRESSIBLE_NOISE_LENGTH); - memcpy(dst, srcToCopy, segLength); /* create a long repetition at long distance for job 2 */ - outBuff.dst = compressedBuffer; - outBuff.size = compressedBufferSize; - outBuff.pos = 0; - inBuff.src = CNBuffer; - inBuff.size = srcSize; assert(srcSize < COMPRESSIBLE_NOISE_LENGTH); - inBuff.pos = 0; - } - { ZSTD_compressionParameters const cParams = ZSTD_getCParams(1, 4 KB, dictionary.filled); /* intentionnally lies on estimatedSrcSize, to push cdict into targeting a small window size */ - ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(dictionary.start, dictionary.filled, ZSTD_dlm_byRef, ZSTD_dm_fullDict, cParams, ZSTD_defaultCMem); - DISPLAYLEVEL(5, "cParams.windowLog = %u : ", cParams.windowLog); - ZSTD_CCtx_refCDict(zc, cdict); - CHECK_Z( ZSTD_compress_generic(zc, &outBuff, &inBuff, ZSTD_e_end) ); - ZSTD_CCtx_refCDict(zc, NULL); /* do not keep a reference to cdict, as its lifetime ends */ - ZSTD_freeCDict(cdict); - } - if (inBuff.pos != inBuff.size) goto _output_error; /* entire input should be consumed */ - cSize = outBuff.pos; - DISPLAYLEVEL(3, "OK \n"); - - DISPLAYLEVEL(3, "test%3i : decompress large frame created from multiple threads + dictionary : ", testNb++); - { ZSTD_DStream* const dstream = ZSTD_createDCtx(); - ZSTD_frameHeader zfh; - ZSTD_getFrameHeader(&zfh, compressedBuffer, cSize); - DISPLAYLEVEL(5, "frame windowsize = %u : ", (U32)zfh.windowSize); - outBuff.dst = decodedBuffer; - outBuff.size = CNBufferSize; - outBuff.pos = 0; - inBuff.src = compressedBuffer; - inBuff.pos = 0; - CHECK_Z( ZSTD_initDStream_usingDict(dstream, dictionary.start, dictionary.filled) ); - inBuff.size = 1; /* avoid shortcut to single-pass mode */ - CHECK_Z( ZSTD_decompressStream(dstream, &outBuff, &inBuff) ); - inBuff.size = cSize; - CHECK_Z( ZSTD_decompressStream(dstream, &outBuff, &inBuff) ); - if (inBuff.pos != inBuff.size) goto _output_error; /* entire input should be consumed */ - ZSTD_freeDStream(dstream); - } - DISPLAYLEVEL(3, "OK \n"); - DISPLAYLEVEL(3, "test%3i : check dictionary FSE tables can represent every code : ", testNb++); { unsigned const kMaxWindowLog = 24; unsigned value; From 03832b7aa590ed708354b086842406bc7c4f9b3a Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 12 Dec 2017 14:01:54 -0800 Subject: [PATCH 119/145] re-added test case messing with revert ... :( --- lib/compress/zstd_compress.c | 11 ++--- lib/compress/zstdmt_compress.c | 30 ++++++------ lib/decompress/zstd_decompress.c | 19 +++++--- lib/zstd.h | 19 +++++--- tests/zstreamtest.c | 81 +++++++++++++++++++++++++++----- 5 files changed, 114 insertions(+), 46 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 3f39875f5..107291888 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -477,7 +477,7 @@ size_t ZSTD_CCtx_setParametersUsingCCtxParams( ZSTDLIB_API size_t ZSTD_CCtx_setPledgedSrcSize(ZSTD_CCtx* cctx, unsigned long long pledgedSrcSize) { - DEBUGLOG(4, " setting pledgedSrcSize to %u", (U32)pledgedSrcSize); + DEBUGLOG(4, "ZSTD_CCtx_setPledgedSrcSize to %u bytes", (U32)pledgedSrcSize); if (cctx->streamStage != zcss_init) return ERROR(stage_wrong); cctx->pledgedSrcSizePlusOne = pledgedSrcSize+1; return 0; @@ -489,7 +489,7 @@ size_t ZSTD_CCtx_loadDictionary_advanced( { if (cctx->streamStage != zcss_init) return ERROR(stage_wrong); if (cctx->staticSize) return ERROR(memory_allocation); /* no malloc for static CCtx */ - DEBUGLOG(4, "load dictionary of size %u", (U32)dictSize); + DEBUGLOG(4, "ZSTD_CCtx_loadDictionary_advanced (size: %u)", (U32)dictSize); ZSTD_freeCDict(cctx->cdictLocal); /* in case one already exists */ if (dict==NULL || dictSize==0) { /* no dictionary mode */ cctx->cdictLocal = NULL; @@ -2273,7 +2273,7 @@ static size_t ZSTD_initCDict_internal( ZSTD_dictMode_e dictMode, ZSTD_compressionParameters cParams) { - DEBUGLOG(4, "ZSTD_initCDict_internal, mode %u", (U32)dictMode); + DEBUGLOG(3, "ZSTD_initCDict_internal, mode %u", (U32)dictMode); if ((dictLoadMethod == ZSTD_dlm_byRef) || (!dictBuffer) || (!dictSize)) { cdict->dictBuffer = NULL; cdict->dictContent = dictBuffer; @@ -2303,7 +2303,7 @@ ZSTD_CDict* ZSTD_createCDict_advanced(const void* dictBuffer, size_t dictSize, ZSTD_dictMode_e dictMode, ZSTD_compressionParameters cParams, ZSTD_customMem customMem) { - DEBUGLOG(4, "ZSTD_createCDict_advanced, mode %u", (U32)dictMode); + DEBUGLOG(3, "ZSTD_createCDict_advanced, mode %u", (U32)dictMode); if (!customMem.customAlloc ^ !customMem.customFree) return NULL; { ZSTD_CDict* const cdict = (ZSTD_CDict*)ZSTD_malloc(sizeof(ZSTD_CDict), customMem); @@ -2809,7 +2809,7 @@ size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, ZSTD_inBuffer* input, ZSTD_EndDirective endOp) { - DEBUGLOG(5, "ZSTD_compress_generic"); + DEBUGLOG(5, "ZSTD_compress_generic, endOp=%u ", (U32)endOp); /* check conditions */ if (output->pos > output->size) return ERROR(GENERIC); if (input->pos > input->size) return ERROR(GENERIC); @@ -2856,7 +2856,6 @@ size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, #ifdef ZSTD_MULTITHREAD if (cctx->appliedParams.nbThreads > 1) { size_t const flushMin = ZSTDMT_compressStream_generic(cctx->mtctx, output, input, endOp); - DEBUGLOG(5, "ZSTDMT_compressStream_generic result : %u", (U32)flushMin); if ( ZSTD_isError(flushMin) || (endOp == ZSTD_e_end && flushMin == 0) ) { /* compression completed */ ZSTD_startNewCompression(cctx); diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 6b6466fe5..8347cf29d 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -310,7 +310,7 @@ static void ZSTDMT_releaseCCtx(ZSTDMT_CCtxPool* pool, ZSTD_CCtx* cctx) typedef struct { buffer_t src; const void* srcStart; - size_t dictSize; + size_t prefixSize; size_t srcSize; buffer_t dstBuff; size_t cSize; @@ -333,10 +333,10 @@ void ZSTDMT_compressChunk(void* jobDescription) { ZSTDMT_jobDescription* const job = (ZSTDMT_jobDescription*)jobDescription; ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(job->cctxPool); - const void* const src = (const char*)job->srcStart + job->dictSize; + const void* const src = (const char*)job->srcStart + job->prefixSize; buffer_t dstBuff = job->dstBuff; - DEBUGLOG(5, "ZSTDMT_compressChunk: job (first:%u) (last:%u) : dictSize %u, srcSize %u", - job->firstChunk, job->lastChunk, (U32)job->dictSize, (U32)job->srcSize); + DEBUGLOG(5, "ZSTDMT_compressChunk: job (first:%u) (last:%u) : prefixSize %u, srcSize %u ", + job->firstChunk, job->lastChunk, (U32)job->prefixSize, (U32)job->srcSize); if (cctx==NULL) { job->cSize = ERROR(memory_allocation); @@ -356,20 +356,21 @@ void ZSTDMT_compressChunk(void* jobDescription) if (job->cdict) { size_t const initError = ZSTD_compressBegin_usingCDict_advanced(cctx, job->cdict, job->params.fParams, job->fullFrameSize); DEBUGLOG(4, "ZSTDMT_compressChunk: init using CDict"); - assert(job->firstChunk); /* should only happen for first segment */ + assert(job->firstChunk); /* only allowed for first job */ if (ZSTD_isError(initError)) { job->cSize = initError; goto _endJob; } } else { /* srcStart points at reloaded section */ ZSTD_CCtx_params jobParams = job->params; /* do not modify job->params ! copy it, modify the copy */ size_t const forceWindowError = ZSTD_CCtxParam_setParameter(&jobParams, ZSTD_p_forceMaxWindow, !job->firstChunk); U64 const pledgedSrcSize = job->firstChunk ? job->fullFrameSize : ZSTD_CONTENTSIZE_UNKNOWN; /* load dictionary in "content-only" mode (no header analysis) */ - size_t const initError = ZSTD_compressBegin_advanced_internal(cctx, job->srcStart, job->dictSize, ZSTD_dm_rawContent, jobParams, pledgedSrcSize); + size_t const initError = ZSTD_compressBegin_advanced_internal(cctx, job->srcStart, job->prefixSize, ZSTD_dm_rawContent, jobParams, pledgedSrcSize); + DEBUGLOG(5, "ZSTD_compressBegin_advanced_internal called with windowLog = %u ", jobParams.cParams.windowLog); if (ZSTD_isError(initError) || ZSTD_isError(forceWindowError)) { job->cSize = initError; goto _endJob; } } - if (!job->firstChunk) { /* flush and overwrite frame header when it's not first segment */ + if (!job->firstChunk) { /* flush and overwrite frame header when it's not first job */ size_t const hSize = ZSTD_compressContinue(cctx, dstBuff.start, dstBuff.size, src, 0); if (ZSTD_isError(hSize)) { job->cSize = hSize; goto _endJob; } ZSTD_invalidateRepCodes(cctx); @@ -380,9 +381,9 @@ void ZSTDMT_compressChunk(void* jobDescription) job->cSize = (job->lastChunk) ? ZSTD_compressEnd (cctx, dstBuff.start, dstBuff.size, src, job->srcSize) : ZSTD_compressContinue(cctx, dstBuff.start, dstBuff.size, src, job->srcSize); - DEBUGLOG(5, "compressed %u bytes into %u bytes (first:%u) (last:%u)", + DEBUGLOG(5, "compressed %u bytes into %u bytes (first:%u) (last:%u) ", (unsigned)job->srcSize, (unsigned)job->cSize, job->firstChunk, job->lastChunk); - DEBUGLOG(5, "dstBuff.size : %u ; => %s", (U32)dstBuff.size, ZSTD_getErrorName(job->cSize)); + DEBUGLOG(5, "dstBuff.size : %u ; => %s ", (U32)dstBuff.size, ZSTD_getErrorName(job->cSize)); _endJob: ZSTDMT_releaseCCtx(job->cctxPool, cctx); @@ -618,7 +619,7 @@ static size_t ZSTDMT_compress_advanced_internal( size_t const overlapSize = (overlapRLog>=9) ? 0 : (size_t)1 << (params.cParams.windowLog - overlapRLog); unsigned nbChunks = computeNbChunks(srcSize, params.cParams.windowLog, params.nbThreads); size_t const proposedChunkSize = (srcSize + (nbChunks-1)) / nbChunks; - size_t const avgChunkSize = ((proposedChunkSize & 0x1FFFF) < 0x7FFF) ? proposedChunkSize + 0xFFFF : proposedChunkSize; /* avoid too small last block */ + size_t const avgChunkSize = (((proposedChunkSize-1) & 0x1FFFF) < 0x7FFF) ? proposedChunkSize + 0xFFFF : proposedChunkSize; /* avoid too small last block */ const char* const srcStart = (const char*)src; size_t remainingSrcSize = srcSize; unsigned const compressWithinDst = (dstCapacity >= ZSTD_compressBound(srcSize)) ? nbChunks : (unsigned)(dstCapacity / ZSTD_compressBound(avgChunkSize)); /* presumes avgChunkSize >= 256 KB, which should be the case */ @@ -628,7 +629,8 @@ static size_t ZSTDMT_compress_advanced_internal( assert(mtctx->cctxPool->totalCCtx == params.nbThreads); DEBUGLOG(4, "ZSTDMT_compress_advanced_internal"); - DEBUGLOG(4, "nbChunks : %2u (chunkSize : %u bytes) ", nbChunks, (U32)avgChunkSize); + DEBUGLOG(4, "nbChunks : %2u (raw chunkSize : %u bytes; fixed chunkSize: %u) ", + nbChunks, (U32)proposedChunkSize, (U32)avgChunkSize); if (nbChunks==1) { /* fallback to single-thread mode */ ZSTD_CCtx* const cctx = mtctx->cctxPool->cctx[0]; if (cdict) return ZSTD_compress_usingCDict_advanced(cctx, dst, dstCapacity, src, srcSize, cdict, jobParams.fParams); @@ -657,7 +659,7 @@ static size_t ZSTDMT_compress_advanced_internal( mtctx->jobs[u].src = g_nullBuffer; mtctx->jobs[u].srcStart = srcStart + frameStartPos - dictSize; - mtctx->jobs[u].dictSize = dictSize; + mtctx->jobs[u].prefixSize = dictSize; mtctx->jobs[u].srcSize = chunkSize; mtctx->jobs[u].cdict = (u==0) ? cdict : NULL; mtctx->jobs[u].fullFrameSize = srcSize; @@ -888,7 +890,7 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi zcs->jobs[jobID].src = zcs->inBuff.buffer; zcs->jobs[jobID].srcStart = zcs->inBuff.buffer.start; zcs->jobs[jobID].srcSize = srcSize; - zcs->jobs[jobID].dictSize = zcs->dictSize; + zcs->jobs[jobID].prefixSize = zcs->dictSize; assert(zcs->inBuff.filled >= srcSize + zcs->dictSize); zcs->jobs[jobID].params = zcs->params; /* do not calculate checksum within sections, but write it in header for first section */ @@ -1014,7 +1016,7 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx, { size_t const newJobThreshold = mtctx->dictSize + mtctx->targetSectionSize; unsigned forwardInputProgress = 0; - DEBUGLOG(5, "ZSTDMT_compressStream_generic"); + DEBUGLOG(5, "ZSTDMT_compressStream_generic "); assert(output->pos <= output->size); assert(input->pos <= input->size); diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index f0cf967cb..a59d94411 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -2451,14 +2451,16 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB return ZSTD_decompressLegacyStream(zds->legacyContext, legacyVersion, output, input); } #endif - return hSize; /* error */ + return hSize; /* error */ } if (hSize != 0) { /* need more input */ size_t const toLoad = hSize - zds->lhSize; /* if hSize!=0, hSize > zds->lhSize */ - if (toLoad > (size_t)(iend-ip)) { /* not enough input to load full header */ - if (iend-ip > 0) { - memcpy(zds->headerBuffer + zds->lhSize, ip, iend-ip); - zds->lhSize += iend-ip; + size_t const remainingInput = (size_t)(iend-ip); + assert(iend >= ip); + if (toLoad > remainingInput) { /* not enough input to load full header */ + if (remainingInput > 0) { + memcpy(zds->headerBuffer + zds->lhSize, ip, remainingInput); + zds->lhSize += remainingInput; } input->pos = input->size; return (MAX(ZSTD_frameHeaderSize_min, hSize) - zds->lhSize) + ZSTD_blockHeaderSize; /* remaining header bytes + next block header */ @@ -2473,8 +2475,10 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB && (U64)(size_t)(oend-op) >= zds->fParams.frameContentSize) { size_t const cSize = ZSTD_findFrameCompressedSize(istart, iend-istart); if (cSize <= (size_t)(iend-istart)) { + /* shortcut : using single-pass mode */ size_t const decompressedSize = ZSTD_decompress_usingDDict(zds, op, oend-op, istart, cSize, zds->ddict); if (ZSTD_isError(decompressedSize)) return decompressedSize; + DEBUGLOG(4, "shortcut to single-pass ZSTD_decompress_usingDDict()") ip = istart + cSize; op += decompressedSize; zds->expected = 0; @@ -2497,8 +2501,9 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB } /* control buffer memory usage */ - DEBUGLOG(4, "Control max buffer memory usage (max %u KB)", - (U32)(zds->maxWindowSize >> 10)); + DEBUGLOG(4, "Control max memory usage (%u KB <= max %u KB)", + (U32)(zds->fParams.windowSize >>10), + (U32)(zds->maxWindowSize >> 10) ); zds->fParams.windowSize = MAX(zds->fParams.windowSize, 1U << ZSTD_WINDOWLOG_ABSOLUTEMIN); if (zds->fParams.windowSize > zds->maxWindowSize) return ERROR(frameParameter_windowTooLarge); diff --git a/lib/zstd.h b/lib/zstd.h index 15c467535..e4541c3f4 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -1016,7 +1016,8 @@ typedef enum { * More threads improve speed, but also increase memory usage. * Can only receive a value > 1 if ZSTD_MULTITHREAD is enabled. * Special: value 0 means "do not change nbThreads" */ - ZSTD_p_jobSize, /* Size of a compression job. Each compression job is completed in parallel. + ZSTD_p_jobSize, /* Size of a compression job. This value is only enforced in streaming (non-blocking) mode. + * Each compression job is completed in parallel, so indirectly controls the nb of active threads. * 0 means default, which is dynamically determined based on compression parameters. * Job size must be a minimum of overlapSize, or 1 KB, whichever is largest * The minimum size is automatically and transparently enforced */ @@ -1144,13 +1145,19 @@ typedef enum { * - Compression parameters cannot be changed once compression is started. * - outpot->pos must be <= dstCapacity, input->pos must be <= srcSize * - outpot->pos and input->pos will be updated. They are guaranteed to remain below their respective limit. - * - @return provides the minimum amount of data still to flush from internal buffers + * - In single-thread mode (default), function is blocking : it completed its job before returning to caller. + * - In multi-thread mode, function is non-blocking : it just acquires a copy of input, and distribute job to internal worker threads, + * and then immediately returns, just indicating that there is some data remaining to be flushed. + * The function nonetheless guarantees forward progress : it will return only after it reads or write at least 1+ byte. + * - Exception : in multi-threading mode, if the first call requests a ZSTD_e_end directive, it is blocking : it will complete compression before giving back control to caller. + * - @return provides the minimum amount of data remaining to be flushed from internal buffers * or an error code, which can be tested using ZSTD_isError(). - * if @return != 0, flush is not fully completed, there is some data left within internal buffers. - * - after a ZSTD_e_end directive, if internal buffer is not fully flushed, + * if @return != 0, flush is not fully completed, there is still some data left within internal buffers. + * This is useful to determine if a ZSTD_e_flush or ZSTD_e_end directive is completed. + * - after a ZSTD_e_end directive, if internal buffer is not fully flushed (@return != 0), * only ZSTD_e_end or ZSTD_e_flush operations are allowed. - * It is necessary to fully flush internal buffers - * before starting a new compression job, or changing compression parameters. + * Before starting a new compression job, or changing compression parameters, + * it is required to fully flush internal buffers. */ ZSTDLIB_API size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, ZSTD_outBuffer* output, diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index e08a96d8f..6910457ff 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -524,12 +524,12 @@ static int basicUnitTests(U32 seed, double compressibility) { ZSTD_DDict* const ddict = ZSTD_createDDict(dictionary.start, dictionary.filled); size_t const initError = ZSTD_initDStream_usingDDict(zd, ddict); if (ZSTD_isError(initError)) goto _output_error; - inBuff.src = compressedBuffer; - inBuff.size = cSize; - inBuff.pos = 0; outBuff.dst = decodedBuffer; outBuff.size = CNBufferSize; outBuff.pos = 0; + inBuff.src = compressedBuffer; + inBuff.size = cSize; + inBuff.pos = 0; { size_t const r = ZSTD_decompressStream(zd, &outBuff, &inBuff); if (r != 0) goto _output_error; } /* should reach end of frame == 0; otherwise, some data left, or an error */ if (outBuff.pos != CNBufferSize) goto _output_error; /* should regenerate the same amount */ @@ -548,12 +548,12 @@ static int basicUnitTests(U32 seed, double compressibility) DISPLAYLEVEL(3, "test%3i : maxWindowSize < frame requirement : ", testNb++); ZSTD_initDStream_usingDict(zd, CNBuffer, dictSize); CHECK_Z( ZSTD_setDStreamParameter(zd, DStream_p_maxWindowSize, 1000) ); /* too small limit */ - inBuff.src = compressedBuffer; - inBuff.size = cSize; - inBuff.pos = 0; outBuff.dst = decodedBuffer; outBuff.size = CNBufferSize; outBuff.pos = 0; + inBuff.src = compressedBuffer; + inBuff.size = cSize; + inBuff.pos = 0; { size_t const r = ZSTD_decompressStream(zd, &outBuff, &inBuff); if (!ZSTD_isError(r)) goto _output_error; /* must fail : frame requires > 100 bytes */ DISPLAYLEVEL(3, "OK (%s)\n", ZSTD_getErrorName(r)); } @@ -642,12 +642,12 @@ static int basicUnitTests(U32 seed, double compressibility) params.fParams.contentSizeFlag = 1; CHECK_Z( ZSTD_initCStream_advanced(zc, dictionary.start, dictionary.filled, params, 0 /* pledgedSrcSize==0 means "empty" when params.fParams.contentSizeFlag is set */) ); } /* cstream advanced shall write content size = 0 */ - inBuff.src = CNBuffer; - inBuff.size = 0; - inBuff.pos = 0; outBuff.dst = compressedBuffer; outBuff.size = compressedBufferSize; outBuff.pos = 0; + inBuff.src = CNBuffer; + inBuff.size = 0; + inBuff.pos = 0; CHECK_Z( ZSTD_compressStream(zc, &outBuff, &inBuff) ); if (ZSTD_endStream(zc, &outBuff) != 0) goto _output_error; cSize = outBuff.pos; @@ -671,12 +671,12 @@ static int basicUnitTests(U32 seed, double compressibility) if (ZSTD_findDecompressedSize(compressedBuffer, cSize) != 0) goto _output_error; ZSTD_resetCStream(zc, 0); /* resetCStream should treat 0 as unknown */ - inBuff.src = CNBuffer; - inBuff.size = 0; - inBuff.pos = 0; outBuff.dst = compressedBuffer; outBuff.size = compressedBufferSize; outBuff.pos = 0; + inBuff.src = CNBuffer; + inBuff.size = 0; + inBuff.pos = 0; CHECK_Z( ZSTD_compressStream(zc, &outBuff, &inBuff) ); if (ZSTD_endStream(zc, &outBuff) != 0) goto _output_error; cSize = outBuff.pos; @@ -688,7 +688,7 @@ static int basicUnitTests(U32 seed, double compressibility) { ZSTD_parameters const params = ZSTD_getParams(1, 0, 0); CHECK_Z( ZSTDMT_initCStream_advanced(mtctx, CNBuffer, dictSize, params, CNBufferSize) ); } - outBuff.dst = (char*)(compressedBuffer); + outBuff.dst = compressedBuffer; outBuff.size = compressedBufferSize; outBuff.pos = 0; inBuff.src = CNBuffer; @@ -700,6 +700,61 @@ static int basicUnitTests(U32 seed, double compressibility) if (r != 0) goto _output_error; } /* error, or some data not flushed */ DISPLAYLEVEL(3, "OK \n"); + /* Complex multithreading + dictionary test */ + { U32 const nbThreads = 2; + size_t const jobSize = 4 * 1 MB; + size_t const srcSize = jobSize * nbThreads; /* we want each job to have predictable size */ + size_t const segLength = 2 KB; + size_t const offset = 600 KB; /* must be larger than window defined in cdict */ + size_t const start = jobSize + (offset-1); + const BYTE* const srcToCopy = (const BYTE*)CNBuffer + start; + BYTE* const dst = (BYTE*)CNBuffer + start - offset; + DISPLAYLEVEL(3, "test%3i : compress %u bytes with multiple threads + dictionary : ", testNb++, (U32)srcSize); + ZSTD_CCtx_setParameter(zc, ZSTD_p_compressionLevel, 3); + ZSTD_CCtx_setParameter(zc, ZSTD_p_nbThreads, 2); + ZSTD_CCtx_setParameter(zc, ZSTD_p_jobSize, jobSize); + assert(start > offset); + assert(start + segLength < COMPRESSIBLE_NOISE_LENGTH); + memcpy(dst, srcToCopy, segLength); /* create a long repetition at long distance for job 2 */ + outBuff.dst = compressedBuffer; + outBuff.size = compressedBufferSize; + outBuff.pos = 0; + inBuff.src = CNBuffer; + inBuff.size = srcSize; assert(srcSize < COMPRESSIBLE_NOISE_LENGTH); + inBuff.pos = 0; + } + { ZSTD_compressionParameters const cParams = ZSTD_getCParams(1, 4 KB, dictionary.filled); /* intentionnally lies on estimatedSrcSize, to push cdict into targeting a small window size */ + ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(dictionary.start, dictionary.filled, ZSTD_dlm_byRef, ZSTD_dm_fullDict, cParams, ZSTD_defaultCMem); + DISPLAYLEVEL(5, "cParams.windowLog = %u : ", cParams.windowLog); + ZSTD_CCtx_refCDict(zc, cdict); + CHECK_Z( ZSTD_compress_generic(zc, &outBuff, &inBuff, ZSTD_e_end) ); + ZSTD_CCtx_refCDict(zc, NULL); /* do not keep a reference to cdict, as its lifetime ends */ + ZSTD_freeCDict(cdict); + } + if (inBuff.pos != inBuff.size) goto _output_error; /* entire input should be consumed */ + cSize = outBuff.pos; + DISPLAYLEVEL(3, "OK \n"); + + DISPLAYLEVEL(3, "test%3i : decompress large frame created from multiple threads + dictionary : ", testNb++); + { ZSTD_DStream* const dstream = ZSTD_createDCtx(); + ZSTD_frameHeader zfh; + ZSTD_getFrameHeader(&zfh, compressedBuffer, cSize); + DISPLAYLEVEL(5, "frame windowsize = %u : ", (U32)zfh.windowSize); + outBuff.dst = decodedBuffer; + outBuff.size = CNBufferSize; + outBuff.pos = 0; + inBuff.src = compressedBuffer; + inBuff.pos = 0; + CHECK_Z( ZSTD_initDStream_usingDict(dstream, dictionary.start, dictionary.filled) ); + inBuff.size = 1; /* avoid shortcut to single-pass mode */ + CHECK_Z( ZSTD_decompressStream(dstream, &outBuff, &inBuff) ); + inBuff.size = cSize; + CHECK_Z( ZSTD_decompressStream(dstream, &outBuff, &inBuff) ); + if (inBuff.pos != inBuff.size) goto _output_error; /* entire input should be consumed */ + ZSTD_freeDStream(dstream); + } + DISPLAYLEVEL(3, "OK \n"); + DISPLAYLEVEL(3, "test%3i : check dictionary FSE tables can represent every code : ", testNb++); { unsigned const kMaxWindowLog = 24; unsigned value; From e28305fcca441564a57758d84da8ef73afe7d85d Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 12 Dec 2017 16:20:51 -0800 Subject: [PATCH 120/145] fix #944 : ZSTDMT with large files and dictionary now works correctly windowLog is now enforced from provided compression parameters, instead of being copied blindly from `cdict` where it could be smaller. also : - fix a minor bug in zstreamtest --mt : advanced parameters must be set before init - changed advanced parameter name to ZSTDMT_jobSize --- lib/compress/zstd_compress.c | 62 +++++++++++++++------------ lib/compress/zstd_compress_internal.h | 1 + lib/compress/zstdmt_compress.c | 43 ++++++++++++------- lib/compress/zstdmt_compress.h | 8 ++-- tests/zstreamtest.c | 16 +++---- 5 files changed, 75 insertions(+), 55 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 107291888..7586c3a0c 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -410,7 +410,7 @@ size_t ZSTD_CCtxParam_setParameter( return ERROR(parameter_unsupported); #else if (CCtxParams->nbThreads <= 1) return ERROR(parameter_unsupported); - return ZSTDMT_CCtxParam_setMTCtxParameter(CCtxParams, ZSTDMT_p_sectionSize, value); + return ZSTDMT_CCtxParam_setMTCtxParameter(CCtxParams, ZSTDMT_p_jobSize, value); #endif case ZSTD_p_overlapSizeLog : @@ -987,15 +987,16 @@ void ZSTD_invalidateRepCodes(ZSTD_CCtx* cctx) { /*! ZSTD_copyCCtx_internal() : * Duplicate an existing context `srcCCtx` into another one `dstCCtx`. - * The "context", in this case, refers to the hash and chain tables, - * entropy tables, and dictionary offsets. * Only works during stage ZSTDcs_init (i.e. after creation, but before first call to ZSTD_compressContinue()). - * pledgedSrcSize=0 means "empty". - * @return : 0, or an error code */ + * The "context", in this case, refers to the hash and chain tables, + * entropy tables, and dictionary references. + * `windowLog` value is enforced if != 0, otherwise value is copied from srcCCtx. + * @return : 0, or an error code */ static size_t ZSTD_copyCCtx_internal(ZSTD_CCtx* dstCCtx, const ZSTD_CCtx* srcCCtx, + unsigned windowLog, ZSTD_frameParameters fParams, - unsigned long long pledgedSrcSize, + U64 pledgedSrcSize, ZSTD_buffered_policy_e zbuff) { DEBUGLOG(5, "ZSTD_copyCCtx_internal"); @@ -1005,6 +1006,7 @@ static size_t ZSTD_copyCCtx_internal(ZSTD_CCtx* dstCCtx, { ZSTD_CCtx_params params = dstCCtx->requestedParams; /* Copy only compression parameters related to tables. */ params.cParams = srcCCtx->appliedParams.cParams; + if (windowLog) params.cParams.windowLog = windowLog; params.fParams = fParams; ZSTD_resetCCtx_internal(dstCCtx, params, pledgedSrcSize, ZSTDcrp_noMemset, zbuff); @@ -1050,7 +1052,9 @@ size_t ZSTD_copyCCtx(ZSTD_CCtx* dstCCtx, const ZSTD_CCtx* srcCCtx, unsigned long if (pledgedSrcSize==0) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN; fParams.contentSizeFlag = (pledgedSrcSize != ZSTD_CONTENTSIZE_UNKNOWN); - return ZSTD_copyCCtx_internal(dstCCtx, srcCCtx, fParams, pledgedSrcSize, zbuff); + return ZSTD_copyCCtx_internal(dstCCtx, srcCCtx, + 0 /*windowLog from srcCCtx*/, fParams, pledgedSrcSize, + zbuff); } @@ -2037,12 +2041,12 @@ static size_t ZSTD_compress_insertDictionary(ZSTD_CCtx* cctx, /*! ZSTD_compressBegin_internal() : * @return : 0, or an error code */ -static size_t ZSTD_compressBegin_internal(ZSTD_CCtx* cctx, +size_t ZSTD_compressBegin_internal(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_dictMode_e dictMode, const ZSTD_CDict* cdict, - ZSTD_CCtx_params params, U64 pledgedSrcSize, - ZSTD_buffered_policy_e zbuff) + ZSTD_CCtx_params params, U64 pledgedSrcSize, + ZSTD_buffered_policy_e zbuff) { DEBUGLOG(4, "ZSTD_compressBegin_internal"); /* params are supposed to be fully validated at this point */ @@ -2052,7 +2056,7 @@ static size_t ZSTD_compressBegin_internal(ZSTD_CCtx* cctx, if (cdict && cdict->dictContentSize>0) { cctx->requestedParams = params; return ZSTD_copyCCtx_internal(cctx, cdict->refContext, - params.fParams, pledgedSrcSize, + params.cParams.windowLog, params.fParams, pledgedSrcSize, zbuff); } @@ -2061,17 +2065,19 @@ static size_t ZSTD_compressBegin_internal(ZSTD_CCtx* cctx, return ZSTD_compress_insertDictionary(cctx, dict, dictSize, dictMode); } -size_t ZSTD_compressBegin_advanced_internal( - ZSTD_CCtx* cctx, +size_t ZSTD_compressBegin_advanced_internal(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_dictMode_e dictMode, + const ZSTD_CDict* cdict, ZSTD_CCtx_params params, unsigned long long pledgedSrcSize) { DEBUGLOG(4, "ZSTD_compressBegin_advanced_internal"); /* compression parameters verification and optimization */ CHECK_F( ZSTD_checkCParams(params.cParams) ); - return ZSTD_compressBegin_internal(cctx, dict, dictSize, dictMode, NULL, + return ZSTD_compressBegin_internal(cctx, + dict, dictSize, dictMode, + cdict, params, pledgedSrcSize, ZSTDb_not_buffered); } @@ -2084,9 +2090,10 @@ size_t ZSTD_compressBegin_advanced(ZSTD_CCtx* cctx, { ZSTD_CCtx_params const cctxParams = ZSTD_assignParamsToCCtxParams(cctx->requestedParams, params); - return ZSTD_compressBegin_advanced_internal(cctx, dict, dictSize, ZSTD_dm_auto, - cctxParams, - pledgedSrcSize); + return ZSTD_compressBegin_advanced_internal(cctx, + dict, dictSize, ZSTD_dm_auto, + NULL /*cdict*/, + cctxParams, pledgedSrcSize); } size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel) @@ -2507,10 +2514,10 @@ static size_t ZSTD_resetCStream_internal(ZSTD_CStream* zcs, assert(!((dict) && (cdict))); /* either dict or cdict, not both */ CHECK_F( ZSTD_compressBegin_internal(zcs, - dict, dictSize, dictMode, - cdict, - params, pledgedSrcSize, - ZSTDb_buffered) ); + dict, dictSize, dictMode, + cdict, + params, pledgedSrcSize, + ZSTDb_buffered) ); zcs->inToCompress = 0; zcs->inBuffPos = 0; @@ -2534,7 +2541,7 @@ size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pledgedSrcSize) } /*! ZSTD_initCStream_internal() : - * Note : not static, but hidden (not exposed). Used by zstdmt_compress.c + * Note : for lib/compress only. Used by zstdmt_compress.c. * Assumption 1 : params are valid * Assumption 2 : either dict, or cdict, is defined, not both */ size_t ZSTD_initCStream_internal(ZSTD_CStream* zcs, @@ -2546,7 +2553,7 @@ size_t ZSTD_initCStream_internal(ZSTD_CStream* zcs, assert(!((dict) && (cdict))); /* either dict or cdict, not both */ if (dict && dictSize >= 8) { - DEBUGLOG(5, "loading dictionary of size %u", (U32)dictSize); + DEBUGLOG(4, "loading dictionary of size %u", (U32)dictSize); if (zcs->staticSize) { /* static CCtx : never uses malloc */ /* incompatible with internal cdict creation */ return ERROR(memory_allocation); @@ -2559,14 +2566,14 @@ size_t ZSTD_initCStream_internal(ZSTD_CStream* zcs, if (zcs->cdictLocal == NULL) return ERROR(memory_allocation); } else { if (cdict) { - params.cParams = ZSTD_getCParamsFromCDict(cdict); /* cParams are enforced from cdict */ + params.cParams = ZSTD_getCParamsFromCDict(cdict); /* cParams are enforced from cdict; this includes windowLog (should it be enforced from `params` instead ?) */ } ZSTD_freeCDict(zcs->cdictLocal); zcs->cdictLocal = NULL; zcs->cdict = cdict; } - params.compressionLevel = ZSTD_CLEVEL_CUSTOM; + params.compressionLevel = ZSTD_CLEVEL_CUSTOM; /* enforce usage of cParams, instead of a dynamic derivation from cLevel (but does that happen ?) */ zcs->requestedParams = params; return ZSTD_resetCStream_internal(zcs, NULL, 0, ZSTD_dm_auto, zcs->cdict, params, pledgedSrcSize); @@ -2606,10 +2613,9 @@ size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, const void* dict, size_t dictSize, ZSTD_parameters params, unsigned long long pledgedSrcSize) { - ZSTD_CCtx_params const cctxParams = - ZSTD_assignParamsToCCtxParams(zcs->requestedParams, params); + ZSTD_CCtx_params const cctxParams = ZSTD_assignParamsToCCtxParams(zcs->requestedParams, params); DEBUGLOG(4, "ZSTD_initCStream_advanced: pledgedSrcSize=%u, flag=%u", - (U32)pledgedSrcSize, params.fParams.contentSizeFlag); + (U32)pledgedSrcSize, params.fParams.contentSizeFlag); CHECK_F( ZSTD_checkCParams(params.cParams) ); if ((pledgedSrcSize==0) && (params.fParams.contentSizeFlag==0)) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN; /* for compatibility with older programs relying on this behavior. Users should now specify ZSTD_CONTENTSIZE_UNKNOWN. This line will be removed in the future. */ return ZSTD_initCStream_internal(zcs, dict, dictSize, NULL /*cdict*/, cctxParams, pledgedSrcSize); diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 278f2427c..f104fe981 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -447,6 +447,7 @@ ZSTD_compressionParameters ZSTD_getCParamsFromCDict(const ZSTD_CDict* cdict); size_t ZSTD_compressBegin_advanced_internal(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_dictMode_e dictMode, + const ZSTD_CDict* cdict, ZSTD_CCtx_params params, unsigned long long pledgedSrcSize); diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 8347cf29d..a5e996d3e 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -350,29 +350,35 @@ void ZSTDMT_compressChunk(void* jobDescription) goto _endJob; } job->dstBuff = dstBuff; - DEBUGLOG(5, "ZSTDMT_compressChunk: allocated dstBuff of size %u", (U32)dstBuff.size); + DEBUGLOG(5, "ZSTDMT_compressChunk: received dstBuff of size %u", (U32)dstBuff.size); } if (job->cdict) { - size_t const initError = ZSTD_compressBegin_usingCDict_advanced(cctx, job->cdict, job->params.fParams, job->fullFrameSize); + size_t const initError = ZSTD_compressBegin_advanced_internal(cctx, NULL, 0, ZSTD_dm_auto, job->cdict, job->params, job->fullFrameSize); DEBUGLOG(4, "ZSTDMT_compressChunk: init using CDict"); assert(job->firstChunk); /* only allowed for first job */ if (ZSTD_isError(initError)) { job->cSize = initError; goto _endJob; } } else { /* srcStart points at reloaded section */ + U64 const pledgedSrcSize = job->firstChunk ? job->fullFrameSize : ZSTD_CONTENTSIZE_UNKNOWN; ZSTD_CCtx_params jobParams = job->params; /* do not modify job->params ! copy it, modify the copy */ size_t const forceWindowError = ZSTD_CCtxParam_setParameter(&jobParams, ZSTD_p_forceMaxWindow, !job->firstChunk); - U64 const pledgedSrcSize = job->firstChunk ? job->fullFrameSize : ZSTD_CONTENTSIZE_UNKNOWN; - /* load dictionary in "content-only" mode (no header analysis) */ - size_t const initError = ZSTD_compressBegin_advanced_internal(cctx, job->srcStart, job->prefixSize, ZSTD_dm_rawContent, jobParams, pledgedSrcSize); - DEBUGLOG(5, "ZSTD_compressBegin_advanced_internal called with windowLog = %u ", jobParams.cParams.windowLog); - if (ZSTD_isError(initError) || ZSTD_isError(forceWindowError)) { - job->cSize = initError; + if (ZSTD_isError(forceWindowError)) { + DEBUGLOG(5, "ZSTD_CCtxParam_setParameter error : %s ", ZSTD_getErrorName(forceWindowError)); + job->cSize = forceWindowError; goto _endJob; } + /* load dictionary in "content-only" mode (no header analysis) */ + { size_t const initError = ZSTD_compressBegin_advanced_internal(cctx, job->srcStart, job->prefixSize, ZSTD_dm_rawContent, NULL, jobParams, pledgedSrcSize); + DEBUGLOG(5, "ZSTD_compressBegin_advanced_internal called with windowLog = %u ", jobParams.cParams.windowLog); + if (ZSTD_isError(initError)) { + DEBUGLOG(5, "ZSTD_compressBegin_advanced_internal error : %s ", ZSTD_getErrorName(initError)); + job->cSize = initError; + goto _endJob; + } } } if (!job->firstChunk) { /* flush and overwrite frame header when it's not first job */ size_t const hSize = ZSTD_compressContinue(cctx, dstBuff.start, dstBuff.size, src, 0); - if (ZSTD_isError(hSize)) { job->cSize = hSize; goto _endJob; } + if (ZSTD_isError(hSize)) { job->cSize = hSize; /* save error code */ goto _endJob; } ZSTD_invalidateRepCodes(cctx); } @@ -559,11 +565,13 @@ size_t ZSTDMT_sizeof_CCtx(ZSTDMT_CCtx* mtctx) } /* Internal only */ -size_t ZSTDMT_CCtxParam_setMTCtxParameter( - ZSTD_CCtx_params* params, ZSTDMT_parameter parameter, unsigned value) { +size_t ZSTDMT_CCtxParam_setMTCtxParameter(ZSTD_CCtx_params* params, + ZSTDMT_parameter parameter, unsigned value) { + DEBUGLOG(4, "ZSTDMT_CCtxParam_setMTCtxParameter"); switch(parameter) { - case ZSTDMT_p_sectionSize : + case ZSTDMT_p_jobSize : + DEBUGLOG(4, "ZSTDMT_CCtxParam_setMTCtxParameter : set jobSize to %u", value); if ( (value > 0) /* value==0 => automatic job size */ & (value < ZSTDMT_JOBSIZE_MIN) ) value = ZSTDMT_JOBSIZE_MIN; @@ -581,9 +589,10 @@ size_t ZSTDMT_CCtxParam_setMTCtxParameter( size_t ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSTDMT_parameter parameter, unsigned value) { + DEBUGLOG(4, "ZSTDMT_setMTCtxParameter"); switch(parameter) { - case ZSTDMT_p_sectionSize : + case ZSTDMT_p_jobSize : return ZSTDMT_CCtxParam_setMTCtxParameter(&mtctx->params, parameter, value); case ZSTDMT_p_overlapSectionLog : return ZSTDMT_CCtxParam_setMTCtxParameter(&mtctx->params, parameter, value); @@ -817,7 +826,7 @@ size_t ZSTDMT_initCStream_internal( zcs->targetSectionSize = params.jobSize ? params.jobSize : (size_t)1 << (params.cParams.windowLog + 2); if (zcs->targetSectionSize < ZSTDMT_JOBSIZE_MIN) zcs->targetSectionSize = ZSTDMT_JOBSIZE_MIN; if (zcs->targetSectionSize < zcs->targetDictSize) zcs->targetSectionSize = zcs->targetDictSize; /* job size must be >= overlap size */ - DEBUGLOG(4, "Job Size : %u KB", (U32)(zcs->targetSectionSize>>10)); + DEBUGLOG(4, "Job Size : %u KB (note : set to %u)", (U32)(zcs->targetSectionSize>>10), params.jobSize); zcs->inBuffSize = zcs->targetDictSize + zcs->targetSectionSize; DEBUGLOG(4, "inBuff Size : %u KB", (U32)(zcs->inBuffSize>>10)); ZSTDMT_setBufferSize(zcs->bufPool, MAX(zcs->inBuffSize, ZSTD_compressBound(zcs->targetSectionSize)) ); @@ -955,6 +964,7 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi static size_t ZSTDMT_flushNextJob(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsigned blockToFlush) { unsigned const wJobID = zcs->doneJobID & zcs->jobIDMask; + DEBUGLOG(5, "ZSTDMT_flushNextJob"); if (zcs->doneJobID == zcs->nextJobID) return 0; /* all flushed ! */ ZSTD_PTHREAD_MUTEX_LOCK(&zcs->jobCompleted_mutex); while (zcs->jobs[wJobID].jobCompleted==0) { @@ -967,7 +977,8 @@ static size_t ZSTDMT_flushNextJob(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, unsi { ZSTDMT_jobDescription job = zcs->jobs[wJobID]; if (!job.jobScanned) { if (ZSTD_isError(job.cSize)) { - DEBUGLOG(5, "compression error detected "); + DEBUGLOG(5, "job %u : compression error detected : %s", + zcs->doneJobID, ZSTD_getErrorName(job.cSize)); ZSTDMT_waitForAllJobsCompleted(zcs); ZSTDMT_releaseAllJobResources(zcs); return job.cSize; @@ -1099,9 +1110,11 @@ size_t ZSTDMT_compressStream(ZSTDMT_CCtx* zcs, ZSTD_outBuffer* output, ZSTD_inBu static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, unsigned endFrame) { size_t const srcSize = mtctx->inBuff.filled - mtctx->dictSize; + DEBUGLOG(5, "ZSTDMT_flushStream_internal"); if ( ((srcSize > 0) || (endFrame && !mtctx->frameEnded)) && (mtctx->nextJobID <= mtctx->doneJobID + mtctx->jobIDMask) ) { + DEBUGLOG(5, "ZSTDMT_flushStream_internal : create a new job"); CHECK_F( ZSTDMT_createCompressionJob(mtctx, srcSize, endFrame) ); } diff --git a/lib/compress/zstdmt_compress.h b/lib/compress/zstdmt_compress.h index 269c54b1a..4209cf3c5 100644 --- a/lib/compress/zstdmt_compress.h +++ b/lib/compress/zstdmt_compress.h @@ -84,13 +84,13 @@ ZSTDLIB_API size_t ZSTDMT_initCStream_usingCDict(ZSTDMT_CCtx* mtctx, /* ZSTDMT_parameter : * List of parameters that can be set using ZSTDMT_setMTCtxParameter() */ typedef enum { - ZSTDMT_p_sectionSize, /* size of input "section". Each section is compressed in parallel. 0 means default, which is dynamically determined within compression functions */ - ZSTDMT_p_overlapSectionLog /* Log of overlapped section; 0 == no overlap, 6(default) == use 1/8th of window, >=9 == use full window */ + ZSTDMT_p_jobSize, /* Each job is compressed in parallel. By default, this value is dynamically determined depending on compression parameters. Can be set explicitly here. */ + ZSTDMT_p_overlapSectionLog /* Each job may reload a part of previous job to enhance compressionr ratio; 0 == no overlap, 6(default) == use 1/8th of window, >=9 == use full window */ } ZSTDMT_parameter; /* ZSTDMT_setMTCtxParameter() : * allow setting individual parameters, one at a time, among a list of enums defined in ZSTDMT_parameter. - * The function must be called typically after ZSTD_createCCtx(). + * The function must be called typically after ZSTD_createCCtx() but __before ZSTDMT_init*() !__ * Parameters not explicitly reset by ZSTDMT_init*() remain the same in consecutive compression sessions. * @return : 0, or an error code (which can be tested using ZSTD_isError()) */ ZSTDLIB_API size_t ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSTDMT_parameter parameter, unsigned value); @@ -114,7 +114,7 @@ size_t ZSTDMT_CCtxParam_setMTCtxParameter(ZSTD_CCtx_params* params, ZSTDMT_param /* ZSTDMT_CCtxParam_setNbThreads() * Set nbThreads, and clamp it correctly, - * but also reset jobSize and overlapLog */ + * but also reset jobSize and overlapLog */ size_t ZSTDMT_CCtxParam_setNbThreads(ZSTD_CCtx_params* params, unsigned nbThreads); /*! ZSTDMT_initCStream_internal() : diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index 6910457ff..67ef6ce80 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -1263,15 +1263,15 @@ static int fuzzerTests_MT(U32 seed, U32 nbTests, unsigned startTest, double comp } { U64 const pledgedSrcSize = (FUZ_rand(&lseed) & 3) ? ZSTD_CONTENTSIZE_UNKNOWN : maxTestSize; ZSTD_parameters params = ZSTD_getParams(cLevel, pledgedSrcSize, dictSize); - DISPLAYLEVEL(5, "Init with windowLog = %u and pledgedSrcSize = %u \n", - params.cParams.windowLog, (U32)pledgedSrcSize); + DISPLAYLEVEL(5, "Init with windowLog = %u, pledgedSrcSize = %u, dictSize = %u \n", + params.cParams.windowLog, (U32)pledgedSrcSize, (U32)dictSize); params.fParams.checksumFlag = FUZ_rand(&lseed) & 1; params.fParams.noDictIDFlag = FUZ_rand(&lseed) & 1; params.fParams.contentSizeFlag = FUZ_rand(&lseed) & 1; DISPLAYLEVEL(5, "checksumFlag : %u \n", params.fParams.checksumFlag); - CHECK_Z( ZSTDMT_initCStream_advanced(zc, dict, dictSize, params, pledgedSrcSize) ); CHECK_Z( ZSTDMT_setMTCtxParameter(zc, ZSTDMT_p_overlapSectionLog, FUZ_rand(&lseed) % 12) ); - CHECK_Z( ZSTDMT_setMTCtxParameter(zc, ZSTDMT_p_sectionSize, FUZ_rand(&lseed) % (2*maxTestSize+1)) ); + CHECK_Z( ZSTDMT_setMTCtxParameter(zc, ZSTDMT_p_jobSize, FUZ_rand(&lseed) % (2*maxTestSize+1)) ); /* custome job size */ + CHECK_Z( ZSTDMT_initCStream_advanced(zc, dict, dictSize, params, pledgedSrcSize) ); } } /* multi-segments compression test */ @@ -1288,9 +1288,9 @@ static int fuzzerTests_MT(U32 seed, U32 nbTests, unsigned startTest, double comp ZSTD_inBuffer inBuff = { srcBuffer+srcStart, srcSize, 0 }; outBuff.size = outBuff.pos + dstBuffSize; - DISPLAYLEVEL(5, "Sending %u bytes to compress \n", (U32)srcSize); + DISPLAYLEVEL(6, "Sending %u bytes to compress \n", (U32)srcSize); CHECK_Z( ZSTDMT_compressStream(zc, &outBuff, &inBuff) ); - DISPLAYLEVEL(5, "%u bytes read by ZSTDMT_compressStream \n", (U32)inBuff.pos); + DISPLAYLEVEL(6, "%u bytes read by ZSTDMT_compressStream \n", (U32)inBuff.pos); XXH64_update(&xxhState, srcBuffer+srcStart, inBuff.pos); memcpy(copyBuffer+totalTestSize, srcBuffer+srcStart, inBuff.pos); @@ -1337,10 +1337,10 @@ static int fuzzerTests_MT(U32 seed, U32 nbTests, unsigned startTest, double comp size_t const dstBuffSize = MIN(dstBufferSize - totalGenSize, randomDstSize); inBuff.size = inBuff.pos + readCSrcSize; outBuff.size = outBuff.pos + dstBuffSize; - DISPLAYLEVEL(5, "ZSTD_decompressStream input %u bytes \n", (U32)readCSrcSize); + DISPLAYLEVEL(6, "ZSTD_decompressStream input %u bytes \n", (U32)readCSrcSize); decompressionResult = ZSTD_decompressStream(zd, &outBuff, &inBuff); CHECK (ZSTD_isError(decompressionResult), "decompression error : %s", ZSTD_getErrorName(decompressionResult)); - DISPLAYLEVEL(5, "inBuff.pos = %u \n", (U32)readCSrcSize); + DISPLAYLEVEL(6, "inBuff.pos = %u \n", (U32)readCSrcSize); } CHECK (outBuff.pos != totalTestSize, "decompressed data : wrong size (%u != %u)", (U32)outBuff.pos, (U32)totalTestSize); CHECK (inBuff.pos != cSize, "compressed data should be fully read (%u != %u)", (U32)inBuff.pos, (U32)cSize); From e9dc0ab8843437ff590a5acbc479ff9769b2f449 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 12 Dec 2017 18:20:04 -0800 Subject: [PATCH 121/145] zstd binary, when compiled from /tests, enables debug mode adds ZSTD_DEBUG=1 and -g --- tests/Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Makefile b/tests/Makefile index f944fd5a8..342a648c0 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -76,16 +76,16 @@ allnothread: fullbench fuzzer paramgrill datagen decodecorpus dll: fuzzer-dll zstreamtest-dll zstd: - $(MAKE) -C $(PRGDIR) $@ + $(MAKE) -C $(PRGDIR) $@ MOREFLAGS="$(DEBUGFLAGS)" zstd32: - $(MAKE) -C $(PRGDIR) $@ + $(MAKE) -C $(PRGDIR) $@ MOREFLAGS="$(DEBUGFLAGS)" zstd-nolegacy: - $(MAKE) -C $(PRGDIR) $@ + $(MAKE) -C $(PRGDIR) $@ MOREFLAGS="$(DEBUGFLAGS)" gzstd: - $(MAKE) -C $(PRGDIR) zstd HAVE_ZLIB=1 + $(MAKE) -C $(PRGDIR) zstd HAVE_ZLIB=1 MOREFLAGS="$(DEBUGFLAGS)" fullbench32: CPPFLAGS += -m32 fullbench fullbench32 : CPPFLAGS += $(MULTITHREAD_CPP) From 98ccc115edffd286d44ac6501479c7462b770825 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 12 Dec 2017 19:47:38 -0800 Subject: [PATCH 122/145] added cli test veryfying bug #944 this test fails on v1.3.2 --- tests/playTests.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/playTests.sh b/tests/playTests.sh index 299c2d883..6b78b305a 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -297,6 +297,8 @@ cp $TESTFILE tmp $ZSTD -f tmp -D tmpDict $ZSTD -d tmp.zst -D tmpDict -fo result $DIFF $TESTFILE result +$ECHO "- Test dictionary compression with multithreading " +./datagen -g5M | $ZSTD -T2 -D tmpDict | $ZSTD -t -D tmpDict # fails with v1.3.2 $ECHO "- Create second (different) dictionary " $ZSTD --train *.c ../programs/*.c ../programs/*.h -o tmpDictC $ZSTD -d tmp.zst -D tmpDictC -fo result && die "wrong dictionary not detected!" From 04a1557e2808be33bf199757482c59fa4bd287da Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 13 Dec 2017 10:08:52 -0800 Subject: [PATCH 123/145] fuzzer tests : added -v command so that unit tests get listed in CI console and we can see which one failed. --- tests/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Makefile b/tests/Makefile index f944fd5a8..79df7dd95 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -300,10 +300,10 @@ test-fullbench32: fullbench32 datagen $(QEMU_SYS) ./fullbench32 -i1 -P0 test-fuzzer: fuzzer - $(QEMU_SYS) ./fuzzer $(FUZZERTEST) $(FUZZER_FLAGS) + $(QEMU_SYS) ./fuzzer -v $(FUZZERTEST) $(FUZZER_FLAGS) test-fuzzer32: fuzzer32 - $(QEMU_SYS) ./fuzzer32 $(FUZZERTEST) $(FUZZER_FLAGS) + $(QEMU_SYS) ./fuzzer32 -v $(FUZZERTEST) $(FUZZER_FLAGS) test-zbuff: zbufftest $(QEMU_SYS) ./zbufftest $(ZSTREAM_TESTTIME) @@ -312,7 +312,7 @@ test-zbuff32: zbufftest32 $(QEMU_SYS) ./zbufftest32 $(ZSTREAM_TESTTIME) test-zstream: zstreamtest - $(QEMU_SYS) ./zstreamtest $(ZSTREAM_TESTTIME) $(FUZZER_FLAGS) + $(QEMU_SYS) ./zstreamtest -v $(ZSTREAM_TESTTIME) $(FUZZER_FLAGS) $(QEMU_SYS) ./zstreamtest --mt -t1 $(ZSTREAM_TESTTIME) $(FUZZER_FLAGS) $(QEMU_SYS) ./zstreamtest --newapi -t1 $(ZSTREAM_TESTTIME) $(FUZZER_FLAGS) $(QEMU_SYS) ./zstreamtest --opaqueapi -t1 $(ZSTREAM_TESTTIME) $(FUZZER_FLAGS) From b5e7f6c0f3e023d9a6622d2b227bf6d15c73c574 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Wed, 13 Dec 2017 10:55:24 -0800 Subject: [PATCH 124/145] [fileio] Fix window size MB calculation Test command: ``` head -c 10000 /dev/zero | ./zstd -c --zstd=wlog=12 | ./zstd -M2048 -t ``` --- programs/fileio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/fileio.c b/programs/fileio.c index 101c99b1a..4758fa0ed 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -1149,7 +1149,7 @@ static void FIO_zstdErrorHelp(dRess_t* ress, size_t ret, char const* srcFileName if (ret == 0) { U32 const windowSize = (U32)header.windowSize; U32 const windowLog = BIT_highbit32(windowSize) + ((windowSize & (windowSize - 1)) != 0); - U32 const windowMB = (windowSize >> 20) + (windowSize & ((1 MB) - 1)); + U32 const windowMB = (windowSize >> 20) + ((windowSize & ((1 MB) - 1)) != 0); assert(header.windowSize <= (U64)((U32)-1)); assert(g_memLimit > 0); DISPLAYLEVEL(1, "%s : Window size larger than maximum : %llu > %u\n", From 22727a7467ff55892c6b9775a0c190cecc8b9b5c Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Tue, 12 Dec 2017 17:37:06 -0800 Subject: [PATCH 125/145] Fix cdict compressor repcodes --- lib/compress/zstd_compress.c | 6 +++++ tests/fuzzer.c | 44 ++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 3f39875f5..8cd288d2a 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1033,6 +1033,12 @@ static size_t ZSTD_copyCCtx_internal(ZSTD_CCtx* dstCCtx, /* copy entropy tables */ memcpy(dstCCtx->entropy, srcCCtx->entropy, sizeof(ZSTD_entropyCTables_t)); + /* copy repcodes */ + { + int i; + for (i = 0; i < ZSTD_REP_NUM; ++i) + dstCCtx->seqStore.rep[i] = srcCCtx->seqStore.rep[i]; + } return 0; } diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 46671a15c..f799b5f9e 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -835,6 +835,50 @@ static int basicUnitTests(U32 seed, double compressibility) } DISPLAYLEVEL(4, "OK \n"); + DISPLAYLEVEL(4, "test%3i : Dictionary with non-default repcodes : ", testNb++); + { U32 u; for (u=0; u= dictLimit) goto _output_error; + MEM_writeLE32(dictPtr + 0, 10); + MEM_writeLE32(dictPtr + 4, 10); + MEM_writeLE32(dictPtr + 8, 10); + /* Set the last 8 bytes to 'x' */ + memset((BYTE*)dictBuffer + dictSize - 8, 'x', 8); + } + /* The optimal parser checks all the repcodes. + * Make sure at least one is a match >= targetLength so that it is + * immediately chosen. This will make sure that the compressor and + * decompressor agree on at least one of the repcodes. + */ + { size_t dSize; + BYTE data[1024]; + ZSTD_compressionParameters const cParams = ZSTD_getCParams(19, CNBuffSize, dictSize); + ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(dictBuffer, dictSize, + ZSTD_dlm_byRef, ZSTD_dm_auto, + cParams, ZSTD_defaultCMem); + memset(data, 'x', sizeof(data)); + cSize = ZSTD_compress_usingCDict(cctx, compressedBuffer, compressedBufferSize, + data, sizeof(data), cdict); + ZSTD_freeCDict(cdict); + if (ZSTD_isError(cSize)) { DISPLAYLEVEL(5, "Compression error %s : ", ZSTD_getErrorName(cSize)); goto _output_error; } + dSize = ZSTD_decompress_usingDict(dctx, decodedBuffer, sizeof(data), compressedBuffer, cSize, dictBuffer, dictSize); + if (ZSTD_isError(dSize)) { DISPLAYLEVEL(5, "Decompression error %s : ", ZSTD_getErrorName(dSize)); goto _output_error; } + if (memcmp(data, decodedBuffer, sizeof(data))) { DISPLAYLEVEL(5, "Data corruption : "); goto _output_error; } + } + DISPLAYLEVEL(4, "OK \n"); + ZSTD_freeCCtx(cctx); free(dictBuffer); free(samplesSizes); From 311878dec3ae7a9d9cf54883c0dac8c1ccedc81a Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 13 Dec 2017 11:48:30 -0800 Subject: [PATCH 126/145] Improved tests - building cli from /tests preserves potential flags in MOREFLAGS (such as asan/usan) - MT dictionary tests check for MT capability (MT is not enabled by default for zstd32) --- tests/Makefile | 6 +++--- tests/playTests.sh | 7 +++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/tests/Makefile b/tests/Makefile index faa7d019c..853f4ee89 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -76,13 +76,13 @@ allnothread: fullbench fuzzer paramgrill datagen decodecorpus dll: fuzzer-dll zstreamtest-dll zstd: - $(MAKE) -C $(PRGDIR) $@ MOREFLAGS="$(DEBUGFLAGS)" + $(MAKE) -C $(PRGDIR) $@ MOREFLAGS+="$(DEBUGFLAGS)" zstd32: - $(MAKE) -C $(PRGDIR) $@ MOREFLAGS="$(DEBUGFLAGS)" + $(MAKE) -C $(PRGDIR) $@ MOREFLAGS+="$(DEBUGFLAGS)" zstd-nolegacy: - $(MAKE) -C $(PRGDIR) $@ MOREFLAGS="$(DEBUGFLAGS)" + $(MAKE) -C $(PRGDIR) $@ MOREFLAGS+="$(DEBUGFLAGS)" gzstd: $(MAKE) -C $(PRGDIR) zstd HAVE_ZLIB=1 MOREFLAGS="$(DEBUGFLAGS)" diff --git a/tests/playTests.sh b/tests/playTests.sh index 6b78b305a..67732d3a1 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -297,8 +297,11 @@ cp $TESTFILE tmp $ZSTD -f tmp -D tmpDict $ZSTD -d tmp.zst -D tmpDict -fo result $DIFF $TESTFILE result -$ECHO "- Test dictionary compression with multithreading " -./datagen -g5M | $ZSTD -T2 -D tmpDict | $ZSTD -t -D tmpDict # fails with v1.3.2 +if [ -n "$hasMT" ] +then + $ECHO "- Test dictionary compression with multithreading " + ./datagen -g5M | $ZSTD -T2 -D tmpDict | $ZSTD -t -D tmpDict # fails with v1.3.2 +fi $ECHO "- Create second (different) dictionary " $ZSTD --train *.c ../programs/*.c ../programs/*.h -o tmpDictC $ZSTD -d tmp.zst -D tmpDictC -fo result && die "wrong dictionary not detected!" From 82bc8fe0cc3746d9b02b86c1bddaed842d07f755 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Wed, 13 Dec 2017 12:04:46 -0800 Subject: [PATCH 127/145] [fileio] Refuse to remove non-regular file --- programs/fileio.c | 12 ++++++++++-- tests/playTests.sh | 8 ++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/programs/fileio.c b/programs/fileio.c index 101c99b1a..a0e370bac 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -261,6 +261,10 @@ void FIO_setLdmHashEveryLog(unsigned ldmHashEveryLog) { * @result : Unlink `fileName`, even if it's read-only */ static int FIO_remove(const char* path) { + if (!UTIL_isRegularFile(path)) { + DISPLAYLEVEL(2, "zstd: Refusing to remove non-regular file %s\n", path); + return 0; + } #if defined(_WIN32) || defined(WIN32) /* windows doesn't allow remove read-only files, * so try to make it writable first */ @@ -314,9 +318,13 @@ static FILE* FIO_openDstFile(const char* dstFileName) g_sparseFileSupport = ZSTD_SPARSE_DEFAULT; } - if (strcmp (dstFileName, nulmark)) { /* not /dev/null */ + if (UTIL_isRegularFile(dstFileName)) { + FILE* fCheck; + if (!strcmp(dstFileName, nulmark)) { + EXM_THROW(40, "%s is unexpectedly a regular file", dstFileName); + } /* Check if destination file already exists */ - FILE* const fCheck = fopen( dstFileName, "rb" ); + fCheck = fopen( dstFileName, "rb" ); if (fCheck != NULL) { /* dst file exists, authorization prompt */ fclose(fCheck); if (!g_overwrite) { diff --git a/tests/playTests.sh b/tests/playTests.sh index 299c2d883..402808c0d 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -56,10 +56,12 @@ fi isWindows=false INTOVOID="/dev/null" +DEVDEVICE="/dev/zero" case "$OS" in Windows*) isWindows=true INTOVOID="NUL" + DEVDEVICE="NUL" ;; esac @@ -159,6 +161,12 @@ $ZSTD -f --rm tmp test ! -f tmp # tmp should no longer be present $ZSTD -f -d --rm tmp.zst test ! -f tmp.zst # tmp.zst should no longer be present +$ECHO "test : should quietly not remove non-regular file" +$ECHO hello > tmp +$ZSTD tmp -f -o "$DEVDEVICE" 2>tmplog > "$INTOVOID" +grep -v "Refusing to remove non-regular file" tmplog +rm -f tmplog +$ZSTD tmp -f -o "$INTONULL" 2>&1 | grep -v "Refusing to remove non-regular file" $ECHO "test : --rm on stdin" $ECHO a | $ZSTD --rm > $INTOVOID # --rm should remain silent rm tmp From d23eb9a098ce99ae0fe039496385687c0e574c60 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 13 Dec 2017 15:35:49 -0800 Subject: [PATCH 128/145] zstreamtest : added missing CHECK_Z() --- lib/compress/zstd_compress.c | 2 +- tests/zstreamtest.c | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 7586c3a0c..34bfb737c 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2566,7 +2566,7 @@ size_t ZSTD_initCStream_internal(ZSTD_CStream* zcs, if (zcs->cdictLocal == NULL) return ERROR(memory_allocation); } else { if (cdict) { - params.cParams = ZSTD_getCParamsFromCDict(cdict); /* cParams are enforced from cdict; this includes windowLog (should it be enforced from `params` instead ?) */ + params.cParams = ZSTD_getCParamsFromCDict(cdict); /* cParams are enforced from cdict; it includes windowLog */ } ZSTD_freeCDict(zcs->cdictLocal); zcs->cdictLocal = NULL; diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index 67ef6ce80..70922e2e0 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -710,9 +710,9 @@ static int basicUnitTests(U32 seed, double compressibility) const BYTE* const srcToCopy = (const BYTE*)CNBuffer + start; BYTE* const dst = (BYTE*)CNBuffer + start - offset; DISPLAYLEVEL(3, "test%3i : compress %u bytes with multiple threads + dictionary : ", testNb++, (U32)srcSize); - ZSTD_CCtx_setParameter(zc, ZSTD_p_compressionLevel, 3); - ZSTD_CCtx_setParameter(zc, ZSTD_p_nbThreads, 2); - ZSTD_CCtx_setParameter(zc, ZSTD_p_jobSize, jobSize); + CHECK_Z( ZSTD_CCtx_setParameter(zc, ZSTD_p_compressionLevel, 3) ); + CHECK_Z( ZSTD_CCtx_setParameter(zc, ZSTD_p_nbThreads, 2) ); + CHECK_Z( ZSTD_CCtx_setParameter(zc, ZSTD_p_jobSize, jobSize) ); assert(start > offset); assert(start + segLength < COMPRESSIBLE_NOISE_LENGTH); memcpy(dst, srcToCopy, segLength); /* create a long repetition at long distance for job 2 */ @@ -726,9 +726,9 @@ static int basicUnitTests(U32 seed, double compressibility) { ZSTD_compressionParameters const cParams = ZSTD_getCParams(1, 4 KB, dictionary.filled); /* intentionnally lies on estimatedSrcSize, to push cdict into targeting a small window size */ ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(dictionary.start, dictionary.filled, ZSTD_dlm_byRef, ZSTD_dm_fullDict, cParams, ZSTD_defaultCMem); DISPLAYLEVEL(5, "cParams.windowLog = %u : ", cParams.windowLog); - ZSTD_CCtx_refCDict(zc, cdict); + CHECK_Z( ZSTD_CCtx_refCDict(zc, cdict) ); CHECK_Z( ZSTD_compress_generic(zc, &outBuff, &inBuff, ZSTD_e_end) ); - ZSTD_CCtx_refCDict(zc, NULL); /* do not keep a reference to cdict, as its lifetime ends */ + CHECK_Z( ZSTD_CCtx_refCDict(zc, NULL) ); /* do not keep a reference to cdict, as its lifetime ends */ ZSTD_freeCDict(cdict); } if (inBuff.pos != inBuff.size) goto _output_error; /* entire input should be consumed */ From 4680e85bdf6d6862564a7465b77f9243592cc66a Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Tue, 12 Dec 2017 18:32:50 -0800 Subject: [PATCH 129/145] Allow -o with multiple files --- programs/fileio.c | 26 +++++++++++--------------- programs/fileio.h | 4 ++-- programs/zstdcli.c | 10 ++-------- tests/playTests.sh | 16 ++++++++++++++++ 4 files changed, 31 insertions(+), 25 deletions(-) diff --git a/programs/fileio.c b/programs/fileio.c index 5a8840b5b..16c2cb8fe 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -939,7 +939,7 @@ int FIO_compressFilename(const char* dstFileName, const char* srcFileName, int FIO_compressMultipleFilenames(const char** inFileNamesTable, unsigned nbFiles, - const char* suffix, + const char* outFileName, const char* suffix, const char* dictFileName, int compressionLevel, ZSTD_compressionParameters* comprParams) { @@ -955,16 +955,15 @@ int FIO_compressMultipleFilenames(const char** inFileNamesTable, unsigned nbFile /* init */ if (dstFileName==NULL) EXM_THROW(27, "FIO_compressMultipleFilenames : allocation error for dstFileName"); - if (suffix == NULL) + if (outFileName == NULL && suffix == NULL) EXM_THROW(28, "FIO_compressMultipleFilenames : dst unknown"); /* should never happen */ /* loop on each file */ - if (!strcmp(suffix, stdoutmark)) { + if (outFileName != NULL) { unsigned u; - ress.dstFile = stdout; - SET_BINARY_MODE(stdout); + ress.dstFile = FIO_openDstFile(outFileName); for (u=0; u1)) { - DISPLAY("Too many files (%u) on the command line. \n", filenameIdx); - CLEAN_RETURN(filenameIdx); - } - #ifndef ZSTD_NOCOMPRESS /* check compression level limits */ { int const maxCLevel = ultra ? ZSTD_maxCLevel() : ZSTDCLI_CLEVEL_MAX; @@ -854,7 +848,7 @@ int main(int argCount, const char* argv[]) if ((filenameIdx==1) && outFileName) operationResult = FIO_compressFilename(outFileName, filenameTable[0], dictFileName, cLevel, &compressionParams); else - operationResult = FIO_compressMultipleFilenames(filenameTable, filenameIdx, outFileName ? outFileName : suffix, dictFileName, cLevel, &compressionParams); + operationResult = FIO_compressMultipleFilenames(filenameTable, filenameIdx, outFileName, suffix, dictFileName, cLevel, &compressionParams); #else (void)suffix; DISPLAY("Compression not supported\n"); @@ -872,7 +866,7 @@ int main(int argCount, const char* argv[]) if (filenameIdx==1 && outFileName) operationResult = FIO_decompressFilename(outFileName, filenameTable[0], dictFileName); else - operationResult = FIO_decompressMultipleFilenames(filenameTable, filenameIdx, outFileName ? outFileName : ZSTD_EXTENSION, dictFileName); + operationResult = FIO_decompressMultipleFilenames(filenameTable, filenameIdx, outFileName, dictFileName); #else DISPLAY("Decompression not supported\n"); #endif diff --git a/tests/playTests.sh b/tests/playTests.sh index 402808c0d..773da845b 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -173,6 +173,22 @@ rm tmp $ZSTD -f tmp && die "tmp not present : should have failed" test ! -f tmp.zst # tmp.zst should not be created +$ECHO "test : compress multiple files" +$ECHO hello > tmp1 +$ECHO world > tmp2 +$ZSTD tmp1 tmp2 -o "$INTOVOID" +$ZSTD tmp1 tmp2 -c | $ZSTD -t +$ZSTD tmp1 tmp2 -o tmp.zst +test ! -f tmp1.zst +test ! -f tmp2.zst +$ZSTD tmp1 tmp2 +$ZSTD -t tmp1.zst tmp2.zst +$ZSTD -dc tmp1.zst tmp2.zst +$ZSTD tmp1.zst tmp2.zst -o "$INTOVOID" +$ZSTD -d tmp1.zst tmp2.zst -o tmp +rm tmp* + + $ECHO "\n===> Advanced compression parameters " $ECHO "Hello world!" | $ZSTD --zstd=windowLog=21, - -o tmp.zst && die "wrong parameters not detected!" From 5432ef6921b840c6cfe25f5d8ffcc87f4f91deff Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 13 Dec 2017 17:45:26 -0800 Subject: [PATCH 130/145] fixes adaptation on srcSize This patch restores capability for each file to receive adapted compression parameters depending on its size. The bug breaking this feature was relatively silly : setting a parameter with a value "0" is supposed to be a no-op. Unfortunately, it would pin down compression parameters as if they were manually set, preventing later automatic adaptation. Unfortunately, I'm currently short of a test case that could check this situation and trigger an error. Compression parameters selection between tableID 0,1,2,3 is largely internal, leaving no trace to outside world, not even in frame header. --- lib/compress/zstd_compress.c | 12 ++++++++++-- programs/fileio.c | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 8cd288d2a..d52261aae 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -144,6 +144,8 @@ const seqStore_t* ZSTD_getSeqStore(const ZSTD_CCtx* ctx) { return &(ctx->seqStor static ZSTD_compressionParameters ZSTD_getCParamsFromCCtxParams( ZSTD_CCtx_params CCtxParams, U64 srcSizeHint, size_t dictSize) { + DEBUGLOG(4, "ZSTD_getCParamsFromCCtxParams: srcSize = %u, dictSize = %u", + (U32)srcSizeHint, (U32)dictSize); return (CCtxParams.compressionLevel == ZSTD_CLEVEL_CUSTOM) ? CCtxParams.cParams : ZSTD_getCParams(CCtxParams.compressionLevel, srcSizeHint, dictSize); @@ -151,18 +153,22 @@ static ZSTD_compressionParameters ZSTD_getCParamsFromCCtxParams( static void ZSTD_cLevelToCCtxParams_srcSize(ZSTD_CCtx_params* CCtxParams, U64 srcSize) { + DEBUGLOG(4, "ZSTD_cLevelToCCtxParams_srcSize: srcSize = %u", + (U32)srcSize); CCtxParams->cParams = ZSTD_getCParamsFromCCtxParams(*CCtxParams, srcSize, 0); CCtxParams->compressionLevel = ZSTD_CLEVEL_CUSTOM; } static void ZSTD_cLevelToCParams(ZSTD_CCtx* cctx) { + DEBUGLOG(4, "ZSTD_cLevelToCParams"); ZSTD_cLevelToCCtxParams_srcSize( &cctx->requestedParams, cctx->pledgedSrcSizePlusOne-1); } static void ZSTD_cLevelToCCtxParams(ZSTD_CCtx_params* CCtxParams) { + DEBUGLOG(4, "ZSTD_cLevelToCCtxParams"); ZSTD_cLevelToCCtxParams_srcSize(CCtxParams, 0); } @@ -261,7 +267,7 @@ size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, unsigned v case ZSTD_p_targetLength: case ZSTD_p_compressionStrategy: if (cctx->cdict) return ERROR(stage_wrong); - ZSTD_cLevelToCParams(cctx); /* Can optimize if srcSize is known */ + if (value>0) ZSTD_cLevelToCParams(cctx); /* Can optimize if srcSize is known */ return ZSTD_CCtxParam_setParameter(&cctx->requestedParams, param, value); case ZSTD_p_contentSizeFlag: @@ -422,7 +428,7 @@ size_t ZSTD_CCtxParam_setParameter( #endif case ZSTD_p_enableLongDistanceMatching : - if (value != 0) { + if (value) { ZSTD_cLevelToCCtxParams(CCtxParams); CCtxParams->cParams.windowLog = ZSTD_LDM_DEFAULT_WINDOW_LOG; } @@ -3056,6 +3062,8 @@ ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, unsigned long l size_t const addedSize = srcSizeHint ? 0 : 500; U64 const rSize = srcSizeHint+dictSize ? srcSizeHint+dictSize+addedSize : (U64)-1; U32 const tableID = (rSize <= 256 KB) + (rSize <= 128 KB) + (rSize <= 16 KB); /* intentional underflow for srcSizeHint == 0 */ + DEBUGLOG(4, "ZSTD_getCParams: cLevel=%i, srcSize=%u, dictSize=%u => table %u", + compressionLevel, (U32)srcSizeHint, (U32)dictSize, tableID); #if defined(ZSTD_DEBUG) && (ZSTD_DEBUG>=1) static int g_monotonicTest = 1; diff --git a/programs/fileio.c b/programs/fileio.c index 4758fa0ed..879954078 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -446,7 +446,7 @@ static cRess_t FIO_createCResources(const char* dictFileName, int cLevel, DISPLAYLEVEL(5,"set nb threads = %u \n", g_nbThreads); CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_nbThreads, g_nbThreads) ); /* dictionary */ - CHECK( ZSTD_CCtx_setPledgedSrcSize(ress.cctx, srcSize) ); /* just for dictionary loading, using good compression parameters */ + CHECK( ZSTD_CCtx_setPledgedSrcSize(ress.cctx, srcSize) ); /* just for dictionary loading, for compression parameters adaptation */ CHECK( ZSTD_CCtx_loadDictionary(ress.cctx, dictBuffer, dictBuffSize) ); CHECK( ZSTD_CCtx_setPledgedSrcSize(ress.cctx, ZSTD_CONTENTSIZE_UNKNOWN) ); /* reset */ From 2e97a6d464d85e82584ba77f971526f1f1fd106b Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 13 Dec 2017 18:50:05 -0800 Subject: [PATCH 131/145] fixed minor declaration-after-statement warning --- lib/compress/zstd_compress.c | 6 ++++-- programs/fileio.c | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index d52261aae..02235c11a 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1738,6 +1738,8 @@ static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx, op += cSize; assert(dstCapacity >= cSize); dstCapacity -= cSize; + DEBUGLOG(5, "ZSTD_compress_frameChunk: adding a block of size %u", + (U32)cSize); } } if (lastFrameChunk && (op>ostart)) cctx->stage = ZSTDcs_ending; @@ -3062,8 +3064,6 @@ ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, unsigned long l size_t const addedSize = srcSizeHint ? 0 : 500; U64 const rSize = srcSizeHint+dictSize ? srcSizeHint+dictSize+addedSize : (U64)-1; U32 const tableID = (rSize <= 256 KB) + (rSize <= 128 KB) + (rSize <= 16 KB); /* intentional underflow for srcSizeHint == 0 */ - DEBUGLOG(4, "ZSTD_getCParams: cLevel=%i, srcSize=%u, dictSize=%u => table %u", - compressionLevel, (U32)srcSizeHint, (U32)dictSize, tableID); #if defined(ZSTD_DEBUG) && (ZSTD_DEBUG>=1) static int g_monotonicTest = 1; @@ -3073,6 +3073,8 @@ ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, unsigned long l } #endif + DEBUGLOG(4, "ZSTD_getCParams: cLevel=%i, srcSize=%u, dictSize=%u => table %u", + compressionLevel, (U32)srcSizeHint, (U32)dictSize, tableID); if (compressionLevel <= 0) compressionLevel = ZSTD_CLEVEL_DEFAULT; /* 0 == default; no negative compressionLevel yet */ if (compressionLevel > ZSTD_MAX_CLEVEL) compressionLevel = ZSTD_MAX_CLEVEL; { ZSTD_compressionParameters const cp = ZSTD_defaultCParameters[tableID][compressionLevel]; diff --git a/programs/fileio.c b/programs/fileio.c index 879954078..68032af94 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -781,6 +781,8 @@ static int FIO_compressFilename_internal(cRess_t ress, &outBuff, &inBuff, ZSTD_e_continue) ); /* Write compressed stream */ + DISPLAYLEVEL(6, "ZSTD_compress_generic,ZSTD_e_continue: generated %u bytes \n", + (U32)outBuff.pos); if (outBuff.pos) { size_t const sizeCheck = fwrite(ress.dstBuffer, 1, outBuff.pos, dstFile); if (sizeCheck!=outBuff.pos) @@ -816,6 +818,8 @@ static int FIO_compressFilename_internal(cRess_t ress, EXM_THROW(26, "Compression error during frame end : %s", ZSTD_getErrorName(result)); } + DISPLAYLEVEL(6, "ZSTD_compress_generic,ZSTD_e_end: generated %u bytes \n", + (U32)outBuff.pos); { size_t const sizeCheck = fwrite(ress.dstBuffer, 1, outBuff.pos, dstFile); if (sizeCheck != outBuff.pos) EXM_THROW(27, "Write error : cannot write frame end into %s", dstFileName); From 281f06e01f6ad818769e71804f625c9750ac459c Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 14 Dec 2017 11:47:02 -0800 Subject: [PATCH 132/145] saves 3-bytes on small input with streaming API zstd streaming API was adding a null-block at end of frame for small input. Reason is : on small input, a single block is enough. ZSTD_CStream would size its input buffer to expect a single block of this size, automatically triggering a flush on reaching this size. Unfortunately, that last byte was generally received before the "end" directive (at least in `fileio`). The later "end" directive would force the creation of a 3-bytes last block to indicate end of frame. The solution is to not flush automatically, which is btw the expected behavior. It happens in this case because blocksize is defined with exactly the same size as input. Just adding one-byte is enough to stop triggering the automatic flush. I initially looked at another solution, solving the problem directly in the compression context. But it felt awkward. Now, the underlying compression API `ZSTD_compressContinue()` would take the decision the close a frame on reaching its expected end (`pledgedSrcSize`). This feels awkward, a responsability over-reach, beyond the definition of this API. ZSTD_compressContinue() is clearly documented as a guaranteed flush, with ZSTD_compressEnd() generating a guaranteed end. I faced similar issue when trying to port a similar mechanism at the higher streaming layer. Having ZSTD_CStream end a frame automatically on reaching `pledgedSrcSize` can surprise the caller, since it did not explicitly requested an end of frame. The only sensible action remaining after that is to end the frame with no additional input. This adds additional logic in the ZSTD_CStream state to check this condition. Plus some potential confusion on the meaning of ZSTD_endStream() with no additional input (ending confirmation ? new 0-size frame ?) In the end, just enlarging input buffer by 1 byte feels the least intrusive change. It's also a contract remaining inside the streaming layer, so the logic is contained in this part of the code. The patch also introduces a new test checking that size of small frame is as expected, without additional 3-bytes null block. --- lib/compress/zstd_compress.c | 9 +++++---- tests/playTests.sh | 4 +++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 0dec78820..37dd5a4b0 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2527,7 +2527,8 @@ static size_t ZSTD_resetCStream_internal(ZSTD_CStream* zcs, zcs->inToCompress = 0; zcs->inBuffPos = 0; - zcs->inBuffTarget = zcs->blockSize; + zcs->inBuffTarget = zcs->blockSize + + (zcs->blockSize == pledgedSrcSize); /* for small input: avoid automatic flush on reaching end of block, since it would require to add a 3-bytes null block to end frame */ zcs->outBuffContentSize = zcs->outBuffFlushedSize = 0; zcs->streamStage = zcss_load; zcs->frameEnded = 0; @@ -2679,9 +2680,9 @@ size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs, /* check expectations */ DEBUGLOG(5, "ZSTD_compressStream_generic, flush=%u", (U32)flushMode); assert(zcs->inBuff != NULL); - assert(zcs->inBuffSize>0); - assert(zcs->outBuff!= NULL); - assert(zcs->outBuffSize>0); + assert(zcs->inBuffSize > 0); + assert(zcs->outBuff != NULL); + assert(zcs->outBuffSize > 0); assert(output->pos <= output->size); assert(input->pos <= input->size); diff --git a/tests/playTests.sh b/tests/playTests.sh index 9db3dadea..c48148fa2 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -126,6 +126,8 @@ $ZSTD -d > $INTOVOID && die "should have refused : compressed data from terminal fi $ECHO "test : null-length file roundtrip" $ECHO -n '' | $ZSTD - --stdout | $ZSTD -d --stdout +$ECHO "test : ensure small file doesn't add 3-bytes null block" +./datagen -g1 | $ZSTD | wc -c | grep "14" $ECHO "test : decompress file with wrong suffix (must fail)" $ZSTD -d tmpCompressed && die "wrong suffix error not detected!" $ZSTD -df tmp && die "should have refused : wrong extension" @@ -324,7 +326,7 @@ $DIFF $TESTFILE result if [ -n "$hasMT" ] then $ECHO "- Test dictionary compression with multithreading " - ./datagen -g5M | $ZSTD -T2 -D tmpDict | $ZSTD -t -D tmpDict # fails with v1.3.2 + ./datagen -g5M | $ZSTD -T2 -D tmpDict | $ZSTD -t -D tmpDict # fails with v1.3.2 fi $ECHO "- Create second (different) dictionary " $ZSTD --train *.c ../programs/*.c ../programs/*.h -o tmpDictC From a0e0985d38d05a9a28b8b94a9f9ece8120c146a8 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 14 Dec 2017 13:32:24 -0800 Subject: [PATCH 133/145] added test on small file on top of test on small stream --- tests/playTests.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/playTests.sh b/tests/playTests.sh index c48148fa2..c93c58fe8 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -127,7 +127,9 @@ fi $ECHO "test : null-length file roundtrip" $ECHO -n '' | $ZSTD - --stdout | $ZSTD -d --stdout $ECHO "test : ensure small file doesn't add 3-bytes null block" -./datagen -g1 | $ZSTD | wc -c | grep "14" +./datagen -g1 > tmp1 +$ZSTD tmp1 -c | wc -c | grep "14" +$ZSTD < tmp1 | wc -c | grep "14" $ECHO "test : decompress file with wrong suffix (must fail)" $ZSTD -d tmpCompressed && die "wrong suffix error not detected!" $ZSTD -df tmp && die "should have refused : wrong extension" From f48d34edba069b33d30ad75d1684b2a8e1071520 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Thu, 14 Dec 2017 13:00:20 -0800 Subject: [PATCH 134/145] [fileio] Merge end loop for small optimization --- programs/fileio.c | 51 +++++++++++++++++------------------------------ 1 file changed, 18 insertions(+), 33 deletions(-) diff --git a/programs/fileio.c b/programs/fileio.c index 339c968b2..b57ce14d2 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -118,12 +118,13 @@ static UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER; exit(error); \ } -#define CHECK(f) { \ - size_t const err = f; \ - if (ZSTD_isError(err)) { \ +#define CHECK_V(v, f) \ + v = f; \ + if (ZSTD_isError(v)) { \ DEBUGLOG(1, "%s \n", #f); \ - EXM_THROW(11, "%s", ZSTD_getErrorName(err)); \ -} } + EXM_THROW(11, "%s", ZSTD_getErrorName(v)); \ + } +#define CHECK(f) { size_t err; CHECK_V(err, f); } /*-************************************ @@ -733,6 +734,7 @@ static int FIO_compressFilename_internal(cRess_t ress, U64 readsize = 0; U64 compressedfilesize = 0; U64 const fileSize = UTIL_getFileSize(srcFileName); + ZSTD_EndDirective directive = ZSTD_e_continue; DISPLAYLEVEL(5, "%s: %u bytes \n", srcFileName, (U32)fileSize); switch (g_compressionType) { @@ -776,17 +778,20 @@ static int FIO_compressFilename_internal(cRess_t ress, ZSTD_CCtx_setPledgedSrcSize(ress.cctx, fileSize); /* Main compression loop */ - while (1) { + do { + size_t result; /* Fill input Buffer */ size_t const inSize = fread(ress.srcBuffer, (size_t)1, ress.srcBufferSize, srcFile); ZSTD_inBuffer inBuff = { ress.srcBuffer, inSize, 0 }; - if (inSize==0) break; readsize += inSize; - while (inBuff.pos != inBuff.size) { + if (inSize == 0 || (fileSize != UTIL_FILESIZE_UNKNOWN && readsize == fileSize)) + directive = ZSTD_e_end; + + result = 1; + while (inBuff.pos != inBuff.size || (directive == ZSTD_e_end && result != 0)) { ZSTD_outBuffer outBuff = { ress.dstBuffer, ress.dstBufferSize, 0 }; - CHECK( ZSTD_compress_generic(ress.cctx, - &outBuff, &inBuff, ZSTD_e_continue) ); + CHECK_V(result, ZSTD_compress_generic(ress.cctx, &outBuff, &inBuff, directive)); /* Write compressed stream */ DISPLAYLEVEL(6, "ZSTD_compress_generic,ZSTD_e_continue: generated %u bytes \n", @@ -796,7 +801,8 @@ static int FIO_compressFilename_internal(cRess_t ress, if (sizeCheck!=outBuff.pos) EXM_THROW(25, "Write error : cannot write compressed block into %s", dstFileName); compressedfilesize += outBuff.pos; - } } + } + } if (g_nbThreads > 1) { if (fileSize == UTIL_FILESIZE_UNKNOWN) DISPLAYUPDATE(2, "\rRead : %u MB", (U32)(readsize>>20)) @@ -813,28 +819,7 @@ static int FIO_compressFilename_internal(cRess_t ress, (U32)(readsize>>20), (U32)(fileSize>>20), (double)compressedfilesize/readsize*100); } - } - - /* End of Frame */ - { size_t result = 1; - while (result != 0) { - ZSTD_outBuffer outBuff = { ress.dstBuffer, ress.dstBufferSize, 0 }; - ZSTD_inBuffer inBuff = { NULL, 0, 0 }; - result = ZSTD_compress_generic(ress.cctx, - &outBuff, &inBuff, ZSTD_e_end); - if (ZSTD_isError(result)) { - EXM_THROW(26, "Compression error during frame end : %s", - ZSTD_getErrorName(result)); - } - DISPLAYLEVEL(6, "ZSTD_compress_generic,ZSTD_e_end: generated %u bytes \n", - (U32)outBuff.pos); - { size_t const sizeCheck = fwrite(ress.dstBuffer, 1, outBuff.pos, dstFile); - if (sizeCheck != outBuff.pos) - EXM_THROW(27, "Write error : cannot write frame end into %s", dstFileName); - } - compressedfilesize += outBuff.pos; - } - } + } while (directive != ZSTD_e_end); finish: /* Status */ From 2cff66b62f68ec2e76f401629c7083f65a8198ec Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 14 Dec 2017 16:11:20 -0800 Subject: [PATCH 135/145] version bump to v1.3.3 --- NEWS | 12 ++++++++++++ doc/zstd_manual.html | 29 ++++++++++++++++++----------- lib/zstd.h | 2 +- programs/Makefile | 4 ++-- programs/zstd.1 | 2 +- 5 files changed, 34 insertions(+), 15 deletions(-) diff --git a/NEWS b/NEWS index 215c0a329..a45960b96 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,15 @@ +v1.3.3 +perf: faster zstd_opt strategy (levels 17-19) +fix : bug #944 : multithreading with shared ditionary and large data, reported by @gsliepen +cli : fix : content size written in header by default +cli : fix : improved LZ4 format support, by @felixhandte +cli : new : hidden command `-S`, to benchmark multiple files while generating one result per file +api : fix : support large skippable frames, by @terrelln +api : fix : streaming interface was adding a useless 3-bytes null block to small frames +api : change : when setting `pledgedSrcSize`, use `ZSTD_CONTENTSIZE_UNKNOWN` macro value to mean "unknown" +build: fix : compilation under rhel6 and centos6, reported by @pixelb +build: added `check` target + v1.3.2 new : long range mode, using --long command, by Stella Lau (@stellamplau) new : ability to generate and decode magicless frames (#591) diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html index 872dbf637..da11ddb2a 100644 --- a/doc/zstd_manual.html +++ b/doc/zstd_manual.html @@ -1,10 +1,10 @@ -zstd 1.3.2 Manual +zstd 1.3.3 Manual -

    zstd 1.3.2 Manual

    +

    zstd 1.3.3 Manual


    Contents

      @@ -633,12 +633,12 @@ size_t ZSTD_estimateDDictSize(size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMet

      Advanced Streaming compression functions

      ZSTD_CStream* ZSTD_createCStream_advanced(ZSTD_customMem customMem);
       ZSTD_CStream* ZSTD_initStaticCStream(void* workspace, size_t workspaceSize);    /**< same as ZSTD_initStaticCCtx() */
      -size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize);   /**< pledgedSrcSize must be correct. If it is not known at init time, use ZSTD_CONTENTSIZE_UNKNOWN. Note that, for compatibility with older programs, a size of 0 is interepreted as "unknown". But it may change in some future version to mean "empty". */
      +size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize);   /**< pledgedSrcSize must be correct. If it is not known at init time, use ZSTD_CONTENTSIZE_UNKNOWN. Note that, for compatibility with older programs, "0" also disables frame content size field. It may be enabled in the future. */
       size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel); /**< creates of an internal CDict (incompatible with static CCtx), except if dict == NULL or dictSize < 8, in which case no dict is used. Note: dict is loaded with ZSTD_dm_auto (treated as a full zstd dictionary if it begins with ZSTD_MAGIC_DICTIONARY, else as raw content) and ZSTD_dlm_byCopy.*/
       size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, const void* dict, size_t dictSize,
      -                                             ZSTD_parameters params, unsigned long long pledgedSrcSize);  /**< pledgedSrcSize : if srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN. dict is loaded with ZSTD_dm_auto and ZSTD_dlm_byCopy. */
      +                                             ZSTD_parameters params, unsigned long long pledgedSrcSize);  /**< pledgedSrcSize must be correct. If srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN. dict is loaded with ZSTD_dm_auto and ZSTD_dlm_byCopy. */
       size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict);  /**< note : cdict will just be referenced, and must outlive compression session */
      -size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs, const ZSTD_CDict* cdict, ZSTD_frameParameters fParams, unsigned long long pledgedSrcSize);  /**< same as ZSTD_initCStream_usingCDict(), with control over frame parameters */
      +size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs, const ZSTD_CDict* cdict, ZSTD_frameParameters fParams, unsigned long long pledgedSrcSize);  /**< same as ZSTD_initCStream_usingCDict(), with control over frame parameters. pledgedSrcSize must be correct. If srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN. */
       

      size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pledgedSrcSize);
       

      start a new compression job, using same parameters from previous job. @@ -863,7 +863,8 @@ size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long * More threads improve speed, but also increase memory usage. * Can only receive a value > 1 if ZSTD_MULTITHREAD is enabled. * Special: value 0 means "do not change nbThreads" */ - ZSTD_p_jobSize, /* Size of a compression job. Each compression job is completed in parallel. + ZSTD_p_jobSize, /* Size of a compression job. This value is only enforced in streaming (non-blocking) mode. + * Each compression job is completed in parallel, so indirectly controls the nb of active threads. * 0 means default, which is dynamically determined based on compression parameters. * Job size must be a minimum of overlapSize, or 1 KB, whichever is largest * The minimum size is automatically and transparently enforced */ @@ -990,13 +991,19 @@ size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const void* prefix, size_t - Compression parameters cannot be changed once compression is started. - outpot->pos must be <= dstCapacity, input->pos must be <= srcSize - outpot->pos and input->pos will be updated. They are guaranteed to remain below their respective limit. - - @return provides the minimum amount of data still to flush from internal buffers + - In single-thread mode (default), function is blocking : it completed its job before returning to caller. + - In multi-thread mode, function is non-blocking : it just acquires a copy of input, and distribute job to internal worker threads, + and then immediately returns, just indicating that there is some data remaining to be flushed. + The function nonetheless guarantees forward progress : it will return only after it reads or write at least 1+ byte. + - Exception : in multi-threading mode, if the first call requests a ZSTD_e_end directive, it is blocking : it will complete compression before giving back control to caller. + - @return provides the minimum amount of data remaining to be flushed from internal buffers or an error code, which can be tested using ZSTD_isError(). - if @return != 0, flush is not fully completed, there is some data left within internal buffers. - - after a ZSTD_e_end directive, if internal buffer is not fully flushed, + if @return != 0, flush is not fully completed, there is still some data left within internal buffers. + This is useful to determine if a ZSTD_e_flush or ZSTD_e_end directive is completed. + - after a ZSTD_e_end directive, if internal buffer is not fully flushed (@return != 0), only ZSTD_e_end or ZSTD_e_flush operations are allowed. - It is necessary to fully flush internal buffers - before starting a new compression job, or changing compression parameters. + Before starting a new compression job, or changing compression parameters, + it is required to fully flush internal buffers.


      diff --git a/lib/zstd.h b/lib/zstd.h index e4541c3f4..9ac0a73dc 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -59,7 +59,7 @@ extern "C" { /*------ Version ------*/ #define ZSTD_VERSION_MAJOR 1 #define ZSTD_VERSION_MINOR 3 -#define ZSTD_VERSION_RELEASE 2 +#define ZSTD_VERSION_RELEASE 3 #define ZSTD_VERSION_NUMBER (ZSTD_VERSION_MAJOR *100*100 + ZSTD_VERSION_MINOR *100 + ZSTD_VERSION_RELEASE) ZSTDLIB_API unsigned ZSTD_versionNumber(void); /**< useful to check dll version */ diff --git a/programs/Makefile b/programs/Makefile index 37788db39..51888d3ef 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -226,8 +226,8 @@ clean: MD2ROFF = ronn MD2ROFF_FLAGS = --roff --warnings --manual="User Commands" --organization="zstd $(ZSTD_VERSION)" -zstd.1: zstd.1.md - cat $^ | $(MD2ROFF) $(MD2ROFF_FLAGS) | sed -n '/^\.\\\".*/!p' > $@ +zstd.1: zstd.1.md ../lib/zstd.h + cat $< | $(MD2ROFF) $(MD2ROFF_FLAGS) | sed -n '/^\.\\\".*/!p' > $@ .PHONY: man man: zstd.1 diff --git a/programs/zstd.1 b/programs/zstd.1 index eb53e2c33..e0902e858 100644 --- a/programs/zstd.1 +++ b/programs/zstd.1 @@ -1,5 +1,5 @@ . -.TH "ZSTD" "1" "November 2017" "zstd 1.3.2" "User Commands" +.TH "ZSTD" "1" "December 2017" "zstd 1.3.3" "User Commands" . .SH "NAME" \fBzstd\fR \- zstd, zstdmt, unzstd, zstdcat \- Compress or decompress \.zst files From 3cbfac1cdb7635b799809575208cc0318055a1fc Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 14 Dec 2017 23:29:00 -0800 Subject: [PATCH 136/145] updated levels 15-20 taking advantage of `btopt` improved speed to tune parameters. Levels 16-19 are stronger than previous release, making the graph more favorable. In theory, I should also update small-size tables, but I got lazy on that one ... --- lib/compress/zstd_compress.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 44f8c68a8..ebc9c1854 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2955,12 +2955,12 @@ static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEV { 22, 20, 22, 5, 5, 16, ZSTD_lazy2 }, /* level 12 */ { 22, 21, 22, 5, 5, 16, ZSTD_lazy2 }, /* level 13 */ { 22, 21, 22, 6, 5, 16, ZSTD_lazy2 }, /* level 14 */ - { 22, 21, 22, 5, 5, 16, ZSTD_btlazy2 }, /* level 15 */ - { 23, 22, 22, 5, 5, 16, ZSTD_btlazy2 }, /* level 16 */ - { 23, 22, 22, 4, 5, 24, ZSTD_btopt }, /* level 17 */ - { 23, 22, 22, 5, 4, 32, ZSTD_btopt }, /* level 18 */ - { 23, 23, 22, 6, 3, 48, ZSTD_btopt }, /* level 19 */ - { 25, 25, 23, 7, 3, 64, ZSTD_btultra }, /* level 20 */ + { 22, 21, 22, 4, 5, 16, ZSTD_btlazy2 }, /* level 15 */ + { 22, 21, 22, 4, 5, 48, ZSTD_btopt }, /* level 16 */ + { 23, 22, 22, 4, 4, 48, ZSTD_btopt }, /* level 17 */ + { 23, 22, 22, 5, 3, 64, ZSTD_btopt }, /* level 18 */ + { 23, 23, 22, 7, 3,128, ZSTD_btopt }, /* level 19 */ + { 25, 25, 23, 7, 3,128, ZSTD_btultra }, /* level 20 */ { 26, 26, 24, 7, 3,256, ZSTD_btultra }, /* level 21 */ { 27, 27, 25, 9, 3,512, ZSTD_btultra }, /* level 22 */ }, From 78de28239fa35a0b0ae7f1ab3b4b552d1c1c5d5c Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 15 Dec 2017 13:26:39 -0800 Subject: [PATCH 137/145] minor readme formatting update --- README.md | 47 +++++++++++++++++++------------------ doc/images/zstd_logo86.png | Bin 0 -> 5963 bytes 2 files changed, 24 insertions(+), 23 deletions(-) create mode 100644 doc/images/zstd_logo86.png diff --git a/README.md b/README.md index 99f320c45..50da24ba4 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,15 @@ - __Zstandard__, or `zstd` as short version, is a fast lossless compression algorithm, - targeting real-time compression scenarios at zlib-level and better compression ratios. +

      Zstandard

      -It is provided as an open-source BSD-licensed **C** library, -and a command line utility producing and decoding `.zst` and `.gz` files. -For other programming languages, -you can consult a list of known ports on [Zstandard homepage](http://www.zstd.net/#other-languages). +__Zstandard__, or `zstd` as short version, is a fast lossless compression algorithm, +targeting real-time compression scenarios at zlib-level and better compression ratios. +It's backed by a very fast entropy stage, provided by [Huff0 and FSE library](https://github.com/Cyan4973/FiniteStateEntropy). -| dev branch status | -|-------------------| -| [![Build Status][travisDevBadge]][travisLink] [![Build status][AppveyorDevBadge]][AppveyorLink] [![Build status][CircleDevBadge]][CircleLink] +The project is provided as an open-source BSD-licensed **C** library, +and a command line utility producing and decoding `.zst`, `.gz`, `.xz` and `.lz4` files. +Should your project require another programming language, +a list of known ports and bindings is provided on [Zstandard homepage](http://www.zstd.net/#other-languages). + +Development branch status : [![Build Status][travisDevBadge]][travisLink] [![Build status][AppveyorDevBadge]][AppveyorLink] [![Build status][CircleDevBadge]][CircleLink] [travisDevBadge]: https://travis-ci.org/facebook/zstd.svg?branch=dev "Continuous Integration test suite" [travisLink]: https://travis-ci.org/facebook/zstd @@ -17,8 +18,9 @@ you can consult a list of known ports on [Zstandard homepage](http://www.zstd.ne [CircleDevBadge]: https://circleci.com/gh/facebook/zstd/tree/dev.svg?style=shield "Short test suite" [CircleLink]: https://circleci.com/gh/facebook/zstd +### Benchmarks -As a reference, several fast compression algorithms were tested and compared +For reference, several fast compression algorithms were tested and compared on a server running Linux Debian (`Linux version 4.8.0-1-amd64`), with a Core i7-6700K CPU @ 4.0GHz, using [lzbench], an open-source in-memory benchmark by @inikep @@ -43,7 +45,9 @@ on the [Silesia compression corpus]. [LZ4]: http://www.lz4.org/ Zstd can also offer stronger compression ratios at the cost of compression speed. -Speed vs Compression trade-off is configurable by small increments. Decompression speed is preserved and remains roughly the same at all settings, a property shared by most LZ compression algorithms, such as [zlib] or lzma. +Speed vs Compression trade-off is configurable by small increments. +Decompression speed is preserved and remains roughly the same at all settings, +a property shared by most LZ compression algorithms, such as [zlib] or lzma. The following tests were run on a server running Linux Debian (`Linux version 4.8.0-1-amd64`) @@ -56,8 +60,8 @@ Compression Speed vs Ratio | Decompression Speed ---------------------------|-------------------- ![Compression Speed vs Ratio](doc/images/Cspeed4.png "Compression Speed vs Ratio") | ![Decompression Speed](doc/images/Dspeed4.png "Decompression Speed") -Several algorithms can produce higher compression ratios, but at slower speeds, falling outside of the graph. -For a larger picture including very slow modes, [click on this link](doc/images/DCspeed5.png). +A few other algorithms can produce higher compression ratios at slower speeds, falling outside of the graph. +For a larger picture including slow modes, [click on this link](doc/images/DCspeed5.png). ### The case for Small Data compression @@ -99,19 +103,16 @@ Dictionary gains are mostly effective in the first few KB. Then, the compression `zstd -D dictionaryName --decompress FILE.zst` -### Build - -Once you have the repository cloned, there are multiple ways provided to build Zstandard. +### Build instructions #### Makefile -If your system is compatible with a standard `make` (or `gmake`) binary generator, -you can simply run it at the root directory. -It will generate `zstd` within root directory. +If your system is compatible with standard `make` (or `gmake`), +invoking `make` in root directory will generate `zstd` cli in root directory. Other available options include: -- `make install` : create and install zstd binary, library and man page -- `make test` : create and run `zstd` and test tools on local platform +- `make install` : create and install zstd cli, library and man pages +- `make check` : create and run `zstd`, tests its behavior on local platform #### cmake @@ -143,8 +144,8 @@ Zstandard is dual-licensed under [BSD](LICENSE) and [GPLv2](COPYING). ### Contributing -The "dev" branch is the one where all contributions will be merged before reaching "master". -If you plan to propose a patch, please commit into the "dev" branch or its own feature branch. +The "dev" branch is the one where all contributions are merged before reaching "master". +If you plan to propose a patch, please commit into the "dev" branch, or its own feature branch. Direct commit to "master" are not permitted. For more information, please read [CONTRIBUTING](CONTRIBUTING.md). diff --git a/doc/images/zstd_logo86.png b/doc/images/zstd_logo86.png new file mode 100644 index 0000000000000000000000000000000000000000..216f228061c460c4e4725785486ebcff38804fbc GIT binary patch literal 5963 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 HN@xNAPrx2g literal 0 HcmV?d00001 From 64d1701c6476f79961e8c20a96986688d7e0a6e7 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Fri, 15 Dec 2017 13:28:34 -0800 Subject: [PATCH 138/145] remove last paragraph --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index 50da24ba4..3755315ad 100644 --- a/README.md +++ b/README.md @@ -148,7 +148,3 @@ The "dev" branch is the one where all contributions are merged before reaching " If you plan to propose a patch, please commit into the "dev" branch, or its own feature branch. Direct commit to "master" are not permitted. For more information, please read [CONTRIBUTING](CONTRIBUTING.md). - -### Miscellaneous - -Zstd entropy stage is provided by [Huff0 and FSE, from Finite State Entropy library](https://github.com/Cyan4973/FiniteStateEntropy). From 5c2f2ebfdb8fc8e074269ad4f85ac79267cc36fe Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 16 Dec 2017 12:48:13 -0800 Subject: [PATCH 139/145] zstdmt via compress_generic: reduce opportunity to free/create mtctx `zstreamtest --newapi` (and `--opaqueapi`) create and destroy way too many threads resulting in failure of tsan tests, and potentially connected to the qemu flaky tests. This is because, at each test, the nb of threads can be changed (random). The `--no-big-tests` directive reduce this choice to 1/2 threads, in order to limit memory usage, especially for qemu and 32-bits builds. Unfortunately, swapping between 1 and 2 threads is enough to constantly create/destroy new mtctx. This patch takes advantage of the following property : via compress_generic, no internal mtctx is needed for nbThreads < 2. As a consequence, when nbThreads == 2, the currently active mtctx is necessarily good. This dramatically reduces the nb of thread creations when invoking `zstreamtest --newapi --no-big-tests` (only when parent cctx itself is created, which is randomized to 1/256 tests). Expected outcome : - at a minimum : tsan tests shall now work continuously without exploding the thread counter - at best : flaky qemu tests on `zstreamtest --newapi --no-big-tests` may stop being flaky, due to less stress from constant thread creation/destruction Real world impact : minimal, I don't expect users to constantly change `nbThreads` between each invocation. If `nbThreads` remains stable, existing implementation re-uses existing mtctx. Also : `zstreamtest --newapi` but without `--no-big-tests` doesn't benefit as much, since this test can select a random `nbThreads` value between 1 and 4. The current patch only reduces opportunity to free/create mtctx (for example : 2->1->2 doesn't need a new mtctx) but doesn't completely eliminate it, since `nbThreads` can still change between 2/3/4. A more complete solution could be to only use 2 out of 4 allocated threads, thus keeping the pool at a constant size. This would require a larger change to `POOL_*` api though. --- lib/compress/zstd_compress.c | 4 +++- lib/compress/zstdmt_compress.c | 9 +++++++++ lib/compress/zstdmt_compress.h | 7 ++++++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index ebc9c1854..0a2d589d7 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2851,7 +2851,9 @@ size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, if ((cctx->pledgedSrcSizePlusOne-1) <= ZSTDMT_JOBSIZE_MIN) params.nbThreads = 1; /* do not invoke multi-threading when src size is too small */ if (params.nbThreads > 1) { - if (cctx->mtctx == NULL || cctx->appliedParams.nbThreads != params.nbThreads) { + if (cctx->mtctx == NULL || (params.nbThreads != ZSTDMT_getNbThreads(cctx->mtctx))) { + DEBUGLOG(4, "ZSTD_compress_generic: creating new mtctx for nbThreads=%u (previous: %u)", + params.nbThreads, ZSTDMT_getNbThreads(cctx->mtctx)); ZSTDMT_freeCCtx(cctx->mtctx); cctx->mtctx = ZSTDMT_createCCtx_advanced(params.nbThreads, cctx->customMem); if (cctx->mtctx == NULL) return ERROR(memory_allocation); diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index a5e996d3e..659992f34 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -459,6 +459,15 @@ size_t ZSTDMT_CCtxParam_setNbThreads(ZSTD_CCtx_params* params, unsigned nbThread return nbThreads; } +/* ZSTDMT_getNbThreads(): + * @return nb threads currently active in mtctx. + * mtctx must be valid */ +size_t ZSTDMT_getNbThreads(const ZSTDMT_CCtx* mtctx) +{ + assert(mtctx != NULL); + return mtctx->params.nbThreads; +} + ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbThreads, ZSTD_customMem cMem) { ZSTDMT_CCtx* mtctx; diff --git a/lib/compress/zstdmt_compress.h b/lib/compress/zstdmt_compress.h index 4209cf3c5..d12f0adb8 100644 --- a/lib/compress/zstdmt_compress.h +++ b/lib/compress/zstdmt_compress.h @@ -114,9 +114,14 @@ size_t ZSTDMT_CCtxParam_setMTCtxParameter(ZSTD_CCtx_params* params, ZSTDMT_param /* ZSTDMT_CCtxParam_setNbThreads() * Set nbThreads, and clamp it correctly, - * but also reset jobSize and overlapLog */ + * also reset jobSize and overlapLog */ size_t ZSTDMT_CCtxParam_setNbThreads(ZSTD_CCtx_params* params, unsigned nbThreads); +/* ZSTDMT_getNbThreads(): + * @return nb threads currently active in mtctx. + * mtctx must be valid */ +size_t ZSTDMT_getNbThreads(const ZSTDMT_CCtx* mtctx); + /*! ZSTDMT_initCStream_internal() : * Private use only. Init streaming operation. * expects params to be valid. From f299fa39ac07b9de897a5f0e930459528fec0bb5 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 19 Dec 2017 09:43:03 +0100 Subject: [PATCH 140/145] fix a subtle issue in continue mode The deep fuzzer tests caught a subtle bug that was probably there for a long time. The impact of the bug is not a crash, or any other clear error signal, rather, it reduces performance, by cutting data into smaller blocks. Eventually, the following test would fail because it produces too many 1-byte blocks, requiring more space than buffer can provide : `./zstreamtest_asan --mt -s3514 -t1678312 -i1678314` The root scenario is as follows : - Create context, initialize it using explicit parameters or a `cdict` to pin them down, set `pledgedSrcSize=1` - The compression parameters will not be adapted, but `windowSize` and `blockSize` will be automatically set to `1`. `windowSize` and `blockSize` are dynamic values, set within `ZSTD_resetCCtx_internal()`. The automatic adaptation makes it possible to generate smaller contexts for smaller input sizes. - Complete compression - New compression with same context, using same parameters, but `pledgedSrcSize=ZSTD_CONTENTSIZE_UNKNOWN` trigger "continue mode" - Continue mode doesn't modify blockSize, because it used to depend on `windowLog` only, but in fact, it also depends on `pledgedSrcSize`. - The "old" blocksize (1) is still there, next compression will use this value to cut input into blocks, resulting in more blocks and worse performance than necessary performance. Given the scenario, and its possible variants, I'm surprised it did not show up before. But I suspect it did show up, it's just that it never triggered an error, because "worse performance" is not a trigger. The above test is a special corner case, where performance is so impacted that it reaches an error case. The fix works, but I'm not completely pleased. I think the current code relies too much on implied relations between variables. This will likely break again in the future when some related part of the code change. Unfortunately, no time to make larger changes if we want to keep the release target for zstd v1.3.3. So a longer term fix will have to be considered after the release. To do : create a reliable test case which triggers this scenario for CI tests. --- lib/common/zstd_internal.h | 19 +++++++------- lib/compress/zstd_compress.c | 45 +++++++++++++++++++++------------- lib/compress/zstdmt_compress.c | 20 +++++++-------- 3 files changed, 47 insertions(+), 37 deletions(-) diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h index 2f8124825..5d2900eb7 100644 --- a/lib/common/zstd_internal.h +++ b/lib/common/zstd_internal.h @@ -57,20 +57,19 @@ extern "C" { extern int g_debuglog_enable; /* recommended values for ZSTD_DEBUG display levels : * 1 : no display, enables assert() only - * 2 : reserved for currently active debugging path - * 3 : events once per object lifetime (CCtx, CDict) + * 2 : reserved for currently active debug path + * 3 : events once per object lifetime (CCtx, CDict, etc.) * 4 : events once per frame * 5 : events once per block * 6 : events once per sequence (*very* verbose) */ -# define RAWLOG(l, ...) { \ - if ((g_debuglog_enable) & (l<=ZSTD_DEBUG)) { \ - fprintf(stderr, __VA_ARGS__); \ +# define RAWLOG(l, ...) { \ + if ((g_debuglog_enable) & (l<=ZSTD_DEBUG)) { \ + fprintf(stderr, __VA_ARGS__); \ } } -# define DEBUGLOG(l, ...) { \ - if ((g_debuglog_enable) & (l<=ZSTD_DEBUG)) { \ - fprintf(stderr, __FILE__ ": "); \ - fprintf(stderr, __VA_ARGS__); \ - fprintf(stderr, " \n"); \ +# define DEBUGLOG(l, ...) { \ + if ((g_debuglog_enable) & (l<=ZSTD_DEBUG)) { \ + fprintf(stderr, __FILE__ ": " __VA_ARGS__); \ + fprintf(stderr, " \n"); \ } } #else # define RAWLOG(l, ...) {} /* disabled */ diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index ebc9c1854..5f952b8c0 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -728,10 +728,7 @@ size_t ZSTD_estimateCStreamSize(int compressionLevel) { static U32 ZSTD_equivalentCParams(ZSTD_compressionParameters cParams1, ZSTD_compressionParameters cParams2) { - U32 bslog1 = MIN(cParams1.windowLog, ZSTD_BLOCKSIZELOG_MAX); - U32 bslog2 = MIN(cParams2.windowLog, ZSTD_BLOCKSIZELOG_MAX); - return (bslog1 == bslog2) /* same block size */ - & (cParams1.hashLog == cParams2.hashLog) + return (cParams1.hashLog == cParams2.hashLog) & (cParams1.chainLog == cParams2.chainLog) & (cParams1.strategy == cParams2.strategy) /* opt parser space */ & ((cParams1.searchLength==3) == (cParams2.searchLength==3)); /* hashlog3 space */ @@ -755,24 +752,29 @@ typedef enum { ZSTDb_not_buffered, ZSTDb_buffered } ZSTD_buffered_policy_e; /* ZSTD_sufficientBuff() : * check internal buffers exist for streaming if buffPol == ZSTDb_buffered . * Note : they are assumed to be correctly sized if ZSTD_equivalentCParams()==1 */ -static U32 ZSTD_sufficientBuff(size_t bufferSize, ZSTD_buffered_policy_e buffPol, ZSTD_compressionParameters cParams2, U64 pledgedSrcSize) +static U32 ZSTD_sufficientBuff(size_t bufferSize1, size_t blockSize1, + ZSTD_buffered_policy_e buffPol2, + ZSTD_compressionParameters cParams2, + U64 pledgedSrcSize) { size_t const windowSize = MAX(1, (size_t)MIN(((U64)1 << cParams2.windowLog), pledgedSrcSize)); - size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, windowSize); - size_t const neededBufferSize = (buffPol==ZSTDb_buffered) ? windowSize + blockSize : 0; - return (neededBufferSize <= bufferSize); + size_t const blockSize2 = MIN(ZSTD_BLOCKSIZE_MAX, windowSize); + size_t const neededBufferSize = (buffPol2==ZSTDb_buffered) ? windowSize + blockSize2 : 0; + return (blockSize2 <= blockSize1) /* seqStore space depends on blockSize */ + & (neededBufferSize <= bufferSize1); } /** Equivalence for resetCCtx purposes */ static U32 ZSTD_equivalentParams(ZSTD_CCtx_params params1, ZSTD_CCtx_params params2, - size_t buffSize1, + size_t buffSize1, size_t blockSize1, ZSTD_buffered_policy_e buffPol2, U64 pledgedSrcSize) { + DEBUGLOG(4, "ZSTD_equivalentParams: pledgedSrcSize=%u", (U32)pledgedSrcSize); return ZSTD_equivalentCParams(params1.cParams, params2.cParams) && ZSTD_equivalentLdmParams(params1.ldmParams, params2.ldmParams) && - ZSTD_sufficientBuff(buffSize1, buffPol2, params2.cParams, pledgedSrcSize); + ZSTD_sufficientBuff(buffSize1, blockSize1, buffPol2, params2.cParams, pledgedSrcSize); } /*! ZSTD_continueCCtx() : @@ -780,7 +782,11 @@ static U32 ZSTD_equivalentParams(ZSTD_CCtx_params params1, static size_t ZSTD_continueCCtx(ZSTD_CCtx* cctx, ZSTD_CCtx_params params, U64 pledgedSrcSize) { U32 const end = (U32)(cctx->nextSrc - cctx->base); - DEBUGLOG(4, "continue mode"); + size_t const windowSize = MAX(1, (size_t)MIN(((U64)1 << params.cParams.windowLog), pledgedSrcSize)); + size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, windowSize); + DEBUGLOG(4, "ZSTD_continueCCtx"); + + cctx->blockSize = blockSize; /* previous block size could be different even for same windowLog, due to pledgedSrcSize */ cctx->appliedParams = params; cctx->pledgedSrcSizePlusOne = pledgedSrcSize+1; cctx->consumedSrcSize = 0; @@ -813,8 +819,11 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams))); if (crp == ZSTDcrp_continue) { - if (ZSTD_equivalentParams(params, zc->appliedParams, zc->inBuffSize, zbuff, pledgedSrcSize)) { - DEBUGLOG(4, "ZSTD_equivalentParams()==1 -> continue mode"); + if (ZSTD_equivalentParams(params, zc->appliedParams, + zc->inBuffSize, zc->blockSize, + zbuff, pledgedSrcSize)) { + DEBUGLOG(4, "ZSTD_equivalentParams()==1 -> continue mode (wLog=%u, blockSize=%u)", + zc->appliedParams.cParams.windowLog, (U32)zc->blockSize); assert(!(params.ldmParams.enableLdm && params.ldmParams.hashEveryLog == ZSTD_LDM_HASHEVERYLOG_NOTSET)); zc->entropy->hufCTable_repeatMode = HUF_repeat_none; @@ -865,8 +874,8 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, tableSpace + tokenSpace + bufferSpace; DEBUGLOG(4, "Need %uKB workspace, including %uKB for tables, and %uKB for buffers", (U32)(neededSpace>>10), (U32)(tableSpace>>10), (U32)(bufferSpace>>10)); - DEBUGLOG(4, "chainSize: %u - hSize: %u - h3Size: %u - windowSize: %u", - (U32)chainSize, (U32)hSize, (U32)h3Size, (U32)windowSize); + DEBUGLOG(4, "chainSize: %u - hSize: %u - h3Size: %u - windowSize: %u - blockSize: %u", + (U32)chainSize, (U32)hSize, (U32)h3Size, (U32)windowSize, (U32)blockSize); if (zc->workSpaceSize < neededSpace) { /* too small : resize */ DEBUGLOG(4, "Need to update workSpaceSize from %uK to %uK", @@ -1666,6 +1675,7 @@ static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx, U32 const maxDist = (U32)1 << cctx->appliedParams.cParams.windowLog; assert(cctx->appliedParams.cParams.windowLog <= 31); + DEBUGLOG(5, "ZSTD_compress_frameChunk (blockSize=%u)", (U32)blockSize); if (cctx->appliedParams.fParams.checksumFlag && srcSize) XXH64_update(&cctx->xxhState, src, srcSize); @@ -1838,6 +1848,7 @@ static size_t ZSTD_compressContinue_internal (ZSTD_CCtx* cctx, cctx->lowLimit = lowLimitMax; } + DEBUGLOG(5, "ZSTD_compressContinue_internal (blockSize=%u)", (U32)cctx->blockSize); { size_t const cSize = frame ? ZSTD_compress_frameChunk (cctx, dst, dstCapacity, src, srcSize, lastFrameChunk) : ZSTD_compressBlock_internal (cctx, dst, dstCapacity, src, srcSize); @@ -2062,7 +2073,7 @@ size_t ZSTD_compressBegin_internal(ZSTD_CCtx* cctx, ZSTD_CCtx_params params, U64 pledgedSrcSize, ZSTD_buffered_policy_e zbuff) { - DEBUGLOG(4, "ZSTD_compressBegin_internal"); + DEBUGLOG(4, "ZSTD_compressBegin_internal: wlog=%u", params.cParams.windowLog); /* params are supposed to be fully validated at this point */ assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams))); assert(!((dict) && (cdict))); /* either dict or cdict, not both */ @@ -2086,7 +2097,7 @@ size_t ZSTD_compressBegin_advanced_internal(ZSTD_CCtx* cctx, ZSTD_CCtx_params params, unsigned long long pledgedSrcSize) { - DEBUGLOG(4, "ZSTD_compressBegin_advanced_internal"); + DEBUGLOG(4, "ZSTD_compressBegin_advanced_internal: wlog=%u", params.cParams.windowLog); /* compression parameters verification and optimization */ CHECK_F( ZSTD_checkCParams(params.cParams) ); return ZSTD_compressBegin_internal(cctx, diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index a5e996d3e..500806188 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -355,7 +355,7 @@ void ZSTDMT_compressChunk(void* jobDescription) if (job->cdict) { size_t const initError = ZSTD_compressBegin_advanced_internal(cctx, NULL, 0, ZSTD_dm_auto, job->cdict, job->params, job->fullFrameSize); - DEBUGLOG(4, "ZSTDMT_compressChunk: init using CDict"); + DEBUGLOG(4, "ZSTDMT_compressChunk: init using CDict (windowLog=%u)", job->params.cParams.windowLog); assert(job->firstChunk); /* only allowed for first job */ if (ZSTD_isError(initError)) { job->cSize = initError; goto _endJob; } } else { /* srcStart points at reloaded section */ @@ -367,9 +367,11 @@ void ZSTDMT_compressChunk(void* jobDescription) job->cSize = forceWindowError; goto _endJob; } - /* load dictionary in "content-only" mode (no header analysis) */ - { size_t const initError = ZSTD_compressBegin_advanced_internal(cctx, job->srcStart, job->prefixSize, ZSTD_dm_rawContent, NULL, jobParams, pledgedSrcSize); - DEBUGLOG(5, "ZSTD_compressBegin_advanced_internal called with windowLog = %u ", jobParams.cParams.windowLog); + DEBUGLOG(5, "ZSTDMT_compressChunk: invoking ZSTD_compressBegin_advanced_internal with windowLog = %u ", jobParams.cParams.windowLog); + { size_t const initError = ZSTD_compressBegin_advanced_internal(cctx, + job->srcStart, job->prefixSize, ZSTD_dm_rawContent, /* load dictionary in "content-only" mode (no header analysis) */ + NULL, + jobParams, pledgedSrcSize); if (ZSTD_isError(initError)) { DEBUGLOG(5, "ZSTD_compressBegin_advanced_internal error : %s ", ZSTD_getErrorName(initError)); job->cSize = initError; @@ -637,8 +639,7 @@ static size_t ZSTDMT_compress_advanced_internal( assert(jobParams.nbThreads == 0); assert(mtctx->cctxPool->totalCCtx == params.nbThreads); - DEBUGLOG(4, "ZSTDMT_compress_advanced_internal"); - DEBUGLOG(4, "nbChunks : %2u (raw chunkSize : %u bytes; fixed chunkSize: %u) ", + DEBUGLOG(4, "ZSTDMT_compress_advanced_internal: nbChunks=%2u (rawSize=%u bytes; fixedSize=%u) ", nbChunks, (U32)proposedChunkSize, (U32)avgChunkSize); if (nbChunks==1) { /* fallback to single-thread mode */ ZSTD_CCtx* const cctx = mtctx->cctxPool->cctx[0]; @@ -688,7 +689,7 @@ static size_t ZSTDMT_compress_advanced_internal( XXH64_update(&xxh64, srcStart + frameStartPos, chunkSize); } - DEBUGLOG(5, "posting job %u (%u bytes)", u, (U32)chunkSize); + DEBUGLOG(5, "ZSTDMT_compress_advanced_internal: posting job %u (%u bytes)", u, (U32)chunkSize); DEBUG_PRINTHEX(6, mtctx->jobs[u].srcStart, 12); POOL_add(mtctx->factory, ZSTDMT_compressChunk, &mtctx->jobs[u]); @@ -893,8 +894,7 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi { unsigned const jobID = zcs->nextJobID & zcs->jobIDMask; - DEBUGLOG(5, "ZSTDMT_createCompressionJob"); - DEBUGLOG(5, "preparing job %u to compress %u bytes with %u preload ", + DEBUGLOG(5, "ZSTDMT_createCompressionJob: preparing job %u to compress %u bytes with %u preload ", zcs->nextJobID, (U32)srcSize, (U32)zcs->dictSize); zcs->jobs[jobID].src = zcs->inBuff.buffer; zcs->jobs[jobID].srcStart = zcs->inBuff.buffer.start; @@ -945,7 +945,7 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi zcs->params.fParams.checksumFlag = 0; } } - DEBUGLOG(4, "posting job %u : %u bytes (end:%u) (note : doneJob = %u=>%u)", + DEBUGLOG(5, "ZSTDMT_createCompressionJob: posting job %u : %u bytes (end:%u) (note : doneJob = %u=>%u)", zcs->nextJobID, (U32)zcs->jobs[jobID].srcSize, zcs->jobs[jobID].lastChunk, From d88c6716633619b629129f6e7d536c1b57746263 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 19 Dec 2017 10:16:09 +0100 Subject: [PATCH 141/145] added test case for "wrong blockSize in continue mode" --- tests/fuzzer.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/fuzzer.c b/tests/fuzzer.c index f799b5f9e..d64b46ba3 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -25,6 +25,7 @@ #include /* free */ #include /* fgets, sscanf */ #include /* strcmp */ +#include #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_compressContinue, ZSTD_compressBlock */ #include "zstd.h" /* ZSTD_VERSION_STRING */ #include "zstd_errors.h" /* ZSTD_getErrorCode */ @@ -365,6 +366,23 @@ static int basicUnitTests(U32 seed, double compressibility) } DISPLAYLEVEL(4, "OK \n"); + DISPLAYLEVEL(4, "test%di : re-use CCtx with expanding block size : ", testNb++); + { ZSTD_CCtx* const cctx = ZSTD_createCCtx(); + ZSTD_parameters const params = ZSTD_getParams(1, ZSTD_CONTENTSIZE_UNKNOWN, 0); + assert(params.fParams.contentSizeFlag == 1); /* block size will be adapted if pledgedSrcSize is enabled */ + CHECK_Z( ZSTD_compressBegin_advanced(cctx, NULL, 0, params, 1 /*pledgedSrcSize*/) ); + CHECK_Z( ZSTD_compressEnd(cctx, compressedBuffer, compressedBufferSize, CNBuffer, 1) ); /* creates a block size of 1 */ + + CHECK_Z( ZSTD_compressBegin_advanced(cctx, NULL, 0, params, ZSTD_CONTENTSIZE_UNKNOWN) ); /* re-use same parameters */ + { size_t const inSize = 2* 128 KB; + size_t const outSize = ZSTD_compressBound(inSize); + CHECK_Z( ZSTD_compressEnd(cctx, compressedBuffer, outSize, CNBuffer, inSize) ); + /* will fail if blockSize is not resized */ + } + ZSTD_freeCCtx(cctx); + } + DISPLAYLEVEL(4, "OK \n"); + /* Static CCtx tests */ #define STATIC_CCTX_LEVEL 3 DISPLAYLEVEL(4, "test%3i : create static CCtx for level %u :", testNb++, STATIC_CCTX_LEVEL); From 574e75354bfa88084a3d162e49e7fdc9d9bc747d Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 19 Dec 2017 11:24:14 +0100 Subject: [PATCH 142/145] fuzzer: ensure existence of CHECK_Z macro beyond OS-X systems --- tests/fuzzer.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/tests/fuzzer.c b/tests/fuzzer.c index d64b46ba3..141bf6548 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -95,6 +95,22 @@ static unsigned FUZ_highbit32(U32 v32) } +/*============================================= +* Test macros +=============================================*/ +#define CHECK_Z(f) { \ + size_t const err = f; \ + if (ZSTD_isError(err)) { \ + DISPLAY("Error => %s : %s ", \ + #f, ZSTD_getErrorName(err)); \ + exit(1); \ +} } + +#define CHECK_V(var, fn) size_t const var = fn; if (ZSTD_isError(var)) goto _output_error +#define CHECK(fn) { CHECK_V(err, fn); } +#define CHECKPLUS(var, fn, more) { CHECK_V(var, fn); more; } + + /*============================================= * Memory Tests =============================================*/ @@ -144,14 +160,6 @@ static void FUZ_displayMallocStats(mallocCounter_t count) (U32)(count.totalMalloc >> 10)); } -#define CHECK_Z(f) { \ - size_t const err = f; \ - if (ZSTD_isError(err)) { \ - DISPLAY("Error => %s : %s ", \ - #f, ZSTD_getErrorName(err)); \ - exit(1); \ -} } - static int FUZ_mallocTests(unsigned seed, double compressibility, unsigned part) { size_t const inSize = 64 MB + 16 MB + 4 MB + 1 MB + 256 KB + 64 KB; /* 85.3 MB */ @@ -257,10 +265,6 @@ static int FUZ_mallocTests(unsigned seed, double compressibility, unsigned part) * Unit tests =============================================*/ -#define CHECK_V(var, fn) size_t const var = fn; if (ZSTD_isError(var)) goto _output_error -#define CHECK(fn) { CHECK_V(err, fn); } -#define CHECKPLUS(var, fn, more) { CHECK_V(var, fn); more; } - static int basicUnitTests(U32 seed, double compressibility) { size_t const CNBuffSize = 5 MB; From 9096088f4529a25763ce4080376919c9479f48cc Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 19 Dec 2017 21:20:46 +0100 Subject: [PATCH 143/145] changed variable name for clarity, suggested by @terrelln --- lib/compress/zstd_compress.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 5f952b8c0..04b03bfcf 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -757,11 +757,11 @@ static U32 ZSTD_sufficientBuff(size_t bufferSize1, size_t blockSize1, ZSTD_compressionParameters cParams2, U64 pledgedSrcSize) { - size_t const windowSize = MAX(1, (size_t)MIN(((U64)1 << cParams2.windowLog), pledgedSrcSize)); - size_t const blockSize2 = MIN(ZSTD_BLOCKSIZE_MAX, windowSize); - size_t const neededBufferSize = (buffPol2==ZSTDb_buffered) ? windowSize + blockSize2 : 0; + size_t const windowSize2 = MAX(1, (size_t)MIN(((U64)1 << cParams2.windowLog), pledgedSrcSize)); + size_t const blockSize2 = MIN(ZSTD_BLOCKSIZE_MAX, windowSize2); + size_t const neededBufferSize2 = (buffPol2==ZSTDb_buffered) ? windowSize2 + blockSize2 : 0; return (blockSize2 <= blockSize1) /* seqStore space depends on blockSize */ - & (neededBufferSize <= bufferSize1); + & (neededBufferSize2 <= bufferSize1); } /** Equivalence for resetCCtx purposes */ From cafedcbbe4b36a6b7253ee3bcc4b28bbc972e433 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 19 Dec 2017 21:49:04 +0100 Subject: [PATCH 144/145] ZSTD_resetCCtx_internal: fixed order of arguments params1 was swapped with params2. This used to be a non-issue when testing for strict equality, but now that some tests look for "sufficient size" `<=`, order matters. --- lib/compress/zstd_compress.c | 20 +++++++++++++------- tests/zstreamtest.c | 2 +- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 04b03bfcf..a9a8a265d 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -161,7 +161,7 @@ static void ZSTD_cLevelToCCtxParams_srcSize(ZSTD_CCtx_params* CCtxParams, U64 sr static void ZSTD_cLevelToCParams(ZSTD_CCtx* cctx) { - DEBUGLOG(4, "ZSTD_cLevelToCParams"); + DEBUGLOG(4, "ZSTD_cLevelToCParams: level=%i", cctx->requestedParams.compressionLevel); ZSTD_cLevelToCCtxParams_srcSize( &cctx->requestedParams, cctx->pledgedSrcSizePlusOne-1); } @@ -169,7 +169,7 @@ static void ZSTD_cLevelToCParams(ZSTD_CCtx* cctx) static void ZSTD_cLevelToCCtxParams(ZSTD_CCtx_params* CCtxParams) { DEBUGLOG(4, "ZSTD_cLevelToCCtxParams"); - ZSTD_cLevelToCCtxParams_srcSize(CCtxParams, 0); + ZSTD_cLevelToCCtxParams_srcSize(CCtxParams, ZSTD_CONTENTSIZE_UNKNOWN); } static ZSTD_CCtx_params ZSTD_makeCCtxParamsFromCParams( @@ -247,7 +247,7 @@ static ZSTD_CCtx_params ZSTD_assignParamsToCCtxParams( size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, unsigned value) { - DEBUGLOG(4, "ZSTD_CCtx_setParameter"); + DEBUGLOG(4, "ZSTD_CCtx_setParameter (%u, %u)", (U32)param, value); if (cctx->streamStage != zcss_init) return ERROR(stage_wrong); switch(param) @@ -311,6 +311,7 @@ size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, unsigned v size_t ZSTD_CCtxParam_setParameter( ZSTD_CCtx_params* CCtxParams, ZSTD_cParameter param, unsigned value) { + DEBUGLOG(4, "ZSTD_CCtxParam_setParameter (%u, %u)", (U32)param, value); switch(param) { case ZSTD_p_format : @@ -326,7 +327,7 @@ size_t ZSTD_CCtxParam_setParameter( return CCtxParams->compressionLevel; case ZSTD_p_windowLog : - DEBUGLOG(4, "setting windowLog=%u", value); + DEBUGLOG(4, "ZSTD_CCtxParam_setParameter: set windowLog=%u", value); if (value) { /* 0 : does not change current windowLog */ CLAMPCHECK(value, ZSTD_WINDOWLOG_MIN, ZSTD_WINDOWLOG_MAX); ZSTD_cLevelToCCtxParams(CCtxParams); @@ -760,6 +761,10 @@ static U32 ZSTD_sufficientBuff(size_t bufferSize1, size_t blockSize1, size_t const windowSize2 = MAX(1, (size_t)MIN(((U64)1 << cParams2.windowLog), pledgedSrcSize)); size_t const blockSize2 = MIN(ZSTD_BLOCKSIZE_MAX, windowSize2); size_t const neededBufferSize2 = (buffPol2==ZSTDb_buffered) ? windowSize2 + blockSize2 : 0; + DEBUGLOG(4, "ZSTD_sufficientBuff: windowSize2=%u from wlog=%u", + (U32)windowSize2, cParams2.windowLog); + DEBUGLOG(4, "ZSTD_sufficientBuff: blockSize2 %u <=? blockSize1 %u", + (U32)blockSize2, (U32)blockSize1); return (blockSize2 <= blockSize1) /* seqStore space depends on blockSize */ & (neededBufferSize2 <= bufferSize1); } @@ -815,14 +820,15 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, ZSTD_compResetPolicy_e const crp, ZSTD_buffered_policy_e const zbuff) { - DEBUGLOG(4, "ZSTD_resetCCtx_internal: pledgedSrcSize=%u", (U32)pledgedSrcSize); + DEBUGLOG(4, "ZSTD_resetCCtx_internal: pledgedSrcSize=%u, wlog=%u", + (U32)pledgedSrcSize, params.cParams.windowLog); assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams))); if (crp == ZSTDcrp_continue) { - if (ZSTD_equivalentParams(params, zc->appliedParams, + if (ZSTD_equivalentParams(zc->appliedParams, params, zc->inBuffSize, zc->blockSize, zbuff, pledgedSrcSize)) { - DEBUGLOG(4, "ZSTD_equivalentParams()==1 -> continue mode (wLog=%u, blockSize=%u)", + DEBUGLOG(4, "ZSTD_equivalentParams()==1 -> continue mode (wLog1=%u, blockSize1=%u)", zc->appliedParams.cParams.windowLog, (U32)zc->blockSize); assert(!(params.ldmParams.enableLdm && params.ldmParams.hashEveryLog == ZSTD_LDM_HASHEVERYLOG_NOTSET)); diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index 70922e2e0..5590c9af1 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -1527,7 +1527,7 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double (MAX(testLog, dictLog) / 2))) + 1; U32 const cLevel = MIN(cLevelCandidate, cLevelMax); - DISPLAYLEVEL(5, "t%u: cLevel : %u \n", testNb, cLevel); + DISPLAYLEVEL(5, "t%u: base cLevel : %u \n", testNb, cLevel); maxTestSize = FUZ_rLogLength(&lseed, testLog); DISPLAYLEVEL(5, "t%u: maxTestSize : %u \n", testNb, (U32)maxTestSize); oldTestLog = testLog; From 3ddfa42fe867f18e999032ee9ace0f86a01ddb5c Mon Sep 17 00:00:00 2001 From: Shawn Landden Date: Tue, 19 Dec 2017 22:00:27 -0800 Subject: [PATCH 145/145] meson: fix build used absolute paths which are deprecated in meson, also missing some sources that got split also move source files each to their own line so future diffs are clearer. --- contrib/meson/meson.build | 49 ++++++++++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/contrib/meson/meson.build b/contrib/meson/meson.build index 8cbdcabec..5ff370fa7 100644 --- a/contrib/meson/meson.build +++ b/contrib/meson/meson.build @@ -2,14 +2,39 @@ project('zstd', 'c', license: 'BSD') libm = meson.get_compiler('c').find_library('m', required: true) -lib_dir = join_paths(meson.source_root(), '..', '..', 'lib') +lib_dir = join_paths('..', '..', 'lib') common_dir = join_paths(lib_dir, 'common') compress_dir = join_paths(lib_dir, 'compress') decompress_dir = join_paths(lib_dir, 'decompress') dictbuilder_dir = join_paths(lib_dir, 'dictBuilder') deprecated_dir = join_paths(lib_dir, 'deprecated') -libzstd_srcs = [join_paths(common_dir, 'entropy_common.c'), join_paths(common_dir, 'fse_decompress.c'), join_paths(common_dir, 'threading.c'), join_paths(common_dir, 'pool.c'), join_paths(common_dir, 'zstd_common.c'), join_paths(common_dir, 'error_private.c'), join_paths(common_dir, 'xxhash.c'), join_paths(compress_dir, 'fse_compress.c'), join_paths(compress_dir, 'huf_compress.c'), join_paths(compress_dir, 'zstd_compress.c'), join_paths(compress_dir, 'zstdmt_compress.c'), join_paths(decompress_dir, 'huf_decompress.c'), join_paths(decompress_dir, 'zstd_decompress.c'), join_paths(dictbuilder_dir, 'cover.c'), join_paths(dictbuilder_dir, 'divsufsort.c'), join_paths(dictbuilder_dir, 'zdict.c'), join_paths(deprecated_dir, 'zbuff_common.c'), join_paths(deprecated_dir, 'zbuff_compress.c'), join_paths(deprecated_dir, 'zbuff_decompress.c')] +libzstd_srcs = [ + join_paths(common_dir, 'entropy_common.c'), + join_paths(common_dir, 'fse_decompress.c'), + join_paths(common_dir, 'threading.c'), + join_paths(common_dir, 'pool.c'), + join_paths(common_dir, 'zstd_common.c'), + join_paths(common_dir, 'error_private.c'), + join_paths(common_dir, 'xxhash.c'), + join_paths(compress_dir, 'fse_compress.c'), + join_paths(compress_dir, 'huf_compress.c'), + join_paths(compress_dir, 'zstd_compress.c'), + join_paths(compress_dir, 'zstd_fast.c'), + join_paths(compress_dir, 'zstd_double_fast.c'), + join_paths(compress_dir, 'zstd_lazy.c'), + join_paths(compress_dir, 'zstd_opt.c'), + join_paths(compress_dir, 'zstd_ldm.c'), + join_paths(compress_dir, 'zstdmt_compress.c'), + join_paths(decompress_dir, 'huf_decompress.c'), + join_paths(decompress_dir, 'zstd_decompress.c'), + join_paths(dictbuilder_dir, 'cover.c'), + join_paths(dictbuilder_dir, 'divsufsort.c'), + join_paths(dictbuilder_dir, 'zdict.c'), + join_paths(deprecated_dir, 'zbuff_common.c'), + join_paths(deprecated_dir, 'zbuff_compress.c'), + join_paths(deprecated_dir, 'zbuff_decompress.c') +] libzstd_includes = [include_directories(common_dir, dictbuilder_dir, compress_dir, lib_dir)] @@ -19,7 +44,15 @@ if get_option('legacy_support') legacy_dir = join_paths(lib_dir, 'legacy') libzstd_includes += [include_directories(legacy_dir)] - libzstd_srcs += [join_paths(legacy_dir, 'zstd_v01.c'), join_paths(legacy_dir, 'zstd_v02.c'), join_paths(legacy_dir, 'zstd_v03.c'), join_paths(legacy_dir, 'zstd_v04.c'), join_paths(legacy_dir, 'zstd_v05.c'), join_paths(legacy_dir, 'zstd_v06.c'), join_paths(legacy_dir, 'zstd_v07.c')] + libzstd_srcs += [ + join_paths(legacy_dir, 'zstd_v01.c'), + join_paths(legacy_dir, 'zstd_v02.c'), + join_paths(legacy_dir, 'zstd_v03.c'), + join_paths(legacy_dir, 'zstd_v04.c'), + join_paths(legacy_dir, 'zstd_v05.c'), + join_paths(legacy_dir, 'zstd_v06.c'), + join_paths(legacy_dir, 'zstd_v07.c') + ] else libzstd_cflags = [] endif @@ -39,16 +72,20 @@ libzstd = library('zstd', dependencies: libzstd_deps, install: true) -programs_dir = join_paths(meson.source_root(), '..', '..', 'programs') +programs_dir = join_paths('..', '..', 'programs') zstd = executable('zstd', - join_paths(programs_dir, 'bench.c'), join_paths(programs_dir, 'datagen.c'), join_paths(programs_dir, 'dibio.c'), join_paths(programs_dir, 'fileio.c'), join_paths(programs_dir, 'zstdcli.c'), + join_paths(programs_dir, 'bench.c'), + join_paths(programs_dir, 'datagen.c'), + join_paths(programs_dir, 'dibio.c'), + join_paths(programs_dir, 'fileio.c'), + join_paths(programs_dir, 'zstdcli.c'), include_directories: libzstd_includes, c_args: ['-DZSTD_NODICT', '-DZSTD_NOBENCH'], link_with: libzstd, install: true) -tests_dir = join_paths(meson.source_root(), '..', '..', 'tests') +tests_dir = join_paths('..', '..', 'tests') datagen_c = join_paths(programs_dir, 'datagen.c') test_includes = libzstd_includes + [include_directories(programs_dir)]
    ZSTD_DStream* ZSTD_createDStream_advanced(ZSTD_customMem customMem);
    @@ -693,10 +695,10 @@ size_t ZSTD_resetDStream(ZSTD_DStream* zds);  /**< re-use decompression para
     
     

    Buffer-less streaming compression functions

    size_t ZSTD_compressBegin(ZSTD_CCtx* cctx, int compressionLevel);
     size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel);
    -size_t ZSTD_compressBegin_advanced(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize is optional and can be 0 (meaning unknown). note: if the contentSizeFlag is set, pledgedSrcSize == 0 means the source size is actually 0 */
    +size_t ZSTD_compressBegin_advanced(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize : If srcSize is not known at init time, use ZSTD_CONTENTSIZE_UNKNOWN */
     size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict); /**< note: fails if cdict==NULL */
    -size_t ZSTD_compressBegin_usingCDict_advanced(ZSTD_CCtx* const cctx, const ZSTD_CDict* const cdict, ZSTD_frameParameters const fParams, unsigned long long const pledgedSrcSize);   /* compression parameters are already set within cdict. pledgedSrcSize=0 means null-size */
    -size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, unsigned long long pledgedSrcSize); /**<  note: if pledgedSrcSize can be 0, indicating unknown size.  if it is non-zero, it must be accurate.  for 0 size frames, use compressBegin_advanced */
    +size_t ZSTD_compressBegin_usingCDict_advanced(ZSTD_CCtx* const cctx, const ZSTD_CDict* const cdict, ZSTD_frameParameters const fParams, unsigned long long const pledgedSrcSize);   /* compression parameters are already set within cdict. pledgedSrcSize must be correct. If srcSize is not known, use macro ZSTD_CONTENTSIZE_UNKNOWN */
    +size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, unsigned long long pledgedSrcSize); /**<  note: if pledgedSrcSize is not known, use ZSTD_CONTENTSIZE_UNKNOWN */
     

    Buffer-less streaming decompression (synchronous mode)

       A ZSTD_DCtx object is required to track streaming operations.
    @@ -848,11 +850,11 @@ size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long
                                   * Special: value 0 means "do not change strategy". */
     
         /* frame parameters */
    -    ZSTD_p_contentSizeFlag=200, /* Content size is written into frame header _whenever known_ (default:1)
    -                              * note that content size must be known at the beginning,
    -                              * it is sent using ZSTD_CCtx_setPledgedSrcSize() */
    +    ZSTD_p_contentSizeFlag=200, /* Content size will be written into frame header _whenever known_ (default:1)
    +                              * Content size must be known at the beginning of compression,
    +                              * it is provided using ZSTD_CCtx_setPledgedSrcSize() */
         ZSTD_p_checksumFlag,     /* A 32-bits checksum of content is written at end of frame (default:0) */
    -    ZSTD_p_dictIDFlag,       /* When applicable, dictID of dictionary is provided in frame header (default:1) */
    +    ZSTD_p_dictIDFlag,       /* When applicable, dictionary's ID is written into frame header (default:1) */
     
         /* multi-threading parameters */
         ZSTD_p_nbThreads=400,    /* Select how many threads a compression job can spawn (default:1)
    @@ -913,7 +915,7 @@ size_t ZSTD_decodingBufferSize_min(unsigned long long windowSize, unsigned long
      @result : 0, or an error code (which can be tested with ZSTD_isError()).
       Note 1 : 0 means zero, empty.
                In order to mean "unknown content size", pass constant ZSTD_CONTENTSIZE_UNKNOWN.
    -           Note that ZSTD_CONTENTSIZE_UNKNOWN is default value for new compression jobs.
    +           ZSTD_CONTENTSIZE_UNKNOWN is default value for any new compression job.
       Note 2 : If all data is provided and consumed in a single round,
                this value is overriden by srcSize instead. 
     


    diff --git a/lib/zstd.h b/lib/zstd.h index 2866866be..e9c4ff9d4 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -731,10 +731,10 @@ ZSTDLIB_API unsigned ZSTD_getDictID_fromFrame(const void* src, size_t srcSize); /*===== Advanced Streaming compression functions =====*/ ZSTDLIB_API ZSTD_CStream* ZSTD_createCStream_advanced(ZSTD_customMem customMem); ZSTDLIB_API ZSTD_CStream* ZSTD_initStaticCStream(void* workspace, size_t workspaceSize); /**< same as ZSTD_initStaticCCtx() */ -ZSTDLIB_API size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize); /**< pledgedSrcSize must be correct, a size of 0 means unknown. for a frame size of 0 use initCStream_advanced */ +ZSTDLIB_API size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize); /**< pledgedSrcSize must be correct. If it is not known at init time, use ZSTD_CONTENTSIZE_UNKNOWN. Note that, for compatibility with older programs, a size of 0 is interepreted as "unknown". But it may change in some future version to mean "empty". */ ZSTDLIB_API size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel); /**< creates of an internal CDict (incompatible with static CCtx), except if dict == NULL or dictSize < 8, in which case no dict is used. Note: dict is loaded with ZSTD_dm_auto (treated as a full zstd dictionary if it begins with ZSTD_MAGIC_DICTIONARY, else as raw content) and ZSTD_dlm_byCopy.*/ ZSTDLIB_API size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, const void* dict, size_t dictSize, - ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize is optional: it can be 0, which means "unknown" if contentSizeFlag is set, or "unknown" if contentSizeFlag is not set. dict is loaded with ZSTD_dm_auto and ZSTD_dlm_byCopy. */ + ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize : If srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN. dict is loaded with ZSTD_dm_auto and ZSTD_dlm_byCopy. */ ZSTDLIB_API size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict); /**< note : cdict will just be referenced, and must outlive compression session */ ZSTDLIB_API size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs, const ZSTD_CDict* cdict, ZSTD_frameParameters fParams, unsigned long long pledgedSrcSize); /**< same as ZSTD_initCStream_usingCDict(), with control over frame parameters */ @@ -742,9 +742,11 @@ ZSTDLIB_API size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs, const * start a new compression job, using same parameters from previous job. * This is typically useful to skip dictionary loading stage, since it will re-use it in-place.. * Note that zcs must be init at least once before using ZSTD_resetCStream(). - * pledgedSrcSize==0 means "srcSize unknown". + * If pledgedSrcSize is not known at reset time, use macro ZSTD_CONTENTSIZE_UNKNOWN. * If pledgedSrcSize > 0, its value must be correct, as it will be written in header, and controlled at the end. - * @return : 0, or an error code (which can be tested using ZSTD_isError()) */ + * For the time being, pledgedSrcSize==0 is interpreted as "srcSize unknown" for compatibility with older programs, + * but it may change to mean "empty" in some future version, so prefer using macro ZSTD_CONTENTSIZE_UNKNOWN. + * @return : 0, or an error code (which can be tested using ZSTD_isError()) */ ZSTDLIB_API size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pledgedSrcSize); @@ -800,10 +802,10 @@ ZSTDLIB_API size_t ZSTD_resetDStream(ZSTD_DStream* zds); /**< re-use decompress /*===== Buffer-less streaming compression functions =====*/ ZSTDLIB_API size_t ZSTD_compressBegin(ZSTD_CCtx* cctx, int compressionLevel); ZSTDLIB_API size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel); -ZSTDLIB_API size_t ZSTD_compressBegin_advanced(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize is optional and can be 0 (meaning unknown). note: if the contentSizeFlag is set, pledgedSrcSize == 0 means the source size is actually 0 */ +ZSTDLIB_API size_t ZSTD_compressBegin_advanced(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize : If srcSize is not known at init time, use ZSTD_CONTENTSIZE_UNKNOWN */ ZSTDLIB_API size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict); /**< note: fails if cdict==NULL */ -ZSTDLIB_API size_t ZSTD_compressBegin_usingCDict_advanced(ZSTD_CCtx* const cctx, const ZSTD_CDict* const cdict, ZSTD_frameParameters const fParams, unsigned long long const pledgedSrcSize); /* compression parameters are already set within cdict. pledgedSrcSize=0 means null-size */ -ZSTDLIB_API size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, unsigned long long pledgedSrcSize); /**< note: if pledgedSrcSize can be 0, indicating unknown size. if it is non-zero, it must be accurate. for 0 size frames, use compressBegin_advanced */ +ZSTDLIB_API size_t ZSTD_compressBegin_usingCDict_advanced(ZSTD_CCtx* const cctx, const ZSTD_CDict* const cdict, ZSTD_frameParameters const fParams, unsigned long long const pledgedSrcSize); /* compression parameters are already set within cdict. pledgedSrcSize must be correct. If srcSize is not known, use macro ZSTD_CONTENTSIZE_UNKNOWN */ +ZSTDLIB_API size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, unsigned long long pledgedSrcSize); /**< note: if pledgedSrcSize is not known, use ZSTD_CONTENTSIZE_UNKNOWN */ ZSTDLIB_API size_t ZSTD_compressContinue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); ZSTDLIB_API size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize); @@ -1066,7 +1068,7 @@ ZSTDLIB_API size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param * @result : 0, or an error code (which can be tested with ZSTD_isError()). * Note 1 : 0 means zero, empty. * In order to mean "unknown content size", pass constant ZSTD_CONTENTSIZE_UNKNOWN. - * Note that ZSTD_CONTENTSIZE_UNKNOWN is default value for new compression jobs. + * ZSTD_CONTENTSIZE_UNKNOWN is default value for any new compression job. * Note 2 : If all data is provided and consumed in a single round, * this value is overriden by srcSize instead. */ ZSTDLIB_API size_t ZSTD_CCtx_setPledgedSrcSize(ZSTD_CCtx* cctx, unsigned long long pledgedSrcSize); From fc8d293460d6767558843ac8231c249dcc704382 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Sat, 14 Oct 2017 01:21:43 -0700 Subject: [PATCH 013/145] dictionary compression use correct file size estimation when determining compression parameters to compress one file only. For multiple files, it still "bets" that files are going to be small. There was also a bug recently added in ZSTD_CCtx_loadDictionary_advanced() making it incapable to use pledgedSrcSize to determine compression parameters. --- lib/compress/zstd_compress.c | 2 +- programs/fileio.c | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 4ceba415c..c7872e7be 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -531,7 +531,7 @@ size_t ZSTD_CCtx_loadDictionary_advanced( cctx->cdict = NULL; } else { ZSTD_compressionParameters const cParams = - ZSTD_getCParamsFromCCtxParams(cctx->requestedParams, 0, dictSize); + ZSTD_getCParamsFromCCtxParams(cctx->requestedParams, cctx->pledgedSrcSizePlusOne-1, dictSize); cctx->cdictLocal = ZSTD_createCDict_advanced( dict, dictSize, dictLoadMethod, dictMode, diff --git a/programs/fileio.c b/programs/fileio.c index 01e901ed8..17ad31b32 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -442,7 +442,6 @@ static cRess_t FIO_createCResources(const char* dictFileName, int cLevel, CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_contentSizeFlag, 1) ); CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_dictIDFlag, g_dictIDFlag) ); CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_checksumFlag, g_checksumFlag) ); - (void)srcSize; /* compression level */ CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_compressionLevel, cLevel) ); /* long distance matching */ @@ -468,7 +467,9 @@ static cRess_t FIO_createCResources(const char* dictFileName, int cLevel, DISPLAYLEVEL(5,"set nb threads = %u \n", g_nbThreads); CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_nbThreads, g_nbThreads) ); /* dictionary */ + CHECK( ZSTD_CCtx_setPledgedSrcSize(ress.cctx, srcSize) ); /* just to load dictionary with good compression parameters */ CHECK( ZSTD_CCtx_loadDictionary(ress.cctx, dictBuffer, dictBuffSize) ); + CHECK( ZSTD_CCtx_setPledgedSrcSize(ress.cctx, ZSTD_CONTENTSIZE_UNKNOWN) ); /* reset */ } #elif defined(ZSTD_MULTITHREAD) { ZSTD_parameters params = ZSTD_getParams(cLevel, srcSize, dictBuffSize); @@ -1006,7 +1007,7 @@ int FIO_compressMultipleFilenames(const char** inFileNamesTable, unsigned nbFile size_t dfnSize = FNSPACE; char* dstFileName = (char*)malloc(FNSPACE); size_t const suffixSize = suffix ? strlen(suffix) : 0; - U64 const srcSize = (nbFiles != 1) ? 0 : UTIL_getFileSize(inFileNamesTable[0]) ; + U64 const srcSize = (nbFiles != 1) ? ZSTD_CONTENTSIZE_UNKNOWN : UTIL_getFileSize(inFileNamesTable[0]) ; cRess_t ress = FIO_createCResources(dictFileName, compressionLevel, srcSize, comprParams); /* init */ @@ -1032,9 +1033,9 @@ int FIO_compressMultipleFilenames(const char** inFileNamesTable, unsigned nbFile free(dstFileName); dfnSize = ifnSize + 20; dstFileName = (char*)malloc(dfnSize); - if (!dstFileName) + if (!dstFileName) { EXM_THROW(30, "zstd: %s", strerror(errno)); - } + } } strcpy(dstFileName, inFileNamesTable[u]); strcat(dstFileName, suffix); missed_files += FIO_compressFilename_dstFile(ress, dstFileName, inFileNamesTable[u], compressionLevel); From 7f961ba6cd592217696cc43c9a133c7ffafada17 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Fri, 13 Oct 2017 13:23:57 -0700 Subject: [PATCH 014/145] Don't allow default tables to repeat It isn't useful in any case to repeat default tables. Saves a few bytes on Silesia, since we don't trigger the dictionary heuristic. Before: 211988480 => 73651998 bytes After: 211988480 => 73651721 bytes --- lib/compress/zstd_compress.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 188b8e717..43e5f5461 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1298,7 +1298,13 @@ symbolEncodingType_e ZSTD_selectEncodingType( if ( isDefaultAllowed && ((nbSeq < MIN_SEQ_FOR_DYNAMIC_FSE) || (mostFrequent < (nbSeq >> (defaultNormLog-1)))) ) { DEBUGLOG(5, "Selected set_basic"); - *repeatMode = FSE_repeat_valid; + /* The format allows default tables to be repeated, but it isn't useful. + * When using simple heuristics to select encoding type, we don't want + * to confuse these tables with dictionaries. When running more careful + * analysis, we don't need to waste time checking both repeating tables + * and default tables. + */ + *repeatMode = FSE_repeat_none; return set_basic; } DEBUGLOG(5, "Selected set_compressed"); From 241c57adffe34a8267dd9526cece87b8751468d2 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 16 Oct 2017 14:01:42 -0700 Subject: [PATCH 015/145] playtest : minor cosmetic changes --- tests/playTests.sh | 68 +++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/tests/playTests.sh b/tests/playTests.sh index 013182aeb..f008d8998 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -91,7 +91,7 @@ else hasMT="true" fi -$ECHO "\n**** simple tests **** " +$ECHO "\n===> simple tests " ./datagen > tmp $ECHO "test : basic compression " @@ -166,7 +166,7 @@ $ZSTD -f tmp && die "tmp not present : should have failed" test ! -f tmp.zst # tmp.zst should not be created -$ECHO "\n**** Advanced compression parameters **** " +$ECHO "\n===> Advanced compression parameters " $ECHO "Hello world!" | $ZSTD --zstd=windowLog=21, - -o tmp.zst && die "wrong parameters not detected!" $ECHO "Hello world!" | $ZSTD --zstd=windowLo=21 - -o tmp.zst && die "wrong parameters not detected!" $ECHO "Hello world!" | $ZSTD --zstd=windowLog=21,slog - -o tmp.zst && die "wrong parameters not detected!" @@ -180,14 +180,14 @@ roundTripTest -g512K " --long --zstd=ldmhlog=20,ldmslen=64,ldmblog=1,ldmhevery=7 roundTripTest -g512K 19 -$ECHO "\n**** Pass-Through mode **** " +$ECHO "\n===> Pass-Through mode " $ECHO "Hello world 1!" | $ZSTD -df $ECHO "Hello world 2!" | $ZSTD -dcf $ECHO "Hello world 3!" > tmp1 $ZSTD -dcf tmp1 -$ECHO "\n**** frame concatenation **** " +$ECHO "\n===> frame concatenation " $ECHO "hello " > hello.tmp $ECHO "world!" > world.tmp @@ -218,7 +218,7 @@ $ECHO "$ECHO foo | $ZSTD | $ZSTD -d > /dev/full" $ECHO foo | $ZSTD | $ZSTD -d > /dev/full && die "write error not detected!" -$ECHO "\n**** symbolic link test **** " +$ECHO "\n===> symbolic link test " rm -f hello.tmp world.tmp hello.tmp.zst world.tmp.zst $ECHO "hello world" > hello.tmp @@ -233,7 +233,7 @@ rm -f hello.tmp world.tmp hello.tmp.zst world.tmp.zst fi -$ECHO "\n**** test sparse file support **** " +$ECHO "\n===> test sparse file support " ./datagen -g5M -P100 > tmpSparse $ZSTD tmpSparse -c | $ZSTD -dv -o tmpSparseRegen @@ -260,7 +260,7 @@ $DIFF tmpSparse2M tmpSparseRegenerated rm tmpSparse* -$ECHO "\n**** multiple files tests **** " +$ECHO "\n===> multiple files tests " ./datagen -s1 > tmp1 2> $INTOVOID ./datagen -s2 -g100K > tmp2 2> $INTOVOID @@ -283,7 +283,7 @@ $ECHO "compress multiple files including a missing one (notHere) : " $ZSTD -f tmp1 notHere tmp2 && die "missing file not detected!" -$ECHO "\n**** dictionary tests **** " +$ECHO "\n===> dictionary tests " $ECHO "- test with raw dict (content only) " ./datagen > tmpDict @@ -339,7 +339,7 @@ $ZSTD --train-legacy -q tmp && die "Dictionary training should fail : source is rm tmp* -$ECHO "\n**** cover dictionary tests **** " +$ECHO "\n===> cover dictionary builder : advanced options " TESTFILE=../programs/zstdcli.c ./datagen > tmpDict @@ -359,7 +359,7 @@ $ECHO "- Create dictionary with size limit" $ZSTD --train-cover=steps=8 *.c ../programs/*.c -o tmpDict2 --maxdict=4K rm tmp* -$ECHO "\n**** legacy dictionary tests **** " +$ECHO "\n===> legacy dictionary builder " TESTFILE=../programs/zstdcli.c ./datagen > tmpDict @@ -380,7 +380,7 @@ $ZSTD --train-legacy -s9 *.c ../programs/*.c -o tmpDict2 --maxdict=4K rm tmp* -$ECHO "\n**** integrity tests **** " +$ECHO "\n===> integrity tests " $ECHO "test one file (tmp1.zst) " ./datagen > tmp1 @@ -405,13 +405,13 @@ $ZSTD -t tmpSplit.* && die "bad file not detected !" -$ECHO "\n**** golden files tests **** " +$ECHO "\n===> golden files tests " $ZSTD -t -r files $ZSTD -c -r files | $ZSTD -t -$ECHO "\n**** benchmark mode tests **** " +$ECHO "\n===> benchmark mode tests " $ECHO "bench one file" ./datagen > tmp1 @@ -422,7 +422,7 @@ $ECHO "with recursive and quiet modes" $ZSTD -rqi1b1e2 tmp1 -$ECHO "\n**** gzip compatibility tests **** " +$ECHO "\n===> gzip compatibility tests " GZIPMODE=1 $ZSTD --format=gzip -V || GZIPMODE=0 @@ -445,7 +445,7 @@ else fi -$ECHO "\n**** gzip frame tests **** " +$ECHO "\n===> gzip frame tests " if [ $GZIPMODE -eq 1 ]; then ./datagen > tmp @@ -459,7 +459,7 @@ else fi -$ECHO "\n**** xz compatibility tests **** " +$ECHO "\n===> xz compatibility tests " LZMAMODE=1 $ZSTD --format=xz -V || LZMAMODE=0 @@ -505,7 +505,7 @@ else fi -$ECHO "\n**** xz frame tests **** " +$ECHO "\n===> xz frame tests " if [ $LZMAMODE -eq 1 ]; then ./datagen > tmp @@ -520,7 +520,7 @@ else $ECHO "xz mode not supported" fi -$ECHO "\n**** lz4 compatibility tests **** " +$ECHO "\n===> lz4 compatibility tests " LZ4MODE=1 $ZSTD --format=lz4 -V || LZ4MODE=0 @@ -543,7 +543,7 @@ else fi -$ECHO "\n**** lz4 frame tests **** " +$ECHO "\n===> lz4 frame tests " if [ $LZ4MODE -eq 1 ]; then ./datagen > tmp @@ -556,7 +556,7 @@ else $ECHO "lz4 mode not supported" fi -$ECHO "\n**** zstd round-trip tests **** " +$ECHO "\n===> zstd round-trip tests " roundTripTest roundTripTest -g15K # TableID==3 @@ -569,7 +569,7 @@ roundTripTest -g516K 19 # btopt fileRoundTripTest -g500K -$ECHO "\n**** zstd long distance matching round-trip tests **** " +$ECHO "\n===> zstd long distance matching round-trip tests " roundTripTest -g0 "2 --long" roundTripTest -g1000K "1 --long" roundTripTest -g517K "6 --long" @@ -580,21 +580,21 @@ fileRoundTripTest -g5M "3 --long" if [ -n "$hasMT" ] then - $ECHO "\n**** zstdmt round-trip tests **** " + $ECHO "\n===> zstdmt round-trip tests " roundTripTest -g4M "1 -T0" roundTripTest -g8M "3 -T2" roundTripTest -g8000K "2 --threads=2" fileRoundTripTest -g4M "19 -T2 -B1M" - $ECHO "\n**** zstdmt long distance matching round-trip tests **** " + $ECHO "\n===> zstdmt long distance matching round-trip tests " roundTripTest -g8M "3 --long -T2" else - $ECHO "\n**** no multithreading, skipping zstdmt tests **** " + $ECHO "\n===> no multithreading, skipping zstdmt tests " fi rm tmp* -$ECHO "\n**** zstd --list/-l single frame tests ****" +$ECHO "\n===> zstd --list/-l single frame tests " ./datagen > tmp1 ./datagen > tmp2 ./datagen > tmp3 @@ -604,19 +604,19 @@ $ZSTD -lv *.zst | grep "Decompressed Size:" # check that decompressed size is p $ZSTD --list *.zst $ZSTD --list -v *.zst -$ECHO "\n**** zstd --list/-l multiple frame tests ****" +$ECHO "\n===> zstd --list/-l multiple frame tests " cat tmp1.zst tmp2.zst > tmp12.zst cat tmp12.zst tmp3.zst > tmp123.zst $ZSTD -l *.zst $ZSTD -lv *.zst -$ECHO "\n**** zstd --list/-l error detection tests ****" +$ECHO "\n===> zstd --list/-l error detection tests " ! $ZSTD -l tmp1 tmp1.zst ! $ZSTD --list tmp* ! $ZSTD -lv tmp1* ! $ZSTD --list -v tmp2 tmp12.zst -$ECHO "\n**** zstd --list/-l test with null files ****" +$ECHO "\n===> zstd --list/-l test with null files " ./datagen -g0 > tmp5 $ZSTD tmp5 $ZSTD -l tmp5.zst @@ -624,12 +624,12 @@ $ZSTD -l tmp5.zst $ZSTD -lv tmp5.zst ! $ZSTD -lv tmp5* -$ECHO "\n**** zstd --list/-l test with no content size field ****" +$ECHO "\n===> zstd --list/-l test with no content size field " ./datagen -g513K | $ZSTD > tmp6.zst $ZSTD -l tmp6.zst ! $ZSTD -lv tmp6.zst | grep "Decompressed Size:" # must NOT be present in header -$ECHO "\n**** zstd --list/-l test with no checksum ****" +$ECHO "\n===> zstd --list/-l test with no checksum " $ZSTD -f --no-check tmp1 $ZSTD -l tmp1.zst $ZSTD -lv tmp1.zst @@ -637,7 +637,7 @@ $ZSTD -lv tmp1.zst rm tmp* -$ECHO "\n**** zstd long distance matching tests **** " +$ECHO "\n===> zstd long distance matching tests " roundTripTest -g0 " --long" roundTripTest -g9M "2 --long" # Test parameter parsing @@ -652,7 +652,7 @@ if [ "$1" != "--test-large-data" ]; then exit 0 fi -$ECHO "\n**** large files tests **** " +$ECHO "\n===> large files tests " roundTripTest -g270000000 1 roundTripTest -g250000000 2 @@ -683,7 +683,7 @@ roundTripTest -g5000000000 -P99 1 fileRoundTripTest -g4193M -P99 1 -$ECHO "\n**** zstd long, long distance matching round-trip tests **** " +$ECHO "\n===> zstd long, long distance matching round-trip tests " roundTripTest -g270000000 "1 --long" roundTripTest -g130000000 -P60 "5 --long" roundTripTest -g35000000 -P70 "8 --long" @@ -695,7 +695,7 @@ roundTripTest -g600M -P50 "1 --long --zstd=wlog=29,clog=28" if [ -n "$hasMT" ] then - $ECHO "\n**** zstdmt long round-trip tests **** " + $ECHO "\n===> zstdmt long round-trip tests " roundTripTest -g80000000 -P99 "19 -T2" " " roundTripTest -g5000000000 -P99 "1 -T2" " " roundTripTest -g500000000 -P97 "1 -T999" " " From 13bfe885aa72eadbeed15eaa86f36c265fa68c7b Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Mon, 16 Oct 2017 14:06:22 -0700 Subject: [PATCH 016/145] edited ZSTD_initCStream_advanced() comment --- lib/compress/zstd_compress.c | 3 ++- lib/zstd.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index c7872e7be..35fa99e6f 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2575,7 +2575,8 @@ size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict) } /* ZSTD_initCStream_advanced() : - * pledgedSrcSize is optional: it can be 0, which means "unknown" if contentSizeFlag is set, or "unknown" if contentSizeFlag is not set. */ + * pledgedSrcSize : if srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN. + * dict is loaded with default parameters ZSTD_dm_auto and ZSTD_dlm_byCopy. */ size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, const void* dict, size_t dictSize, ZSTD_parameters params, unsigned long long pledgedSrcSize) diff --git a/lib/zstd.h b/lib/zstd.h index e9c4ff9d4..4681d3f2a 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -734,7 +734,7 @@ ZSTDLIB_API ZSTD_CStream* ZSTD_initStaticCStream(void* workspace, size_t workspa ZSTDLIB_API size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize); /**< pledgedSrcSize must be correct. If it is not known at init time, use ZSTD_CONTENTSIZE_UNKNOWN. Note that, for compatibility with older programs, a size of 0 is interepreted as "unknown". But it may change in some future version to mean "empty". */ ZSTDLIB_API size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel); /**< creates of an internal CDict (incompatible with static CCtx), except if dict == NULL or dictSize < 8, in which case no dict is used. Note: dict is loaded with ZSTD_dm_auto (treated as a full zstd dictionary if it begins with ZSTD_MAGIC_DICTIONARY, else as raw content) and ZSTD_dlm_byCopy.*/ ZSTDLIB_API size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, const void* dict, size_t dictSize, - ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize : If srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN. dict is loaded with ZSTD_dm_auto and ZSTD_dlm_byCopy. */ + ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize : if srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN. dict is loaded with ZSTD_dm_auto and ZSTD_dlm_byCopy. */ ZSTDLIB_API size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict); /**< note : cdict will just be referenced, and must outlive compression session */ ZSTDLIB_API size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs, const ZSTD_CDict* cdict, ZSTD_frameParameters fParams, unsigned long long pledgedSrcSize); /**< same as ZSTD_initCStream_usingCDict(), with control over frame parameters */ From e600b5d0dab674e480e6538ad7eaffbdbb72f029 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Mon, 16 Oct 2017 17:18:43 -0700 Subject: [PATCH 017/145] [test] Exercise all codes in dictionary tables --- tests/Makefile | 2 +- tests/seqgen.c | 260 ++++++++++++++++++++++++++++++++++++++++++++ tests/seqgen.h | 58 ++++++++++ tests/zstreamtest.c | 131 +++++++++++++++++++--- 4 files changed, 436 insertions(+), 15 deletions(-) create mode 100644 tests/seqgen.c create mode 100644 tests/seqgen.h diff --git a/tests/Makefile b/tests/Makefile index 651833bbb..eb8bb3dd6 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -130,7 +130,7 @@ zbufftest-dll : $(ZSTDDIR)/common/xxhash.c $(PRGDIR)/datagen.c zbufftest.c $(MAKE) -C $(ZSTDDIR) libzstd $(CC) $(CPPFLAGS) $(CFLAGS) $^ $(LDFLAGS) -o $@$(EXT) -ZSTREAMFILES := $(ZSTD_FILES) $(ZDICT_FILES) $(PRGDIR)/datagen.c zstreamtest.c +ZSTREAMFILES := $(ZSTD_FILES) $(ZDICT_FILES) $(PRGDIR)/datagen.c seqgen.c zstreamtest.c zstreamtest : CPPFLAGS += $(MULTITHREAD_CPP) zstreamtest : LDFLAGS += $(MULTITHREAD_LD) zstreamtest : $(ZSTREAMFILES) diff --git a/tests/seqgen.c b/tests/seqgen.c new file mode 100644 index 000000000..8233ecee1 --- /dev/null +++ b/tests/seqgen.c @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under both the BSD-style license (found in the + * LICENSE file in the root directory of this source tree) and the GPLv2 (found + * in the COPYING file in the root directory of this source tree). + * You may select, at your option, one of the above-listed licenses. + */ + +#include "seqgen.h" +#include "mem.h" +#include + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +static const size_t kMatchBytes = 128; + +#define SEQ_rotl32(x,r) ((x << r) | (x >> (32 - r))) +static BYTE SEQ_randByte(U32* src) +{ + static const U32 prime1 = 2654435761U; + static const U32 prime2 = 2246822519U; + U32 rand32 = *src; + rand32 *= prime1; + rand32 ^= prime2; + rand32 = SEQ_rotl32(rand32, 13); + *src = rand32; + return (BYTE)(rand32 >> 5); +} + +SEQ_stream SEQ_initStream(unsigned seed) +{ + SEQ_stream stream; + stream.state = 0; + XXH64_reset(&stream.xxh, 0); + stream.seed = seed; + return stream; +} + +/* Generates a single guard byte, then match length + 1 of a different byte, + * then another guard byte. + */ +static size_t SEQ_gen_matchLength(SEQ_stream* stream, unsigned value, + SEQ_outBuffer* out) +{ + typedef enum { + ml_first_byte = 0, + ml_match_bytes, + ml_last_byte, + } ml_state; + BYTE* const ostart = (BYTE*)out->dst; + BYTE* const oend = ostart + out->size; + BYTE* op = ostart + out->pos; + + switch ((ml_state)stream->state) { + case ml_first_byte: + /* Generate a single byte and pick a different byte for the match */ + if (op >= oend) { + stream->bytesLeft = 1; + break; + } + *op = SEQ_randByte(&stream->seed) & 0xFF; + do { + stream->saved = SEQ_randByte(&stream->seed) & 0xFF; + } while (*op == stream->saved); + ++op; + /* State transition */ + stream->state = ml_match_bytes; + stream->bytesLeft = value + 1; + /* fall-through */ + case ml_match_bytes: { + /* Copy matchLength + 1 bytes to the output buffer */ + size_t const setLength = MIN(stream->bytesLeft, (size_t)(oend - op)); + if (setLength > 0) { + memset(op, stream->saved, setLength); + op += setLength; + stream->bytesLeft -= setLength; + } + if (stream->bytesLeft > 0) + break; + /* State transition */ + stream->state = ml_last_byte; + } + /* fall-through */ + case ml_last_byte: + /* Generate a single byte and pick a different byte for the match */ + if (op >= oend) { + stream->bytesLeft = 1; + break; + } + do { + *op = SEQ_randByte(&stream->seed) & 0xFF; + } while (*op == stream->saved); + ++op; + /* State transition */ + /* fall-through */ + default: + stream->state = 0; + stream->bytesLeft = 0; + break; + } + XXH64_update(&stream->xxh, ostart + out->pos, (op - ostart) - out->pos); + out->pos = op - ostart; + return stream->bytesLeft; +} + +/* Saves the current seed then generates kMatchBytes random bytes >= 128. + * Generates literal length - kMatchBytes random bytes < 128. + * Generates another kMatchBytes using the saved seed to generate a match. + * This way the match is easy to find for the compressors. + */ +static size_t SEQ_gen_litLength(SEQ_stream* stream, unsigned value, SEQ_outBuffer* out) +{ + typedef enum { + ll_start = 0, + ll_run_bytes, + ll_literals, + ll_run_match, + } ll_state; + BYTE* const ostart = (BYTE*)out->dst; + BYTE* const oend = ostart + out->size; + BYTE* op = ostart + out->pos; + + switch ((ll_state)stream->state) { + case ll_start: + stream->state = ll_run_bytes; + stream->saved = stream->seed; + stream->bytesLeft = MIN(kMatchBytes, value); + /* fall-through */ + case ll_run_bytes: + while (stream->bytesLeft > 0 && op < oend) { + *op++ = SEQ_randByte(&stream->seed) | 0x80; + --stream->bytesLeft; + } + if (stream->bytesLeft > 0) + break; + /* State transition */ + stream->state = ll_literals; + stream->bytesLeft = value - MIN(kMatchBytes, value); + /* fall-through */ + case ll_literals: + while (stream->bytesLeft > 0 && op < oend) { + *op++ = SEQ_randByte(&stream->seed) & 0x7F; + --stream->bytesLeft; + } + if (stream->bytesLeft > 0) + break; + /* State transition */ + stream->state = ll_run_match; + stream->bytesLeft = MIN(kMatchBytes, value); + /* fall-through */ + case ll_run_match: { + while (stream->bytesLeft > 0 && op < oend) { + *op++ = SEQ_randByte(&stream->saved) | 0x80; + --stream->bytesLeft; + } + if (stream->bytesLeft > 0) + break; + } + /* fall-through */ + default: + stream->state = 0; + stream->bytesLeft = 0; + break; + } + XXH64_update(&stream->xxh, ostart + out->pos, (op - ostart) - out->pos); + out->pos = op - ostart; + return stream->bytesLeft; +} + +/* Saves the current seed then generates kMatchBytes random bytes >= 128. + * Generates offset - kMatchBytes of zeros to get a large offset without + * polluting the hash tables. + * Generates another kMatchBytes using the saved seed to generate a with the + * required offset. + */ +static size_t SEQ_gen_offset(SEQ_stream* stream, unsigned value, SEQ_outBuffer* out) +{ + typedef enum { + of_start = 0, + of_run_bytes, + of_offset, + of_run_match, + } of_state; + BYTE* const ostart = (BYTE*)out->dst; + BYTE* const oend = ostart + out->size; + BYTE* op = ostart + out->pos; + + switch ((of_state)stream->state) { + case of_start: + stream->state = of_run_bytes; + stream->saved = stream->seed; + stream->bytesLeft = MIN(value, kMatchBytes); + /* fall-through */ + case of_run_bytes: { + while (stream->bytesLeft > 0 && op < oend) { + *op++ = SEQ_randByte(&stream->seed) | 0x80; + --stream->bytesLeft; + } + if (stream->bytesLeft > 0) + break; + /* State transition */ + stream->state = of_offset; + stream->bytesLeft = value - MIN(value, kMatchBytes); + } + /* fall-through */ + case of_offset: { + /* Copy matchLength + 1 bytes to the output buffer */ + size_t const setLength = MIN(stream->bytesLeft, (size_t)(oend - op)); + if (setLength > 0) { + memset(op, 0, setLength); + op += setLength; + stream->bytesLeft -= setLength; + } + if (stream->bytesLeft > 0) + break; + /* State transition */ + stream->state = of_run_match; + stream->bytesLeft = MIN(value, kMatchBytes); + } + /* fall-through */ + case of_run_match: { + while (stream->bytesLeft > 0 && op < oend) { + *op++ = SEQ_randByte(&stream->saved) | 0x80; + --stream->bytesLeft; + } + if (stream->bytesLeft > 0) + break; + } + /* fall-through */ + default: + stream->state = 0; + stream->bytesLeft = 0; + break; + } + XXH64_update(&stream->xxh, ostart + out->pos, (op - ostart) - out->pos); + out->pos = op - ostart; + return stream->bytesLeft; +} + +/* Returns the number of bytes left to generate. + * Must pass the same type/value until it returns 0. + */ +size_t SEQ_gen(SEQ_stream* stream, SEQ_gen_type type, unsigned value, SEQ_outBuffer* out) +{ + switch (type) { + case SEQ_gen_ml: return SEQ_gen_matchLength(stream, value, out); + case SEQ_gen_ll: return SEQ_gen_litLength(stream, value, out); + case SEQ_gen_of: return SEQ_gen_offset(stream, value, out); + case SEQ_gen_max: /* fall-through */ + default: return 0; + } +} + +/* Returns the xxhash of the data produced so far */ +XXH64_hash_t SEQ_digest(SEQ_stream const* stream) +{ + return XXH64_digest(&stream->xxh); +} diff --git a/tests/seqgen.h b/tests/seqgen.h new file mode 100644 index 000000000..72d798846 --- /dev/null +++ b/tests/seqgen.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under both the BSD-style license (found in the + * LICENSE file in the root directory of this source tree) and the GPLv2 (found + * in the COPYING file in the root directory of this source tree). + * You may select, at your option, one of the above-listed licenses. + */ + +#ifndef SEQGEN_H +#define SEQGEN_H + +#define XXH_STATIC_LINKING_ONLY + +#include "xxhash.h" +#include /* size_t */ + +typedef enum { + SEQ_gen_ml = 0, + SEQ_gen_ll, + SEQ_gen_of, + SEQ_gen_max /* Must be the last value */ +} SEQ_gen_type; + +/* Internal state, do not use */ +typedef struct { + XXH64_state_t xxh; /* xxh state for all the data produced so far (seed=0) */ + unsigned seed; + int state; /* enum to control state machine (clean=0) */ + unsigned saved; + size_t bytesLeft; +} SEQ_stream; + +SEQ_stream SEQ_initStream(unsigned seed); + +typedef struct { + void* dst; + size_t size; + size_t pos; +} SEQ_outBuffer; + +/* Returns non-zero until the current type/value has been generated. + * Must pass the same type/value until it returns 0. + * + * Recommended to pick a value in the middle of the range you want, since there + * may be some noise that causes actual results to be slightly different. + * We try to be more accurate for smaller values. + * + * NOTE: Very small values don't work well (< 6). + */ +size_t SEQ_gen(SEQ_stream* stream, SEQ_gen_type type, unsigned value, + SEQ_outBuffer* out); + +/* Returns the xxhash of the data produced so far */ +XXH64_hash_t SEQ_digest(SEQ_stream const* stream); + +#endif /* SEQGEN_H */ diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index 8b4c83694..1b42bd5d8 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -36,6 +36,7 @@ #include "datagen.h" /* RDG_genBuffer */ #define XXH_STATIC_LINKING_ONLY /* XXH64_state_t */ #include "xxhash.h" /* XXH64_* */ +#include "seqgen.h" /*-************************************ @@ -96,15 +97,21 @@ unsigned int FUZ_rand(unsigned int* seedPtr) return rand32 >> 5; } -#define CHECK_Z(f) { \ - size_t const err = f; \ - if (ZSTD_isError(err)) { \ - DISPLAY("Error => %s : %s ", \ - #f, ZSTD_getErrorName(err)); \ - DISPLAY(" (seed %u, test nb %u) \n", seed, testNb); \ +#define CHECK(cond, ...) { \ + if (cond) { \ + DISPLAY("Error => "); \ + DISPLAY(__VA_ARGS__); \ + DISPLAY(" (seed %u, test nb %u, line %u) \n", \ + seed, testNb, __LINE__); \ goto _output_error; \ } } +#define CHECK_Z(f) { \ + size_t const err = f; \ + CHECK(ZSTD_isError(err), "%s : %s ", \ + #f, ZSTD_getErrorName(err)); \ +} + /*====================================================== * Basic Unit tests @@ -144,6 +151,63 @@ static void FUZ_freeDictionary(buffer_t dict) free(dict.start); } +/* Round trips data and updates xxh with the decompressed data produced */ +static size_t SEQ_roundTrip(ZSTD_CCtx* cctx, ZSTD_DCtx* dctx, + XXH64_state_t* xxh, void* data, size_t size, + ZSTD_EndDirective endOp) +{ + static BYTE compressed[1024]; + static BYTE uncompressed[1024]; + + ZSTD_inBuffer cin = {data, size, 0}; + size_t cret; + + do { + ZSTD_outBuffer cout = {compressed, sizeof(compressed), 0}; + ZSTD_inBuffer din = {compressed, 0, 0}; + ZSTD_outBuffer dout = {uncompressed, 0, 0}; + + cret = ZSTD_compress_generic(cctx, &cout, &cin, endOp); + if (ZSTD_isError(cret)) + return cret; + + din.size = cout.pos; + while (din.pos < din.size || (endOp == ZSTD_e_end && cret == 0)) { + size_t dret; + + dout.pos = 0; + dout.size = sizeof(uncompressed); + dret = ZSTD_decompressStream(dctx, &dout, &din); + if (ZSTD_isError(dret)) + return dret; + XXH64_update(xxh, dout.dst, dout.pos); + if (dret == 0) + break; + } + } while (cin.pos < cin.size || (endOp != ZSTD_e_continue && cret != 0)); + return 0; +} + +/* Generates some data and round trips it */ +static size_t SEQ_generateRoundTrip(ZSTD_CCtx* cctx, ZSTD_DCtx* dctx, + XXH64_state_t* xxh, SEQ_stream* seq, + SEQ_gen_type type, unsigned value) +{ + static BYTE data[1024]; + size_t gen; + + do { + SEQ_outBuffer sout = {data, sizeof(data), 0}; + size_t ret; + gen = SEQ_gen(seq, type, value, &sout); + + ret = SEQ_roundTrip(cctx, dctx, xxh, sout.dst, sout.pos, ZSTD_e_continue); + if (ZSTD_isError(ret)) + return ret; + } while (gen != 0); + + return 0; +} static int basicUnitTests(U32 seed, double compressibility, ZSTD_customMem customMem) { @@ -618,6 +682,53 @@ static int basicUnitTests(U32 seed, double compressibility, ZSTD_customMem custo if (r != 0) goto _output_error; } /* error, or some data not flushed */ DISPLAYLEVEL(3, "OK \n"); + DISPLAYLEVEL(3, "test%3i : check dictionary FSE tables can represent every code : ", testNb++); + { unsigned const kMaxWindowLog = 24; + unsigned value; + ZSTD_compressionParameters cParams = ZSTD_getCParams(3, 1U << kMaxWindowLog, 1024); + ZSTD_CDict* cdict; + ZSTD_DDict* ddict; + SEQ_stream seq = SEQ_initStream(0x87654321); + SEQ_gen_type type; + XXH64_state_t xxh; + + XXH64_reset(&xxh, 0); + cParams.windowLog = kMaxWindowLog; + cdict = ZSTD_createCDict_advanced(dictionary.start, dictionary.filled, ZSTD_dlm_byRef, ZSTD_dm_fullDict, cParams, ZSTD_defaultCMem); + ddict = ZSTD_createDDict(dictionary.start, dictionary.filled); + + if (!cdict || !ddict) goto _output_error; + + ZSTD_CCtx_reset(zc); + ZSTD_resetDStream(zd); + CHECK_Z(ZSTD_CCtx_refCDict(zc, cdict)); + CHECK_Z(ZSTD_initDStream_usingDDict(zd, ddict)); + CHECK_Z(ZSTD_setDStreamParameter(zd, DStream_p_maxWindowSize, 1U << kMaxWindowLog)); + /* Test all values < 300 */ + for (value = 0; value < 300; ++value) { + for (type = (SEQ_gen_type)0; type < SEQ_gen_max; ++type) { + CHECK_Z(SEQ_generateRoundTrip(zc, zd, &xxh, &seq, type, value)); + } + } + /* Test values 2^8 to 2^17 */ + for (value = (1 << 8); value < (1 << 17); value <<= 1) { + for (type = (SEQ_gen_type)0; type < SEQ_gen_max; ++type) { + CHECK_Z(SEQ_generateRoundTrip(zc, zd, &xxh, &seq, type, value)); + CHECK_Z(SEQ_generateRoundTrip(zc, zd, &xxh, &seq, type, value + (value >> 2))); + } + } + /* Test offset values up to the max window log */ + for (value = 8; value <= kMaxWindowLog; ++value) { + CHECK_Z(SEQ_generateRoundTrip(zc, zd, &xxh, &seq, SEQ_gen_of, (1U << value) - 1)); + } + + CHECK_Z(SEQ_roundTrip(zc, zd, &xxh, NULL, 0, ZSTD_e_end)); + CHECK(SEQ_digest(&seq) != XXH64_digest(&xxh), "SEQ XXH64 does not match"); + + ZSTD_freeCDict(cdict); + ZSTD_freeDDict(ddict); + } + DISPLAYLEVEL(3, "OK \n"); /* Overlen overwriting window data bug */ DISPLAYLEVEL(3, "test%3i : wildcopy doesn't overwrite potential match data : ", testNb++); @@ -708,14 +819,6 @@ static U32 FUZ_randomClampedLength(U32* seed, U32 minVal, U32 maxVal) return (U32)((FUZ_rand(seed) % mod) + minVal); } -#define CHECK(cond, ...) { \ - if (cond) { \ - DISPLAY("Error => "); \ - DISPLAY(__VA_ARGS__); \ - DISPLAY(" (seed %u, test nb %u) \n", seed, testNb); \ - goto _output_error; \ -} } - static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, double compressibility, int bigTests) { U32 const maxSrcLog = bigTests ? 24 : 22; From e2bf70ece1a623fbe4e6fdf1c4e5f03bb7011873 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Tue, 17 Oct 2017 16:02:39 -0400 Subject: [PATCH 018/145] Add `check` Target to Makefile --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 2805c8ad4..f2487c591 100644 --- a/Makefile +++ b/Makefile @@ -72,6 +72,9 @@ zstdmt: zlibwrapper: $(MAKE) -C $(ZWRAPDIR) test +.PHONY: check +check: shortest + .PHONY: test shortest test shortest: $(MAKE) -C $(PRGDIR) allVariants From 32c9f715ae1532d4ea06ee5f811ed61c46e48d31 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 17 Oct 2017 14:07:43 -0700 Subject: [PATCH 019/145] fixed : Visual build compressing stdin with multi-threading enabled fails It was multiple reasons stacked : - Visual use a different code path, because ZSTD_NEWAPI is not defined - fileio.c sends `0` as `pledgedSrcSize` to mean `ZSTD_CONTENTSIZE_UNKNOWN` (fixed) - ZSTDMT_resetCCtx() interpreted `0` as "empty" instead of "unknown" (fixed) --- lib/compress/zstdmt_compress.c | 5 ++++- lib/compress/zstdmt_compress.h | 2 +- programs/Makefile | 4 ++-- programs/fileio.c | 6 +++--- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 197a2c49e..af7be3167 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -838,9 +838,12 @@ size_t ZSTDMT_initCStream_usingCDict(ZSTDMT_CCtx* mtctx, /* ZSTDMT_resetCStream() : - * pledgedSrcSize is optional and can be zero == unknown */ + * pledgedSrcSize can be zero == unknown (for the time being) + * prefer using ZSTD_CONTENTSIZE_UNKNOWN, + * as `0` might mean "empty" in the future */ size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* zcs, unsigned long long pledgedSrcSize) { + if (!pledgedSrcSize) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN; if (zcs->params.nbThreads==1) return ZSTD_resetCStream(zcs->cctxPool->cctx[0], pledgedSrcSize); return ZSTDMT_initCStream_internal(zcs, NULL, 0, ZSTD_dm_auto, 0, zcs->params, diff --git a/lib/compress/zstdmt_compress.h b/lib/compress/zstdmt_compress.h index 8c59c684f..a7ce7c7dd 100644 --- a/lib/compress/zstdmt_compress.h +++ b/lib/compress/zstdmt_compress.h @@ -50,7 +50,7 @@ ZSTDLIB_API size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx, /* === Streaming functions === */ ZSTDLIB_API size_t ZSTDMT_initCStream(ZSTDMT_CCtx* mtctx, int compressionLevel); -ZSTDLIB_API size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* mtctx, unsigned long long pledgedSrcSize); /**< pledgedSrcSize is optional and can be zero == unknown */ +ZSTDLIB_API size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* mtctx, unsigned long long pledgedSrcSize); /**< if srcSize is not known at reset time, use ZSTD_CONTENTSIZE_UNKNOWN. Note: for compatibility with older programs, 0 means the same as ZSTD_CONTENTSIZE_UNKNOWN, but it may change in the future, to mean "empty" */ ZSTDLIB_API size_t ZSTDMT_compressStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, ZSTD_inBuffer* input); diff --git a/programs/Makefile b/programs/Makefile index cbb67e9fe..1b21281b4 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -37,8 +37,8 @@ endif CPPFLAGS+= -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/compress \ -I$(ZSTDDIR)/dictBuilder \ - -DZSTD_NEWAPI \ - -DXXH_NAMESPACE=ZSTD_ # because xxhash.o already compiled with this macro from library + -DXXH_NAMESPACE=ZSTD_ \ + -DZSTD_NEWAPI CFLAGS ?= -O3 DEBUGFLAGS+=-Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \ -Wstrict-aliasing=1 -Wswitch-enum -Wdeclaration-after-statement \ diff --git a/programs/fileio.c b/programs/fileio.c index 17ad31b32..03e653893 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -817,11 +817,11 @@ static int FIO_compressFilename_internal(cRess_t ress, /* init */ #ifdef ZSTD_NEWAPI if (fileSize!=0) /* when src is stdin, fileSize==0, but is effectively unknown */ - ZSTD_CCtx_setPledgedSrcSize(ress.cctx, fileSize); /* note : fileSize==0 means "empty" */ + ZSTD_CCtx_setPledgedSrcSize(ress.cctx, fileSize); #elif defined(ZSTD_MULTITHREAD) - CHECK( ZSTDMT_resetCStream(ress.cctx, fileSize) ); /* note : fileSize==0 means "unknown" */ + CHECK( ZSTDMT_resetCStream(ress.cctx, fileSize ? fileSize : ZSTD_CONTENTSIZE_UNKNOWN) ); #else - CHECK( ZSTD_resetCStream(ress.cctx, fileSize) ); /* note : fileSize==0 means "unknown" */ + CHECK( ZSTD_resetCStream(ress.cctx, fileSize ? fileSize : ZSTD_CONTENTSIZE_UNKNOWN) ); #endif /* Main compression loop */ From 18b795374a4d1e0b308fdf6e86b1ea08b4489489 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 17 Oct 2017 16:14:25 -0700 Subject: [PATCH 020/145] UTIL_getFileSize() returns UTIL_FILESIZE_UNKNOWN on failure UTIL_getFileSize() used to return zero on failure. This made it impossible to distinguish a failure from a genuine empty file. Both cases where coalesced. Adding UTIL_FILESIZE_UNKNOWN constant has many consequences on user code, since in many places, the `0` was assumed to mean "error". This is no longer the case, and the error code must be actively checked. --- programs/bench.c | 14 +++++++-- programs/dibio.c | 7 +++-- programs/fileio.c | 50 ++++++++++++++++++++----------- programs/util.h | 35 +++++++++++++--------- tests/fullbench.c | 9 ++++-- tests/paramgrill.c | 10 +++++++ zlibWrapper/examples/zwrapbench.c | 13 ++++++-- 7 files changed, 95 insertions(+), 43 deletions(-) diff --git a/programs/bench.c b/programs/bench.c index ec99c61ce..efec1892a 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -551,6 +551,11 @@ static void BMK_loadFiles(void* buffer, size_t bufferSize, fileSizes[n] = 0; continue; } + if (fileSize == UTIL_FILESIZE_UNKNOWN) { + DISPLAYLEVEL(2, "Cannot evaluate size of %s, ignoring ... \n", fileNamesTable[n]); + fileSizes[n] = 0; + continue; + } f = fopen(fileNamesTable[n], "rb"); if (f==NULL) EXM_THROW(10, "impossible to open file %s", fileNamesTable[n]); DISPLAYUPDATE(2, "Loading %s... \r", fileNamesTable[n]); @@ -581,11 +586,14 @@ static void BMK_benchFileTable(const char** fileNamesTable, unsigned nbFiles, co /* Load dictionary */ if (dictFileName != NULL) { - U64 dictFileSize = UTIL_getFileSize(dictFileName); - if (dictFileSize > 64 MB) EXM_THROW(10, "dictionary file %s too large", dictFileName); + U64 const dictFileSize = UTIL_getFileSize(dictFileName); + if (dictFileSize > 64 MB) + EXM_THROW(10, "dictionary file %s too large", dictFileName); dictBufferSize = (size_t)dictFileSize; dictBuffer = malloc(dictBufferSize); - if (dictBuffer==NULL) EXM_THROW(11, "not enough memory for dictionary (%u bytes)", (U32)dictBufferSize); + if (dictBuffer==NULL) + EXM_THROW(11, "not enough memory for dictionary (%u bytes)", + (U32)dictBufferSize); BMK_loadFiles(dictBuffer, dictBufferSize, fileSizes, &dictFileName, 1); } diff --git a/programs/dibio.c b/programs/dibio.c index 2cb2a4267..dea3ec44f 100644 --- a/programs/dibio.c +++ b/programs/dibio.c @@ -117,7 +117,7 @@ static unsigned DiB_loadFiles(void* buffer, size_t* bufferSizePtr, for (fileIndex=0; fileIndex 2*SAMPLESIZE_MAX); diff --git a/programs/fileio.c b/programs/fileio.c index 03e653893..1b18edefa 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -25,7 +25,7 @@ * Includes ***************************************/ #include "platform.h" /* Large Files support, SET_BINARY_MODE */ -#include "util.h" /* UTIL_getFileSize */ +#include "util.h" /* UTIL_getFileSize, UTIL_isRegularFile */ #include /* fprintf, fopen, fread, _fileno, stdin, stdout */ #include /* malloc, free */ #include /* strcmp, strlen */ @@ -564,7 +564,7 @@ static unsigned long long FIO_compressGzFrame(cRess_t* ress, strm.avail_out = (uInt)ress->dstBufferSize; } } - if (!srcFileSize) + if (srcFileSize != UTIL_FILESIZE_UNKNOWN) DISPLAYUPDATE(2, "\rRead : %u MB ==> %.2f%%", (U32)(inFileSize>>20), (double)outFileSize/inFileSize*100) @@ -651,7 +651,7 @@ static unsigned long long FIO_compressLzmaFrame(cRess_t* ress, strm.next_out = (BYTE*)ress->dstBuffer; strm.avail_out = ress->dstBufferSize; } } - if (!srcFileSize) + if (srcFileSize != UTIL_FILESIZE_UNKNOWN) DISPLAYUPDATE(2, "\rRead : %u MB ==> %.2f%%", (U32)(inFileSize>>20), (double)outFileSize/inFileSize*100) @@ -697,18 +697,17 @@ static unsigned long long FIO_compressLz4Frame(cRess_t* ress, prefs.frameInfo.blockSizeID = LZ4F_max4MB; prefs.frameInfo.contentChecksumFlag = (contentChecksum_t)g_checksumFlag; #if LZ4_VERSION_NUMBER >= 10600 - prefs.frameInfo.contentSize = srcFileSize; + prefs.frameInfo.contentSize = (srcFileSize==UTIL_FILESIZE_UNKNOWN) ? 0 : srcFileSize; #endif - { - size_t blockSize = FIO_LZ4_GetBlockSize_FromBlockId(LZ4F_max4MB); + { size_t blockSize = FIO_LZ4_GetBlockSize_FromBlockId(LZ4F_max4MB); size_t readSize; size_t headerSize = LZ4F_compressBegin(ctx, ress->dstBuffer, ress->dstBufferSize, &prefs); if (LZ4F_isError(headerSize)) EXM_THROW(33, "File header generation failed : %s", LZ4F_getErrorName(headerSize)); - { size_t const sizeCheck = fwrite(ress->dstBuffer, 1, headerSize, ress->dstFile); - if (sizeCheck!=headerSize) EXM_THROW(34, "Write error : cannot write header"); } + if (fwrite(ress->dstBuffer, 1, headerSize, ress->dstFile) != headerSize) + EXM_THROW(34, "Write error : cannot write header"); outFileSize += headerSize; /* Read first block */ @@ -725,7 +724,7 @@ static unsigned long long FIO_compressLz4Frame(cRess_t* ress, EXM_THROW(35, "zstd: %s: lz4 compression failed : %s", srcFileName, LZ4F_getErrorName(outSize)); outFileSize += outSize; - if (!srcFileSize) + if (srcFileSize != UTIL_FILESIZE_UNKNOWN) DISPLAYUPDATE(2, "\rRead : %u MB ==> %.2f%%", (U32)(inFileSize>>20), (double)outFileSize/inFileSize*100) @@ -816,12 +815,12 @@ static int FIO_compressFilename_internal(cRess_t ress, /* init */ #ifdef ZSTD_NEWAPI - if (fileSize!=0) /* when src is stdin, fileSize==0, but is effectively unknown */ + if (fileSize!=UTIL_FILESIZE_UNKNOWN) /* when src is stdin, fileSize==0, but is effectively unknown */ ZSTD_CCtx_setPledgedSrcSize(ress.cctx, fileSize); #elif defined(ZSTD_MULTITHREAD) - CHECK( ZSTDMT_resetCStream(ress.cctx, fileSize ? fileSize : ZSTD_CONTENTSIZE_UNKNOWN) ); + CHECK( ZSTDMT_resetCStream(ress.cctx, (fileSize==UTIL_FILESIZE_UNKNOWN) ? ZSTD_CONTENTSIZE_UNKNOWN : fileSize) ); #else - CHECK( ZSTD_resetCStream(ress.cctx, fileSize ? fileSize : ZSTD_CONTENTSIZE_UNKNOWN) ); + CHECK( ZSTD_resetCStream(ress.cctx, (fileSize==UTIL_FILESIZE_UNKNOWN) ? ZSTD_CONTENTSIZE_UNKNOWN : fileSize) ); #endif /* Main compression loop */ @@ -851,13 +850,13 @@ static int FIO_compressFilename_internal(cRess_t ress, compressedfilesize += outBuff.pos; } } if (g_nbThreads > 1) { - if (!fileSize) + if (fileSize != UTIL_FILESIZE_UNKNOWN) DISPLAYUPDATE(2, "\rRead : %u MB", (U32)(readsize>>20)) else DISPLAYUPDATE(2, "\rRead : %u / %u MB", (U32)(readsize>>20), (U32)(fileSize>>20)); } else { - if (!fileSize) + if (fileSize != UTIL_FILESIZE_UNKNOWN) DISPLAYUPDATE(2, "\rRead : %u MB ==> %.2f%%", (U32)(readsize>>20), (double)compressedfilesize/readsize*100) @@ -985,7 +984,8 @@ int FIO_compressFilename(const char* dstFileName, const char* srcFileName, const char* dictFileName, int compressionLevel, ZSTD_compressionParameters* comprParams) { clock_t const start = clock(); - U64 const srcSize = UTIL_getFileSize(srcFileName); + U64 const fileSize = UTIL_getFileSize(srcFileName); + U64 const srcSize = (fileSize == UTIL_FILESIZE_UNKNOWN) ? ZSTD_CONTENTSIZE_UNKNOWN : fileSize; cRess_t const ress = FIO_createCResources(dictFileName, compressionLevel, srcSize, comprParams); int const result = FIO_compressFilename_dstFile(ress, dstFileName, srcFileName, compressionLevel); @@ -1007,7 +1007,9 @@ int FIO_compressMultipleFilenames(const char** inFileNamesTable, unsigned nbFile size_t dfnSize = FNSPACE; char* dstFileName = (char*)malloc(FNSPACE); size_t const suffixSize = suffix ? strlen(suffix) : 0; - U64 const srcSize = (nbFiles != 1) ? ZSTD_CONTENTSIZE_UNKNOWN : UTIL_getFileSize(inFileNamesTable[0]) ; + U64 const firstFileSize = UTIL_getFileSize(inFileNamesTable[0]); + U64 const firstSrcSize = (firstFileSize == UTIL_FILESIZE_UNKNOWN) ? ZSTD_CONTENTSIZE_UNKNOWN : firstFileSize; + U64 const srcSize = (nbFiles != 1) ? ZSTD_CONTENTSIZE_UNKNOWN : firstSrcSize ; cRess_t ress = FIO_createCResources(dictFileName, compressionLevel, srcSize, comprParams); /* init */ @@ -1799,7 +1801,7 @@ typedef struct { * 2 for file not compressed with zstd * 3 for cases in which file could not be opened. */ -static int getFileInfo(fileInfo_t* info, const char* inFileName){ +static int getFileInfo_fileConfirmed(fileInfo_t* info, const char* inFileName){ int detectError = 0; FILE* const srcFile = FIO_openSrcFile(inFileName); if (srcFile == NULL) { @@ -1815,7 +1817,8 @@ static int getFileInfo(fileInfo_t* info, const char* inFileName){ if (numBytesRead < ZSTD_frameHeaderSize_min) { if ( feof(srcFile) && (numBytesRead == 0) - && (info->compressedSize > 0) ) { + && (info->compressedSize > 0) + && (info->compressedSize != UTIL_FILESIZE_UNKNOWN) ) { break; } else if (feof(srcFile)) { @@ -1928,6 +1931,17 @@ static int getFileInfo(fileInfo_t* info, const char* inFileName){ return detectError; } +static int getFileInfo(fileInfo_t* info, const char* srcFileName) +{ + int const isAFile = UTIL_isRegularFile(srcFileName); + if (!isAFile) { + DISPLAY("Error : %s is not a file", srcFileName); + return 3; + } + return getFileInfo_fileConfirmed(info, srcFileName); +} + + static void displayInfo(const char* inFileName, const fileInfo_t* info, int displayLevel){ unsigned const unit = info->compressedSize < (1 MB) ? (1 KB) : (1 MB); const char* const unitStr = info->compressedSize < (1 MB) ? "KB" : "MB"; diff --git a/programs/util.h b/programs/util.h index c8be5f5fb..c5e4365d0 100644 --- a/programs/util.h +++ b/programs/util.h @@ -313,33 +313,40 @@ UTIL_STATIC U32 UTIL_isLink(const char* infilename) } +#define UTIL_FILESIZE_UNKNOWN ((U64)(-1)) UTIL_STATIC U64 UTIL_getFileSize(const char* infilename) { - int r; + if (!UTIL_isRegularFile(infilename)) return UTIL_FILESIZE_UNKNOWN; + { int r; #if defined(_MSC_VER) - struct __stat64 statbuf; - r = _stat64(infilename, &statbuf); - if (r || !(statbuf.st_mode & S_IFREG)) return 0; /* No good... */ + struct __stat64 statbuf; + r = _stat64(infilename, &statbuf); + if (r || !(statbuf.st_mode & S_IFREG)) return UTIL_FILESIZE_UNKNOWN; #elif defined(__MINGW32__) && defined (__MSVCRT__) - struct _stati64 statbuf; - r = _stati64(infilename, &statbuf); - if (r || !(statbuf.st_mode & S_IFREG)) return 0; /* No good... */ + struct _stati64 statbuf; + r = _stati64(infilename, &statbuf); + if (r || !(statbuf.st_mode & S_IFREG)) return UTIL_FILESIZE_UNKNOWN; #else - struct stat statbuf; - r = stat(infilename, &statbuf); - if (r || !S_ISREG(statbuf.st_mode)) return 0; /* No good... */ + struct stat statbuf; + r = stat(infilename, &statbuf); + if (r || !S_ISREG(statbuf.st_mode)) return UTIL_FILESIZE_UNKNOWN; #endif - return (U64)statbuf.st_size; + return (U64)statbuf.st_size; + } } UTIL_STATIC U64 UTIL_getTotalFileSize(const char** fileNamesTable, unsigned nbFiles) { U64 total = 0; + int error = 0; unsigned n; - for (n=0; n inFileSize) benchedSize = (size_t)inFileSize; if (benchedSize < inFileSize) diff --git a/tests/paramgrill.c b/tests/paramgrill.c index 317ec461c..a387a44c6 100644 --- a/tests/paramgrill.c +++ b/tests/paramgrill.c @@ -687,6 +687,11 @@ int benchFiles(const char** fileNamesTable, int nbFiles) DISPLAY( "Pb opening %s\n", inFileName); return 11; } + if (inFileSize == UTIL_FILESIZE_UNKNOWN) { + DISPLAY("Pb evaluatin size of %s \n", inFileName); + fclose(inFile); + return 11; + } /* Memory allocation */ benchedSize = BMK_findMaxMem(inFileSize*3) / 3; @@ -740,6 +745,11 @@ int optimizeForSize(const char* inFileName, U32 targetSpeed) /* Init */ if (inFile==NULL) { DISPLAY( "Pb opening %s\n", inFileName); return 11; } + if (inFileSize == UTIL_FILESIZE_UNKNOWN) { + DISPLAY("Pb evaluatin size of %s \n", inFileName); + fclose(inFile); + return 11; + } /* Memory allocation & restrictions */ if ((U64)benchedSize > inFileSize) benchedSize = (size_t)inFileSize; diff --git a/zlibWrapper/examples/zwrapbench.c b/zlibWrapper/examples/zwrapbench.c index 25a23f885..9226932b5 100644 --- a/zlibWrapper/examples/zwrapbench.c +++ b/zlibWrapper/examples/zwrapbench.c @@ -684,6 +684,11 @@ static void BMK_loadFiles(void* buffer, size_t bufferSize, fileSizes[n] = 0; continue; } + if (fileSize == UTIL_FILESIZE_UNKNOWN) { + DISPLAYLEVEL(2, "Cannot determine size of %s ... \n", fileNamesTable[n]); + fileSizes[n] = 0; + continue; + } f = fopen(fileNamesTable[n], "rb"); if (f==NULL) EXM_THROW(10, "impossible to open file %s", fileNamesTable[n]); DISPLAYUPDATE(2, "Loading %s... \r", fileNamesTable[n]); @@ -714,11 +719,13 @@ static void BMK_benchFileTable(const char** fileNamesTable, unsigned nbFiles, /* Load dictionary */ if (dictFileName != NULL) { - U64 dictFileSize = UTIL_getFileSize(dictFileName); - if (dictFileSize > 64 MB) EXM_THROW(10, "dictionary file %s too large", dictFileName); + U64 const dictFileSize = UTIL_getFileSize(dictFileName); + if (dictFileSize > 64 MB) + EXM_THROW(10, "dictionary file %s too large", dictFileName); dictBufferSize = (size_t)dictFileSize; dictBuffer = malloc(dictBufferSize); - if (dictBuffer==NULL) EXM_THROW(11, "not enough memory for dictionary (%u bytes)", (U32)dictBufferSize); + if (dictBuffer==NULL) + EXM_THROW(11, "not enough memory for dictionary (%u bytes)", (U32)dictBufferSize); BMK_loadFiles(dictBuffer, dictBufferSize, fileSizes, &dictFileName, 1); } From 5891f1d952b26f75ab80c1869e7a6af249e104eb Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 17 Oct 2017 16:23:20 -0700 Subject: [PATCH 021/145] added a check to ensure 0-size is present in frame header --- tests/playTests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/playTests.sh b/tests/playTests.sh index f008d8998..299c2d883 100755 --- a/tests/playTests.sh +++ b/tests/playTests.sh @@ -621,7 +621,7 @@ $ECHO "\n===> zstd --list/-l test with null files " $ZSTD tmp5 $ZSTD -l tmp5.zst ! $ZSTD -l tmp5* -$ZSTD -lv tmp5.zst +$ZSTD -lv tmp5.zst | grep "Decompressed Size: 0.00 KB (0 B)" # check that 0 size is present in header ! $ZSTD -lv tmp5* $ECHO "\n===> zstd --list/-l test with no content size field " From ca1a9ebac54b01766373e138833dc39674bad7c3 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 18 Oct 2017 11:18:27 -0700 Subject: [PATCH 022/145] fixed zlib wrapper it was invoking ZSTD_initCStream_advanced() with pledgedSrcSize==0 and contentSizeFlag=1 which means "empty" while the intention was to mean "unknown". The contentSizeFlag==1 is new, it is a consequence of setting this value to 1 by default. The solution selected here is to pass ZSTD_CONTENTSIZE_UNKNOWN to mean "unknown". So contentSizeFlag remains set (it wasn't in previous versions). --- doc/zstd_manual.html | 4 ++-- lib/zstd.h | 2 +- zlibWrapper/zstd_zlibwrapper.c | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html index 7ef86a23d..9697ebd57 100644 --- a/doc/zstd_manual.html +++ b/doc/zstd_manual.html @@ -518,7 +518,7 @@ size_t ZSTD_estimateDDictSize(size_t dictSize, ZSTD_dictLoadMethod_e dictLoadMet
    ZSTD_parameters ZSTD_getParams(int compressionLevel, unsigned long long estimatedSrcSize, size_t dictSize);
     

    same as ZSTD_getCParams(), but @return a full `ZSTD_parameters` object instead of sub-component `ZSTD_compressionParameters`. - All fields of `ZSTD_frameParameters` are set to default (0) + All fields of `ZSTD_frameParameters` are set to default : contentSize=1, checksum=0, noDictID=0


    size_t ZSTD_checkCParams(ZSTD_compressionParameters params);
    @@ -634,7 +634,7 @@ ZSTD_CStream* ZSTD_initStaticCStream(void* workspace, size_t workspaceSize);
     size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pledgedSrcSize);   /**< pledgedSrcSize must be correct. If it is not known at init time, use ZSTD_CONTENTSIZE_UNKNOWN. Note that, for compatibility with older programs, a size of 0 is interepreted as "unknown". But it may change in some future version to mean "empty". */
     size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel); /**< creates of an internal CDict (incompatible with static CCtx), except if dict == NULL or dictSize < 8, in which case no dict is used. Note: dict is loaded with ZSTD_dm_auto (treated as a full zstd dictionary if it begins with ZSTD_MAGIC_DICTIONARY, else as raw content) and ZSTD_dlm_byCopy.*/
     size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, const void* dict, size_t dictSize,
    -                                             ZSTD_parameters params, unsigned long long pledgedSrcSize);  /**< pledgedSrcSize : If srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN. dict is loaded with ZSTD_dm_auto and ZSTD_dlm_byCopy. */
    +                                             ZSTD_parameters params, unsigned long long pledgedSrcSize);  /**< pledgedSrcSize : if srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN. dict is loaded with ZSTD_dm_auto and ZSTD_dlm_byCopy. */
     size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict);  /**< note : cdict will just be referenced, and must outlive compression session */
     size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs, const ZSTD_CDict* cdict, ZSTD_frameParameters fParams, unsigned long long pledgedSrcSize);  /**< same as ZSTD_initCStream_usingCDict(), with control over frame parameters */
     

    diff --git a/lib/zstd.h b/lib/zstd.h index 4681d3f2a..6b7bfd03c 100644 --- a/lib/zstd.h +++ b/lib/zstd.h @@ -613,7 +613,7 @@ ZSTDLIB_API ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, uns /*! ZSTD_getParams() : * same as ZSTD_getCParams(), but @return a full `ZSTD_parameters` object instead of sub-component `ZSTD_compressionParameters`. -* All fields of `ZSTD_frameParameters` are set to default (0) */ +* All fields of `ZSTD_frameParameters` are set to default : contentSize=1, checksum=0, noDictID=0 */ ZSTDLIB_API ZSTD_parameters ZSTD_getParams(int compressionLevel, unsigned long long estimatedSrcSize, size_t dictSize); /*! ZSTD_checkCParams() : diff --git a/zlibWrapper/zstd_zlibwrapper.c b/zlibWrapper/zstd_zlibwrapper.c index c5fbdf98e..b3e5f3672 100644 --- a/zlibWrapper/zstd_zlibwrapper.c +++ b/zlibWrapper/zstd_zlibwrapper.c @@ -270,7 +270,7 @@ ZEXTERN int ZEXPORT z_deflateSetDictionary OF((z_streamp strm, zwc->zbc = ZSTD_createCStream_advanced(zwc->customMem); if (zwc->zbc == NULL) return ZWRAPC_finishWithError(zwc, strm, 0); } - { int res = ZWRAP_initializeCStream(zwc, dictionary, dictLength, 0); + { int res = ZWRAP_initializeCStream(zwc, dictionary, dictLength, ZSTD_CONTENTSIZE_UNKNOWN); if (res != Z_OK) return ZWRAPC_finishWithError(zwc, strm, res); } zwc->comprState = ZWRAP_useReset; } @@ -295,7 +295,7 @@ ZEXTERN int ZEXPORT z_deflate OF((z_streamp strm, int flush)) if (zwc->zbc == NULL) { zwc->zbc = ZSTD_createCStream_advanced(zwc->customMem); if (zwc->zbc == NULL) return ZWRAPC_finishWithError(zwc, strm, 0); - { int const initErr = ZWRAP_initializeCStream(zwc, NULL, 0, (flush == Z_FINISH) ? strm->avail_in : 0); + { int const initErr = ZWRAP_initializeCStream(zwc, NULL, 0, (flush == Z_FINISH) ? strm->avail_in : ZSTD_CONTENTSIZE_UNKNOWN); if (initErr != Z_OK) return ZWRAPC_finishWithError(zwc, strm, initErr); } if (flush != Z_FINISH) zwc->comprState = ZWRAP_useReset; } else { @@ -308,7 +308,7 @@ ZEXTERN int ZEXPORT z_deflate OF((z_streamp strm, int flush)) return ZWRAPC_finishWithError(zwc, strm, 0); } } else { - int const res = ZWRAP_initializeCStream(zwc, NULL, 0, (flush == Z_FINISH) ? strm->avail_in : 0); + int const res = ZWRAP_initializeCStream(zwc, NULL, 0, (flush == Z_FINISH) ? strm->avail_in : ZSTD_CONTENTSIZE_UNKNOWN); if (res != Z_OK) return ZWRAPC_finishWithError(zwc, strm, res); if (flush != Z_FINISH) zwc->comprState = ZWRAP_useReset; } From 300e1df0a38b8200155fe11a851b3ecc9bc6ddfe Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 18 Oct 2017 11:41:52 -0700 Subject: [PATCH 023/145] fixed wrong test to display compression status --- programs/fileio.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/programs/fileio.c b/programs/fileio.c index 1b18edefa..4ddb8f8e0 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -564,7 +564,7 @@ static unsigned long long FIO_compressGzFrame(cRess_t* ress, strm.avail_out = (uInt)ress->dstBufferSize; } } - if (srcFileSize != UTIL_FILESIZE_UNKNOWN) + if (srcFileSize == UTIL_FILESIZE_UNKNOWN) DISPLAYUPDATE(2, "\rRead : %u MB ==> %.2f%%", (U32)(inFileSize>>20), (double)outFileSize/inFileSize*100) @@ -651,7 +651,7 @@ static unsigned long long FIO_compressLzmaFrame(cRess_t* ress, strm.next_out = (BYTE*)ress->dstBuffer; strm.avail_out = ress->dstBufferSize; } } - if (srcFileSize != UTIL_FILESIZE_UNKNOWN) + if (srcFileSize == UTIL_FILESIZE_UNKNOWN) DISPLAYUPDATE(2, "\rRead : %u MB ==> %.2f%%", (U32)(inFileSize>>20), (double)outFileSize/inFileSize*100) @@ -724,7 +724,7 @@ static unsigned long long FIO_compressLz4Frame(cRess_t* ress, EXM_THROW(35, "zstd: %s: lz4 compression failed : %s", srcFileName, LZ4F_getErrorName(outSize)); outFileSize += outSize; - if (srcFileSize != UTIL_FILESIZE_UNKNOWN) + if (srcFileSize == UTIL_FILESIZE_UNKNOWN) DISPLAYUPDATE(2, "\rRead : %u MB ==> %.2f%%", (U32)(inFileSize>>20), (double)outFileSize/inFileSize*100) @@ -850,18 +850,18 @@ static int FIO_compressFilename_internal(cRess_t ress, compressedfilesize += outBuff.pos; } } if (g_nbThreads > 1) { - if (fileSize != UTIL_FILESIZE_UNKNOWN) + if (fileSize == UTIL_FILESIZE_UNKNOWN) DISPLAYUPDATE(2, "\rRead : %u MB", (U32)(readsize>>20)) else DISPLAYUPDATE(2, "\rRead : %u / %u MB", (U32)(readsize>>20), (U32)(fileSize>>20)); } else { - if (fileSize != UTIL_FILESIZE_UNKNOWN) + if (fileSize == UTIL_FILESIZE_UNKNOWN) DISPLAYUPDATE(2, "\rRead : %u MB ==> %.2f%%", (U32)(readsize>>20), (double)compressedfilesize/readsize*100) else - DISPLAYUPDATE(2, "\rRead : %u / %u MB ==> %.2f%%", + DISPLAYUPDATE(2, "\rRead : %u / %u MB ==> %.2f%%", (U32)(readsize>>20), (U32)(fileSize>>20), (double)compressedfilesize/readsize*100); } From d3b9547aa4cab7421946ef4010a998de34703da8 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 18 Oct 2017 17:01:53 -0700 Subject: [PATCH 024/145] IO and bench : ZSTD_NEWAPI is the only remaining code path removed the other 2 code paths (single thread, and ZSTDMT ones) keeping only the new advanced API, for easier code coverage. It shall also fix identified issue with Visual Studio which doesn't have ZSTD_NEWAPI defined. --- programs/Makefile | 3 +- programs/bench.c | 39 -------------- programs/fileio.c | 135 +++++++++++----------------------------------- 3 files changed, 33 insertions(+), 144 deletions(-) diff --git a/programs/Makefile b/programs/Makefile index 1b21281b4..e1c5f50b6 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -37,8 +37,7 @@ endif CPPFLAGS+= -I$(ZSTDDIR) -I$(ZSTDDIR)/common -I$(ZSTDDIR)/compress \ -I$(ZSTDDIR)/dictBuilder \ - -DXXH_NAMESPACE=ZSTD_ \ - -DZSTD_NEWAPI + -DXXH_NAMESPACE=ZSTD_ CFLAGS ?= -O3 DEBUGFLAGS+=-Wall -Wextra -Wcast-qual -Wcast-align -Wshadow \ -Wstrict-aliasing=1 -Wswitch-enum -Wdeclaration-after-statement \ diff --git a/programs/bench.c b/programs/bench.c index efec1892a..5b67d5c76 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -287,7 +287,6 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, U64 const clockLoop = g_nbSeconds ? TIMELOOP_MICROSEC : 1; U32 nbLoops = 0; ZSTD_CDict* cdict = NULL; -#ifdef ZSTD_NEWAPI ZSTD_CCtx_setParameter(ctx, ZSTD_p_nbThreads, g_nbThreads); ZSTD_CCtx_setParameter(ctx, ZSTD_p_compressionLevel, cLevel); ZSTD_CCtx_setParameter(ctx, ZSTD_p_enableLongDistanceMatching, g_ldmFlag); @@ -306,25 +305,10 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, ZSTD_CCtx_setParameter(ctx, ZSTD_p_targetLength, comprParams->targetLength); ZSTD_CCtx_setParameter(ctx, ZSTD_p_compressionStrategy, comprParams->strategy); ZSTD_CCtx_loadDictionary(ctx, dictBuffer, dictBufferSize); -#else - size_t const avgSize = MIN(blockSize, (srcSize / nbFiles)); - ZSTD_parameters zparams = ZSTD_getParams(cLevel, avgSize, dictBufferSize); - ZSTD_customMem const cmem = { NULL, NULL, NULL }; - if (comprParams->windowLog) zparams.cParams.windowLog = comprParams->windowLog; - if (comprParams->chainLog) zparams.cParams.chainLog = comprParams->chainLog; - if (comprParams->hashLog) zparams.cParams.hashLog = comprParams->hashLog; - if (comprParams->searchLog) zparams.cParams.searchLog = comprParams->searchLog; - if (comprParams->searchLength) zparams.cParams.searchLength = comprParams->searchLength; - if (comprParams->targetLength) zparams.cParams.targetLength = comprParams->targetLength; - if (comprParams->strategy) zparams.cParams.strategy = comprParams->strategy; - cdict = ZSTD_createCDict_advanced(dictBuffer, dictBufferSize, ZSTD_dlm_byRef, ZSTD_dm_auto, zparams.cParams, cmem); - if (cdict==NULL) EXM_THROW(1, "ZSTD_createCDict_advanced() allocation failure"); -#endif do { U32 blockNb; for (blockNb=0; blockNbwindowLog) ); - CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_chainLog, comprParams->chainLog) ); - CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_hashLog, comprParams->hashLog) ); - CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_searchLog, comprParams->searchLog) ); - CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_minMatch, comprParams->searchLength) ); - CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_targetLength, comprParams->targetLength) ); - CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_compressionStrategy, (U32)comprParams->strategy) ); - /* multi-threading */ - DISPLAYLEVEL(5,"set nb threads = %u \n", g_nbThreads); - CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_nbThreads, g_nbThreads) ); - /* dictionary */ - CHECK( ZSTD_CCtx_setPledgedSrcSize(ress.cctx, srcSize) ); /* just to load dictionary with good compression parameters */ - CHECK( ZSTD_CCtx_loadDictionary(ress.cctx, dictBuffer, dictBuffSize) ); - CHECK( ZSTD_CCtx_setPledgedSrcSize(ress.cctx, ZSTD_CONTENTSIZE_UNKNOWN) ); /* reset */ + CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_contentSizeFlag, 1) ); /* always enable content size, when available (note: supposed to be default anyway) */ + CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_dictIDFlag, g_dictIDFlag) ); + CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_checksumFlag, g_checksumFlag) ); + /* compression level */ + CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_compressionLevel, cLevel) ); + /* long distance matching */ + CHECK( ZSTD_CCtx_setParameter( + ress.cctx, ZSTD_p_enableLongDistanceMatching, g_ldmFlag) ); + CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_ldmHashLog, g_ldmHashLog) ); + CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_ldmMinMatch, g_ldmMinMatch) ); + if (g_ldmBucketSizeLog != FIO_LDM_PARAM_NOTSET) { + CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_ldmBucketSizeLog, g_ldmBucketSizeLog) ); } -#elif defined(ZSTD_MULTITHREAD) - { ZSTD_parameters params = ZSTD_getParams(cLevel, srcSize, dictBuffSize); - params.fParams.checksumFlag = g_checksumFlag; - params.fParams.noDictIDFlag = !g_dictIDFlag; - if (comprParams->windowLog) params.cParams.windowLog = comprParams->windowLog; - if (comprParams->chainLog) params.cParams.chainLog = comprParams->chainLog; - if (comprParams->hashLog) params.cParams.hashLog = comprParams->hashLog; - if (comprParams->searchLog) params.cParams.searchLog = comprParams->searchLog; - if (comprParams->searchLength) params.cParams.searchLength = comprParams->searchLength; - if (comprParams->targetLength) params.cParams.targetLength = comprParams->targetLength; - if (comprParams->strategy) params.cParams.strategy = (ZSTD_strategy) comprParams->strategy; - CHECK( ZSTDMT_initCStream_advanced(ress.cctx, dictBuffer, dictBuffSize, params, srcSize) ); - ZSTDMT_setMTCtxParameter(ress.cctx, ZSTDMT_p_sectionSize, g_blockSize); + if (g_ldmHashEveryLog != FIO_LDM_PARAM_NOTSET) { + CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_ldmHashEveryLog, g_ldmHashEveryLog) ); } -#else - { ZSTD_parameters params = ZSTD_getParams(cLevel, srcSize, dictBuffSize); - params.fParams.checksumFlag = g_checksumFlag; - params.fParams.noDictIDFlag = !g_dictIDFlag; - if (comprParams->windowLog) params.cParams.windowLog = comprParams->windowLog; - if (comprParams->chainLog) params.cParams.chainLog = comprParams->chainLog; - if (comprParams->hashLog) params.cParams.hashLog = comprParams->hashLog; - if (comprParams->searchLog) params.cParams.searchLog = comprParams->searchLog; - if (comprParams->searchLength) params.cParams.searchLength = comprParams->searchLength; - if (comprParams->targetLength) params.cParams.targetLength = comprParams->targetLength; - if (comprParams->strategy) params.cParams.strategy = (ZSTD_strategy) comprParams->strategy; - CHECK( ZSTD_initCStream_advanced(ress.cctx, dictBuffer, dictBuffSize, params, srcSize) ); - } -#endif + /* compression parameters */ + CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_windowLog, comprParams->windowLog) ); + CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_chainLog, comprParams->chainLog) ); + CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_hashLog, comprParams->hashLog) ); + CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_searchLog, comprParams->searchLog) ); + CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_minMatch, comprParams->searchLength) ); + CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_targetLength, comprParams->targetLength) ); + CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_compressionStrategy, (U32)comprParams->strategy) ); + /* multi-threading */ + DISPLAYLEVEL(5,"set nb threads = %u \n", g_nbThreads); + CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_nbThreads, g_nbThreads) ); + /* dictionary */ + CHECK( ZSTD_CCtx_setPledgedSrcSize(ress.cctx, srcSize) ); /* just to load dictionary with good compression parameters */ + CHECK( ZSTD_CCtx_loadDictionary(ress.cctx, dictBuffer, dictBuffSize) ); + CHECK( ZSTD_CCtx_setPledgedSrcSize(ress.cctx, ZSTD_CONTENTSIZE_UNKNOWN) ); /* reset */ + free(dictBuffer); } @@ -509,11 +460,7 @@ static void FIO_freeCResources(cRess_t ress) { free(ress.srcBuffer); free(ress.dstBuffer); -#if !defined(ZSTD_NEWAPI) && defined(ZSTD_MULTITHREAD) - ZSTDMT_freeCCtx(ress.cctx); -#else ZSTD_freeCStream(ress.cctx); /* never fails */ -#endif } @@ -814,14 +761,8 @@ static int FIO_compressFilename_internal(cRess_t ress, } /* init */ -#ifdef ZSTD_NEWAPI - if (fileSize!=UTIL_FILESIZE_UNKNOWN) /* when src is stdin, fileSize==0, but is effectively unknown */ + if (fileSize != UTIL_FILESIZE_UNKNOWN) ZSTD_CCtx_setPledgedSrcSize(ress.cctx, fileSize); -#elif defined(ZSTD_MULTITHREAD) - CHECK( ZSTDMT_resetCStream(ress.cctx, (fileSize==UTIL_FILESIZE_UNKNOWN) ? ZSTD_CONTENTSIZE_UNKNOWN : fileSize) ); -#else - CHECK( ZSTD_resetCStream(ress.cctx, (fileSize==UTIL_FILESIZE_UNKNOWN) ? ZSTD_CONTENTSIZE_UNKNOWN : fileSize) ); -#endif /* Main compression loop */ while (1) { @@ -833,14 +774,8 @@ static int FIO_compressFilename_internal(cRess_t ress, while (inBuff.pos != inBuff.size) { ZSTD_outBuffer outBuff = { ress.dstBuffer, ress.dstBufferSize, 0 }; -#ifdef ZSTD_NEWAPI CHECK( ZSTD_compress_generic(ress.cctx, &outBuff, &inBuff, ZSTD_e_continue) ); -#elif defined(ZSTD_MULTITHREAD) - CHECK( ZSTDMT_compressStream(ress.cctx, &outBuff, &inBuff) ); -#else - CHECK( ZSTD_compressStream(ress.cctx, &outBuff, &inBuff) ); -#endif /* Write compressed stream */ if (outBuff.pos) { @@ -871,21 +806,15 @@ static int FIO_compressFilename_internal(cRess_t ress, { size_t result = 1; while (result != 0) { ZSTD_outBuffer outBuff = { ress.dstBuffer, ress.dstBufferSize, 0 }; -#ifdef ZSTD_NEWAPI ZSTD_inBuffer inBuff = { NULL, 0, 0 }; result = ZSTD_compress_generic(ress.cctx, &outBuff, &inBuff, ZSTD_e_end); -#elif defined(ZSTD_MULTITHREAD) - result = ZSTDMT_endStream(ress.cctx, &outBuff); -#else - result = ZSTD_endStream(ress.cctx, &outBuff); -#endif if (ZSTD_isError(result)) { EXM_THROW(26, "Compression error during frame end : %s", ZSTD_getErrorName(result)); } { size_t const sizeCheck = fwrite(ress.dstBuffer, 1, outBuff.pos, dstFile); - if (sizeCheck!=outBuff.pos) + if (sizeCheck != outBuff.pos) EXM_THROW(27, "Write error : cannot write frame end into %s", dstFileName); } compressedfilesize += outBuff.pos; From eac42534fe4f631606c2d604d5db0b6613cf01cd Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 19 Oct 2017 11:56:14 -0700 Subject: [PATCH 025/145] bench: fixed Visual warning regarding struct initialization also : removed dependency on zstdmt_compress.h removed several unused macros fileio : small code refactoring to reduce some variable scope --- programs/bench.c | 51 ++++++++-------- programs/fileio.c | 146 +++++++++++++++++++++------------------------- 2 files changed, 93 insertions(+), 104 deletions(-) diff --git a/programs/bench.c b/programs/bench.c index 5b67d5c76..8ecae9ebd 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -41,7 +41,6 @@ #include "zstd.h" #include "datagen.h" /* RDG_genBuffer */ #include "xxhash.h" -#include "zstdmt_compress.h" /* ************************************* @@ -189,7 +188,6 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, size_t const maxCompressedSize = ZSTD_compressBound(srcSize) + (maxNbBlocks * 1024); /* add some room for safety */ void* const compressedBuffer = malloc(maxCompressedSize); void* resultBuffer = malloc(srcSize); - ZSTDMT_CCtx* const mtctx = ZSTDMT_createCCtx(g_nbThreads); ZSTD_CCtx* const ctx = ZSTD_createCCtx(); ZSTD_DCtx* const dctx = ZSTD_createDCtx(); size_t const loadedCompressedSize = srcSize; @@ -286,7 +284,6 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, if (!cCompleted) { /* still some time to do compression tests */ U64 const clockLoop = g_nbSeconds ? TIMELOOP_MICROSEC : 1; U32 nbLoops = 0; - ZSTD_CDict* cdict = NULL; ZSTD_CCtx_setParameter(ctx, ZSTD_p_nbThreads, g_nbThreads); ZSTD_CCtx_setParameter(ctx, ZSTD_p_compressionLevel, cLevel); ZSTD_CCtx_setParameter(ctx, ZSTD_p_enableLongDistanceMatching, g_ldmFlag); @@ -308,27 +305,31 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, do { U32 blockNb; for (blockNb=0; blockNb= maxTime); + { U64 const loopDuration = UTIL_clockSpanMicro(clockStart); + if (loopDuration < fastestC*nbLoops) + fastestC = loopDuration / nbLoops; + totalCTime += loopDuration; + cCompleted = (totalCTime >= maxTime); /* end compression tests */ } } cSize = 0; @@ -336,15 +337,15 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, ratio = (double)srcSize / (double)cSize; markNb = (markNb+1) % NB_MARKS; DISPLAYLEVEL(2, "%2s-%-17.17s :%10u ->%10u (%5.3f),%6.1f MB/s\r", - marks[markNb], displayName, (U32)srcSize, (U32)cSize, ratio, - (double)srcSize / fastestC ); + marks[markNb], displayName, (U32)srcSize, (U32)cSize, + ratio, (double)srcSize / fastestC ); } else { /* g_decodeOnly */ memcpy(compressedBuffer, srcBuffer, loadedCompressedSize); } #if 0 /* disable decompression test */ dCompleted=1; - (void)totalDTime; (void)fastestD; (void)crcOrig; /* unused when decompression disabled */ + (void)totalDTime; (void)fastestD; (void)crcOrig; /* unused when decompression disabled */ #else /* Decompression */ if (!dCompleted) memset(resultBuffer, 0xD6, srcSize); /* warm result buffer */ @@ -374,9 +375,10 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, nbLoops++; } while (UTIL_clockSpanMicro(clockStart) < clockLoop); ZSTD_freeDDict(ddict); - { U64 const clockSpanMicro = UTIL_clockSpanMicro(clockStart); - if (clockSpanMicro < fastestD*nbLoops) fastestD = clockSpanMicro / nbLoops; - totalDTime += clockSpanMicro; + { U64 const loopDuration = UTIL_clockSpanMicro(clockStart); + if (loopDuration < fastestD*nbLoops) + fastestD = loopDuration / nbLoops; + totalDTime += loopDuration; dCompleted = (totalDTime >= maxTime); } } @@ -439,7 +441,6 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, free(blockTable); free(compressedBuffer); free(resultBuffer); - ZSTDMT_freeCCtx(mtctx); ZSTD_freeCCtx(ctx); ZSTD_freeDCtx(dctx); return 0; diff --git a/programs/fileio.c b/programs/fileio.c index c8eab0679..7efa3c656 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -42,9 +42,6 @@ #include "fileio.h" #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_magicNumber, ZSTD_frameHeaderSize_max */ #include "zstd.h" -#ifdef ZSTD_MULTITHREAD -# include "zstdmt_compress.h" -#endif #if defined(ZSTD_GZCOMPRESS) || defined(ZSTD_GZDECOMPRESS) # include # if !defined(z_const) @@ -70,18 +67,6 @@ #define MB *(1<<20) #define GB *(1U<<30) -#define _1BIT 0x01 -#define _2BITS 0x03 -#define _3BITS 0x07 -#define _4BITS 0x0F -#define _6BITS 0x3F -#define _8BITS 0xFF - -#define BLOCKSIZE (128 KB) -#define ROLLBUFFERSIZE (BLOCKSIZE*8*64) - -#define FIO_FRAMEHEADERSIZE 5 /* as a define, because needed to allocated table on stack */ - #define DICTSIZE_MAX (32 MB) /* protection against large input (attack scenario) */ #define FNSPACE 30 @@ -122,14 +107,14 @@ static clock_t g_time = 0; # define ZSTD_DEBUG 0 #endif #define DEBUGLOG(l,...) if (l<=ZSTD_DEBUG) DISPLAY(__VA_ARGS__); -#define EXM_THROW(error, ...) \ -{ \ - DISPLAYLEVEL(1, "zstd: "); \ - DEBUGLOG(1, "Error defined at %s, line %i : \n", __FILE__, __LINE__); \ - DISPLAYLEVEL(1, "error %i : ", error); \ - DISPLAYLEVEL(1, __VA_ARGS__); \ - DISPLAYLEVEL(1, " \n"); \ - exit(error); \ +#define EXM_THROW(error, ...) \ +{ \ + DISPLAYLEVEL(1, "zstd: "); \ + DEBUGLOG(1, "Error defined at %s, line %i : \n", __FILE__, __LINE__); \ + DISPLAYLEVEL(1, "error %i : ", error); \ + DISPLAYLEVEL(1, __VA_ARGS__); \ + DISPLAYLEVEL(1, " \n"); \ + exit(error); \ } #define CHECK(f) { \ @@ -273,86 +258,88 @@ static int FIO_remove(const char* path) } /** FIO_openSrcFile() : - * condition : `dstFileName` must be non-NULL. - * @result : FILE* to `dstFileName`, or NULL if it fails */ + * condition : `srcFileName` must be non-NULL. + * @result : FILE* to `srcFileName`, or NULL if it fails */ static FILE* FIO_openSrcFile(const char* srcFileName) { - FILE* f; - + assert(srcFileName != NULL); if (!strcmp (srcFileName, stdinmark)) { DISPLAYLEVEL(4,"Using stdin for input\n"); - f = stdin; SET_BINARY_MODE(stdin); - } else { - if (!UTIL_isRegularFile(srcFileName)) { - DISPLAYLEVEL(1, "zstd: %s is not a regular file -- ignored \n", - srcFileName); - return NULL; - } - f = fopen(srcFileName, "rb"); - if ( f==NULL ) - DISPLAYLEVEL(1, "zstd: %s: %s \n", srcFileName, strerror(errno)); + return stdin; } - return f; + if (!UTIL_isRegularFile(srcFileName)) { + DISPLAYLEVEL(1, "zstd: %s is not a regular file -- ignored \n", + srcFileName); + return NULL; + } + + { FILE* const f = fopen(srcFileName, "rb"); + if (f == NULL) + DISPLAYLEVEL(1, "zstd: %s: %s \n", srcFileName, strerror(errno)); + return f; + } } /** FIO_openDstFile() : - * condition : `dstFileName` must be non-NULL. + * condition : `dstFileName` must be non-NULL. * @result : FILE* to `dstFileName`, or NULL if it fails */ static FILE* FIO_openDstFile(const char* dstFileName) { - FILE* f; - + assert(dstFileName != NULL); if (!strcmp (dstFileName, stdoutmark)) { DISPLAYLEVEL(4,"Using stdout for output\n"); - f = stdout; SET_BINARY_MODE(stdout); if (g_sparseFileSupport==1) { g_sparseFileSupport = 0; DISPLAYLEVEL(4, "Sparse File Support is automatically disabled on stdout ; try --sparse \n"); } - } else { - if (g_sparseFileSupport == 1) { - g_sparseFileSupport = ZSTD_SPARSE_DEFAULT; - } - if (strcmp (dstFileName, nulmark)) { - /* Check if destination file already exists */ - f = fopen( dstFileName, "rb" ); - if (f != 0) { /* dst file exists, prompt for overwrite authorization */ - fclose(f); - if (!g_overwrite) { - if (g_displayLevel <= 1) { - /* No interaction possible */ - DISPLAY("zstd: %s already exists; not overwritten \n", - dstFileName); - return NULL; - } - DISPLAY("zstd: %s already exists; do you wish to overwrite (y/N) ? ", - dstFileName); - { int ch = getchar(); - if ((ch!='Y') && (ch!='y')) { - DISPLAY(" not overwritten \n"); - return NULL; - } - /* flush rest of input line */ - while ((ch!=EOF) && (ch!='\n')) ch = getchar(); - } } - /* need to unlink */ - FIO_remove(dstFileName); - } } - f = fopen( dstFileName, "wb" ); - if (f==NULL) DISPLAYLEVEL(1, "zstd: %s: %s\n", dstFileName, strerror(errno)); + return stdout; } - return f; + if (g_sparseFileSupport == 1) { + g_sparseFileSupport = ZSTD_SPARSE_DEFAULT; + } + + if (strcmp (dstFileName, nulmark)) { /* not /dev/null */ + /* Check if destination file already exists */ + FILE* const fCheck = fopen( dstFileName, "rb" ); + if (fCheck != NULL) { /* dst file exists, authorization prompt */ + fclose(fCheck); + if (!g_overwrite) { + if (g_displayLevel <= 1) { + /* No interaction possible */ + DISPLAY("zstd: %s already exists; not overwritten \n", + dstFileName); + return NULL; + } + DISPLAY("zstd: %s already exists; overwrite (y/N) ? ", + dstFileName); + { int ch = getchar(); + if ((ch!='Y') && (ch!='y')) { + DISPLAY(" not overwritten \n"); + return NULL; + } + /* flush rest of input line */ + while ((ch!=EOF) && (ch!='\n')) ch = getchar(); + } } + /* need to unlink */ + FIO_remove(dstFileName); + } } + + { FILE* const f = fopen( dstFileName, "wb" ); + if (f == NULL) + DISPLAYLEVEL(1, "zstd: %s: %s\n", dstFileName, strerror(errno)); + return f; + } } /*! FIO_createDictBuffer() : * creates a buffer, pointed by `*bufferPtr`, * loads `filename` content into it, up to DICTSIZE_MAX bytes. - * @return : loaded size + * @return : loaded size * if fileName==NULL, returns 0 and a NULL pointer */ static size_t FIO_createDictBuffer(void** bufferPtr, const char* fileName) @@ -360,12 +347,13 @@ static size_t FIO_createDictBuffer(void** bufferPtr, const char* fileName) FILE* fileHandle; U64 fileSize; + assert(bufferPtr != NULL); *bufferPtr = NULL; if (fileName == NULL) return 0; DISPLAYLEVEL(4,"Loading %s as dictionary \n", fileName); fileHandle = fopen(fileName, "rb"); - if (fileHandle==0) EXM_THROW(31, "%s: %s", fileName, strerror(errno)); + if (fileHandle==NULL) EXM_THROW(31, "%s: %s", fileName, strerror(errno)); fileSize = UTIL_getFileSize(fileName); if (fileSize > DICTSIZE_MAX) { EXM_THROW(32, "Dictionary file %s is too large (> %u MB)", @@ -412,13 +400,13 @@ static cRess_t FIO_createCResources(const char* dictFileName, int cLevel, if (!ress.srcBuffer || !ress.dstBuffer) EXM_THROW(31, "allocation error : not enough memory"); - /* dictionary */ + /* Advances parameters, including dictionary */ { void* dictBuffer; size_t const dictBuffSize = FIO_createDictBuffer(&dictBuffer, dictFileName); /* works with dictFileName==NULL */ if (dictFileName && (dictBuffer==NULL)) EXM_THROW(32, "allocation error : can't create dictBuffer"); - CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_contentSizeFlag, 1) ); /* always enable content size, when available (note: supposed to be default anyway) */ + CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_contentSizeFlag, 1) ); /* always enable content size when available (note: supposed to be default) */ CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_dictIDFlag, g_dictIDFlag) ); CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_checksumFlag, g_checksumFlag) ); /* compression level */ @@ -446,7 +434,7 @@ static cRess_t FIO_createCResources(const char* dictFileName, int cLevel, DISPLAYLEVEL(5,"set nb threads = %u \n", g_nbThreads); CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_p_nbThreads, g_nbThreads) ); /* dictionary */ - CHECK( ZSTD_CCtx_setPledgedSrcSize(ress.cctx, srcSize) ); /* just to load dictionary with good compression parameters */ + CHECK( ZSTD_CCtx_setPledgedSrcSize(ress.cctx, srcSize) ); /* just for dictionary loading, using good compression parameters */ CHECK( ZSTD_CCtx_loadDictionary(ress.cctx, dictBuffer, dictBuffSize) ); CHECK( ZSTD_CCtx_setPledgedSrcSize(ress.cctx, ZSTD_CONTENTSIZE_UNKNOWN) ); /* reset */ From 91535d71ec58b9ef9090c5b4ed39a31243a5ced4 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 19 Oct 2017 12:09:34 -0700 Subject: [PATCH 026/145] fixed missing zstdmt_compress.h dependency we lose a warning message : when a job size is chosen < minimum job size for multithreading, it is automatically resized to minimum size. If this information is really useful, it should be present in zstd.h now. --- programs/fileio.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/programs/fileio.c b/programs/fileio.c index 7efa3c656..a8e33994c 100644 --- a/programs/fileio.c +++ b/programs/fileio.c @@ -203,10 +203,6 @@ static U32 g_blockSize = 0; void FIO_setBlockSize(unsigned blockSize) { if (blockSize && g_nbThreads==1) DISPLAYLEVEL(2, "Setting block size is useless in single-thread mode \n"); -#ifdef ZSTD_MULTITHREAD - if (blockSize-1 < ZSTDMT_SECTION_SIZE_MIN-1) /* intentional underflow */ - DISPLAYLEVEL(2, "Note : minimum block size is %u KB \n", (ZSTDMT_SECTION_SIZE_MIN>>10)); -#endif g_blockSize = blockSize; } #define FIO_OVERLAP_LOG_NOTSET 9999 From 97dccbbb2b9905d491c65ccab79d53e3b0a05187 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Thu, 19 Oct 2017 14:05:23 -0700 Subject: [PATCH 027/145] fixed zbufftest preserve "pledgedSrcSize=0" means "unknown" in init_advanced() --- lib/deprecated/zbuff_compress.c | 1 + tests/zbufftest.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/deprecated/zbuff_compress.c b/lib/deprecated/zbuff_compress.c index 8adbaec26..f39c60d89 100644 --- a/lib/deprecated/zbuff_compress.c +++ b/lib/deprecated/zbuff_compress.c @@ -72,6 +72,7 @@ size_t ZBUFF_compressInit_advanced(ZBUFF_CCtx* zbc, const void* dict, size_t dictSize, ZSTD_parameters params, unsigned long long pledgedSrcSize) { + if (pledgedSrcSize==0) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN; /* preserve "0 == unknown" behavior */ return ZSTD_initCStream_advanced(zbc, dict, dictSize, params, pledgedSrcSize); } diff --git a/tests/zbufftest.c b/tests/zbufftest.c index e64c886a5..ce5e5518a 100644 --- a/tests/zbufftest.c +++ b/tests/zbufftest.c @@ -357,7 +357,7 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, double compres { ZSTD_parameters params = ZSTD_getParams(cLevel, 0, dictSize); params.fParams.checksumFlag = FUZ_rand(&lseed) & 1; params.fParams.noDictIDFlag = FUZ_rand(&lseed) & 1; - { size_t const initError = ZBUFF_compressInit_advanced(zc, dict, dictSize, params, 0); + { size_t const initError = ZBUFF_compressInit_advanced(zc, dict, dictSize, params, ZSTD_CONTENTSIZE_UNKNOWN); CHECK (ZBUFF_isError(initError),"init error : %s", ZBUFF_getErrorName(initError)); } } } From b495140f67cb638beaa9dd0120fe7954861d0bdf Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Wed, 25 Oct 2017 12:47:57 -0700 Subject: [PATCH 028/145] Update BUCK files * Correct XXH namespace (Fixes #901) * Multithreading always enabled * GZIP/LZ4/LZMA always enabled * Legacy support always fully enabled --- lib/BUCK | 41 +++++++++++++++++++++++++++++++---------- programs/BUCK | 41 +++++++++++------------------------------ zlibWrapper/BUCK | 2 +- 3 files changed, 43 insertions(+), 41 deletions(-) diff --git a/lib/BUCK b/lib/BUCK index 6812c1b1e..9e01468f4 100644 --- a/lib/BUCK +++ b/lib/BUCK @@ -15,15 +15,9 @@ cxx_library( header_namespace='', visibility=['PUBLIC'], exported_headers=subdir_glob([ - ('compress', 'zstdmt_compress.h'), + ('compress', 'zstd*.h'), ]), - headers=subdir_glob([ - ('compress', 'zstd_opt.h'), - ]), - srcs=[ - 'compress/zstd_compress.c', - 'compress/zstdmt_compress.c', - ], + srcs=glob(['compress/zstd*.c']), deps=[':common'], ) @@ -31,7 +25,7 @@ cxx_library( name='decompress', header_namespace='', visibility=['PUBLIC'], - srcs=['decompress/zstd_decompress.c'], + srcs=glob(['decompress/zstd*.c']), deps=[ ':common', ':legacy', @@ -58,6 +52,9 @@ cxx_library( ]), srcs=glob(['legacy/*.c']), deps=[':common'], + exported_preprocessor_flags=[ + '-DZSTD_LEGACY_SUPPORT=4', + ], ) cxx_library( @@ -74,6 +71,15 @@ cxx_library( deps=[':common'], ) +cxx_library( + name='compiler', + header_namespace='', + visibility=['PUBLIC'], + exported_headers=subdir_glob([ + ('common', 'compiler.h'), + ]), +) + cxx_library( name='bitstream', header_namespace='', @@ -100,6 +106,7 @@ cxx_library( ], deps=[ ':bitstream', + ':compiler', ':errors', ':mem', ], @@ -133,7 +140,10 @@ cxx_library( ('common', 'pool.h'), ]), srcs=['common/pool.c'], - deps=[':threading'], + deps=[ + ':threading', + ':zstd_common', + ], ) cxx_library( @@ -144,6 +154,12 @@ cxx_library( ('common', 'threading.h'), ]), srcs=['common/threading.c'], + exported_preprocessor_flags=[ + '-DZSTD_MULTITHREAD', + ], + exported_linker_flags=[ + '-pthread', + ], ) cxx_library( @@ -154,6 +170,9 @@ cxx_library( ('common', 'xxhash.h'), ]), srcs=['common/xxhash.c'], + exported_preprocessor_flags=[ + '-DXXH_NAMESPACE=ZSTD_', + ], ) cxx_library( @@ -166,6 +185,7 @@ cxx_library( ]), srcs=['common/zstd_common.c'], deps=[ + ':compiler', ':errors', ':mem', ], @@ -175,6 +195,7 @@ cxx_library( name='common', deps=[ ':bitstream', + ':compiler', ':entropy', ':errors', ':mem', diff --git a/programs/BUCK b/programs/BUCK index 069403042..d2aa6373f 100644 --- a/programs/BUCK +++ b/programs/BUCK @@ -10,38 +10,19 @@ cxx_binary( '//lib:mem', '//lib:xxhash', ], -) - -cxx_binary( - name='zstdmt', - headers=glob(['*.h'], excludes=['datagen.h', 'platform.h', 'util.h']), - srcs=glob(['*.c'], excludes=['datagen.c']), - deps=[ - ':datagen', - ':util', - '//lib:zstd', - '//lib:zdict', - '//lib:mem', - '//lib:xxhash', + preprocessor_flags=[ + '-DZSTD_GZCOMPRESS', + '-DZSTD_GZDECOMPRESS', + '-DZSTD_LZMACOMPRESS', + '-DZSTD_LZMADECOMPRES', + '-DZSTD_LZ4COMPRESS', + '-DZSTD_LZ4DECOMPRES', ], - preprocessor_flags=['-DZSTD_MULTITHREAD'], - linker_flags=['-lpthread'], -) - -cxx_binary( - name='gzstd', - headers=glob(['*.h'], excludes=['datagen.h', 'platform.h', 'util.h']), - srcs=glob(['*.c'], excludes=['datagen.c']), - deps=[ - ':datagen', - ':util', - '//lib:zstd', - '//lib:zdict', - '//lib:mem', - '//lib:xxhash', + linker_flags=[ + '-lz', + '-llzma', + '-llz4', ], - preprocessor_flags=['-DZSTD_GZDECOMPRESS'], - linker_flags=['-lz'], ) cxx_library( diff --git a/zlibWrapper/BUCK b/zlibWrapper/BUCK index a3b74ac3f..a0710e1cf 100644 --- a/zlibWrapper/BUCK +++ b/zlibWrapper/BUCK @@ -12,7 +12,7 @@ cxx_library( deps=[ '//lib:zstd', '//lib:zstd_common', - ] + ], ) cxx_binary( From 86b8134cadadc7dcfb7f9e43baa54212ea7c58c3 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Wed, 25 Oct 2017 17:10:57 -0700 Subject: [PATCH 029/145] [libzstd] Fix parameter selection for empty input ZSTD_compress() and friends would treat an empty input as an unknown size when selecting parameters. Thus, they would drastically overallocate the context. Tell ZSTD_getParams() that the source size is 1 when it is empty. --- lib/compress/zstd_compress.c | 2 +- tests/fuzzer.c | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 635cc665c..21b84d104 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2213,7 +2213,7 @@ size_t ZSTD_compress_advanced_internal( size_t ZSTD_compress_usingDict(ZSTD_CCtx* ctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize, const void* dict, size_t dictSize, int compressionLevel) { - ZSTD_parameters params = ZSTD_getParams(compressionLevel, srcSize, dict ? dictSize : 0); + ZSTD_parameters params = ZSTD_getParams(compressionLevel, srcSize ? srcSize : 1, dict ? dictSize : 0); params.fParams.contentSizeFlag = 1; return ZSTD_compress_internal(ctx, dst, dstCapacity, src, srcSize, dict, dictSize, params); } diff --git a/tests/fuzzer.c b/tests/fuzzer.c index 76df77af9..e40f19978 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -359,6 +359,14 @@ static int basicUnitTests(U32 seed, double compressibility) if (ZSTD_getErrorCode(r) != ZSTD_error_srcSize_wrong) goto _output_error; } DISPLAYLEVEL(4, "OK \n"); + DISPLAYLEVEL(4, "test%di : check CCtx size after compressing empty input : ", testNb++); + { ZSTD_CCtx* cctx = ZSTD_createCCtx(); + size_t const r = ZSTD_compressCCtx(cctx, compressedBuffer, compressedBufferSize, NULL, 0, 19); + if (ZSTD_isError(r)) goto _output_error; + if (ZSTD_sizeof_CCtx(cctx) > (1U << 20)) goto _output_error; + ZSTD_freeCCtx(cctx); + } + DISPLAYLEVEL(4, "OK \n"); /* Static CCtx tests */ #define STATIC_CCTX_LEVEL 3 From 61e5a1adfc1645bf44e7c69a41c372b595202b22 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 31 Oct 2017 17:43:24 -0700 Subject: [PATCH 030/145] removed direct call to malloc() from pool.c --- lib/common/pool.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/common/pool.c b/lib/common/pool.c index 1b0fe1035..98b109e72 100644 --- a/lib/common/pool.c +++ b/lib/common/pool.c @@ -11,7 +11,6 @@ /* ====== Dependencies ======= */ #include /* size_t */ -#include /* malloc, calloc, free */ #include "pool.h" /* ====== Compiler specifics ====== */ @@ -115,7 +114,7 @@ POOL_ctx* POOL_create_advanced(size_t numThreads, size_t queueSize, ZSTD_customM * and full queues. */ ctx->queueSize = queueSize + 1; - ctx->queue = (POOL_job*) malloc(ctx->queueSize * sizeof(POOL_job)); + ctx->queue = (POOL_job*)ZSTD_malloc(ctx->queueSize * sizeof(POOL_job), customMem); ctx->queueHead = 0; ctx->queueTail = 0; ctx->numThreadsBusy = 0; From 1fc4f593da3cc810ae1744550196dacfa36d628d Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Wed, 1 Nov 2017 12:49:51 -0700 Subject: [PATCH 031/145] Allow skippable frames of any size --- lib/decompress/zstd_decompress.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c index 96fc60908..caeafc255 100644 --- a/lib/decompress/zstd_decompress.c +++ b/lib/decompress/zstd_decompress.c @@ -2555,17 +2555,21 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB /* fall-through */ case zdss_load: { size_t const neededInSize = ZSTD_nextSrcSizeToDecompress(zds); - size_t const toLoad = neededInSize - zds->inPos; /* should always be <= remaining space within inBuff */ + size_t const toLoad = neededInSize - zds->inPos; + int const isSkipFrame = ZSTD_isSkipFrame(zds); size_t loadedSize; - if (toLoad > zds->inBuffSize - zds->inPos) return ERROR(corruption_detected); /* should never happen */ - loadedSize = ZSTD_limitCopy(zds->inBuff + zds->inPos, toLoad, ip, iend-ip); + if (isSkipFrame) { + loadedSize = MIN(toLoad, (size_t)(iend-ip)); + } else { + if (toLoad > zds->inBuffSize - zds->inPos) return ERROR(corruption_detected); /* should never happen */ + loadedSize = ZSTD_limitCopy(zds->inBuff + zds->inPos, toLoad, ip, iend-ip); + } ip += loadedSize; zds->inPos += loadedSize; if (loadedSize < toLoad) { someMoreWork = 0; break; } /* not enough input, wait for more */ /* decode loaded input */ - { const int isSkipFrame = ZSTD_isSkipFrame(zds); - size_t const decodedSize = ZSTD_decompressContinue(zds, + { size_t const decodedSize = ZSTD_decompressContinue(zds, zds->outBuff + zds->outStart, zds->outBuffSize - zds->outStart, zds->inBuff, neededInSize); if (ZSTD_isError(decodedSize)) return decodedSize; From e19b0822bc08e0427daa4b1a7c366e51a3efb809 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Wed, 1 Nov 2017 13:10:03 -0700 Subject: [PATCH 032/145] Test large skippable frames --- tests/fuzzer.c | 2 +- tests/zstreamtest.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/fuzzer.c b/tests/fuzzer.c index e40f19978..be9dc3e06 100644 --- a/tests/fuzzer.c +++ b/tests/fuzzer.c @@ -525,7 +525,7 @@ static int basicUnitTests(U32 seed, double compressibility) off += r; if (i == segs/2) { /* insert skippable frame */ - const U32 skipLen = 128 KB; + const U32 skipLen = 129 KB; MEM_writeLE32((BYTE*)compressedBuffer + off, ZSTD_MAGIC_SKIPPABLE_START); MEM_writeLE32((BYTE*)compressedBuffer + off + 4, skipLen); off += skipLen + ZSTD_skippableHeaderSize; diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index 6f1f6df18..53dbaf3ba 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -213,7 +213,7 @@ static int basicUnitTests(U32 seed, double compressibility, ZSTD_customMem custo { size_t const CNBufferSize = COMPRESSIBLE_NOISE_LENGTH; void* CNBuffer = malloc(CNBufferSize); - size_t const skippableFrameSize = 11; + size_t const skippableFrameSize = 200 KB; size_t const compressedBufferSize = (8 + skippableFrameSize) + ZSTD_compressBound(COMPRESSIBLE_NOISE_LENGTH); void* compressedBuffer = malloc(compressedBufferSize); size_t const decodedBufferSize = CNBufferSize; From 428e8b3bf49ed9d36e732acf634d6150d0e2b302 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 1 Nov 2017 13:15:23 -0700 Subject: [PATCH 033/145] fix : ZSTD_compress_generic(,,,ZSTD_e_end) automatically sets pledgedSrcSize as per documentation, on ZSTD_setPledgedSrcSize() : > If all data is provided and consumed in a single round, > this value (pledgedSrcSize) is overriden by srcSize instead. This wasn't applied before compression level is transformed into compression parameters. As a consequence, small input missed compression parameters adaptation. It seems to work fine now : compression was compared with ZSTD_compress_advanced(), results were the same. --- lib/compress/zstd_compress.c | 1 + programs/bench.c | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 21b84d104..d50f35116 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2814,6 +2814,7 @@ size_t ZSTD_compress_generic (ZSTD_CCtx* cctx, if (cctx->streamStage == zcss_init) { ZSTD_prefixDict const prefixDict = cctx->prefixDict; ZSTD_CCtx_params params = cctx->requestedParams; + if (endOp == ZSTD_e_end) cctx->pledgedSrcSizePlusOne = input->size + 1; params.cParams = ZSTD_getCParamsFromCCtxParams( cctx->requestedParams, cctx->pledgedSrcSizePlusOne-1, 0 /*dictSize*/); memset(&cctx->prefixDict, 0, sizeof(cctx->prefixDict)); /* single usage */ diff --git a/programs/bench.c b/programs/bench.c index 8ecae9ebd..66c406abd 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -286,6 +286,7 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, U32 nbLoops = 0; ZSTD_CCtx_setParameter(ctx, ZSTD_p_nbThreads, g_nbThreads); ZSTD_CCtx_setParameter(ctx, ZSTD_p_compressionLevel, cLevel); + //ZSTD_CCtx_setPledgedSrcSize(ctx, blockTable[0].srcSize); /* estimation, only to help cLevel conversion before applying advanced compression parameters */ ZSTD_CCtx_setParameter(ctx, ZSTD_p_enableLongDistanceMatching, g_ldmFlag); ZSTD_CCtx_setParameter(ctx, ZSTD_p_ldmMinMatch, g_ldmMinMatch); ZSTD_CCtx_setParameter(ctx, ZSTD_p_ldmHashLog, g_ldmHashLog); @@ -305,6 +306,14 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, do { U32 blockNb; for (blockNb=0; blockNb Date: Wed, 1 Nov 2017 16:57:48 -0700 Subject: [PATCH 034/145] minor refactor added some traces and assert related to hunting a potential ubsan error in 32-bits more (it ends up being a compiler-side issue : https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82802). Modified one pointer arithmetic expression for a more conformant way. --- lib/compress/zstd_compress.c | 15 +++++++++----- lib/compress/zstdmt_compress.c | 36 ++++++++++++++++++++++------------ tests/zstreamtest.c | 3 ++- 3 files changed, 36 insertions(+), 18 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index d50f35116..808418a60 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1468,14 +1468,18 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, entropy, cParams->strategy, op, dstCapacity, literals, litSize); if (ZSTD_isError(cSize)) return cSize; + assert(cSize <= dstCapacity); op += cSize; } /* Sequences Header */ - if ((oend-op) < 3 /*max nbSeq Size*/ + 1 /*seqHead */) return ERROR(dstSize_tooSmall); - if (nbSeq < 0x7F) *op++ = (BYTE)nbSeq; - else if (nbSeq < LONGNBSEQ) op[0] = (BYTE)((nbSeq>>8) + 0x80), op[1] = (BYTE)nbSeq, op+=2; - else op[0]=0xFF, MEM_writeLE16(op+1, (U16)(nbSeq - LONGNBSEQ)), op+=3; + if ((oend-op) < 3 /*max nbSeq Size*/ + 1 /*seqHead*/) return ERROR(dstSize_tooSmall); + if (nbSeq < 0x7F) + *op++ = (BYTE)nbSeq; + else if (nbSeq < LONGNBSEQ) + op[0] = (BYTE)((nbSeq>>8) + 0x80), op[1] = (BYTE)nbSeq, op+=2; + else + op[0]=0xFF, MEM_writeLE16(op+1, (U16)(nbSeq - LONGNBSEQ)), op+=3; if (nbSeq==0) return op - ostart; /* seqHead : flags for FSE encoding type */ @@ -1606,6 +1610,7 @@ static void ZSTD_storeLastLiterals(seqStore_t* seqStorePtr, static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, void* dst, size_t dstCapacity, const void* src, size_t srcSize) { + DEBUGLOG(5, "ZSTD_compressBlock_internal : dstCapacity = %u", (U32)dstCapacity); if (srcSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1) return 0; /* don't even attempt compression below a certain srcSize */ ZSTD_resetSeqStore(&(zc->seqStore)); @@ -1867,7 +1872,7 @@ static size_t ZSTD_loadDictionaryContent(ZSTD_CCtx* zc, const void* src, size_t zc->lowLimit = zc->dictLimit; zc->dictLimit = (U32)(zc->nextSrc - zc->base); zc->dictBase = zc->base; - zc->base += ip - zc->nextSrc; + zc->base = ip - zc->dictLimit; zc->nextToUpdate = zc->dictLimit; zc->loadedDictEnd = zc->appliedParams.forceWindow ? 0 : (U32)(iend - zc->base); diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index af7be3167..a855745be 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -142,7 +142,10 @@ static size_t ZSTDMT_sizeof_bufferPool(ZSTDMT_bufferPool* bufPool) static void ZSTDMT_setBufferSize(ZSTDMT_bufferPool* bufPool, size_t bSize) { + ZSTD_pthread_mutex_lock(&bufPool->poolMutex); + DEBUGLOG(4, "ZSTDMT_setBufferSize: bSize = %u", (U32)bSize); bufPool->bufferSize = bSize; + ZSTD_pthread_mutex_unlock(&bufPool->poolMutex); } /** ZSTDMT_getBuffer() : @@ -150,28 +153,31 @@ static void ZSTDMT_setBufferSize(ZSTDMT_bufferPool* bufPool, size_t bSize) static buffer_t ZSTDMT_getBuffer(ZSTDMT_bufferPool* bufPool) { size_t const bSize = bufPool->bufferSize; - DEBUGLOG(5, "ZSTDMT_getBuffer"); + DEBUGLOG(5, "ZSTDMT_getBuffer: bSize = %u", (U32)bufPool->bufferSize); ZSTD_pthread_mutex_lock(&bufPool->poolMutex); if (bufPool->nbBuffers) { /* try to use an existing buffer */ buffer_t const buf = bufPool->bTable[--(bufPool->nbBuffers)]; size_t const availBufferSize = buf.size; bufPool->bTable[bufPool->nbBuffers] = g_nullBuffer; - if ((availBufferSize >= bSize) & (availBufferSize <= 10*bSize)) { + if ((availBufferSize >= bSize) & ((availBufferSize>>3) <= bSize)) { /* large enough, but not too much */ + DEBUGLOG(5, "ZSTDMT_getBuffer: provide buffer %u of size %u", + bufPool->nbBuffers, (U32)buf.size); ZSTD_pthread_mutex_unlock(&bufPool->poolMutex); return buf; } /* size conditions not respected : scratch this buffer, create new one */ - DEBUGLOG(5, "existing buffer does not meet size conditions => freeing"); + DEBUGLOG(5, "ZSTDMT_getBuffer: existing buffer does not meet size conditions => freeing"); ZSTD_free(buf.start, bufPool->cMem); } ZSTD_pthread_mutex_unlock(&bufPool->poolMutex); /* create new buffer */ - DEBUGLOG(5, "create a new buffer"); + DEBUGLOG(5, "ZSTDMT_getBuffer: create a new buffer"); { buffer_t buffer; void* const start = ZSTD_malloc(bSize, bufPool->cMem); buffer.start = start; /* note : start can be NULL if malloc fails ! */ buffer.size = (start==NULL) ? 0 : bSize; + DEBUGLOG(5, "ZSTDMT_getBuffer: created buffer of size %u", (U32)bSize); return buffer; } } @@ -184,12 +190,14 @@ static void ZSTDMT_releaseBuffer(ZSTDMT_bufferPool* bufPool, buffer_t buf) ZSTD_pthread_mutex_lock(&bufPool->poolMutex); if (bufPool->nbBuffers < bufPool->totalBuffers) { bufPool->bTable[bufPool->nbBuffers++] = buf; /* stored for later use */ + DEBUGLOG(5, "ZSTDMT_releaseBuffer: stored buffer of size %u in slot %u", + (U32)buf.size, (U32)(bufPool->nbBuffers-1)); ZSTD_pthread_mutex_unlock(&bufPool->poolMutex); return; } ZSTD_pthread_mutex_unlock(&bufPool->poolMutex); /* Reached bufferPool capacity (should not happen) */ - DEBUGLOG(5, "buffer pool capacity reached => freeing "); + DEBUGLOG(5, "ZSTDMT_releaseBuffer: pool capacity reached => freeing "); ZSTD_free(buf.start, bufPool->cMem); } @@ -324,11 +332,10 @@ typedef struct { void ZSTDMT_compressChunk(void* jobDescription) { ZSTDMT_jobDescription* const job = (ZSTDMT_jobDescription*)jobDescription; - ZSTD_CCtx* cctx = ZSTDMT_getCCtx(job->cctxPool); + ZSTD_CCtx* const cctx = ZSTDMT_getCCtx(job->cctxPool); const void* const src = (const char*)job->srcStart + job->dictSize; buffer_t dstBuff = job->dstBuff; - DEBUGLOG(5, "ZSTDMT_compressChunk") - DEBUGLOG(5, "job (first:%u) (last:%u) : dictSize %u, srcSize %u", + DEBUGLOG(5, "ZSTDMT_compressChunk: job (first:%u) (last:%u) : dictSize %u, srcSize %u", job->firstChunk, job->lastChunk, (U32)job->dictSize, (U32)job->srcSize); if (cctx==NULL) { @@ -343,11 +350,12 @@ void ZSTDMT_compressChunk(void* jobDescription) goto _endJob; } job->dstBuff = dstBuff; + DEBUGLOG(5, "ZSTDMT_compressChunk: allocated dstBuff of size %u", (U32)dstBuff.size); } if (job->cdict) { size_t const initError = ZSTD_compressBegin_usingCDict_advanced(cctx, job->cdict, job->params.fParams, job->fullFrameSize); - DEBUGLOG(4, "ZSTDMT_compressChunk, using CDict"); + DEBUGLOG(4, "ZSTDMT_compressChunk: init using CDict"); assert(job->firstChunk); /* should only happen for first segment */ if (ZSTD_isError(initError)) { job->cSize = initError; goto _endJob; } } else { /* srcStart points at reloaded section */ @@ -367,7 +375,7 @@ void ZSTDMT_compressChunk(void* jobDescription) ZSTD_invalidateRepCodes(cctx); } - DEBUGLOG(5, "Compressing : "); + DEBUGLOG(5, "Compressing into dstBuff of size %u", (U32)dstBuff.size); DEBUG_PRINTHEX(6, job->srcStart, 12); job->cSize = (job->lastChunk) ? ZSTD_compressEnd (cctx, dstBuff.start, dstBuff.size, src, job->srcSize) : @@ -769,6 +777,7 @@ size_t ZSTDMT_initCStream_internal( dict, dictSize, cdict, singleThreadParams, pledgedSrcSize); } + DEBUGLOG(4, "multi - threading mode"); if (zcs->allJobsCompleted == 0) { /* previous compression not correctly finished */ ZSTDMT_waitForAllJobsCompleted(zcs); @@ -796,9 +805,10 @@ size_t ZSTDMT_initCStream_internal( DEBUGLOG(4, "overlap Size : %u KB", (U32)(zcs->targetDictSize>>10)); zcs->targetSectionSize = params.jobSize ? params.jobSize : (size_t)1 << (params.cParams.windowLog + 2); zcs->targetSectionSize = MAX(ZSTDMT_SECTION_SIZE_MIN, zcs->targetSectionSize); - zcs->targetSectionSize = MAX(zcs->targetDictSize, zcs->targetSectionSize); + zcs->targetSectionSize = MAX(zcs->targetDictSize, zcs->targetSectionSize); /* one job size must be at least overlap */ DEBUGLOG(4, "Section Size : %u KB", (U32)(zcs->targetSectionSize>>10)); zcs->inBuffSize = zcs->targetDictSize + zcs->targetSectionSize; + DEBUGLOG(4, "inBuff Size : %u KB", (U32)(zcs->inBuffSize>>10)); ZSTDMT_setBufferSize(zcs->bufPool, MAX(zcs->inBuffSize, ZSTD_compressBound(zcs->targetSectionSize)) ); zcs->inBuff.buffer = g_nullBuffer; zcs->dictSize = 0; @@ -863,7 +873,8 @@ static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* zcs, size_t srcSize, unsi { unsigned const jobID = zcs->nextJobID & zcs->jobIDMask; - DEBUGLOG(4, "preparing job %u to compress %u bytes with %u preload ", + DEBUGLOG(5, "ZSTDMT_createCompressionJob"); + DEBUGLOG(5, "preparing job %u to compress %u bytes with %u preload ", zcs->nextJobID, (U32)srcSize, (U32)zcs->dictSize); zcs->jobs[jobID].src = zcs->inBuff.buffer; zcs->jobs[jobID].srcStart = zcs->inBuff.buffer.start; @@ -994,6 +1005,7 @@ size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx, { size_t const newJobThreshold = mtctx->dictSize + mtctx->targetSectionSize; unsigned forwardInputProgress = 0; + DEBUGLOG(5, "ZSTDMT_compressStream_generic"); assert(output->pos <= output->size); assert(input->pos <= input->size); if ((mtctx->frameEnded) && (endOp==ZSTD_e_continue)) { diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index 6f1f6df18..84dcc6fbf 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -951,7 +951,7 @@ static int fuzzerTests(U32 seed, U32 nbTests, unsigned startTest, double compres for (n=0, cSize=0, totalTestSize=0 ; totalTestSize < maxTestSize ; n++) { /* compress random chunks into randomly sized dst buffers */ { size_t const randomSrcSize = FUZ_randomLength(&lseed, maxSampleLog); - size_t const srcSize = MIN (maxTestSize-totalTestSize, randomSrcSize); + size_t const srcSize = MIN(maxTestSize-totalTestSize, randomSrcSize); size_t const srcStart = FUZ_rand(&lseed) % (srcBufferSize - srcSize); size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog); size_t const dstBuffSize = MIN(cBufferSize - cSize, randomDstSize); @@ -1564,6 +1564,7 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double size_t const srcStart = FUZ_rand(&lseed) % (srcBufferSize - srcSize); size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog+1); size_t const dstBuffSize = MIN(cBufferSize - cSize, randomDstSize); + DISPLAYLEVEL(2, "dstBuffSize : %u \n", (U32)dstBuffSize); ZSTD_EndDirective const flush = (FUZ_rand(&lseed) & 15) ? ZSTD_e_continue : ZSTD_e_flush; ZSTD_inBuffer inBuff = { srcBuffer+srcStart, srcSize, 0 }; outBuff.size = outBuff.pos + dstBuffSize; From 6f1dfa8adf0cc3a8038266ce8926534d08ab7d71 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 1 Nov 2017 17:01:45 -0700 Subject: [PATCH 035/145] removed line with `//` comment this is for a different topic (better parameter adaptation for small files + dictionary and/or custome parameters) --- programs/bench.c | 1 - 1 file changed, 1 deletion(-) diff --git a/programs/bench.c b/programs/bench.c index 66c406abd..5093f110d 100644 --- a/programs/bench.c +++ b/programs/bench.c @@ -286,7 +286,6 @@ static int BMK_benchMem(const void* srcBuffer, size_t srcSize, U32 nbLoops = 0; ZSTD_CCtx_setParameter(ctx, ZSTD_p_nbThreads, g_nbThreads); ZSTD_CCtx_setParameter(ctx, ZSTD_p_compressionLevel, cLevel); - //ZSTD_CCtx_setPledgedSrcSize(ctx, blockTable[0].srcSize); /* estimation, only to help cLevel conversion before applying advanced compression parameters */ ZSTD_CCtx_setParameter(ctx, ZSTD_p_enableLongDistanceMatching, g_ldmFlag); ZSTD_CCtx_setParameter(ctx, ZSTD_p_ldmMinMatch, g_ldmMinMatch); ZSTD_CCtx_setParameter(ctx, ZSTD_p_ldmHashLog, g_ldmHashLog); From 3b59a010397f5da74aabdf8eca7c8f9c6765b6b5 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Wed, 1 Nov 2017 17:36:31 -0700 Subject: [PATCH 036/145] fixed zstreamtest declaration after statement --- tests/zstreamtest.c | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c index 84dcc6fbf..6dc9cada1 100644 --- a/tests/zstreamtest.c +++ b/tests/zstreamtest.c @@ -1564,7 +1564,6 @@ static int fuzzerTests_newAPI(U32 seed, U32 nbTests, unsigned startTest, double size_t const srcStart = FUZ_rand(&lseed) % (srcBufferSize - srcSize); size_t const randomDstSize = FUZ_randomLength(&lseed, maxSampleLog+1); size_t const dstBuffSize = MIN(cBufferSize - cSize, randomDstSize); - DISPLAYLEVEL(2, "dstBuffSize : %u \n", (U32)dstBuffSize); ZSTD_EndDirective const flush = (FUZ_rand(&lseed) & 15) ? ZSTD_e_continue : ZSTD_e_flush; ZSTD_inBuffer inBuff = { srcBuffer+srcStart, srcSize, 0 }; outBuff.size = outBuff.pos + dstBuffSize; From 8b6aecf2cb6671c943a31a069bbed1e8ffea5064 Mon Sep 17 00:00:00 2001 From: Yann Collet Date: Tue, 7 Nov 2017 15:27:06 -0800 Subject: [PATCH 037/145] moved a few structures from `zstd_internal.h` to `zstd_compress.h` which is a more precise scope --- lib/common/zstd_internal.h | 153 +++------------------------------ lib/compress/zstd_compress.h | 146 ++++++++++++++++++++++++++++++- lib/compress/zstd_opt.c | 15 ++-- lib/compress/zstdmt_compress.c | 2 +- 4 files changed, 164 insertions(+), 152 deletions(-) diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h index e91cd20ba..0fb8e49da 100644 --- a/lib/common/zstd_internal.h +++ b/lib/common/zstd_internal.h @@ -11,6 +11,10 @@ #ifndef ZSTD_CCOMMON_H_MODULE #define ZSTD_CCOMMON_H_MODULE +/* this module contains definitions which must be identical + * across compression, decompression and dictBuilder. + * It also contains a few functions useful to at least 2 of them + * and which benefit from being inlined */ /*-************************************* * Dependencies @@ -191,17 +195,14 @@ MEM_STATIC void ZSTD_wildcopy_e(void* dst, const void* src, void* dstEnd) /* s /*-******************************************* -* Private interfaces +* Private declarations *********************************************/ -typedef struct ZSTD_stats_s ZSTD_stats_t; - typedef struct seqDef_s { U32 offset; U16 litLength; U16 matchLength; } seqDef; - typedef struct { seqDef* sequencesStart; seqDef* sequences; @@ -216,100 +217,8 @@ typedef struct { U32 repToConfirm[ZSTD_REP_NUM]; } seqStore_t; -typedef struct { - U32 off; - U32 len; -} ZSTD_match_t; - -typedef struct { - U32 price; - U32 off; - U32 mlen; - U32 litlen; - U32 rep[ZSTD_REP_NUM]; -} ZSTD_optimal_t; - -typedef struct { - U32* litFreq; - U32* litLengthFreq; - U32* matchLengthFreq; - U32* offCodeFreq; - ZSTD_match_t* matchTable; - ZSTD_optimal_t* priceTable; - - U32 matchLengthSum; - U32 matchSum; - U32 litLengthSum; - U32 litSum; - U32 offCodeSum; - U32 log2matchLengthSum; - U32 log2matchSum; - U32 log2litLengthSum; - U32 log2litSum; - U32 log2offCodeSum; - U32 factor; - U32 staticPrices; - U32 cachedPrice; - U32 cachedLitLength; - const BYTE* cachedLiterals; -} optState_t; - -typedef struct { - U32 offset; - U32 checksum; -} ldmEntry_t; - -typedef struct { - ldmEntry_t* hashTable; - BYTE* bucketOffsets; /* Next position in bucket to insert entry */ - U64 hashPower; /* Used to compute the rolling hash. - * Depends on ldmParams.minMatchLength */ -} ldmState_t; - -typedef struct { - U32 enableLdm; /* 1 if enable long distance matching */ - U32 hashLog; /* Log size of hashTable */ - U32 bucketSizeLog; /* Log bucket size for collision resolution, at most 8 */ - U32 minMatchLength; /* Minimum match length */ - U32 hashEveryLog; /* Log number of entries to skip */ -} ldmParams_t; - -typedef struct { - U32 hufCTable[HUF_CTABLE_SIZE_U32(255)]; - FSE_CTable offcodeCTable[FSE_CTABLE_SIZE_U32(OffFSELog, MaxOff)]; - FSE_CTable matchlengthCTable[FSE_CTABLE_SIZE_U32(MLFSELog, MaxML)]; - FSE_CTable litlengthCTable[FSE_CTABLE_SIZE_U32(LLFSELog, MaxLL)]; - U32 workspace[HUF_WORKSPACE_SIZE_U32]; - HUF_repeat hufCTable_repeatMode; - FSE_repeat offcode_repeatMode; - FSE_repeat matchlength_repeatMode; - FSE_repeat litlength_repeatMode; -} ZSTD_entropyCTables_t; - -struct ZSTD_CCtx_params_s { - ZSTD_format_e format; - ZSTD_compressionParameters cParams; - ZSTD_frameParameters fParams; - - int compressionLevel; - U32 forceWindow; /* force back-references to respect limit of - * 1<