From f92ec5ea54d64207f5ffd30b5c746367a6e75dc4 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Wed, 29 Dec 2021 12:03:36 -0800
Subject: [PATCH 001/472] change the offset|repcode sumtype format to match
offBase
directly at ZSTD_storeSeq() interface.
In the process, remove ZSTD_REP_MOVE.
This makes it possible, in future commits,
to update and effectively simplify the naming scheme
to properly label the updated processing pipeline :
offset | repcode => offBase => offCode + offBits
---
lib/compress/zstd_compress_internal.h | 17 ++++++++---------
1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h
index c406e794bdb..f71791ec0a5 100644
--- a/lib/compress/zstd_compress_internal.h
+++ b/lib/compress/zstd_compress_internal.h
@@ -577,18 +577,17 @@ ZSTD_safecopyLiterals(BYTE* op, BYTE const* ip, BYTE const* const iend, BYTE con
while (ip < iend) *op++ = *ip++;
}
-#define ZSTD_REP_MOVE (ZSTD_REP_NUM-1)
#define STORE_REPCODE_1 STORE_REPCODE(1)
#define STORE_REPCODE_2 STORE_REPCODE(2)
#define STORE_REPCODE_3 STORE_REPCODE(3)
-#define STORE_REPCODE(r) (assert((r)>=1), assert((r)<=3), (r)-1)
-#define STORE_OFFSET(o) (assert((o)>0), o + ZSTD_REP_MOVE)
-#define STORED_IS_OFFSET(o) ((o) > ZSTD_REP_MOVE)
-#define STORED_IS_REPCODE(o) ((o) <= ZSTD_REP_MOVE)
-#define STORED_OFFSET(o) (assert(STORED_IS_OFFSET(o)), (o)-ZSTD_REP_MOVE)
-#define STORED_REPCODE(o) (assert(STORED_IS_REPCODE(o)), (o)+1) /* returns ID 1,2,3 */
-#define STORED_TO_OFFBASE(o) ((o)+1)
-#define OFFBASE_TO_STORED(o) ((o)-1)
+#define STORE_REPCODE(r) (assert((r)>=1), assert((r)<=ZSTD_REP_NUM), (r)) /* accepts IDs 1,2,3 */
+#define STORE_OFFSET(o) (assert((o)>0), o + ZSTD_REP_NUM)
+#define STORED_IS_OFFSET(o) ((o) > ZSTD_REP_NUM)
+#define STORED_IS_REPCODE(o) ( 1 <= (o) && (o) <= ZSTD_REP_NUM)
+#define STORED_OFFSET(o) (assert(STORED_IS_OFFSET(o)), (o) - ZSTD_REP_NUM)
+#define STORED_REPCODE(o) (assert(STORED_IS_REPCODE(o)), (o)) /* returns ID 1,2,3 */
+#define STORED_TO_OFFBASE(o) (o)
+#define OFFBASE_TO_STORED(o) (o)
/*! ZSTD_storeSeq() :
* Store a sequence (litlen, litPtr, offCode and matchLength) into seqStore_t.
From 7a18d709ae5a8ba53c5199adb2e8461cb216fb00 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Wed, 29 Dec 2021 17:30:43 -0800
Subject: [PATCH 002/472] updated all names to offBase convention
---
lib/compress/zstd_compress.c | 72 ++++++------
lib/compress/zstd_compress_internal.h | 55 +++++----
lib/compress/zstd_compress_superblock.c | 34 +++---
lib/compress/zstd_double_fast.c | 20 ++--
lib/compress/zstd_fast.c | 20 ++--
lib/compress/zstd_lazy.c | 142 ++++++++++++------------
lib/compress/zstd_ldm.c | 2 +-
lib/compress/zstd_opt.c | 60 +++++-----
tests/decodecorpus.c | 14 +--
tests/fuzzer.c | 3 +-
10 files changed, 214 insertions(+), 208 deletions(-)
diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
index f06456af926..1cb229f7aa9 100644
--- a/lib/compress/zstd_compress.c
+++ b/lib/compress/zstd_compress.c
@@ -2940,7 +2940,7 @@ static void ZSTD_copyBlockSequences(ZSTD_CCtx* zc)
/* seqStoreSeqs[i].offset == offCode+1, and ZSTD_updateRep() expects offCode
so we provide seqStoreSeqs[i].offset - 1 */
ZSTD_updateRep(updatedRepcodes.rep,
- seqStoreSeqs[i].offBase - 1,
+ seqStoreSeqs[i].offBase,
seqStoreSeqs[i].litLength == 0);
literalsRead += outSeqs[i].litLength;
}
@@ -3433,20 +3433,22 @@ static void ZSTD_deriveSeqStoreChunk(seqStore_t* resultSeqStore,
}
/**
- * Returns the raw offset represented by the combination of offCode, ll0, and repcode history.
- * offCode must represent a repcode in the numeric representation of ZSTD_storeSeq().
+ * Returns the raw offset represented by the combination of offBase, ll0, and repcode history.
+ * offBase must represent a repcode in the numeric representation of ZSTD_storeSeq().
*/
static U32
-ZSTD_resolveRepcodeToRawOffset(const U32 rep[ZSTD_REP_NUM], const U32 offCode, const U32 ll0)
+ZSTD_resolveRepcodeToRawOffset(const U32 rep[ZSTD_REP_NUM], const U32 offBase, const U32 ll0)
{
- U32 const adjustedOffCode = STORED_REPCODE(offCode) - 1 + ll0; /* [ 0 - 3 ] */
- assert(STORED_IS_REPCODE(offCode));
- if (adjustedOffCode == ZSTD_REP_NUM) {
- /* litlength == 0 and offCode == 2 implies selection of first repcode - 1 */
- assert(rep[0] > 0);
+ U32 const adjustedRepCode = OFFBASE_TO_REPCODE(offBase) - 1 + ll0; /* [ 0 - 3 ] */
+ assert(OFFBASE_IS_REPCODE(offBase));
+ if (adjustedRepCode == ZSTD_REP_NUM) {
+ /* litlength == 0 and offCode == 2 implies selection of first repcode - 1
+ * This is only valid if it results in a valid offset value, aka > 0.
+ */
+ assert(rep[0] > 1);
return rep[0] - 1;
}
- return rep[adjustedOffCode];
+ return rep[adjustedRepCode];
}
/**
@@ -3468,11 +3470,11 @@ static void ZSTD_seqStore_resolveOffCodes(repcodes_t* const dRepcodes, repcodes_
for (; idx < nbSeq; ++idx) {
seqDef* const seq = seqStore->sequencesStart + idx;
U32 const ll0 = (seq->litLength == 0);
- U32 const offCode = OFFBASE_TO_STORED(seq->offBase);
+ U32 const offBase = seq->offBase;
assert(seq->offBase > 0);
- if (STORED_IS_REPCODE(offCode)) {
- U32 const dRawOffset = ZSTD_resolveRepcodeToRawOffset(dRepcodes->rep, offCode, ll0);
- U32 const cRawOffset = ZSTD_resolveRepcodeToRawOffset(cRepcodes->rep, offCode, ll0);
+ if (OFFBASE_IS_REPCODE(offBase)) {
+ U32 const dRawOffset = ZSTD_resolveRepcodeToRawOffset(dRepcodes->rep, offBase, ll0);
+ U32 const cRawOffset = ZSTD_resolveRepcodeToRawOffset(cRepcodes->rep, offBase, ll0);
/* Adjust simulated decompression repcode history if we come across a mismatch. Replace
* the repcode with the offset it actually references, determined by the compression
* repcode history.
@@ -3484,8 +3486,8 @@ static void ZSTD_seqStore_resolveOffCodes(repcodes_t* const dRepcodes, repcodes_
/* Compression repcode history is always updated with values directly from the unmodified seqStore.
* Decompression repcode history may use modified seq->offset value taken from compression repcode history.
*/
- ZSTD_updateRep(dRepcodes->rep, OFFBASE_TO_STORED(seq->offBase), ll0);
- ZSTD_updateRep(cRepcodes->rep, offCode, ll0);
+ ZSTD_updateRep(dRepcodes->rep, seq->offBase, ll0);
+ ZSTD_updateRep(cRepcodes->rep, offBase, ll0);
}
}
@@ -5770,26 +5772,26 @@ ZSTD_validateSequence(U32 offCode, U32 matchLength,
* window size. After output surpasses windowSize, we're limited to windowSize offsets again.
*/
size_t const offsetBound = posInSrc > windowSize ? (size_t)windowSize : posInSrc + (size_t)dictSize;
- RETURN_ERROR_IF(offCode > STORE_OFFSET(offsetBound), corruption_detected, "Offset too large!");
+ RETURN_ERROR_IF(offCode > OFFSET_TO_OFFBASE(offsetBound), corruption_detected, "Offset too large!");
RETURN_ERROR_IF(matchLength < MINMATCH, corruption_detected, "Matchlength too small");
return 0;
}
/* Returns an offset code, given a sequence's raw offset, the ongoing repcode array, and whether litLength == 0 */
-static U32 ZSTD_finalizeOffCode(U32 rawOffset, const U32 rep[ZSTD_REP_NUM], U32 ll0)
+static U32 ZSTD_finalizeOffBase(U32 rawOffset, const U32 rep[ZSTD_REP_NUM], U32 ll0)
{
- U32 offCode = STORE_OFFSET(rawOffset);
+ U32 offBase = OFFSET_TO_OFFBASE(rawOffset);
if (!ll0 && rawOffset == rep[0]) {
- offCode = STORE_REPCODE_1;
+ offBase = REPCODE1_TO_OFFBASE;
} else if (rawOffset == rep[1]) {
- offCode = STORE_REPCODE(2 - ll0);
+ offBase = REPCODE_TO_OFFBASE(2 - ll0);
} else if (rawOffset == rep[2]) {
- offCode = STORE_REPCODE(3 - ll0);
+ offBase = REPCODE_TO_OFFBASE(3 - ll0);
} else if (ll0 && rawOffset == rep[0] - 1) {
- offCode = STORE_REPCODE_3;
+ offBase = REPCODE3_TO_OFFBASE;
}
- return offCode;
+ return offBase;
}
/* Returns 0 on success, and a ZSTD_error otherwise. This function scans through an array of
@@ -5819,19 +5821,19 @@ ZSTD_copySequencesToSeqStoreExplicitBlockDelim(ZSTD_CCtx* cctx,
U32 const litLength = inSeqs[idx].litLength;
U32 const ll0 = (litLength == 0);
U32 const matchLength = inSeqs[idx].matchLength;
- U32 const offCode = ZSTD_finalizeOffCode(inSeqs[idx].offset, updatedRepcodes.rep, ll0);
- ZSTD_updateRep(updatedRepcodes.rep, offCode, ll0);
+ U32 const offBase = ZSTD_finalizeOffBase(inSeqs[idx].offset, updatedRepcodes.rep, ll0);
+ ZSTD_updateRep(updatedRepcodes.rep, offBase, ll0);
- DEBUGLOG(6, "Storing sequence: (of: %u, ml: %u, ll: %u)", offCode, matchLength, litLength);
+ DEBUGLOG(6, "Storing sequence: (of: %u, ml: %u, ll: %u)", offBase, matchLength, litLength);
if (cctx->appliedParams.validateSequences) {
seqPos->posInSrc += litLength + matchLength;
- FORWARD_IF_ERROR(ZSTD_validateSequence(offCode, matchLength, seqPos->posInSrc,
+ FORWARD_IF_ERROR(ZSTD_validateSequence(offBase, matchLength, seqPos->posInSrc,
cctx->appliedParams.cParams.windowLog, dictSize),
"Sequence validation failed");
}
RETURN_ERROR_IF(idx - seqPos->idx > cctx->seqStore.maxNbSeq, memory_allocation,
"Not enough memory allocated. Try adjusting ZSTD_c_minMatch.");
- ZSTD_storeSeq(&cctx->seqStore, litLength, ip, iend, offCode, matchLength);
+ ZSTD_storeSeq(&cctx->seqStore, litLength, ip, iend, offBase, matchLength);
ip += matchLength + litLength;
}
ZSTD_memcpy(cctx->blockState.nextCBlock->rep, updatedRepcodes.rep, sizeof(repcodes_t));
@@ -5888,7 +5890,7 @@ ZSTD_copySequencesToSeqStoreNoBlockDelim(ZSTD_CCtx* cctx, ZSTD_sequencePosition*
U32 litLength = currSeq.litLength;
U32 matchLength = currSeq.matchLength;
U32 const rawOffset = currSeq.offset;
- U32 offCode;
+ U32 offBase;
/* Modify the sequence depending on where endPosInSequence lies */
if (endPosInSequence >= currSeq.litLength + currSeq.matchLength) {
@@ -5942,20 +5944,20 @@ ZSTD_copySequencesToSeqStoreNoBlockDelim(ZSTD_CCtx* cctx, ZSTD_sequencePosition*
}
/* Check if this offset can be represented with a repcode */
{ U32 const ll0 = (litLength == 0);
- offCode = ZSTD_finalizeOffCode(rawOffset, updatedRepcodes.rep, ll0);
- ZSTD_updateRep(updatedRepcodes.rep, offCode, ll0);
+ offBase = ZSTD_finalizeOffBase(rawOffset, updatedRepcodes.rep, ll0);
+ ZSTD_updateRep(updatedRepcodes.rep, offBase, ll0);
}
if (cctx->appliedParams.validateSequences) {
seqPos->posInSrc += litLength + matchLength;
- FORWARD_IF_ERROR(ZSTD_validateSequence(offCode, matchLength, seqPos->posInSrc,
+ FORWARD_IF_ERROR(ZSTD_validateSequence(offBase, matchLength, seqPos->posInSrc,
cctx->appliedParams.cParams.windowLog, dictSize),
"Sequence validation failed");
}
- DEBUGLOG(6, "Storing sequence: (of: %u, ml: %u, ll: %u)", offCode, matchLength, litLength);
+ DEBUGLOG(6, "Storing sequence: (of: %u, ml: %u, ll: %u)", offBase, matchLength, litLength);
RETURN_ERROR_IF(idx - seqPos->idx > cctx->seqStore.maxNbSeq, memory_allocation,
"Not enough memory allocated. Try adjusting ZSTD_c_minMatch.");
- ZSTD_storeSeq(&cctx->seqStore, litLength, ip, iend, offCode, matchLength);
+ ZSTD_storeSeq(&cctx->seqStore, litLength, ip, iend, offBase, matchLength);
ip += matchLength + litLength;
}
DEBUGLOG(5, "Ending seq: idx: %u (of: %u ml: %u ll: %u)", idx, inSeqs[idx].offset, inSeqs[idx].matchLength, inSeqs[idx].litLength);
diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h
index f71791ec0a5..9fe3affab6b 100644
--- a/lib/compress/zstd_compress_internal.h
+++ b/lib/compress/zstd_compress_internal.h
@@ -577,28 +577,27 @@ ZSTD_safecopyLiterals(BYTE* op, BYTE const* ip, BYTE const* const iend, BYTE con
while (ip < iend) *op++ = *ip++;
}
-#define STORE_REPCODE_1 STORE_REPCODE(1)
-#define STORE_REPCODE_2 STORE_REPCODE(2)
-#define STORE_REPCODE_3 STORE_REPCODE(3)
-#define STORE_REPCODE(r) (assert((r)>=1), assert((r)<=ZSTD_REP_NUM), (r)) /* accepts IDs 1,2,3 */
-#define STORE_OFFSET(o) (assert((o)>0), o + ZSTD_REP_NUM)
-#define STORED_IS_OFFSET(o) ((o) > ZSTD_REP_NUM)
-#define STORED_IS_REPCODE(o) ( 1 <= (o) && (o) <= ZSTD_REP_NUM)
-#define STORED_OFFSET(o) (assert(STORED_IS_OFFSET(o)), (o) - ZSTD_REP_NUM)
-#define STORED_REPCODE(o) (assert(STORED_IS_REPCODE(o)), (o)) /* returns ID 1,2,3 */
-#define STORED_TO_OFFBASE(o) (o)
-#define OFFBASE_TO_STORED(o) (o)
+
+#define REPCODE1_TO_OFFBASE REPCODE_TO_OFFBASE(1)
+#define REPCODE2_TO_OFFBASE REPCODE_TO_OFFBASE(2)
+#define REPCODE3_TO_OFFBASE REPCODE_TO_OFFBASE(3)
+#define REPCODE_TO_OFFBASE(r) (assert((r)>=1), assert((r)<=ZSTD_REP_NUM), (r)) /* accepts IDs 1,2,3 */
+#define OFFSET_TO_OFFBASE(o) (assert((o)>0), o + ZSTD_REP_NUM)
+#define OFFBASE_IS_OFFSET(o) ((o) > ZSTD_REP_NUM)
+#define OFFBASE_IS_REPCODE(o) ( 1 <= (o) && (o) <= ZSTD_REP_NUM)
+#define OFFBASE_TO_OFFSET(o) (assert(OFFBASE_IS_OFFSET(o)), (o) - ZSTD_REP_NUM)
+#define OFFBASE_TO_REPCODE(o) (assert(OFFBASE_IS_REPCODE(o)), (o)) /* returns ID 1,2,3 */
/*! ZSTD_storeSeq() :
- * Store a sequence (litlen, litPtr, offCode and matchLength) into seqStore_t.
- * @offBase_minus1 : Users should use employ macros STORE_REPCODE_X and STORE_OFFSET().
+ * Store a sequence (litlen, litPtr, offBase and matchLength) into seqStore_t.
+ * @offBase : Users should employ macros REPCODE_TO_OFFBASE() and OFFSET_TO_OFFBASE().
* @matchLength : must be >= MINMATCH
- * Allowed to overread literals up to litLimit.
+ * Allowed to over-read literals up to litLimit.
*/
HINT_INLINE UNUSED_ATTR void
ZSTD_storeSeq(seqStore_t* seqStorePtr,
size_t litLength, const BYTE* literals, const BYTE* litLimit,
- U32 offBase_minus1,
+ U32 offBase,
size_t matchLength)
{
BYTE const* const litLimit_w = litLimit - WILDCOPY_OVERLENGTH;
@@ -607,8 +606,8 @@ ZSTD_storeSeq(seqStore_t* seqStorePtr,
static const BYTE* g_start = NULL;
if (g_start==NULL) g_start = (const BYTE*)literals; /* note : index only works for compression within a single segment */
{ U32 const pos = (U32)((const BYTE*)literals - g_start);
- DEBUGLOG(6, "Cpos%7u :%3u literals, match%4u bytes at offCode%7u",
- pos, (U32)litLength, (U32)matchLength, (U32)offBase_minus1);
+ DEBUGLOG(6, "Cpos%7u :%3u literals, match%4u bytes at offBase%7u",
+ pos, (U32)litLength, (U32)matchLength, (U32)offBase);
}
#endif
assert((size_t)(seqStorePtr->sequences - seqStorePtr->sequencesStart) < seqStorePtr->maxNbSeq);
@@ -618,9 +617,9 @@ ZSTD_storeSeq(seqStore_t* seqStorePtr,
assert(literals + litLength <= litLimit);
if (litEnd <= litLimit_w) {
/* Common case we can use wildcopy.
- * First copy 16 bytes, because literals are likely short.
- */
- assert(WILDCOPY_OVERLENGTH >= 16);
+ * First copy 16 bytes, because literals are likely short.
+ */
+ ZSTD_STATIC_ASSERT(WILDCOPY_OVERLENGTH >= 16);
ZSTD_copy16(seqStorePtr->lit, literals);
if (litLength > 16) {
ZSTD_wildcopy(seqStorePtr->lit+16, literals+16, (ptrdiff_t)litLength-16, ZSTD_no_overlap);
@@ -639,7 +638,7 @@ ZSTD_storeSeq(seqStore_t* seqStorePtr,
seqStorePtr->sequences[0].litLength = (U16)litLength;
/* match offset */
- seqStorePtr->sequences[0].offBase = STORED_TO_OFFBASE(offBase_minus1);
+ seqStorePtr->sequences[0].offBase = offBase;
/* match Length */
assert(matchLength >= MINMATCH);
@@ -657,17 +656,17 @@ ZSTD_storeSeq(seqStore_t* seqStorePtr,
/* ZSTD_updateRep() :
* updates in-place @rep (array of repeat offsets)
- * @offBase_minus1 : sum-type, with same numeric representation as ZSTD_storeSeq()
+ * @offBase : sum-type, using numeric representation of ZSTD_storeSeq()
*/
MEM_STATIC void
-ZSTD_updateRep(U32 rep[ZSTD_REP_NUM], U32 const offBase_minus1, U32 const ll0)
+ZSTD_updateRep(U32 rep[ZSTD_REP_NUM], U32 const offBase, U32 const ll0)
{
- if (STORED_IS_OFFSET(offBase_minus1)) { /* full offset */
+ if (OFFBASE_IS_OFFSET(offBase)) { /* full offset */
rep[2] = rep[1];
rep[1] = rep[0];
- rep[0] = STORED_OFFSET(offBase_minus1);
+ rep[0] = OFFBASE_TO_OFFSET(offBase);
} else { /* repcode */
- U32 const repCode = STORED_REPCODE(offBase_minus1) - 1 + ll0;
+ U32 const repCode = OFFBASE_TO_REPCODE(offBase) - 1 + ll0;
if (repCode > 0) { /* note : if repCode==0, no change */
U32 const currentOffset = (repCode==ZSTD_REP_NUM) ? (rep[0] - 1) : rep[repCode];
rep[2] = (repCode >= 2) ? rep[1] : rep[2];
@@ -684,11 +683,11 @@ typedef struct repcodes_s {
} repcodes_t;
MEM_STATIC repcodes_t
-ZSTD_newRep(U32 const rep[ZSTD_REP_NUM], U32 const offBase_minus1, U32 const ll0)
+ZSTD_newRep(U32 const rep[ZSTD_REP_NUM], U32 const offBase, U32 const ll0)
{
repcodes_t newReps;
ZSTD_memcpy(&newReps, rep, sizeof(newReps));
- ZSTD_updateRep(newReps.rep, offBase_minus1, ll0);
+ ZSTD_updateRep(newReps.rep, offBase, ll0);
return newReps;
}
diff --git a/lib/compress/zstd_compress_superblock.c b/lib/compress/zstd_compress_superblock.c
index 10e33785778..5e89a706c0e 100644
--- a/lib/compress/zstd_compress_superblock.c
+++ b/lib/compress/zstd_compress_superblock.c
@@ -38,11 +38,12 @@
* @return : compressed size of literals section of a sub-block
* Or 0 if it unable to compress.
* Or error code */
-static size_t ZSTD_compressSubBlock_literal(const HUF_CElt* hufTable,
- const ZSTD_hufCTablesMetadata_t* hufMetadata,
- const BYTE* literals, size_t litSize,
- void* dst, size_t dstSize,
- const int bmi2, int writeEntropy, int* entropyWritten)
+static size_t
+ZSTD_compressSubBlock_literal(const HUF_CElt* hufTable,
+ const ZSTD_hufCTablesMetadata_t* hufMetadata,
+ const BYTE* literals, size_t litSize,
+ void* dst, size_t dstSize,
+ const int bmi2, int writeEntropy, int* entropyWritten)
{
size_t const header = writeEntropy ? 200 : 0;
size_t const lhSize = 3 + (litSize >= (1 KB - header)) + (litSize >= (16 KB - header));
@@ -126,7 +127,11 @@ static size_t ZSTD_compressSubBlock_literal(const HUF_CElt* hufTable,
return op-ostart;
}
-static size_t ZSTD_seqDecompressedSize(seqStore_t const* seqStore, const seqDef* sequences, size_t nbSeq, size_t litSize, int lastSequence) {
+static size_t
+ZSTD_seqDecompressedSize(seqStore_t const* seqStore,
+ const seqDef* sequences, size_t nbSeq,
+ size_t litSize, int lastSequence)
+{
const seqDef* const sstart = sequences;
const seqDef* const send = sequences + nbSeq;
const seqDef* sp = sstart;
@@ -156,13 +161,14 @@ static size_t ZSTD_seqDecompressedSize(seqStore_t const* seqStore, const seqDef*
* @return : compressed size of sequences section of a sub-block
* Or 0 if it is unable to compress
* Or error code. */
-static size_t ZSTD_compressSubBlock_sequences(const ZSTD_fseCTables_t* fseTables,
- const ZSTD_fseCTablesMetadata_t* fseMetadata,
- const seqDef* sequences, size_t nbSeq,
- const BYTE* llCode, const BYTE* mlCode, const BYTE* ofCode,
- const ZSTD_CCtx_params* cctxParams,
- void* dst, size_t dstCapacity,
- const int bmi2, int writeEntropy, int* entropyWritten)
+static size_t
+ZSTD_compressSubBlock_sequences(const ZSTD_fseCTables_t* fseTables,
+ const ZSTD_fseCTablesMetadata_t* fseMetadata,
+ const seqDef* sequences, size_t nbSeq,
+ const BYTE* llCode, const BYTE* mlCode, const BYTE* ofCode,
+ const ZSTD_CCtx_params* cctxParams,
+ void* dst, size_t dstCapacity,
+ const int bmi2, int writeEntropy, int* entropyWritten)
{
const int longOffsets = cctxParams->cParams.windowLog > STREAM_ACCUMULATOR_MIN;
BYTE* const ostart = (BYTE*)dst;
@@ -539,7 +545,7 @@ static size_t ZSTD_compressSubBlock_multi(const seqStore_t* seqStorePtr,
repcodes_t rep;
ZSTD_memcpy(&rep, prevCBlock->rep, sizeof(rep));
for (seq = sstart; seq < sp; ++seq) {
- ZSTD_updateRep(rep.rep, seq->offBase - 1, ZSTD_getSequenceLength(seqStorePtr, seq).litLength == 0);
+ ZSTD_updateRep(rep.rep, seq->offBase, ZSTD_getSequenceLength(seqStorePtr, seq).litLength == 0);
}
ZSTD_memcpy(nextCBlock->rep, &rep, sizeof(rep));
}
diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c
index 76933dea262..610a1b3ecbe 100644
--- a/lib/compress/zstd_double_fast.c
+++ b/lib/compress/zstd_double_fast.c
@@ -131,7 +131,7 @@ size_t ZSTD_compressBlock_doubleFast_noDict_generic(
if ((offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1))) {
mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4;
ip++;
- ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, STORE_REPCODE_1, mLength);
+ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, REPCODE1_TO_OFFBASE, mLength);
goto _match_stored;
}
@@ -217,7 +217,7 @@ size_t ZSTD_compressBlock_doubleFast_noDict_generic(
hashLong[hl1] = (U32)(ip1 - base);
}
- ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, STORE_OFFSET(offset), mLength);
+ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, OFFSET_TO_OFFBASE(offset), mLength);
_match_stored:
/* match found */
@@ -243,7 +243,7 @@ size_t ZSTD_compressBlock_doubleFast_noDict_generic(
U32 const tmpOff = offset_2; offset_2 = offset_1; offset_1 = tmpOff; /* swap offset_2 <=> offset_1 */
hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = (U32)(ip-base);
hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = (U32)(ip-base);
- ZSTD_storeSeq(seqStore, 0, anchor, iend, STORE_REPCODE_1, rLength);
+ ZSTD_storeSeq(seqStore, 0, anchor, iend, REPCODE1_TO_OFFBASE, rLength);
ip += rLength;
anchor = ip;
continue; /* faster when present ... (?) */
@@ -328,7 +328,7 @@ size_t ZSTD_compressBlock_doubleFast_dictMatchState_generic(
const BYTE* repMatchEnd = repIndex < prefixLowestIndex ? dictEnd : iend;
mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixLowest) + 4;
ip++;
- ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, STORE_REPCODE_1, mLength);
+ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, REPCODE1_TO_OFFBASE, mLength);
goto _match_stored;
}
@@ -419,7 +419,7 @@ size_t ZSTD_compressBlock_doubleFast_dictMatchState_generic(
offset_2 = offset_1;
offset_1 = offset;
- ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, STORE_OFFSET(offset), mLength);
+ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, OFFSET_TO_OFFBASE(offset), mLength);
_match_stored:
/* match found */
@@ -448,7 +448,7 @@ size_t ZSTD_compressBlock_doubleFast_dictMatchState_generic(
const BYTE* const repEnd2 = repIndex2 < prefixLowestIndex ? dictEnd : iend;
size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixLowest) + 4;
U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */
- ZSTD_storeSeq(seqStore, 0, anchor, iend, STORE_REPCODE_1, repLength2);
+ ZSTD_storeSeq(seqStore, 0, anchor, iend, REPCODE1_TO_OFFBASE, repLength2);
hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = current2;
hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = current2;
ip += repLength2;
@@ -585,7 +585,7 @@ static size_t ZSTD_compressBlock_doubleFast_extDict_generic(
const BYTE* repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend;
mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixStart) + 4;
ip++;
- ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, STORE_REPCODE_1, mLength);
+ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, REPCODE1_TO_OFFBASE, mLength);
} else {
if ((matchLongIndex > dictStartIndex) && (MEM_read64(matchLong) == MEM_read64(ip))) {
const BYTE* const matchEnd = matchLongIndex < prefixStartIndex ? dictEnd : iend;
@@ -596,7 +596,7 @@ static size_t ZSTD_compressBlock_doubleFast_extDict_generic(
while (((ip>anchor) & (matchLong>lowMatchPtr)) && (ip[-1] == matchLong[-1])) { ip--; matchLong--; mLength++; } /* catch up */
offset_2 = offset_1;
offset_1 = offset;
- ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, STORE_OFFSET(offset), mLength);
+ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, OFFSET_TO_OFFBASE(offset), mLength);
} else if ((matchIndex > dictStartIndex) && (MEM_read32(match) == MEM_read32(ip))) {
size_t const h3 = ZSTD_hashPtr(ip+1, hBitsL, 8);
@@ -621,7 +621,7 @@ static size_t ZSTD_compressBlock_doubleFast_extDict_generic(
}
offset_2 = offset_1;
offset_1 = offset;
- ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, STORE_OFFSET(offset), mLength);
+ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, OFFSET_TO_OFFBASE(offset), mLength);
} else {
ip += ((ip-anchor) >> kSearchStrength) + 1;
@@ -653,7 +653,7 @@ static size_t ZSTD_compressBlock_doubleFast_extDict_generic(
const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend;
size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixStart) + 4;
U32 const tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */
- ZSTD_storeSeq(seqStore, 0, anchor, iend, STORE_REPCODE_1, repLength2);
+ ZSTD_storeSeq(seqStore, 0, anchor, iend, REPCODE1_TO_OFFBASE, repLength2);
hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = current2;
hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = current2;
ip += repLength2;
diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c
index 802fc315798..62c4c2cea02 100644
--- a/lib/compress/zstd_fast.c
+++ b/lib/compress/zstd_fast.c
@@ -180,7 +180,7 @@ ZSTD_compressBlock_fast_noDict_generic(
mLength = ip0[-1] == match0[-1];
ip0 -= mLength;
match0 -= mLength;
- offcode = STORE_REPCODE_1;
+ offcode = REPCODE1_TO_OFFBASE;
mLength += 4;
goto _match;
}
@@ -267,7 +267,7 @@ ZSTD_compressBlock_fast_noDict_generic(
match0 = base + idx;
rep_offset2 = rep_offset1;
rep_offset1 = (U32)(ip0-match0);
- offcode = STORE_OFFSET(rep_offset1);
+ offcode = OFFSET_TO_OFFBASE(rep_offset1);
mLength = 4;
/* Count the backwards match length. */
@@ -306,7 +306,7 @@ ZSTD_compressBlock_fast_noDict_generic(
{ U32 const tmpOff = rep_offset2; rep_offset2 = rep_offset1; rep_offset1 = tmpOff; } /* swap rep_offset2 <=> rep_offset1 */
hashTable[ZSTD_hashPtr(ip0, hlog, mls)] = (U32)(ip0-base);
ip0 += rLength;
- ZSTD_storeSeq(seqStore, 0 /*litLen*/, anchor, iend, STORE_REPCODE_1, rLength);
+ ZSTD_storeSeq(seqStore, 0 /*litLen*/, anchor, iend, REPCODE1_TO_OFFBASE, rLength);
anchor = ip0;
continue; /* faster when present (confirmed on gcc-8) ... (?) */
} } }
@@ -439,7 +439,7 @@ size_t ZSTD_compressBlock_fast_dictMatchState_generic(
const BYTE* const repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend;
mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixStart) + 4;
ip++;
- ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, STORE_REPCODE_1, mLength);
+ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, REPCODE1_TO_OFFBASE, mLength);
} else if ( (matchIndex <= prefixStartIndex) ) {
size_t const dictHash = ZSTD_hashPtr(ip, dictHLog, mls);
U32 const dictMatchIndex = dictHashTable[dictHash];
@@ -459,7 +459,7 @@ size_t ZSTD_compressBlock_fast_dictMatchState_generic(
} /* catch up */
offset_2 = offset_1;
offset_1 = offset;
- ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, STORE_OFFSET(offset), mLength);
+ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, OFFSET_TO_OFFBASE(offset), mLength);
}
} else if (MEM_read32(match) != MEM_read32(ip)) {
/* it's not a match, and we're not going to check the dictionary */
@@ -474,7 +474,7 @@ size_t ZSTD_compressBlock_fast_dictMatchState_generic(
&& (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */
offset_2 = offset_1;
offset_1 = offset;
- ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, STORE_OFFSET(offset), mLength);
+ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, OFFSET_TO_OFFBASE(offset), mLength);
}
/* match found */
@@ -499,7 +499,7 @@ size_t ZSTD_compressBlock_fast_dictMatchState_generic(
const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend;
size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixStart) + 4;
U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */
- ZSTD_storeSeq(seqStore, 0, anchor, iend, STORE_REPCODE_1, repLength2);
+ ZSTD_storeSeq(seqStore, 0, anchor, iend, REPCODE1_TO_OFFBASE, repLength2);
hashTable[ZSTD_hashPtr(ip, hlog, mls)] = current2;
ip += repLength2;
anchor = ip;
@@ -598,7 +598,7 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
const BYTE* const repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend;
size_t const rLength = ZSTD_count_2segments(ip+1 +4, repMatch +4, iend, repMatchEnd, prefixStart) + 4;
ip++;
- ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, STORE_REPCODE_1, rLength);
+ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, REPCODE1_TO_OFFBASE, rLength);
ip += rLength;
anchor = ip;
} else {
@@ -614,7 +614,7 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
size_t mLength = ZSTD_count_2segments(ip+4, match+4, iend, matchEnd, prefixStart) + 4;
while (((ip>anchor) & (match>lowMatchPtr)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */
offset_2 = offset_1; offset_1 = offset; /* update offset history */
- ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, STORE_OFFSET(offset), mLength);
+ ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, OFFSET_TO_OFFBASE(offset), mLength);
ip += mLength;
anchor = ip;
} }
@@ -633,7 +633,7 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend;
size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixStart) + 4;
{ U32 const tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; } /* swap offset_2 <=> offset_1 */
- ZSTD_storeSeq(seqStore, 0 /*litlen*/, anchor, iend, STORE_REPCODE_1, repLength2);
+ ZSTD_storeSeq(seqStore, 0 /*litlen*/, anchor, iend, REPCODE1_TO_OFFBASE, repLength2);
hashTable[ZSTD_hashPtr(ip, hlog, mls)] = current2;
ip += repLength2;
anchor = ip;
diff --git a/lib/compress/zstd_lazy.c b/lib/compress/zstd_lazy.c
index 2e38dcb46d2..2a0cfc89333 100644
--- a/lib/compress/zstd_lazy.c
+++ b/lib/compress/zstd_lazy.c
@@ -197,8 +197,8 @@ ZSTD_DUBT_findBetterDictMatch (
U32 matchIndex = dictMatchIndex + dictIndexDelta;
if ( (4*(int)(matchLength-bestLength)) > (int)(ZSTD_highbit32(curr-matchIndex+1) - ZSTD_highbit32((U32)offsetPtr[0]+1)) ) {
DEBUGLOG(9, "ZSTD_DUBT_findBetterDictMatch(%u) : found better match length %u -> %u and offsetCode %u -> %u (dictMatchIndex %u, matchIndex %u)",
- curr, (U32)bestLength, (U32)matchLength, (U32)*offsetPtr, STORE_OFFSET(curr - matchIndex), dictMatchIndex, matchIndex);
- bestLength = matchLength, *offsetPtr = STORE_OFFSET(curr - matchIndex);
+ curr, (U32)bestLength, (U32)matchLength, (U32)*offsetPtr, OFFSET_TO_OFFBASE(curr - matchIndex), dictMatchIndex, matchIndex);
+ bestLength = matchLength, *offsetPtr = OFFSET_TO_OFFBASE(curr - matchIndex);
}
if (ip+matchLength == iend) { /* reached end of input : ip[matchLength] is not valid, no way to know if it's larger or smaller than match */
break; /* drop, to guarantee consistency (miss a little bit of compression) */
@@ -218,7 +218,7 @@ ZSTD_DUBT_findBetterDictMatch (
}
if (bestLength >= MINMATCH) {
- U32 const mIndex = curr - (U32)STORED_OFFSET(*offsetPtr); (void)mIndex;
+ U32 const mIndex = curr - (U32)OFFBASE_TO_OFFSET(*offsetPtr); (void)mIndex;
DEBUGLOG(8, "ZSTD_DUBT_findBetterDictMatch(%u) : found match of length %u and offsetCode %u (pos %u)",
curr, (U32)bestLength, (U32)*offsetPtr, mIndex);
}
@@ -328,7 +328,7 @@ ZSTD_DUBT_findBestMatch(ZSTD_matchState_t* ms,
if (matchLength > matchEndIdx - matchIndex)
matchEndIdx = matchIndex + (U32)matchLength;
if ( (4*(int)(matchLength-bestLength)) > (int)(ZSTD_highbit32(curr-matchIndex+1) - ZSTD_highbit32((U32)offsetPtr[0]+1)) )
- bestLength = matchLength, *offsetPtr = STORE_OFFSET(curr - matchIndex);
+ bestLength = matchLength, *offsetPtr = OFFSET_TO_OFFBASE(curr - matchIndex);
if (ip+matchLength == iend) { /* equal : no way to know if inf or sup */
if (dictMode == ZSTD_dictMatchState) {
nbCompares = 0; /* in addition to avoiding checking any
@@ -368,7 +368,7 @@ ZSTD_DUBT_findBestMatch(ZSTD_matchState_t* ms,
assert(matchEndIdx > curr+8); /* ensure nextToUpdate is increased */
ms->nextToUpdate = matchEndIdx - 8; /* skip repetitive patterns */
if (bestLength >= MINMATCH) {
- U32 const mIndex = curr - (U32)STORED_OFFSET(*offsetPtr); (void)mIndex;
+ U32 const mIndex = curr - (U32)OFFBASE_TO_OFFSET(*offsetPtr); (void)mIndex;
DEBUGLOG(8, "ZSTD_DUBT_findBestMatch(%u) : found match of length %u and offsetCode %u (pos %u)",
curr, (U32)bestLength, (U32)*offsetPtr, mIndex);
}
@@ -561,7 +561,7 @@ size_t ZSTD_dedicatedDictSearch_lazy_search(size_t* offsetPtr, size_t ml, U32 nb
/* save best solution */
if (currentMl > ml) {
ml = currentMl;
- *offsetPtr = STORE_OFFSET(curr - (matchIndex + ddsIndexDelta));
+ *offsetPtr = OFFSET_TO_OFFBASE(curr - (matchIndex + ddsIndexDelta));
if (ip+currentMl == iLimit) {
/* best possible, avoids read overflow on next attempt */
return ml;
@@ -598,7 +598,7 @@ size_t ZSTD_dedicatedDictSearch_lazy_search(size_t* offsetPtr, size_t ml, U32 nb
/* save best solution */
if (currentMl > ml) {
ml = currentMl;
- *offsetPtr = STORE_OFFSET(curr - (matchIndex + ddsIndexDelta));
+ *offsetPtr = OFFSET_TO_OFFBASE(curr - (matchIndex + ddsIndexDelta));
if (ip+currentMl == iLimit) break; /* best possible, avoids read overflow on next attempt */
}
}
@@ -703,7 +703,7 @@ size_t ZSTD_HcFindBestMatch(
/* save best solution */
if (currentMl > ml) {
ml = currentMl;
- *offsetPtr = STORE_OFFSET(curr - matchIndex);
+ *offsetPtr = OFFSET_TO_OFFBASE(curr - matchIndex);
if (ip+currentMl == iLimit) break; /* best possible, avoids read overflow on next attempt */
}
@@ -739,7 +739,7 @@ size_t ZSTD_HcFindBestMatch(
if (currentMl > ml) {
ml = currentMl;
assert(curr > matchIndex + dmsIndexDelta);
- *offsetPtr = STORE_OFFSET(curr - (matchIndex + dmsIndexDelta));
+ *offsetPtr = OFFSET_TO_OFFBASE(curr - (matchIndex + dmsIndexDelta));
if (ip+currentMl == iLimit) break; /* best possible, avoids read overflow on next attempt */
}
@@ -1245,7 +1245,7 @@ size_t ZSTD_RowFindBestMatch(
/* Save best solution */
if (currentMl > ml) {
ml = currentMl;
- *offsetPtr = STORE_OFFSET(curr - matchIndex);
+ *offsetPtr = OFFSET_TO_OFFBASE(curr - matchIndex);
if (ip+currentMl == iLimit) break; /* best possible, avoids read overflow on next attempt */
}
}
@@ -1294,7 +1294,7 @@ size_t ZSTD_RowFindBestMatch(
if (currentMl > ml) {
ml = currentMl;
assert(curr > matchIndex + dmsIndexDelta);
- *offsetPtr = STORE_OFFSET(curr - (matchIndex + dmsIndexDelta));
+ *offsetPtr = OFFSET_TO_OFFBASE(curr - (matchIndex + dmsIndexDelta));
if (ip+currentMl == iLimit) break;
}
}
@@ -1539,7 +1539,7 @@ ZSTD_compressBlock_lazy_generic(
#endif
while (ip < ilimit) {
size_t matchLength=0;
- size_t offcode=STORE_REPCODE_1;
+ size_t offBase = REPCODE1_TO_OFFBASE;
const BYTE* start=ip+1;
DEBUGLOG(7, "search baseline (depth 0)");
@@ -1564,10 +1564,10 @@ ZSTD_compressBlock_lazy_generic(
}
/* first search (depth 0) */
- { size_t offsetFound = 999999999;
- size_t const ml2 = searchMax(ms, ip, iend, &offsetFound);
+ { size_t offbaseFound = 999999999;
+ size_t const ml2 = searchMax(ms, ip, iend, &offbaseFound);
if (ml2 > matchLength)
- matchLength = ml2, start = ip, offcode=offsetFound;
+ matchLength = ml2, start = ip, offBase = offbaseFound;
}
if (matchLength < 4) {
@@ -1581,12 +1581,12 @@ ZSTD_compressBlock_lazy_generic(
DEBUGLOG(7, "search depth 1");
ip ++;
if ( (dictMode == ZSTD_noDict)
- && (offcode) && ((offset_1>0) & (MEM_read32(ip) == MEM_read32(ip - offset_1)))) {
+ && (offBase) && ((offset_1>0) & (MEM_read32(ip) == MEM_read32(ip - offset_1)))) {
size_t const mlRep = ZSTD_count(ip+4, ip+4-offset_1, iend) + 4;
int const gain2 = (int)(mlRep * 3);
- int const gain1 = (int)(matchLength*3 - ZSTD_highbit32((U32)STORED_TO_OFFBASE(offcode)) + 1);
+ int const gain1 = (int)(matchLength*3 - ZSTD_highbit32((U32)offBase) + 1);
if ((mlRep >= 4) && (gain2 > gain1))
- matchLength = mlRep, offcode = STORE_REPCODE_1, start = ip;
+ matchLength = mlRep, offBase = REPCODE1_TO_OFFBASE, start = ip;
}
if (isDxS) {
const U32 repIndex = (U32)(ip - base) - offset_1;
@@ -1598,17 +1598,17 @@ ZSTD_compressBlock_lazy_generic(
const BYTE* repMatchEnd = repIndex < prefixLowestIndex ? dictEnd : iend;
size_t const mlRep = ZSTD_count_2segments(ip+4, repMatch+4, iend, repMatchEnd, prefixLowest) + 4;
int const gain2 = (int)(mlRep * 3);
- int const gain1 = (int)(matchLength*3 - ZSTD_highbit32((U32)STORED_TO_OFFBASE(offcode)) + 1);
+ int const gain1 = (int)(matchLength*3 - ZSTD_highbit32((U32)offBase) + 1);
if ((mlRep >= 4) && (gain2 > gain1))
- matchLength = mlRep, offcode = STORE_REPCODE_1, start = ip;
+ matchLength = mlRep, offBase = REPCODE1_TO_OFFBASE, start = ip;
}
}
- { size_t offset2=999999999;
- size_t const ml2 = searchMax(ms, ip, iend, &offset2);
- int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)STORED_TO_OFFBASE(offset2))); /* raw approx */
- int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)STORED_TO_OFFBASE(offcode)) + 4);
+ { size_t ofbCandidate=999999999;
+ size_t const ml2 = searchMax(ms, ip, iend, &ofbCandidate);
+ int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)ofbCandidate)); /* raw approx */
+ int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offBase) + 4);
if ((ml2 >= 4) && (gain2 > gain1)) {
- matchLength = ml2, offcode = offset2, start = ip;
+ matchLength = ml2, offBase = ofbCandidate, start = ip;
continue; /* search a better one */
} }
@@ -1617,12 +1617,12 @@ ZSTD_compressBlock_lazy_generic(
DEBUGLOG(7, "search depth 2");
ip ++;
if ( (dictMode == ZSTD_noDict)
- && (offcode) && ((offset_1>0) & (MEM_read32(ip) == MEM_read32(ip - offset_1)))) {
+ && (offBase) && ((offset_1>0) & (MEM_read32(ip) == MEM_read32(ip - offset_1)))) {
size_t const mlRep = ZSTD_count(ip+4, ip+4-offset_1, iend) + 4;
int const gain2 = (int)(mlRep * 4);
- int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)STORED_TO_OFFBASE(offcode)) + 1);
+ int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offBase) + 1);
if ((mlRep >= 4) && (gain2 > gain1))
- matchLength = mlRep, offcode = STORE_REPCODE_1, start = ip;
+ matchLength = mlRep, offBase = REPCODE1_TO_OFFBASE, start = ip;
}
if (isDxS) {
const U32 repIndex = (U32)(ip - base) - offset_1;
@@ -1634,17 +1634,17 @@ ZSTD_compressBlock_lazy_generic(
const BYTE* repMatchEnd = repIndex < prefixLowestIndex ? dictEnd : iend;
size_t const mlRep = ZSTD_count_2segments(ip+4, repMatch+4, iend, repMatchEnd, prefixLowest) + 4;
int const gain2 = (int)(mlRep * 4);
- int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)STORED_TO_OFFBASE(offcode)) + 1);
+ int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offBase) + 1);
if ((mlRep >= 4) && (gain2 > gain1))
- matchLength = mlRep, offcode = STORE_REPCODE_1, start = ip;
+ matchLength = mlRep, offBase = REPCODE1_TO_OFFBASE, start = ip;
}
}
- { size_t offset2=999999999;
- size_t const ml2 = searchMax(ms, ip, iend, &offset2);
- int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)STORED_TO_OFFBASE(offset2))); /* raw approx */
- int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)STORED_TO_OFFBASE(offcode)) + 7);
+ { size_t ofbCandidate=999999999;
+ size_t const ml2 = searchMax(ms, ip, iend, &ofbCandidate);
+ int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)ofbCandidate)); /* raw approx */
+ int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offBase) + 7);
if ((ml2 >= 4) && (gain2 > gain1)) {
- matchLength = ml2, offcode = offset2, start = ip;
+ matchLength = ml2, offBase = ofbCandidate, start = ip;
continue;
} } }
break; /* nothing found : store previous solution */
@@ -1655,24 +1655,24 @@ ZSTD_compressBlock_lazy_generic(
* notably if `value` is unsigned, resulting in a large positive `-value`.
*/
/* catch up */
- if (STORED_IS_OFFSET(offcode)) {
+ if (OFFBASE_IS_OFFSET(offBase)) {
if (dictMode == ZSTD_noDict) {
- while ( ((start > anchor) & (start - STORED_OFFSET(offcode) > prefixLowest))
- && (start[-1] == (start-STORED_OFFSET(offcode))[-1]) ) /* only search for offset within prefix */
+ while ( ((start > anchor) & (start - OFFBASE_TO_OFFSET(offBase) > prefixLowest))
+ && (start[-1] == (start-OFFBASE_TO_OFFSET(offBase))[-1]) ) /* only search for offset within prefix */
{ start--; matchLength++; }
}
if (isDxS) {
- U32 const matchIndex = (U32)((size_t)(start-base) - STORED_OFFSET(offcode));
+ U32 const matchIndex = (U32)((size_t)(start-base) - OFFBASE_TO_OFFSET(offBase));
const BYTE* match = (matchIndex < prefixLowestIndex) ? dictBase + matchIndex - dictIndexDelta : base + matchIndex;
const BYTE* const mStart = (matchIndex < prefixLowestIndex) ? dictLowest : prefixLowest;
while ((start>anchor) && (match>mStart) && (start[-1] == match[-1])) { start--; match--; matchLength++; } /* catch up */
}
- offset_2 = offset_1; offset_1 = (U32)STORED_OFFSET(offcode);
+ offset_2 = offset_1; offset_1 = (U32)OFFBASE_TO_OFFSET(offBase);
}
/* store sequence */
_storeSequence:
{ size_t const litLength = (size_t)(start - anchor);
- ZSTD_storeSeq(seqStore, litLength, anchor, iend, (U32)offcode, matchLength);
+ ZSTD_storeSeq(seqStore, litLength, anchor, iend, (U32)offBase, matchLength);
anchor = ip = start + matchLength;
}
@@ -1688,8 +1688,8 @@ ZSTD_compressBlock_lazy_generic(
&& (MEM_read32(repMatch) == MEM_read32(ip)) ) {
const BYTE* const repEnd2 = repIndex < prefixLowestIndex ? dictEnd : iend;
matchLength = ZSTD_count_2segments(ip+4, repMatch+4, iend, repEnd2, prefixLowest) + 4;
- offcode = offset_2; offset_2 = offset_1; offset_1 = (U32)offcode; /* swap offset_2 <=> offset_1 */
- ZSTD_storeSeq(seqStore, 0, anchor, iend, STORE_REPCODE_1, matchLength);
+ offBase = offset_2; offset_2 = offset_1; offset_1 = (U32)offBase; /* swap offset_2 <=> offset_1 */
+ ZSTD_storeSeq(seqStore, 0, anchor, iend, REPCODE1_TO_OFFBASE, matchLength);
ip += matchLength;
anchor = ip;
continue;
@@ -1703,8 +1703,8 @@ ZSTD_compressBlock_lazy_generic(
&& (MEM_read32(ip) == MEM_read32(ip - offset_2)) ) {
/* store sequence */
matchLength = ZSTD_count(ip+4, ip+4-offset_2, iend) + 4;
- offcode = offset_2; offset_2 = offset_1; offset_1 = (U32)offcode; /* swap repcodes */
- ZSTD_storeSeq(seqStore, 0, anchor, iend, STORE_REPCODE_1, matchLength);
+ offBase = offset_2; offset_2 = offset_1; offset_1 = (U32)offBase; /* swap repcodes */
+ ZSTD_storeSeq(seqStore, 0, anchor, iend, REPCODE1_TO_OFFBASE, matchLength);
ip += matchLength;
anchor = ip;
continue; /* faster when present ... (?) */
@@ -1905,7 +1905,7 @@ size_t ZSTD_compressBlock_lazy_extDict_generic(
#endif
while (ip < ilimit) {
size_t matchLength=0;
- size_t offcode=STORE_REPCODE_1;
+ size_t offBase = REPCODE1_TO_OFFBASE;
const BYTE* start=ip+1;
U32 curr = (U32)(ip-base);
@@ -1924,10 +1924,10 @@ size_t ZSTD_compressBlock_lazy_extDict_generic(
} }
/* first search (depth 0) */
- { size_t offsetFound = 999999999;
- size_t const ml2 = searchMax(ms, ip, iend, &offsetFound);
+ { size_t ofbCandidate = 999999999;
+ size_t const ml2 = searchMax(ms, ip, iend, &ofbCandidate);
if (ml2 > matchLength)
- matchLength = ml2, start = ip, offcode=offsetFound;
+ matchLength = ml2, start = ip, offBase = ofbCandidate;
}
if (matchLength < 4) {
@@ -1941,7 +1941,7 @@ size_t ZSTD_compressBlock_lazy_extDict_generic(
ip ++;
curr++;
/* check repCode */
- if (offcode) {
+ if (offBase) {
const U32 windowLow = ZSTD_getLowestMatchIndex(ms, curr, windowLog);
const U32 repIndex = (U32)(curr - offset_1);
const BYTE* const repBase = repIndex < dictLimit ? dictBase : base;
@@ -1953,18 +1953,18 @@ size_t ZSTD_compressBlock_lazy_extDict_generic(
const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend;
size_t const repLength = ZSTD_count_2segments(ip+4, repMatch+4, iend, repEnd, prefixStart) + 4;
int const gain2 = (int)(repLength * 3);
- int const gain1 = (int)(matchLength*3 - ZSTD_highbit32((U32)STORED_TO_OFFBASE(offcode)) + 1);
+ int const gain1 = (int)(matchLength*3 - ZSTD_highbit32((U32)offBase) + 1);
if ((repLength >= 4) && (gain2 > gain1))
- matchLength = repLength, offcode = STORE_REPCODE_1, start = ip;
+ matchLength = repLength, offBase = REPCODE1_TO_OFFBASE, start = ip;
} }
/* search match, depth 1 */
- { size_t offset2=999999999;
- size_t const ml2 = searchMax(ms, ip, iend, &offset2);
- int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)STORED_TO_OFFBASE(offset2))); /* raw approx */
- int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)STORED_TO_OFFBASE(offcode)) + 4);
+ { size_t ofbCandidate=999999999;
+ size_t const ml2 = searchMax(ms, ip, iend, &ofbCandidate);
+ int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)ofbCandidate)); /* raw approx */
+ int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offBase) + 4);
if ((ml2 >= 4) && (gain2 > gain1)) {
- matchLength = ml2, offcode = offset2, start = ip;
+ matchLength = ml2, offBase = ofbCandidate, start = ip;
continue; /* search a better one */
} }
@@ -1973,7 +1973,7 @@ size_t ZSTD_compressBlock_lazy_extDict_generic(
ip ++;
curr++;
/* check repCode */
- if (offcode) {
+ if (offBase) {
const U32 windowLow = ZSTD_getLowestMatchIndex(ms, curr, windowLog);
const U32 repIndex = (U32)(curr - offset_1);
const BYTE* const repBase = repIndex < dictLimit ? dictBase : base;
@@ -1985,36 +1985,36 @@ size_t ZSTD_compressBlock_lazy_extDict_generic(
const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend;
size_t const repLength = ZSTD_count_2segments(ip+4, repMatch+4, iend, repEnd, prefixStart) + 4;
int const gain2 = (int)(repLength * 4);
- int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)STORED_TO_OFFBASE(offcode)) + 1);
+ int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offBase) + 1);
if ((repLength >= 4) && (gain2 > gain1))
- matchLength = repLength, offcode = STORE_REPCODE_1, start = ip;
+ matchLength = repLength, offBase = REPCODE1_TO_OFFBASE, start = ip;
} }
/* search match, depth 2 */
- { size_t offset2=999999999;
- size_t const ml2 = searchMax(ms, ip, iend, &offset2);
- int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)STORED_TO_OFFBASE(offset2))); /* raw approx */
- int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)STORED_TO_OFFBASE(offcode)) + 7);
+ { size_t ofbCandidate=999999999;
+ size_t const ml2 = searchMax(ms, ip, iend, &ofbCandidate);
+ int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)ofbCandidate)); /* raw approx */
+ int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offBase) + 7);
if ((ml2 >= 4) && (gain2 > gain1)) {
- matchLength = ml2, offcode = offset2, start = ip;
+ matchLength = ml2, offBase = ofbCandidate, start = ip;
continue;
} } }
break; /* nothing found : store previous solution */
}
/* catch up */
- if (STORED_IS_OFFSET(offcode)) {
- U32 const matchIndex = (U32)((size_t)(start-base) - STORED_OFFSET(offcode));
+ if (OFFBASE_IS_OFFSET(offBase)) {
+ U32 const matchIndex = (U32)((size_t)(start-base) - OFFBASE_TO_OFFSET(offBase));
const BYTE* match = (matchIndex < dictLimit) ? dictBase + matchIndex : base + matchIndex;
const BYTE* const mStart = (matchIndex < dictLimit) ? dictStart : prefixStart;
while ((start>anchor) && (match>mStart) && (start[-1] == match[-1])) { start--; match--; matchLength++; } /* catch up */
- offset_2 = offset_1; offset_1 = (U32)STORED_OFFSET(offcode);
+ offset_2 = offset_1; offset_1 = (U32)OFFBASE_TO_OFFSET(offBase);
}
/* store sequence */
_storeSequence:
{ size_t const litLength = (size_t)(start - anchor);
- ZSTD_storeSeq(seqStore, litLength, anchor, iend, (U32)offcode, matchLength);
+ ZSTD_storeSeq(seqStore, litLength, anchor, iend, (U32)offBase, matchLength);
anchor = ip = start + matchLength;
}
@@ -2031,8 +2031,8 @@ size_t ZSTD_compressBlock_lazy_extDict_generic(
/* repcode detected we should take it */
const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend;
matchLength = ZSTD_count_2segments(ip+4, repMatch+4, iend, repEnd, prefixStart) + 4;
- offcode = offset_2; offset_2 = offset_1; offset_1 = (U32)offcode; /* swap offset history */
- ZSTD_storeSeq(seqStore, 0, anchor, iend, STORE_REPCODE_1, matchLength);
+ offBase = offset_2; offset_2 = offset_1; offset_1 = (U32)offBase; /* swap offset history */
+ ZSTD_storeSeq(seqStore, 0, anchor, iend, REPCODE1_TO_OFFBASE, matchLength);
ip += matchLength;
anchor = ip;
continue; /* faster when present ... (?) */
diff --git a/lib/compress/zstd_ldm.c b/lib/compress/zstd_ldm.c
index f662b2546e0..1c04a36f9c7 100644
--- a/lib/compress/zstd_ldm.c
+++ b/lib/compress/zstd_ldm.c
@@ -711,7 +711,7 @@ size_t ZSTD_ldm_blockCompress(rawSeqStore_t* rawSeqStore,
rep[0] = sequence.offset;
/* Store the sequence */
ZSTD_storeSeq(seqStore, newLitLength, ip - newLitLength, iend,
- STORE_OFFSET(sequence.offset),
+ OFFSET_TO_OFFBASE(sequence.offset),
sequence.matchLength);
ip += sequence.matchLength;
}
diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c
index 2fa10816f9f..5cd34acd073 100644
--- a/lib/compress/zstd_opt.c
+++ b/lib/compress/zstd_opt.c
@@ -282,17 +282,17 @@ static U32 ZSTD_litLengthPrice(U32 const litLength, const optState_t* const optP
/* 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.
- * @offcode : expects a scale where 0,1,2 are repcodes 1-3, and 3+ are real_offsets+2
+ * @offBase : sumtype, representing an offset or a repcode, and using numeric representation of ZSTD_storeSeq()
* @optLevel: when <2, favors small offset for decompression speed (improved cache efficiency)
*/
FORCE_INLINE_TEMPLATE U32
-ZSTD_getMatchPrice(U32 const offcode,
+ZSTD_getMatchPrice(U32 const offBase,
U32 const matchLength,
const optState_t* const optPtr,
int const optLevel)
{
U32 price;
- U32 const offCode = ZSTD_highbit32(STORED_TO_OFFBASE(offcode));
+ U32 const offCode = ZSTD_highbit32(offBase);
U32 const mlBase = matchLength - MINMATCH;
assert(matchLength >= MINMATCH);
@@ -316,10 +316,10 @@ ZSTD_getMatchPrice(U32 const offcode,
}
/* ZSTD_updateStats() :
- * assumption : literals + litLengtn <= iend */
+ * assumption : literals + litLength <= iend */
static void ZSTD_updateStats(optState_t* const optPtr,
U32 litLength, const BYTE* literals,
- U32 offsetCode, U32 matchLength)
+ U32 offBase, U32 matchLength)
{
/* literals */
if (ZSTD_compressedLiterals(optPtr)) {
@@ -336,7 +336,7 @@ static void ZSTD_updateStats(optState_t* const optPtr,
}
/* offset code : expected to follow storeSeq() numeric representation */
- { U32 const offCode = ZSTD_highbit32(STORED_TO_OFFBASE(offsetCode));
+ { U32 const offCode = ZSTD_highbit32(offBase);
assert(offCode <= MaxOff);
optPtr->offCodeFreq[offCode]++;
optPtr->offCodeSum++;
@@ -635,7 +635,7 @@ U32 ZSTD_insertBtAndGetAllMatches (
DEBUGLOG(8, "found repCode %u (ll0:%u, offset:%u) of length %u",
repCode, ll0, repOffset, repLen);
bestLength = repLen;
- matches[mnum].off = STORE_REPCODE(repCode - ll0 + 1); /* expect value between 1 and 3 */
+ matches[mnum].off = REPCODE_TO_OFFBASE(repCode - ll0 + 1); /* expect value between 1 and 3 */
matches[mnum].len = (U32)repLen;
mnum++;
if ( (repLen > sufficient_len)
@@ -664,7 +664,7 @@ U32 ZSTD_insertBtAndGetAllMatches (
bestLength = mlen;
assert(curr > matchIndex3);
assert(mnum==0); /* no prior solution */
- matches[0].off = STORE_OFFSET(curr - matchIndex3);
+ matches[0].off = OFFSET_TO_OFFBASE(curr - matchIndex3);
matches[0].len = (U32)mlen;
mnum = 1;
if ( (mlen > sufficient_len) |
@@ -697,13 +697,13 @@ U32 ZSTD_insertBtAndGetAllMatches (
}
if (matchLength > bestLength) {
- DEBUGLOG(8, "found match of length %u at distance %u (offCode=%u)",
- (U32)matchLength, curr - matchIndex, STORE_OFFSET(curr - matchIndex));
+ DEBUGLOG(8, "found match of length %u at distance %u (offBase=%u)",
+ (U32)matchLength, curr - matchIndex, OFFSET_TO_OFFBASE(curr - matchIndex));
assert(matchEndIdx > matchIndex);
if (matchLength > matchEndIdx - matchIndex)
matchEndIdx = matchIndex + (U32)matchLength;
bestLength = matchLength;
- matches[mnum].off = STORE_OFFSET(curr - matchIndex);
+ matches[mnum].off = OFFSET_TO_OFFBASE(curr - matchIndex);
matches[mnum].len = (U32)matchLength;
mnum++;
if ( (matchLength > ZSTD_OPT_NUM)
@@ -745,12 +745,12 @@ U32 ZSTD_insertBtAndGetAllMatches (
if (matchLength > bestLength) {
matchIndex = dictMatchIndex + dmsIndexDelta;
- DEBUGLOG(8, "found dms match of length %u at distance %u (offCode=%u)",
- (U32)matchLength, curr - matchIndex, STORE_OFFSET(curr - matchIndex));
+ DEBUGLOG(8, "found dms match of length %u at distance %u (offBase=%u)",
+ (U32)matchLength, curr - matchIndex, OFFSET_TO_OFFBASE(curr - matchIndex));
if (matchLength > matchEndIdx - matchIndex)
matchEndIdx = matchIndex + (U32)matchLength;
bestLength = matchLength;
- matches[mnum].off = STORE_OFFSET(curr - matchIndex);
+ matches[mnum].off = OFFSET_TO_OFFBASE(curr - matchIndex);
matches[mnum].len = (U32)matchLength;
mnum++;
if ( (matchLength > ZSTD_OPT_NUM)
@@ -951,7 +951,7 @@ static void ZSTD_optLdm_maybeAddMatch(ZSTD_match_t* matches, U32* nbMatches,
const ZSTD_optLdm_t* optLdm, U32 currPosInBlock)
{
U32 const posDiff = currPosInBlock - optLdm->startPosInBlock;
- /* Note: ZSTD_match_t actually contains offCode and matchLength (before subtracting MINMATCH) */
+ /* Note: ZSTD_match_t actually contains offBase and matchLength (before subtracting MINMATCH) */
U32 const candidateMatchLength = optLdm->endPosInBlock - optLdm->startPosInBlock - posDiff;
/* Ensure that current block position is not outside of the match */
@@ -962,11 +962,11 @@ static void ZSTD_optLdm_maybeAddMatch(ZSTD_match_t* matches, U32* nbMatches,
}
if (*nbMatches == 0 || ((candidateMatchLength > matches[*nbMatches-1].len) && *nbMatches < ZSTD_OPT_NUM)) {
- U32 const candidateOffCode = STORE_OFFSET(optLdm->offset);
- DEBUGLOG(6, "ZSTD_optLdm_maybeAddMatch(): Adding ldm candidate match (offCode: %u matchLength %u) at block position=%u",
- candidateOffCode, candidateMatchLength, currPosInBlock);
+ U32 const candidateOffBase = OFFSET_TO_OFFBASE(optLdm->offset);
+ DEBUGLOG(6, "ZSTD_optLdm_maybeAddMatch(): Adding ldm candidate match (offBase: %u matchLength %u) at block position=%u",
+ candidateOffBase, candidateMatchLength, currPosInBlock);
matches[*nbMatches].len = candidateMatchLength;
- matches[*nbMatches].off = candidateOffCode;
+ matches[*nbMatches].off = candidateOffBase;
(*nbMatches)++;
}
}
@@ -1089,14 +1089,14 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms,
/* large match -> immediate encoding */
{ U32 const maxML = matches[nbMatches-1].len;
- U32 const maxOffcode = matches[nbMatches-1].off;
- DEBUGLOG(6, "found %u matches of maxLength=%u and maxOffCode=%u at cPos=%u => start new series",
- nbMatches, maxML, maxOffcode, (U32)(ip-prefixStart));
+ U32 const maxOffBase = matches[nbMatches-1].off;
+ DEBUGLOG(6, "found %u matches of maxLength=%u and maxOffBase=%u at cPos=%u => start new series",
+ nbMatches, maxML, maxOffBase, (U32)(ip-prefixStart));
if (maxML > sufficient_len) {
lastSequence.litlen = litlen;
lastSequence.mlen = maxML;
- lastSequence.off = maxOffcode;
+ lastSequence.off = maxOffBase;
DEBUGLOG(6, "large match (%u>%u), immediate encoding",
maxML, sufficient_len);
cur = 0;
@@ -1113,15 +1113,15 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms,
opt[pos].price = ZSTD_MAX_PRICE; /* mlen, litlen and price will be fixed during forward scanning */
}
for (matchNb = 0; matchNb < nbMatches; matchNb++) {
- U32 const offcode = matches[matchNb].off;
+ U32 const offBase = matches[matchNb].off;
U32 const end = matches[matchNb].len;
for ( ; pos <= end ; pos++ ) {
- U32 const matchPrice = ZSTD_getMatchPrice(offcode, pos, optStatePtr, optLevel);
+ U32 const matchPrice = ZSTD_getMatchPrice(offBase, pos, optStatePtr, optLevel);
U32 const sequencePrice = literalsPrice + matchPrice;
DEBUGLOG(7, "rPos:%u => set initial price : %.2f",
pos, ZSTD_fCost(sequencePrice));
opt[pos].mlen = pos;
- opt[pos].off = offcode;
+ opt[pos].off = offBase;
opt[pos].litlen = litlen;
opt[pos].price = (int)sequencePrice;
} }
@@ -1221,7 +1221,7 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms,
U32 const startML = (matchNb>0) ? matches[matchNb-1].len+1 : minMatch;
U32 mlen;
- DEBUGLOG(7, "testing match %u => offCode=%4u, mlen=%2u, llen=%2u",
+ DEBUGLOG(7, "testing match %u => offBase=%4u, mlen=%2u, llen=%2u",
matchNb, matches[matchNb].off, lastML, litlen);
for (mlen = lastML; mlen >= startML; mlen--) { /* scan downward */
@@ -1287,7 +1287,7 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms,
for (storePos=storeStart; storePos <= storeEnd; storePos++) {
U32 const llen = opt[storePos].litlen;
U32 const mlen = opt[storePos].mlen;
- U32 const offCode = opt[storePos].off;
+ U32 const offBase = opt[storePos].off;
U32 const advance = llen + mlen;
DEBUGLOG(6, "considering seq starting at %zi, llen=%u, mlen=%u",
anchor - istart, (unsigned)llen, (unsigned)mlen);
@@ -1299,8 +1299,8 @@ ZSTD_compressBlock_opt_generic(ZSTD_matchState_t* ms,
}
assert(anchor + llen <= iend);
- ZSTD_updateStats(optStatePtr, llen, anchor, offCode, mlen);
- ZSTD_storeSeq(seqStore, llen, anchor, iend, offCode, mlen);
+ ZSTD_updateStats(optStatePtr, llen, anchor, offBase, mlen);
+ ZSTD_storeSeq(seqStore, llen, anchor, iend, offBase, mlen);
anchor += advance;
ip = anchor;
} }
diff --git a/tests/decodecorpus.c b/tests/decodecorpus.c
index 1037a36596c..d2b126f39ff 100644
--- a/tests/decodecorpus.c
+++ b/tests/decodecorpus.c
@@ -671,7 +671,7 @@ generateSequences(U32* seed, frame_t* frame, seqStore_t* seqStore,
: 0;
/* actual offset, code to send, and point to copy up to when shifting
* codes in the repeat offsets history */
- U32 offset, offsetCode, repIndex;
+ U32 offset, offBase, repIndex;
/* bounds checks */
matchLen = (U32) MIN(matchLen, excessMatch + MIN_SEQ_LEN);
@@ -707,12 +707,12 @@ generateSequences(U32* seed, frame_t* frame, seqStore_t* seqStore,
}
}
}
- offsetCode = STORE_OFFSET(offset);
+ offBase = OFFSET_TO_OFFBASE(offset);
repIndex = 2;
} else {
/* do a repeat offset */
U32 const randomRepIndex = RAND(seed) % 3;
- offsetCode = STORE_REPCODE(randomRepIndex + 1); /* expects values between 1 & 3 */
+ offBase = REPCODE_TO_OFFBASE(randomRepIndex + 1); /* expects values between 1 & 3 */
if (literalLen > 0) {
offset = frame->stats.rep[randomRepIndex];
repIndex = randomRepIndex;
@@ -751,12 +751,12 @@ generateSequences(U32* seed, frame_t* frame, seqStore_t* seqStore,
DISPLAYLEVEL(7, " srcPos: %8u seqNb: %3u",
(unsigned)((BYTE*)srcPtr - (BYTE*)frame->srcStart), (unsigned)i);
DISPLAYLEVEL(6, "\n");
- if (STORED_IS_REPCODE(offsetCode)) { /* expects sumtype numeric representation of ZSTD_storeSeq() */
+ if (OFFBASE_IS_REPCODE(offBase)) { /* expects sumtype numeric representation of ZSTD_storeSeq() */
DISPLAYLEVEL(7, " repeat offset: %d\n", (int)repIndex);
}
/* use libzstd sequence handling */
ZSTD_storeSeq(seqStore, literalLen, literals, literals + literalLen,
- offsetCode, matchLen);
+ offBase, matchLen);
literalsSize -= literalLen;
excessMatch -= (matchLen - MIN_SEQ_LEN);
@@ -765,8 +765,8 @@ generateSequences(U32* seed, frame_t* frame, seqStore_t* seqStore,
memcpy(srcPtr, literals, literalsSize);
srcPtr += literalsSize;
- DISPLAYLEVEL(6, " excess literals: %5u", (unsigned)literalsSize);
- DISPLAYLEVEL(7, " srcPos: %8u", (unsigned)((BYTE*)srcPtr - (BYTE*)frame->srcStart));
+ DISPLAYLEVEL(6, " excess literals: %5u ", (unsigned)literalsSize);
+ DISPLAYLEVEL(7, "srcPos: %8u ", (unsigned)((BYTE*)srcPtr - (BYTE*)frame->srcStart));
DISPLAYLEVEL(6, "\n");
return numSequences;
diff --git a/tests/fuzzer.c b/tests/fuzzer.c
index d168d657611..823db775f35 100644
--- a/tests/fuzzer.c
+++ b/tests/fuzzer.c
@@ -3283,8 +3283,7 @@ static int basicUnitTests(U32 const seed, double compressibility)
{ U32 u;
for (u = 0; u < CNBuffSize; ++u) {
((BYTE*)CNBuffer)[u] = 255 - ((BYTE*)CNBuffer)[u];
- }
- }
+ } }
{ /* Compress the data */
size_t const inputSize = 500;
size_t const outputSize = ZSTD_compressBound(inputSize);
From 03903f57012054852c0c26daca7131a130bb5cbf Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Wed, 29 Dec 2021 18:51:03 -0800
Subject: [PATCH 003/472] fixed minor compression difference in btlazy2
subtle dependency on sumtype numeric representation
---
contrib/seekable_format/zstdseek_decompress.c | 53 ++++++++++++++++++-
lib/compress/zstd_lazy.c | 24 ++++-----
tests/regression/.gitignore | 1 +
3 files changed, 65 insertions(+), 13 deletions(-)
diff --git a/contrib/seekable_format/zstdseek_decompress.c b/contrib/seekable_format/zstdseek_decompress.c
index 5eed024950b..0848d2519f8 100644
--- a/contrib/seekable_format/zstdseek_decompress.c
+++ b/contrib/seekable_format/zstdseek_decompress.c
@@ -23,13 +23,64 @@
# endif
#endif
+/* ************************************************************
+* Detect POSIX version
+* PLATFORM_POSIX_VERSION = 0 for non-Unix e.g. Windows
+* PLATFORM_POSIX_VERSION = 1 for Unix-like but non-POSIX
+* PLATFORM_POSIX_VERSION > 1 is equal to found _POSIX_VERSION
+* Value of PLATFORM_POSIX_VERSION can be forced on command line
+***************************************************************/
+#ifndef PLATFORM_POSIX_VERSION
+
+# if (defined(__APPLE__) && defined(__MACH__)) || defined(__SVR4) || defined(_AIX) || defined(__hpux) /* POSIX.1-2001 (SUSv3) conformant */ \
+ || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) /* BSD distros */
+ /* exception rule : force posix version to 200112L,
+ * note: it's better to use unistd.h's _POSIX_VERSION whenever possible */
+# define PLATFORM_POSIX_VERSION 200112L
+
+/* try to determine posix version through official unistd.h's _POSIX_VERSION (http://pubs.opengroup.org/onlinepubs/7908799/xsh/unistd.h.html).
+ * note : there is no simple way to know in advance if is present or not on target system,
+ * Posix specification mandates its presence and its content, but target system must respect this spec.
+ * It's necessary to _not_ #include whenever target OS is not unix-like
+ * otherwise it will block preprocessing stage.
+ * The following list of build macros tries to "guess" if target OS is likely unix-like, and therefore can #include
+ */
+# elif !defined(_WIN32) \
+ && ( defined(__unix__) || defined(__unix) \
+ || defined(__midipix__) || defined(__VMS) || defined(__HAIKU__) )
+
+# if defined(__linux__) || defined(__linux) || defined(__CYGWIN__)
+# ifndef _POSIX_C_SOURCE
+# define _POSIX_C_SOURCE 200809L /* feature test macro : https://www.gnu.org/software/libc/manual/html_node/Feature-Test-Macros.html */
+# endif
+# endif
+# include /* declares _POSIX_VERSION */
+# if defined(_POSIX_VERSION) /* POSIX compliant */
+# define PLATFORM_POSIX_VERSION _POSIX_VERSION
+# else
+# define PLATFORM_POSIX_VERSION 1
+# endif
+
+# ifdef __UCLIBC__
+# ifndef __USE_MISC
+# define __USE_MISC /* enable st_mtim on uclibc */
+# endif
+# endif
+
+# else /* non-unix target platform (like Windows) */
+# define PLATFORM_POSIX_VERSION 0
+# endif
+
+#endif /* PLATFORM_POSIX_VERSION */
+
+
/* ************************************************************
* Avoid fseek()'s 2GiB barrier with MSVC, macOS, *BSD, MinGW
***************************************************************/
#if defined(_MSC_VER) && _MSC_VER >= 1400
# define LONG_SEEK _fseeki64
#elif !defined(__64BIT__) && (PLATFORM_POSIX_VERSION >= 200112L) /* No point defining Large file for 64 bit */
-# define LONG_SEEK fseeko
+# define LONG_SEEK fseeko
#elif defined(__MINGW32__) && !defined(__STRICT_ANSI__) && !defined(__NO_MINGW_LFS) && defined(__MSVCRT__)
# define LONG_SEEK fseeko64
#elif defined(_WIN32) && !defined(__DJGPP__)
diff --git a/lib/compress/zstd_lazy.c b/lib/compress/zstd_lazy.c
index 2a0cfc89333..47c0687c95d 100644
--- a/lib/compress/zstd_lazy.c
+++ b/lib/compress/zstd_lazy.c
@@ -230,7 +230,7 @@ ZSTD_DUBT_findBetterDictMatch (
static size_t
ZSTD_DUBT_findBestMatch(ZSTD_matchState_t* ms,
const BYTE* const ip, const BYTE* const iend,
- size_t* offsetPtr,
+ size_t* offBasePtr,
U32 const mls,
const ZSTD_dictMode_e dictMode)
{
@@ -327,8 +327,8 @@ ZSTD_DUBT_findBestMatch(ZSTD_matchState_t* ms,
if (matchLength > bestLength) {
if (matchLength > matchEndIdx - matchIndex)
matchEndIdx = matchIndex + (U32)matchLength;
- if ( (4*(int)(matchLength-bestLength)) > (int)(ZSTD_highbit32(curr-matchIndex+1) - ZSTD_highbit32((U32)offsetPtr[0]+1)) )
- bestLength = matchLength, *offsetPtr = OFFSET_TO_OFFBASE(curr - matchIndex);
+ if ( (4*(int)(matchLength-bestLength)) > (int)(ZSTD_highbit32(curr - matchIndex + 1) - ZSTD_highbit32((U32)*offBasePtr)) )
+ bestLength = matchLength, *offBasePtr = OFFSET_TO_OFFBASE(curr - matchIndex);
if (ip+matchLength == iend) { /* equal : no way to know if inf or sup */
if (dictMode == ZSTD_dictMatchState) {
nbCompares = 0; /* in addition to avoiding checking any
@@ -361,16 +361,16 @@ ZSTD_DUBT_findBestMatch(ZSTD_matchState_t* ms,
if (dictMode == ZSTD_dictMatchState && nbCompares) {
bestLength = ZSTD_DUBT_findBetterDictMatch(
ms, ip, iend,
- offsetPtr, bestLength, nbCompares,
+ offBasePtr, bestLength, nbCompares,
mls, dictMode);
}
assert(matchEndIdx > curr+8); /* ensure nextToUpdate is increased */
ms->nextToUpdate = matchEndIdx - 8; /* skip repetitive patterns */
if (bestLength >= MINMATCH) {
- U32 const mIndex = curr - (U32)OFFBASE_TO_OFFSET(*offsetPtr); (void)mIndex;
+ U32 const mIndex = curr - (U32)OFFBASE_TO_OFFSET(*offBasePtr); (void)mIndex;
DEBUGLOG(8, "ZSTD_DUBT_findBestMatch(%u) : found match of length %u and offsetCode %u (pos %u)",
- curr, (U32)bestLength, (U32)*offsetPtr, mIndex);
+ curr, (U32)bestLength, (U32)*offBasePtr, mIndex);
}
return bestLength;
}
@@ -381,14 +381,14 @@ ZSTD_DUBT_findBestMatch(ZSTD_matchState_t* ms,
FORCE_INLINE_TEMPLATE size_t
ZSTD_BtFindBestMatch( ZSTD_matchState_t* ms,
const BYTE* const ip, const BYTE* const iLimit,
- size_t* offsetPtr,
+ size_t* offBasePtr,
const U32 mls /* template */,
const ZSTD_dictMode_e dictMode)
{
DEBUGLOG(7, "ZSTD_BtFindBestMatch");
if (ip < ms->window.base + ms->nextToUpdate) return 0; /* skipped area */
ZSTD_updateDUBT(ms, ip, iLimit, mls);
- return ZSTD_DUBT_findBestMatch(ms, ip, iLimit, offsetPtr, mls, dictMode);
+ return ZSTD_DUBT_findBestMatch(ms, ip, iLimit, offBasePtr, mls, dictMode);
}
/***********************************
@@ -1337,10 +1337,10 @@ typedef struct {
static size_t ZSTD_BtFindBestMatch_##dictMode##_##mls( \
ZSTD_matchState_t* ms, \
const BYTE* ip, const BYTE* const iLimit, \
- size_t* offsetPtr) \
+ size_t* offBasePtr) \
{ \
assert(MAX(4, MIN(6, ms->cParams.minMatch)) == mls); \
- return ZSTD_BtFindBestMatch(ms, ip, iLimit, offsetPtr, mls, ZSTD_##dictMode); \
+ return ZSTD_BtFindBestMatch(ms, ip, iLimit, offBasePtr, mls, ZSTD_##dictMode);\
} \
static const ZSTD_LazyVTable ZSTD_BtVTable_##dictMode##_##mls = { \
ZSTD_BtFindBestMatch_##dictMode##_##mls \
@@ -1959,7 +1959,7 @@ size_t ZSTD_compressBlock_lazy_extDict_generic(
} }
/* search match, depth 1 */
- { size_t ofbCandidate=999999999;
+ { size_t ofbCandidate = 999999999;
size_t const ml2 = searchMax(ms, ip, iend, &ofbCandidate);
int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)ofbCandidate)); /* raw approx */
int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offBase) + 4);
@@ -1991,7 +1991,7 @@ size_t ZSTD_compressBlock_lazy_extDict_generic(
} }
/* search match, depth 2 */
- { size_t ofbCandidate=999999999;
+ { size_t ofbCandidate = 999999999;
size_t const ml2 = searchMax(ms, ip, iend, &ofbCandidate);
int const gain2 = (int)(ml2*4 - ZSTD_highbit32((U32)ofbCandidate)); /* raw approx */
int const gain1 = (int)(matchLength*4 - ZSTD_highbit32((U32)offBase) + 7);
diff --git a/tests/regression/.gitignore b/tests/regression/.gitignore
index 1b2618f415d..3da209d40e3 100644
--- a/tests/regression/.gitignore
+++ b/tests/regression/.gitignore
@@ -1,3 +1,4 @@
# regression test artifacts
data-cache
+cache
test
From 9e1b4828e56a028efa0efdd7c30a58e6dd48c8c1 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Fri, 7 Jan 2022 13:51:28 -0800
Subject: [PATCH 004/472] enforce a minimum price of 1 bit per literal in the
optimal parser
---
lib/compress/zstd_opt.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c
index 1b1ddad4289..2c48ae55e44 100644
--- a/lib/compress/zstd_opt.c
+++ b/lib/compress/zstd_opt.c
@@ -255,11 +255,13 @@ static U32 ZSTD_rawLiteralsCost(const BYTE* const literals, U32 const litLength,
return (litLength*6) * BITCOST_MULTIPLIER; /* 6 bit per literal - no statistic used */
/* dynamic statistics */
- { U32 price = litLength * optPtr->litSumBasePrice;
+ { U32 price = 0;
U32 u;
for (u=0; u < litLength; u++) {
+ U32 litPrice = optPtr->litSumBasePrice - WEIGHT(optPtr->litFreq[literals[u]], optLevel);
assert(WEIGHT(optPtr->litFreq[literals[u]], optLevel) <= optPtr->litSumBasePrice); /* literal cost should never be negative */
- price -= WEIGHT(optPtr->litFreq[literals[u]], optLevel);
+ if (litPrice < BITCOST_MULTIPLIER) litPrice = BITCOST_MULTIPLIER;
+ price += litPrice;
}
return price;
}
From ca0135c2fd562058a099a20bb0c2e569d354f08b Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Fri, 7 Jan 2022 14:37:53 -0800
Subject: [PATCH 005/472] new Formulation
presumes faster
---
lib/compress/zstd_opt.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c
index 2c48ae55e44..143e101a645 100644
--- a/lib/compress/zstd_opt.c
+++ b/lib/compress/zstd_opt.c
@@ -255,13 +255,14 @@ static U32 ZSTD_rawLiteralsCost(const BYTE* const literals, U32 const litLength,
return (litLength*6) * BITCOST_MULTIPLIER; /* 6 bit per literal - no statistic used */
/* dynamic statistics */
- { U32 price = 0;
+ { U32 price = optPtr->litSumBasePrice * litLength;
+ U32 const litPriceMax = optPtr->litSumBasePrice - BITCOST_MULTIPLIER;
U32 u;
+ assert(optPtr->litSumBasePrice >= BITCOST_MULTIPLIER);
for (u=0; u < litLength; u++) {
- U32 litPrice = optPtr->litSumBasePrice - WEIGHT(optPtr->litFreq[literals[u]], optLevel);
- assert(WEIGHT(optPtr->litFreq[literals[u]], optLevel) <= optPtr->litSumBasePrice); /* literal cost should never be negative */
- if (litPrice < BITCOST_MULTIPLIER) litPrice = BITCOST_MULTIPLIER;
- price += litPrice;
+ U32 litPrice = WEIGHT(optPtr->litFreq[literals[u]], optLevel);
+ if (UNLIKELY(litPrice > litPriceMax)) litPrice = litPriceMax;
+ price -= litPrice;
}
return price;
}
From 5595aec6294541075e8978d26cfb86dd3cefc3c3 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Fri, 7 Jan 2022 15:08:06 -0800
Subject: [PATCH 006/472] updated regression results
---
tests/regression/results.csv | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/tests/regression/results.csv b/tests/regression/results.csv
index f43129e6c2e..3385c504931 100644
--- a/tests/regression/results.csv
+++ b/tests/regression/results.csv
@@ -46,7 +46,7 @@ silesia, level 7, compress
silesia, level 9, compress cctx, 4543018
silesia, level 13, compress cctx, 4493990
silesia, level 16, compress cctx, 4359864
-silesia, level 19, compress cctx, 4296880
+silesia, level 19, compress cctx, 4296686
silesia, long distance mode, compress cctx, 4842075
silesia, multithreaded, compress cctx, 4842075
silesia, multithreaded long distance mode, compress cctx, 4842075
@@ -55,7 +55,7 @@ silesia, small hash log, compress
silesia, small chain log, compress cctx, 4912197
silesia, explicit params, compress cctx, 4794052
silesia, uncompressed literals, compress cctx, 4842075
-silesia, uncompressed literals optimal, compress cctx, 4296880
+silesia, uncompressed literals optimal, compress cctx, 4296686
silesia, huffman literals, compress cctx, 6172178
silesia, multithreaded with advanced params, compress cctx, 4842075
github, level -5, compress cctx, 204411
@@ -110,7 +110,7 @@ silesia, level 7, zstdcli,
silesia, level 9, zstdcli, 4543066
silesia, level 13, zstdcli, 4494038
silesia, level 16, zstdcli, 4359912
-silesia, level 19, zstdcli, 4296928
+silesia, level 19, zstdcli, 4296734
silesia, long distance mode, zstdcli, 4833785
silesia, multithreaded, zstdcli, 4842123
silesia, multithreaded long distance mode, zstdcli, 4833785
@@ -249,7 +249,7 @@ silesia, level 12 row 1, advanced
silesia, level 12 row 2, advanced one pass, 4503116
silesia, level 13, advanced one pass, 4493990
silesia, level 16, advanced one pass, 4359864
-silesia, level 19, advanced one pass, 4296880
+silesia, level 19, advanced one pass, 4296686
silesia, no source size, advanced one pass, 4842075
silesia, long distance mode, advanced one pass, 4833710
silesia, multithreaded, advanced one pass, 4842075
@@ -567,7 +567,7 @@ silesia, level 12 row 1, advanced
silesia, level 12 row 2, advanced one pass small out, 4503116
silesia, level 13, advanced one pass small out, 4493990
silesia, level 16, advanced one pass small out, 4359864
-silesia, level 19, advanced one pass small out, 4296880
+silesia, level 19, advanced one pass small out, 4296686
silesia, no source size, advanced one pass small out, 4842075
silesia, long distance mode, advanced one pass small out, 4833710
silesia, multithreaded, advanced one pass small out, 4842075
@@ -885,7 +885,7 @@ silesia, level 12 row 1, advanced
silesia, level 12 row 2, advanced streaming, 4503116
silesia, level 13, advanced streaming, 4493990
silesia, level 16, advanced streaming, 4359864
-silesia, level 19, advanced streaming, 4296880
+silesia, level 19, advanced streaming, 4296686
silesia, no source size, advanced streaming, 4842039
silesia, long distance mode, advanced streaming, 4833710
silesia, multithreaded, advanced streaming, 4842075
@@ -1195,10 +1195,10 @@ silesia, level 7, old stre
silesia, level 9, old streaming, 4543018
silesia, level 13, old streaming, 4493990
silesia, level 16, old streaming, 4359864
-silesia, level 19, old streaming, 4296880
+silesia, level 19, old streaming, 4296686
silesia, no source size, old streaming, 4842039
silesia, uncompressed literals, old streaming, 4842075
-silesia, uncompressed literals optimal, old streaming, 4296880
+silesia, uncompressed literals optimal, old streaming, 4296686
silesia, huffman literals, old streaming, 6179294
silesia.tar, level -5, old streaming, 7043687
silesia.tar, level -3, old streaming, 6671317
@@ -1297,7 +1297,7 @@ silesia, level 7, old stre
silesia, level 9, old streaming advanced, 4543018
silesia, level 13, old streaming advanced, 4493990
silesia, level 16, old streaming advanced, 4359864
-silesia, level 19, old streaming advanced, 4296880
+silesia, level 19, old streaming advanced, 4296686
silesia, no source size, old streaming advanced, 4842039
silesia, long distance mode, old streaming advanced, 4842075
silesia, multithreaded, old streaming advanced, 4842075
@@ -1307,7 +1307,7 @@ silesia, small hash log, old stre
silesia, small chain log, old streaming advanced, 4912197
silesia, explicit params, old streaming advanced, 4795452
silesia, uncompressed literals, old streaming advanced, 4842075
-silesia, uncompressed literals optimal, old streaming advanced, 4296880
+silesia, uncompressed literals optimal, old streaming advanced, 4296686
silesia, huffman literals, old streaming advanced, 6179294
silesia, multithreaded with advanced params, old streaming advanced, 4842075
silesia.tar, level -5, old streaming advanced, 7043687
From df5013b4632eda82a0cc745969ca305ac55dfe36 Mon Sep 17 00:00:00 2001
From: Yonatan Komornik
Date: Fri, 7 Jan 2022 15:55:19 -0800
Subject: [PATCH 007/472] ZSTD CLI: Use buffered output for improved
performance
---
programs/fileio.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/programs/fileio.c b/programs/fileio.c
index 379d334eb02..9945daa46d4 100644
--- a/programs/fileio.c
+++ b/programs/fileio.c
@@ -718,6 +718,17 @@ FIO_openDstFile(FIO_ctx_t* fCtx, FIO_prefs_t* const prefs,
if (f == NULL) {
DISPLAYLEVEL(1, "zstd: %s: %s\n", dstFileName, strerror(errno));
}
+ /* An increased buffer size can provide a significant performance boost on some platforms.
+ * Note that providing a NULL buf with a size that's not 0 is not defined in ANSI C, but is defined
+ * in an extension. There are three possibilities here -
+ * 1. Libc supports the extended version and everything is good.
+ * 2. Libc ignores the size when buf is NULL, in which case everything will continue as if we didn't
+ * call `setvbuf`.
+ * 3. We fail the call and execution continues but a warning message might be shown.
+ * In all cases due execution continues. For now, I believe that this is a more cost-effective
+ * solution than managing the buffers allocations ourselves (will require an API change). */
+ if(setvbuf(f, NULL, _IOFBF, 1 MB))
+ DISPLAYLEVEL(2, "Warning: setvbuf failed for %s\n", dstFileName);
return f;
}
}
From 51ab182bd4af347b08fd1b9df179078c4f596e24 Mon Sep 17 00:00:00 2001
From: "H.J. Lu"
Date: Tue, 11 Jan 2022 07:28:25 -0800
Subject: [PATCH 008/472] x86-64: Enable Intel CET
Intel Control-flow Enforcement Technology (CET):
https://en.wikipedia.org/wiki/Control-flow_integrity#Intel_Control-flow_Enforcement_Technology
requires that on Linux, all linker input files are marked as CET enabled
in .note.gnu.property section. For high-level language source codes,
.note.gnu.property section is added by compiler with the -fcf-protection
option. For assembly sources, include to add .note.gnu.property
section.
---
lib/common/portability_macros.h | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/lib/common/portability_macros.h b/lib/common/portability_macros.h
index 2143817f574..23a6508852b 100644
--- a/lib/common/portability_macros.h
+++ b/lib/common/portability_macros.h
@@ -134,4 +134,15 @@
# define ZSTD_ENABLE_ASM_X86_64_BMI2 0
#endif
+/*
+ * For x86 ELF targets, add .note.gnu.property section for Intel CET in
+ * assembly sources when CET is enabled.
+ */
+#if defined(__ELF__) && (defined(__x86_64__) || defined(__i386__)) \
+ && defined(__has_include)
+# if __has_include()
+# include
+# endif
+#endif
+
#endif /* ZSTD_PORTABILITY_MACROS_H */
From 7e50d1e8c167d8f3fb0ae01e05826da78acdccf9 Mon Sep 17 00:00:00 2001
From: Carl Woffenden
Date: Mon, 17 Jan 2022 18:59:54 +0100
Subject: [PATCH 009/472] Using faster Python script to amalgamate
---
build/single_file_libs/combine.py | 156 ++++++++++++++++++
.../create_single_file_decoder.sh | 7 +-
.../create_single_file_library.sh | 7 +-
3 files changed, 168 insertions(+), 2 deletions(-)
create mode 100755 build/single_file_libs/combine.py
diff --git a/build/single_file_libs/combine.py b/build/single_file_libs/combine.py
new file mode 100755
index 00000000000..994b36b1ddb
--- /dev/null
+++ b/build/single_file_libs/combine.py
@@ -0,0 +1,156 @@
+#!/usr/bin/env python3
+
+# Tool to bundle multiple C/C++ source files, inlining any includes.
+#
+# Author: Carl Woffenden, Numfum GmbH (this script is released under a CC0 license/Public Domain)
+
+import argparse, os, re, sys
+
+from pathlib import Path
+
+# File roots when searching (equivalent to -I paths for the compiler).
+roots = set()
+
+# File Path objects previously inlined.
+found = set()
+
+# Destination file object (or stdout if no output file was supplied).
+destn = None
+
+# Regex to handle the following type of file includes:
+#
+# #include "file"
+# #include "file"
+# # include "file"
+# #include "file"
+# #include "file" // comment
+# #include "file" // comment with quote "
+#
+# And all combinations of, as well as ignoring the following:
+#
+# #include
+# //#include "file"
+# /*#include "file"*/
+#
+# We don't try to catch errors since the compiler will do this (and the code is
+# expected to be valid before processing) and we don't care what follows the
+# file (whether it's a valid comment or not, since anything after the quoted
+# string is ignored)
+#
+include_regex = re.compile(r'^\s*#\s*include\s*"(.+?)"')
+
+# Simple tests to prove include_regex's cases.
+#
+def test_match_include():
+ if (include_regex.match('#include "file"') and
+ include_regex.match(' #include "file"') and
+ include_regex.match('# include "file"') and
+ include_regex.match('#include "file"') and
+ include_regex.match('#include "file" // comment')):
+ if (not include_regex.match('#include ') and
+ not include_regex.match('//#include "file"') and
+ not include_regex.match('/*#include "file"*/')):
+ found = include_regex.match('#include "file" // "')
+ if (found and found.group(1) == 'file'):
+ print('#include match valid')
+ return True
+ return False
+
+# Regex to handle "#pragma once" in various formats:
+#
+# #pragma once
+# #pragma once
+# # pragma once
+# #pragma once
+# #pragma once // comment
+#
+# Ignoring commented versions, same as include_regex.
+#
+pragma_regex = re.compile(r'^\s*#\s*pragma\s*once\s*')
+
+# Simple tests to prove pragma_regex's cases.
+#
+def text_match_pragma():
+ if (pragma_regex.match('#pragma once') and
+ pragma_regex.match(' #pragma once') and
+ pragma_regex.match('# pragma once') and
+ pragma_regex.match('#pragma once') and
+ pragma_regex.match('#pragma once // comment')):
+ if (not pragma_regex.match('//#pragma once') and
+ not pragma_regex.match('/*#pragma once*/')):
+ print('#pragma once match valid')
+ return True
+ return False
+
+# Finds 'file'. First the currently processing file's 'parent' path is looked at
+# for a match, followed by the list of 'root', returning a valid Path in
+# canonical form. If no match is found None is returned.
+#
+def resolve_include(parent: Path, file: str):
+ found = parent.joinpath(file).resolve();
+ if (found.is_file()):
+ return found
+ for root in roots:
+ found = root.joinpath(file).resolve()
+ if (found.is_file()):
+ return found
+ return None
+
+# Writes 'line' to the open file 'destn' (or stdout).
+#
+def write_line(line):
+ print(line, file=destn)
+
+# Logs 'line' to stderr.
+#
+def log_line(line):
+ print(line, file=sys.stderr)
+
+def add_file(file):
+ if (isinstance(file, Path) and file.is_file()):
+ log_line(f'Processing: {file}')
+ with file.open('r') as opened:
+ for line in opened:
+ line = line.rstrip('\n')
+ match_include = include_regex.match(line);
+ if (match_include):
+ inc_name = match_include.group(1)
+ resolved = resolve_include(file.parent, inc_name)
+ if (resolved not in found):
+ # The file was not previously encountered
+ found.add(resolved)
+ write_line(f'/**** start inlining {inc_name} ****/')
+ add_file(resolved)
+ write_line(f'/**** ended inlining {inc_name} ****/')
+ else:
+ write_line(f'/**** skipping file: {inc_name} ****/')
+ else:
+ if (not pragma_regex.match(line)):
+ write_line(line)
+ else:
+ log_line(f'Error: Unable to find: {file}')
+
+
+parser = argparse.ArgumentParser(description='Amalgamate Tool', epilog=f'example: {sys.argv[0]} -r ../my/path -r ../other/path -o out.c in.c')
+parser.add_argument('-r', '--root', action='append', type=Path, help='file root search path')
+parser.add_argument('-x', '--exclude', action='append', help='file to completely exclude from inlining')
+parser.add_argument('-k', '--keep', action='append', help='file to exclude from inlining but keep the include directive')
+parser.add_argument('-p', '--pragma', action='store_true', default=False, help='keep any "#pragma once" directives (removed by default)')
+parser.add_argument('-o', '--output', type=argparse.FileType('w'), help='output file (otherwise stdout)')
+parser.add_argument('input', type=Path, help='input file')
+args = parser.parse_args()
+
+# Resolve all of the root paths upfront (we'll halt here on invalid roots)
+if (args.root is not None):
+ for path in args.root:
+ roots.add(path.resolve(strict=True))
+
+try:
+ if (args.output is None):
+ destn = sys.stdout
+ else:
+ destn = args.output
+ add_file(args.input)
+finally:
+ if (destn is not None):
+ destn.close()
diff --git a/build/single_file_libs/create_single_file_decoder.sh b/build/single_file_libs/create_single_file_decoder.sh
index b5f5613ae2e..1c8841d1870 100755
--- a/build/single_file_libs/create_single_file_decoder.sh
+++ b/build/single_file_libs/create_single_file_decoder.sh
@@ -5,7 +5,12 @@ ZSTD_SRC_ROOT="../../lib"
# Amalgamate the sources
echo "Amalgamating files... this can take a while"
-./combine.sh -r "$ZSTD_SRC_ROOT" -o zstddeclib.c zstddeclib-in.c
+# Using the faster Python script if we have 3.8 or higher
+if python3 -c 'import sys; assert sys.version_info >= (3,8)' 2>/dev/null; then
+ ./combine.py -r "$ZSTD_SRC_ROOT" -o zstddeclib.c zstddeclib-in.c
+else
+ ./combine.sh -r "$ZSTD_SRC_ROOT" -o zstddeclib.c zstddeclib-in.c
+fi
# Did combining work?
if [ $? -ne 0 ]; then
echo "Combine script: FAILED"
diff --git a/build/single_file_libs/create_single_file_library.sh b/build/single_file_libs/create_single_file_library.sh
index 6f38526d5bf..9b2f22a9d95 100755
--- a/build/single_file_libs/create_single_file_library.sh
+++ b/build/single_file_libs/create_single_file_library.sh
@@ -5,7 +5,12 @@ ZSTD_SRC_ROOT="../../lib"
# Amalgamate the sources
echo "Amalgamating files... this can take a while"
-./combine.sh -r "$ZSTD_SRC_ROOT" -o zstd.c zstd-in.c
+# Using the faster Python script if we have 3.8 or higher
+if python3 -c 'import sys; assert sys.version_info >= (3,8)' 2>/dev/null; then
+ ./combine.py -r "$ZSTD_SRC_ROOT" -o zstd.c zstd-in.c
+else
+ ./combine.sh -r "$ZSTD_SRC_ROOT" -o zstd.c zstd-in.c
+fi
# Did combining work?
if [ $? -ne 0 ]; then
echo "Combine script: FAILED"
From 829ac2e9cedb0b360cfee9d7df88ed44e677ef20 Mon Sep 17 00:00:00 2001
From: Carl Woffenden
Date: Tue, 18 Jan 2022 11:43:01 +0100
Subject: [PATCH 010/472] Work-in-progress; annotated types, added docs, parsed
and resolved excluded files
---
build/single_file_libs/combine.py | 66 +++++++++++++++++++++++--------
1 file changed, 49 insertions(+), 17 deletions(-)
diff --git a/build/single_file_libs/combine.py b/build/single_file_libs/combine.py
index 994b36b1ddb..4dd3a072054 100755
--- a/build/single_file_libs/combine.py
+++ b/build/single_file_libs/combine.py
@@ -4,20 +4,27 @@
#
# Author: Carl Woffenden, Numfum GmbH (this script is released under a CC0 license/Public Domain)
-import argparse, os, re, sys
+import argparse, io, os, re, sys
from pathlib import Path
-# File roots when searching (equivalent to -I paths for the compiler).
+# Set of file roots when searching (equivalent to -I paths for the compiler).
roots = set()
-# File Path objects previously inlined.
+# Set of (canonical) file Path objects to exclude from inlining (and not only
+# exclude but to add a compiler error directive when they're encountered).
+excludes = set()
+
+# Set of (canonical) file Path objects to keep as include directives.
+keeps = set()
+
+# Set of file Path objects previously inlined.
found = set()
-# Destination file object (or stdout if no output file was supplied).
+# Destination file TextIOBase object (or stdout if no output file was supplied).
destn = None
-# Regex to handle the following type of file includes:
+# Compiled regex Patern to handle the following type of file includes:
#
# #include "file"
# #include "file"
@@ -41,7 +48,7 @@
# Simple tests to prove include_regex's cases.
#
-def test_match_include():
+def test_match_include() -> bool:
if (include_regex.match('#include "file"') and
include_regex.match(' #include "file"') and
include_regex.match('# include "file"') and
@@ -56,7 +63,7 @@ def test_match_include():
return True
return False
-# Regex to handle "#pragma once" in various formats:
+# Compiled regex Patern to handle "#pragma once" in various formats:
#
# #pragma once
# #pragma once
@@ -70,7 +77,7 @@ def test_match_include():
# Simple tests to prove pragma_regex's cases.
#
-def text_match_pragma():
+def text_match_pragma() -> bool:
if (pragma_regex.match('#pragma once') and
pragma_regex.match(' #pragma once') and
pragma_regex.match('# pragma once') and
@@ -83,10 +90,10 @@ def text_match_pragma():
return False
# Finds 'file'. First the currently processing file's 'parent' path is looked at
-# for a match, followed by the list of 'root', returning a valid Path in
+# for a match, followed by the list of 'root' paths, returning a valid Path in
# canonical form. If no match is found None is returned.
#
-def resolve_include(parent: Path, file: str):
+def resolve_include(parent: Path, file: str) -> Path:
found = parent.joinpath(file).resolve();
if (found.is_file()):
return found
@@ -98,15 +105,17 @@ def resolve_include(parent: Path, file: str):
# Writes 'line' to the open file 'destn' (or stdout).
#
-def write_line(line):
+def write_line(line: str) -> None:
print(line, file=destn)
# Logs 'line' to stderr.
#
-def log_line(line):
+def log_line(line: str) -> None:
print(line, file=sys.stderr)
-def add_file(file):
+# Adds the contents of 'file' with any of its includes inlined.
+#
+def add_file(file: Path) -> None:
if (isinstance(file, Path) and file.is_file()):
log_line(f'Processing: {file}')
with file.open('r') as opened:
@@ -129,8 +138,8 @@ def add_file(file):
write_line(line)
else:
log_line(f'Error: Unable to find: {file}')
-
+# Start here
parser = argparse.ArgumentParser(description='Amalgamate Tool', epilog=f'example: {sys.argv[0]} -r ../my/path -r ../other/path -o out.c in.c')
parser.add_argument('-r', '--root', action='append', type=Path, help='file root search path')
parser.add_argument('-x', '--exclude', action='append', help='file to completely exclude from inlining')
@@ -140,17 +149,40 @@ def add_file(file):
parser.add_argument('input', type=Path, help='input file')
args = parser.parse_args()
+# Fail early on an invalid input (and store it so we don't recurse)
+args.input = args.input.resolve(strict=True)
+found.add(args.input)
+
# Resolve all of the root paths upfront (we'll halt here on invalid roots)
-if (args.root is not None):
+if (args.root):
for path in args.root:
roots.add(path.resolve(strict=True))
+# Resolve the excluded files
+if (args.exclude):
+ for filename in args.exclude:
+ resolved = resolve_include(args.input.parent, filename)
+ if (resolved):
+ excludes.add(resolved)
+ else:
+ log_line(f'Warning: excluded file not found: {filename}')
+
+# And the files to keep
+if (args.keep):
+ for filename in args.keep:
+ resolved = resolve_include(args.input.parent, filename)
+ if (resolved):
+ keeps.add(resolved)
+ else:
+ log_line(f'Warning: kept #include not found: {filename}')
+
+# Then recursively process the input file
try:
- if (args.output is None):
+ if (not args.output):
destn = sys.stdout
else:
destn = args.output
add_file(args.input)
finally:
- if (destn is not None):
+ if (not destn):
destn.close()
From 8f1e51f99fa6ca6d132e53e58265b4d79d2fbb2b Mon Sep 17 00:00:00 2001
From: Carl Woffenden
Date: Tue, 18 Jan 2022 19:07:18 +0100
Subject: [PATCH 011/472] Feature parity with original shell script; needs
further testing
---
build/single_file_libs/combine.py | 123 ++++++++++++++++++------------
1 file changed, 75 insertions(+), 48 deletions(-)
diff --git a/build/single_file_libs/combine.py b/build/single_file_libs/combine.py
index 4dd3a072054..00a6f11f7e0 100755
--- a/build/single_file_libs/combine.py
+++ b/build/single_file_libs/combine.py
@@ -4,25 +4,30 @@
#
# Author: Carl Woffenden, Numfum GmbH (this script is released under a CC0 license/Public Domain)
-import argparse, io, os, re, sys
+import argparse, re, sys
from pathlib import Path
+from typing import Any, List, Optional, Pattern, Set, TextIO
# Set of file roots when searching (equivalent to -I paths for the compiler).
-roots = set()
+roots: Set[Path] = set()
# Set of (canonical) file Path objects to exclude from inlining (and not only
# exclude but to add a compiler error directive when they're encountered).
-excludes = set()
+excludes: Set[Path] = set()
# Set of (canonical) file Path objects to keep as include directives.
-keeps = set()
+keeps: Set[Path] = set()
-# Set of file Path objects previously inlined.
-found = set()
+# Whether to keep the #pragma once directives (unlikely, since this will result
+# in a warning, but the option is there).
+keep_pragma: bool = False
-# Destination file TextIOBase object (or stdout if no output file was supplied).
-destn = None
+# Destination file object (or stdout if no output file was supplied).
+destn:TextIO = sys.stdout
+
+# Set of file Path objects previously inlined (and to ignore if reencountering).
+found: Set[Path] = set()
# Compiled regex Patern to handle the following type of file includes:
#
@@ -44,7 +49,7 @@
# file (whether it's a valid comment or not, since anything after the quoted
# string is ignored)
#
-include_regex = re.compile(r'^\s*#\s*include\s*"(.+?)"')
+include_regex: Pattern = re.compile(r'^\s*#\s*include\s*"(.+?)"')
# Simple tests to prove include_regex's cases.
#
@@ -73,7 +78,7 @@ def test_match_include() -> bool:
#
# Ignoring commented versions, same as include_regex.
#
-pragma_regex = re.compile(r'^\s*#\s*pragma\s*once\s*')
+pragma_regex: Pattern = re.compile(r'^\s*#\s*pragma\s*once\s*')
# Simple tests to prove pragma_regex's cases.
#
@@ -93,8 +98,11 @@ def text_match_pragma() -> bool:
# for a match, followed by the list of 'root' paths, returning a valid Path in
# canonical form. If no match is found None is returned.
#
-def resolve_include(parent: Path, file: str) -> Path:
- found = parent.joinpath(file).resolve();
+def resolve_include(file: str, parent: Optional[Path] = None) -> Optional[Path]:
+ if (parent):
+ found = parent.joinpath(file).resolve();
+ else:
+ found = Path(file)
if (found.is_file()):
return found
for root in roots:
@@ -103,41 +111,75 @@ def resolve_include(parent: Path, file: str) -> Path:
return found
return None
-# Writes 'line' to the open file 'destn' (or stdout).
+# Helper to resolve lists of files. 'file_list' is passed in from the arguments
+# and each entry resolved to its canonical path (like any include entry, either
+# from the list of root paths or the owning file's 'parent', which in this case
+# is case is the input file). The results are stored in 'resolved'.
+#
+def resolve_files(file_list: Optional[List[str]], resolved: Set[Path], parent: Optional[Path] = None) -> None:
+ if (file_list):
+ for filename in file_list:
+ found = resolve_include(filename, parent)
+ if (found):
+ resolved.add(found)
+ else:
+ error_line(f'Warning: excluded file not found: {filename}')
+
+# Writes 'line' to the open 'destn' (or stdout).
#
def write_line(line: str) -> None:
print(line, file=destn)
-# Logs 'line' to stderr.
+# Logs 'line' to stderr. This is also used for general notifications that we
+# don't want to go to stdout (so the source can be piped).
#
-def log_line(line: str) -> None:
+def error_line(line: Any) -> None:
print(line, file=sys.stderr)
-# Adds the contents of 'file' with any of its includes inlined.
+# Inline the contents of 'file' (with any of its includes also inlined, etc.).
#
def add_file(file: Path) -> None:
- if (isinstance(file, Path) and file.is_file()):
- log_line(f'Processing: {file}')
+ if (file.is_file()):
+ error_line(f'Processing: {file}')
with file.open('r') as opened:
for line in opened:
line = line.rstrip('\n')
match_include = include_regex.match(line);
if (match_include):
+ # We have a quoted include directive so grab the file
inc_name = match_include.group(1)
- resolved = resolve_include(file.parent, inc_name)
- if (resolved not in found):
- # The file was not previously encountered
- found.add(resolved)
- write_line(f'/**** start inlining {inc_name} ****/')
- add_file(resolved)
- write_line(f'/**** ended inlining {inc_name} ****/')
+ resolved = resolve_include(inc_name, file.parent)
+ if (resolved):
+ if (resolved in excludes):
+ # The file was excluded so error if the source attempts to use it
+ write_line(f'#error Using excluded file: {inc_name}')
+ error_line(f'Excluding: {inc_name}')
+ else:
+ if (resolved not in found):
+ # The file was not previously encountered
+ found.add(resolved)
+ if (resolved in keeps):
+ # But the include was flagged to keep as included
+ write_line(f'/**** *NOT* inlining {inc_name} ****/')
+ write_line(line)
+ error_line('Not Inlining: {inc_name}')
+ else:
+ # The file was neither excluded nor seen before so inline it
+ write_line(f'/**** start inlining {inc_name} ****/')
+ add_file(resolved)
+ write_line(f'/**** ended inlining {inc_name} ****/')
+ else:
+ write_line(f'/**** skipping file: {inc_name} ****/')
else:
- write_line(f'/**** skipping file: {inc_name} ****/')
+ # The include file didn't resolve to a file
+ write_line(f'#error Unable to find: {inc_name}')
+ error_line(f'Error: Unable to find: {inc_name}')
else:
- if (not pragma_regex.match(line)):
+ # Skip any 'pragma once' directives, otherwise write the source line
+ if (keep_pragma or not pragma_regex.match(line)):
write_line(line)
else:
- log_line(f'Error: Unable to find: {file}')
+ error_line(f'Error: Invalid file: {file}')
# Start here
parser = argparse.ArgumentParser(description='Amalgamate Tool', epilog=f'example: {sys.argv[0]} -r ../my/path -r ../other/path -o out.c in.c')
@@ -158,29 +200,14 @@ def add_file(file: Path) -> None:
for path in args.root:
roots.add(path.resolve(strict=True))
-# Resolve the excluded files
-if (args.exclude):
- for filename in args.exclude:
- resolved = resolve_include(args.input.parent, filename)
- if (resolved):
- excludes.add(resolved)
- else:
- log_line(f'Warning: excluded file not found: {filename}')
-
-# And the files to keep
-if (args.keep):
- for filename in args.keep:
- resolved = resolve_include(args.input.parent, filename)
- if (resolved):
- keeps.add(resolved)
- else:
- log_line(f'Warning: kept #include not found: {filename}')
+# The remaining params: so resolve the excluded files and #pragma once directive
+resolve_files(args.exclude, excludes, args.input.parent)
+resolve_files(args.keep, keeps, args.input.parent)
+keep_pragma = args.pragma;
# Then recursively process the input file
try:
- if (not args.output):
- destn = sys.stdout
- else:
+ if (args.output):
destn = args.output
add_file(args.input)
finally:
From 7d90f0b520fda6cd9def6cd248c54b075717e948 Mon Sep 17 00:00:00 2001
From: Carl Woffenden
Date: Wed, 19 Jan 2022 11:32:53 +0100
Subject: [PATCH 012/472] Test and tidy
Made the Python more Python-like. Added notes and general tidy. Tested exclusions and building with various options. Tested all scripts.
---
build/single_file_libs/build_library_test.sh | 2 +-
build/single_file_libs/combine.py | 232 ++++++++++--------
build/single_file_libs/combine.sh | 1 +
.../create_single_file_decoder.sh | 6 +-
.../create_single_file_library.sh | 6 +-
build/single_file_libs/zstd-in.c | 2 +-
build/single_file_libs/zstddeclib-in.c | 2 +-
7 files changed, 133 insertions(+), 118 deletions(-)
diff --git a/build/single_file_libs/build_library_test.sh b/build/single_file_libs/build_library_test.sh
index 7fb99656bcc..31545fc3fe3 100755
--- a/build/single_file_libs/build_library_test.sh
+++ b/build/single_file_libs/build_library_test.sh
@@ -69,7 +69,7 @@ fi
echo "Single file library creation script: PASSED"
# Copy the header to here (for the tests)
-cp "$ZSTD_SRC_ROOT/zstd.h" zstd.h
+cp "$ZSTD_SRC_ROOT/zstd.h" examples/zstd.h
# Compile the generated output
cc -Wall -Wextra -Werror -Wshadow -pthread -I. -Os -g0 -o $OUT_FILE zstd.c examples/roundtrip.c
diff --git a/build/single_file_libs/combine.py b/build/single_file_libs/combine.py
index 00a6f11f7e0..0538ccb6992 100755
--- a/build/single_file_libs/combine.py
+++ b/build/single_file_libs/combine.py
@@ -2,6 +2,18 @@
# Tool to bundle multiple C/C++ source files, inlining any includes.
#
+# Note: there are two types of exclusion options: the '-x' flag, which besides
+# excluding a file also adds an #error directive in place of the #include, and
+# the '-k' flag, which keeps the #include and doesn't inline the file. The
+# intended use cases are: '-x' for files that would normally be #if'd out, so
+# features that 100% won't be used in the amalgamated file, for which every
+# occurrence adds the error, and '-k' for headers that we wish to manually
+# include, such as a project's public API, for which occurrences after the first
+# are removed.
+#
+# Todo: the error handling could be better, which currently throws and halts
+# (which is functional just not very friendly).
+#
# Author: Carl Woffenden, Numfum GmbH (this script is released under a CC0 license/Public Domain)
import argparse, re, sys
@@ -31,18 +43,18 @@
# Compiled regex Patern to handle the following type of file includes:
#
-# #include "file"
-# #include "file"
-# # include "file"
-# #include "file"
-# #include "file" // comment
-# #include "file" // comment with quote "
+# #include "file"
+# #include "file"
+# # include "file"
+# #include "file"
+# #include "file" // comment
+# #include "file" // comment with quote "
#
# And all combinations of, as well as ignoring the following:
#
-# #include
-# //#include "file"
-# /*#include "file"*/
+# #include
+# //#include "file"
+# /*#include "file"*/
#
# We don't try to catch errors since the compiler will do this (and the code is
# expected to be valid before processing) and we don't care what follows the
@@ -54,27 +66,27 @@
# Simple tests to prove include_regex's cases.
#
def test_match_include() -> bool:
- if (include_regex.match('#include "file"') and
- include_regex.match(' #include "file"') and
- include_regex.match('# include "file"') and
- include_regex.match('#include "file"') and
- include_regex.match('#include "file" // comment')):
- if (not include_regex.match('#include ') and
- not include_regex.match('//#include "file"') and
- not include_regex.match('/*#include "file"*/')):
- found = include_regex.match('#include "file" // "')
- if (found and found.group(1) == 'file'):
- print('#include match valid')
- return True
- return False
+ if (include_regex.match('#include "file"') and
+ include_regex.match(' #include "file"') and
+ include_regex.match('# include "file"') and
+ include_regex.match('#include "file"') and
+ include_regex.match('#include "file" // comment')):
+ if (not include_regex.match('#include ') and
+ not include_regex.match('//#include "file"') and
+ not include_regex.match('/*#include "file"*/')):
+ found = include_regex.match('#include "file" // "')
+ if (found and found.group(1) == 'file'):
+ print('#include match valid')
+ return True
+ return False
# Compiled regex Patern to handle "#pragma once" in various formats:
#
-# #pragma once
-# #pragma once
-# # pragma once
-# #pragma once
-# #pragma once // comment
+# #pragma once
+# #pragma once
+# # pragma once
+# #pragma once
+# #pragma once // comment
#
# Ignoring commented versions, same as include_regex.
#
@@ -83,103 +95,105 @@ def test_match_include() -> bool:
# Simple tests to prove pragma_regex's cases.
#
def text_match_pragma() -> bool:
- if (pragma_regex.match('#pragma once') and
- pragma_regex.match(' #pragma once') and
- pragma_regex.match('# pragma once') and
- pragma_regex.match('#pragma once') and
- pragma_regex.match('#pragma once // comment')):
- if (not pragma_regex.match('//#pragma once') and
- not pragma_regex.match('/*#pragma once*/')):
- print('#pragma once match valid')
- return True
- return False
+ if (pragma_regex.match('#pragma once') and
+ pragma_regex.match(' #pragma once') and
+ pragma_regex.match('# pragma once') and
+ pragma_regex.match('#pragma once') and
+ pragma_regex.match('#pragma once // comment')):
+ if (not pragma_regex.match('//#pragma once') and
+ not pragma_regex.match('/*#pragma once*/')):
+ print('#pragma once match valid')
+ return True
+ return False
# Finds 'file'. First the currently processing file's 'parent' path is looked at
# for a match, followed by the list of 'root' paths, returning a valid Path in
# canonical form. If no match is found None is returned.
#
def resolve_include(file: str, parent: Optional[Path] = None) -> Optional[Path]:
- if (parent):
- found = parent.joinpath(file).resolve();
- else:
- found = Path(file)
- if (found.is_file()):
- return found
- for root in roots:
- found = root.joinpath(file).resolve()
- if (found.is_file()):
- return found
- return None
+ if (parent):
+ found = parent.joinpath(file).resolve();
+ else:
+ found = Path(file)
+ if (found.is_file()):
+ return found
+ for root in roots:
+ found = root.joinpath(file).resolve()
+ if (found.is_file()):
+ return found
+ return None
# Helper to resolve lists of files. 'file_list' is passed in from the arguments
# and each entry resolved to its canonical path (like any include entry, either
# from the list of root paths or the owning file's 'parent', which in this case
# is case is the input file). The results are stored in 'resolved'.
#
-def resolve_files(file_list: Optional[List[str]], resolved: Set[Path], parent: Optional[Path] = None) -> None:
- if (file_list):
- for filename in file_list:
- found = resolve_include(filename, parent)
- if (found):
- resolved.add(found)
- else:
- error_line(f'Warning: excluded file not found: {filename}')
+def resolve_excluded_files(file_list: Optional[List[str]], resolved: Set[Path], parent: Optional[Path] = None) -> None:
+ if (file_list):
+ for filename in file_list:
+ found = resolve_include(filename, parent)
+ if (found):
+ resolved.add(found)
+ else:
+ error_line(f'Warning: excluded file not found: {filename}')
# Writes 'line' to the open 'destn' (or stdout).
#
def write_line(line: str) -> None:
- print(line, file=destn)
+ print(line, file=destn)
# Logs 'line' to stderr. This is also used for general notifications that we
# don't want to go to stdout (so the source can be piped).
#
def error_line(line: Any) -> None:
- print(line, file=sys.stderr)
+ print(line, file=sys.stderr)
# Inline the contents of 'file' (with any of its includes also inlined, etc.).
#
-def add_file(file: Path) -> None:
- if (file.is_file()):
- error_line(f'Processing: {file}')
- with file.open('r') as opened:
- for line in opened:
- line = line.rstrip('\n')
- match_include = include_regex.match(line);
- if (match_include):
- # We have a quoted include directive so grab the file
- inc_name = match_include.group(1)
- resolved = resolve_include(inc_name, file.parent)
- if (resolved):
- if (resolved in excludes):
- # The file was excluded so error if the source attempts to use it
- write_line(f'#error Using excluded file: {inc_name}')
- error_line(f'Excluding: {inc_name}')
- else:
- if (resolved not in found):
- # The file was not previously encountered
- found.add(resolved)
- if (resolved in keeps):
- # But the include was flagged to keep as included
- write_line(f'/**** *NOT* inlining {inc_name} ****/')
- write_line(line)
- error_line('Not Inlining: {inc_name}')
- else:
- # The file was neither excluded nor seen before so inline it
- write_line(f'/**** start inlining {inc_name} ****/')
- add_file(resolved)
- write_line(f'/**** ended inlining {inc_name} ****/')
- else:
- write_line(f'/**** skipping file: {inc_name} ****/')
- else:
- # The include file didn't resolve to a file
- write_line(f'#error Unable to find: {inc_name}')
- error_line(f'Error: Unable to find: {inc_name}')
- else:
- # Skip any 'pragma once' directives, otherwise write the source line
- if (keep_pragma or not pragma_regex.match(line)):
- write_line(line)
- else:
- error_line(f'Error: Invalid file: {file}')
+def add_file(file: Path, file_name: str = None) -> None:
+ if (file.is_file()):
+ if (not file_name):
+ file_name = file.name
+ error_line(f'Processing: {file_name}')
+ with file.open('r') as opened:
+ for line in opened:
+ line = line.rstrip('\n')
+ match_include = include_regex.match(line);
+ if (match_include):
+ # We have a quoted include directive so grab the file
+ inc_name = match_include.group(1)
+ resolved = resolve_include(inc_name, file.parent)
+ if (resolved):
+ if (resolved in excludes):
+ # The file was excluded so error if the compiler uses it
+ write_line(f'#error Using excluded file: {inc_name}')
+ error_line(f'Excluding: {inc_name}')
+ else:
+ if (resolved not in found):
+ # The file was not previously encountered
+ found.add(resolved)
+ if (resolved in keeps):
+ # But the include was flagged to keep as included
+ write_line(f'/**** *NOT* inlining {inc_name} ****/')
+ write_line(line)
+ error_line(f'Not inlining: {inc_name}')
+ else:
+ # The file was neither excluded nor seen before so inline it
+ write_line(f'/**** start inlining {inc_name} ****/')
+ add_file(resolved, inc_name)
+ write_line(f'/**** ended inlining {inc_name} ****/')
+ else:
+ write_line(f'/**** skipping file: {inc_name} ****/')
+ else:
+ # The include file didn't resolve to a file
+ write_line(f'#error Unable to find: {inc_name}')
+ error_line(f'Error: Unable to find: {inc_name}')
+ else:
+ # Skip any 'pragma once' directives, otherwise write the source line
+ if (keep_pragma or not pragma_regex.match(line)):
+ write_line(line)
+ else:
+ error_line(f'Error: Invalid file: {file}')
# Start here
parser = argparse.ArgumentParser(description='Amalgamate Tool', epilog=f'example: {sys.argv[0]} -r ../my/path -r ../other/path -o out.c in.c')
@@ -197,19 +211,19 @@ def add_file(file: Path) -> None:
# Resolve all of the root paths upfront (we'll halt here on invalid roots)
if (args.root):
- for path in args.root:
- roots.add(path.resolve(strict=True))
+ for path in args.root:
+ roots.add(path.resolve(strict=True))
# The remaining params: so resolve the excluded files and #pragma once directive
-resolve_files(args.exclude, excludes, args.input.parent)
-resolve_files(args.keep, keeps, args.input.parent)
+resolve_excluded_files(args.exclude, excludes, args.input.parent)
+resolve_excluded_files(args.keep, keeps, args.input.parent)
keep_pragma = args.pragma;
# Then recursively process the input file
try:
- if (args.output):
- destn = args.output
- add_file(args.input)
+ if (args.output):
+ destn = args.output
+ add_file(args.input)
finally:
- if (not destn):
- destn.close()
+ if (not destn):
+ destn.close()
diff --git a/build/single_file_libs/combine.sh b/build/single_file_libs/combine.sh
index 8eac4f9eb14..674a5a275c8 100755
--- a/build/single_file_libs/combine.sh
+++ b/build/single_file_libs/combine.sh
@@ -200,6 +200,7 @@ if [ -n "$1" ]; then
printf "" > "$DESTN"
fi
test_deps
+ log_line "Processing using the slower shell script; this might take a while"
add_file "$1"
else
echo "Input file not found: \"$1\""
diff --git a/build/single_file_libs/create_single_file_decoder.sh b/build/single_file_libs/create_single_file_decoder.sh
index 1c8841d1870..3c0c577df5f 100755
--- a/build/single_file_libs/create_single_file_decoder.sh
+++ b/build/single_file_libs/create_single_file_decoder.sh
@@ -4,12 +4,12 @@
ZSTD_SRC_ROOT="../../lib"
# Amalgamate the sources
-echo "Amalgamating files... this can take a while"
+echo "Amalgamating files..."
# Using the faster Python script if we have 3.8 or higher
if python3 -c 'import sys; assert sys.version_info >= (3,8)' 2>/dev/null; then
- ./combine.py -r "$ZSTD_SRC_ROOT" -o zstddeclib.c zstddeclib-in.c
+ ./combine.py -r "$ZSTD_SRC_ROOT" -x legacy/zstd_legacy.h -o zstddeclib.c zstddeclib-in.c
else
- ./combine.sh -r "$ZSTD_SRC_ROOT" -o zstddeclib.c zstddeclib-in.c
+ ./combine.sh -r "$ZSTD_SRC_ROOT" -x legacy/zstd_legacy.h -o zstddeclib.c zstddeclib-in.c
fi
# Did combining work?
if [ $? -ne 0 ]; then
diff --git a/build/single_file_libs/create_single_file_library.sh b/build/single_file_libs/create_single_file_library.sh
index 9b2f22a9d95..a6f71f0f085 100755
--- a/build/single_file_libs/create_single_file_library.sh
+++ b/build/single_file_libs/create_single_file_library.sh
@@ -4,12 +4,12 @@
ZSTD_SRC_ROOT="../../lib"
# Amalgamate the sources
-echo "Amalgamating files... this can take a while"
+echo "Amalgamating files..."
# Using the faster Python script if we have 3.8 or higher
if python3 -c 'import sys; assert sys.version_info >= (3,8)' 2>/dev/null; then
- ./combine.py -r "$ZSTD_SRC_ROOT" -o zstd.c zstd-in.c
+ ./combine.py -r "$ZSTD_SRC_ROOT" -x legacy/zstd_legacy.h -o zstd.c zstd-in.c
else
- ./combine.sh -r "$ZSTD_SRC_ROOT" -o zstd.c zstd-in.c
+ ./combine.sh -r "$ZSTD_SRC_ROOT" -x legacy/zstd_legacy.h -o zstd.c zstd-in.c
fi
# Did combining work?
if [ $? -ne 0 ]; then
diff --git a/build/single_file_libs/zstd-in.c b/build/single_file_libs/zstd-in.c
index b694681396a..c22e4cd971e 100644
--- a/build/single_file_libs/zstd-in.c
+++ b/build/single_file_libs/zstd-in.c
@@ -4,7 +4,7 @@
*
* Generate using:
* \code
- * combine.sh -r ../../lib -o zstd.c zstd-in.c
+ * combine.sh -r ../../lib -x legacy/zstd_legacy.h -o zstd.c zstd-in.c
* \endcode
*/
/*
diff --git a/build/single_file_libs/zstddeclib-in.c b/build/single_file_libs/zstddeclib-in.c
index 72abe61343d..42c510736b9 100644
--- a/build/single_file_libs/zstddeclib-in.c
+++ b/build/single_file_libs/zstddeclib-in.c
@@ -4,7 +4,7 @@
*
* Generate using:
* \code
- * combine.sh -r ../../lib -o zstddeclib.c zstddeclib-in.c
+ * combine.sh -r ../../lib -x legacy/zstd_legacy.h -o zstddeclib.c zstddeclib-in.c
* \endcode
*/
/*
From dd7d29a19c7ac00f02f524c0f8822c691b7ed64c Mon Sep 17 00:00:00 2001
From: Carl Woffenden
Date: Wed, 19 Jan 2022 15:57:33 +0100
Subject: [PATCH 013/472] Updated README
---
build/single_file_libs/README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/build/single_file_libs/README.md b/build/single_file_libs/README.md
index 1705b769b85..64c973a68d6 100644
--- a/build/single_file_libs/README.md
+++ b/build/single_file_libs/README.md
@@ -12,7 +12,7 @@ This is the most common use case. The decompression library is small, adding, fo
Create `zstddeclib.c` from the Zstd source using:
```
cd zstd/build/single_file_libs
-./combine.sh -r ../../lib -o zstddeclib.c zstddeclib-in.c
+python3 combine.py -r ../../lib -x legacy/zstd_legacy.h -o zstddeclib.c zstddeclib-in.c
```
Then add the resulting file to your project (see the [example files](examples)).
@@ -26,7 +26,7 @@ The same tool can amalgamate the entire Zstd library for ease of adding both com
Create `zstd.c` from the Zstd source using:
```
cd zstd/build/single_file_libs
-./combine.sh -r ../../lib -o zstd.c zstd-in.c
+python3 combine.py -r ../../lib -x legacy/zstd_legacy.h -k zstd.h -o zstd.c zstd-in.c
```
It's possible to create a compressor-only library but since the decompressor is so small in comparison this doesn't bring much of a gain (but for the curious, simply remove the files in the _decompress_ section at the end of `zstd-in.c`).
From 5fd6ddaf8b633ea5999cd593b0671cb2bbe8f8d5 Mon Sep 17 00:00:00 2001
From: Carl Woffenden
Date: Wed, 19 Jan 2022 16:56:03 +0100
Subject: [PATCH 014/472] Fixed bugs found in other projects
When testing amalgamating other projects it was found: invalid Unicode errors were tripping Python's text IO, and the header search order appears differs from the shell version.
---
build/single_file_libs/combine.py | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)
diff --git a/build/single_file_libs/combine.py b/build/single_file_libs/combine.py
index 0538ccb6992..3d1018d5b3a 100755
--- a/build/single_file_libs/combine.py
+++ b/build/single_file_libs/combine.py
@@ -106,21 +106,21 @@ def text_match_pragma() -> bool:
return True
return False
-# Finds 'file'. First the currently processing file's 'parent' path is looked at
-# for a match, followed by the list of 'root' paths, returning a valid Path in
+# Finds 'file'. First the list of 'root' paths are searched, followed by the
+# the currently processing file's 'parent' path, returning a valid Path in
# canonical form. If no match is found None is returned.
#
def resolve_include(file: str, parent: Optional[Path] = None) -> Optional[Path]:
+ for root in roots:
+ found = root.joinpath(file).resolve()
+ if (found.is_file()):
+ return found
if (parent):
found = parent.joinpath(file).resolve();
else:
found = Path(file)
if (found.is_file()):
return found
- for root in roots:
- found = root.joinpath(file).resolve()
- if (found.is_file()):
- return found
return None
# Helper to resolve lists of files. 'file_list' is passed in from the arguments
@@ -150,12 +150,17 @@ def error_line(line: Any) -> None:
# Inline the contents of 'file' (with any of its includes also inlined, etc.).
#
+# Note: text encoding errors are ignored and replaced with ? when reading the
+# input files. This isn't ideal, but it's more than likely in the comments than
+# code and a) the text editor has probably also failed to read the same content,
+# and b) the compiler probably did too.
+#
def add_file(file: Path, file_name: str = None) -> None:
if (file.is_file()):
if (not file_name):
file_name = file.name
error_line(f'Processing: {file_name}')
- with file.open('r') as opened:
+ with file.open('r', errors='replace') as opened:
for line in opened:
line = line.rstrip('\n')
match_include = include_regex.match(line);
From 566ebce347aef4f7ea48e4c87fa58934a5944bd8 Mon Sep 17 00:00:00 2001
From: Carl Woffenden
Date: Wed, 19 Jan 2022 17:33:20 +0100
Subject: [PATCH 015/472] Python style change
Co-authored-by: Alexandre Bury
---
build/single_file_libs/combine.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build/single_file_libs/combine.py b/build/single_file_libs/combine.py
index 3d1018d5b3a..125e7d427a8 100755
--- a/build/single_file_libs/combine.py
+++ b/build/single_file_libs/combine.py
@@ -36,7 +36,7 @@
keep_pragma: bool = False
# Destination file object (or stdout if no output file was supplied).
-destn:TextIO = sys.stdout
+destn: TextIO = sys.stdout
# Set of file Path objects previously inlined (and to ignore if reencountering).
found: Set[Path] = set()
From 786263ea85c9325fc03eca3a8d78bfe1c7a19547 Mon Sep 17 00:00:00 2001
From: Carl Woffenden
Date: Wed, 19 Jan 2022 17:48:10 +0100
Subject: [PATCH 016/472] Suggestion from code review
---
build/single_file_libs/combine.py | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/build/single_file_libs/combine.py b/build/single_file_libs/combine.py
index 125e7d427a8..55cf32c5ea2 100755
--- a/build/single_file_libs/combine.py
+++ b/build/single_file_libs/combine.py
@@ -41,6 +41,18 @@
# Set of file Path objects previously inlined (and to ignore if reencountering).
found: Set[Path] = set()
+# Compiled regex Patern to handle "#pragma once" in various formats:
+#
+# #pragma once
+# #pragma once
+# # pragma once
+# #pragma once
+# #pragma once // comment
+#
+# Ignoring commented versions, same as include_regex.
+#
+pragma_regex: Pattern = re.compile(r'^\s*#\s*pragma\s*once\s*')
+
# Compiled regex Patern to handle the following type of file includes:
#
# #include "file"
@@ -80,18 +92,6 @@ def test_match_include() -> bool:
return True
return False
-# Compiled regex Patern to handle "#pragma once" in various formats:
-#
-# #pragma once
-# #pragma once
-# # pragma once
-# #pragma once
-# #pragma once // comment
-#
-# Ignoring commented versions, same as include_regex.
-#
-pragma_regex: Pattern = re.compile(r'^\s*#\s*pragma\s*once\s*')
-
# Simple tests to prove pragma_regex's cases.
#
def text_match_pragma() -> bool:
From dc983e7d68aa2a78ceace5a73f3f29ba86b1383b Mon Sep 17 00:00:00 2001
From: Carl Woffenden
Date: Wed, 19 Jan 2022 18:05:35 +0100
Subject: [PATCH 017/472] Typo (and missing commit)
---
build/single_file_libs/combine.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/build/single_file_libs/combine.py b/build/single_file_libs/combine.py
index 55cf32c5ea2..6443edad3bc 100755
--- a/build/single_file_libs/combine.py
+++ b/build/single_file_libs/combine.py
@@ -94,7 +94,7 @@ def test_match_include() -> bool:
# Simple tests to prove pragma_regex's cases.
#
-def text_match_pragma() -> bool:
+def test_match_pragma() -> bool:
if (pragma_regex.match('#pragma once') and
pragma_regex.match(' #pragma once') and
pragma_regex.match('# pragma once') and
@@ -230,5 +230,5 @@ def add_file(file: Path, file_name: str = None) -> None:
destn = args.output
add_file(args.input)
finally:
- if (not destn):
+ if (destn):
destn.close()
From e74ca7979e021960aabce58c8307d8b827ec97d1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wojciech=20Mu=C5=82a?=
Date: Wed, 19 Jan 2022 18:29:54 +0100
Subject: [PATCH 018/472] Simplify
HUF_decompress4X2_usingDTable_internal_bmi2_asm_loop
Get rid of three divisions. The original expression was:
opmin := min((oend0 - op0) / 10, (oend1 - op1) / 10, (oend2 - op2) / 10, (oend3 - op3) / 10)
r15 := min(r15, opmin)
The division by 10 can be moved outside the `min`:
opmin := min(oend0 - op0, oend1 - op1, oend2 - op2, oend3 - op3)
r15 := min(r15, opmin/10)
---
lib/decompress/huf_decompress_amd64.S | 41 ++++++++++-----------------
1 file changed, 15 insertions(+), 26 deletions(-)
diff --git a/lib/decompress/huf_decompress_amd64.S b/lib/decompress/huf_decompress_amd64.S
index 49589cb6114..3f0e5c26c7d 100644
--- a/lib/decompress/huf_decompress_amd64.S
+++ b/lib/decompress/huf_decompress_amd64.S
@@ -427,41 +427,30 @@ HUF_decompress4X2_usingDTable_internal_bmi2_asm_loop:
/* r15 = (ip0 - ilimit) / 7 */
movq %rdx, %r15
- movabsq $-3689348814741910323, %rdx
- movq 8(%rsp), %rax /* rax = oend0 */
- subq %op0, %rax /* rax = oend0 - op0 */
- mulq %rdx
- shrq $3, %rdx /* rdx = rax / 10 */
-
- /* r15 = min(%rdx, %r15) */
- cmpq %rdx, %r15
- cmova %rdx, %r15
+ /* r15 = min(r15, min(oend0 - op0, oend1 - op1, oend2 - op2, oend3 - op3) / 10) */
+ movq 8(%rsp), %rax /* rax = oend0 */
+ subq %op0, %rax /* rax = oend0 - op0 */
+ movq 16(%rsp), %rdx /* rdx = oend1 */
+ subq %op1, %rdx /* rdx = oend1 - op1 */
- movabsq $-3689348814741910323, %rdx
- movq 16(%rsp), %rax /* rax = oend1 */
- subq %op1, %rax /* rax = oend1 - op1 */
- mulq %rdx
- shrq $3, %rdx /* rdx = rax / 10 */
-
- /* r15 = min(%rdx, %r15) */
- cmpq %rdx, %r15
- cmova %rdx, %r15
+ cmpq %rax, %rdx
+ cmova %rax, %rdx /* rdx = min(%rdx, %rax) */
- movabsq $-3689348814741910323, %rdx
movq 24(%rsp), %rax /* rax = oend2 */
subq %op2, %rax /* rax = oend2 - op2 */
- mulq %rdx
- shrq $3, %rdx /* rdx = rax / 10 */
- /* r15 = min(%rdx, %r15) */
- cmpq %rdx, %r15
- cmova %rdx, %r15
+ cmpq %rax, %rdx
+ cmova %rax, %rdx /* rdx = min(%rdx, %rax) */
- movabsq $-3689348814741910323, %rdx
movq 32(%rsp), %rax /* rax = oend3 */
subq %op3, %rax /* rax = oend3 - op3 */
+
+ cmpq %rax, %rdx
+ cmova %rax, %rdx /* rdx = min(%rdx, %rax) */
+
+ movabsq $-3689348814741910323, %rax
mulq %rdx
- shrq $3, %rdx /* rdx = rax / 10 */
+ shrq $3, %rdx /* rdx = rdx / 10 */
/* r15 = min(%rdx, %r15) */
cmpq %rdx, %r15
From 3f181b61927b0f517b11ddec2ca43490b6de693f Mon Sep 17 00:00:00 2001
From: Carl Woffenden
Date: Thu, 20 Jan 2022 14:50:31 +0100
Subject: [PATCH 019/472] More descriptive exclusion error; updated docs and
copyright
---
build/single_file_libs/combine.py | 2 +-
build/single_file_libs/combine.sh | 2 +-
build/single_file_libs/zstd-in.c | 8 ++++++--
build/single_file_libs/zstddeclib-in.c | 8 ++++++--
4 files changed, 14 insertions(+), 6 deletions(-)
diff --git a/build/single_file_libs/combine.py b/build/single_file_libs/combine.py
index 6443edad3bc..badd68a91af 100755
--- a/build/single_file_libs/combine.py
+++ b/build/single_file_libs/combine.py
@@ -171,7 +171,7 @@ def add_file(file: Path, file_name: str = None) -> None:
if (resolved):
if (resolved in excludes):
# The file was excluded so error if the compiler uses it
- write_line(f'#error Using excluded file: {inc_name}')
+ write_line(f'#error Using excluded file: {inc_name} (re-amalgamate source to fix)')
error_line(f'Excluding: {inc_name}')
else:
if (resolved not in found):
diff --git a/build/single_file_libs/combine.sh b/build/single_file_libs/combine.sh
index 674a5a275c8..a4933bf21d6 100755
--- a/build/single_file_libs/combine.sh
+++ b/build/single_file_libs/combine.sh
@@ -130,7 +130,7 @@ add_file() {
local res_inc="$(resolve_include "$srcdir" "$inc")"
if list_has_item "$XINCS" "$inc"; then
# The file was excluded so error if the source attempts to use it
- write_line "#error Using excluded file: $inc"
+ write_line "#error Using excluded file: $inc (re-amalgamate source to fix)"
log_line "Excluding: $inc"
else
if ! list_has_item "$FOUND" "$res_inc"; then
diff --git a/build/single_file_libs/zstd-in.c b/build/single_file_libs/zstd-in.c
index c22e4cd971e..eecd9a688ee 100644
--- a/build/single_file_libs/zstd-in.c
+++ b/build/single_file_libs/zstd-in.c
@@ -4,11 +4,11 @@
*
* Generate using:
* \code
- * combine.sh -r ../../lib -x legacy/zstd_legacy.h -o zstd.c zstd-in.c
+ * python combine.py -r ../../lib -x legacy/zstd_legacy.h -o zstd.c zstd-in.c
* \endcode
*/
/*
- * Copyright (c) 2016-2021, Yann Collet, Facebook, Inc.
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under both the BSD-style license (found in the
@@ -28,6 +28,10 @@
* Note: the undefs for xxHash allow Zstd's implementation to coincide with with
* standalone xxHash usage (with global defines).
*
+ * Note: if you enable ZSTD_LEGACY_SUPPORT the combine.py script will need
+ * re-running without the "-x legacy/zstd_legacy.h" option (it excludes the
+ * legacy support at the source level).
+ *
* Note: multithreading is enabled for all platforms apart from Emscripten.
*/
#define DEBUGLEVEL 0
diff --git a/build/single_file_libs/zstddeclib-in.c b/build/single_file_libs/zstddeclib-in.c
index 42c510736b9..d0343c54a46 100644
--- a/build/single_file_libs/zstddeclib-in.c
+++ b/build/single_file_libs/zstddeclib-in.c
@@ -4,11 +4,11 @@
*
* Generate using:
* \code
- * combine.sh -r ../../lib -x legacy/zstd_legacy.h -o zstddeclib.c zstddeclib-in.c
+ * python combine.py -r ../../lib -x legacy/zstd_legacy.h -o zstddeclib.c zstddeclib-in.c
* \endcode
*/
/*
- * Copyright (c) 2016-2021, Yann Collet, Facebook, Inc.
+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under both the BSD-style license (found in the
@@ -27,6 +27,10 @@
*
* Note: the undefs for xxHash allow Zstd's implementation to coincide with with
* standalone xxHash usage (with global defines).
+ *
+ * Note: if you enable ZSTD_LEGACY_SUPPORT the combine.py script will need
+ * re-running without the "-x legacy/zstd_legacy.h" option (it excludes the
+ * legacy support at the source level).
*/
#define DEBUGLEVEL 0
#define MEM_MODULE
From 7cf80cb94c23867f7496f6a847f7d4e712dafb86 Mon Sep 17 00:00:00 2001
From: "W. Felix Handte"
Date: Wed, 12 Jan 2022 14:02:27 -0500
Subject: [PATCH 020/472] Add GitHub Action Checking that Zstd Runs
Successfully Under CET
---
.github/workflows/dev-short-tests.yml | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/.github/workflows/dev-short-tests.yml b/.github/workflows/dev-short-tests.yml
index f06dfdb614f..dfff4cbed45 100644
--- a/.github/workflows/dev-short-tests.yml
+++ b/.github/workflows/dev-short-tests.yml
@@ -390,6 +390,27 @@ jobs:
DIR
.\fuzzer.exe -T2m
+ intel-cet-compatibility:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - name: Build Zstd
+ run: |
+ make -j zstd V=1
+ readelf -n zstd
+ - name: Get Intel SDE
+ run: |
+ curl -LO https://downloadmirror.intel.com/684899/sde-external-9.0.0-2021-11-07-lin.tar.xz
+ tar xJvf sde-external-9.0.0-2021-11-07-lin.tar.xz
+ - name: Configure Permissions
+ run: |
+ echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
+ - name: Run Under SDE
+ run: |
+ sde-external-9.0.0-2021-11-07-lin/sde -cet -cet-raise 0 -cet-endbr-exe -cet-stderr -cet-abort -- ./zstd -b3
+
+
+
# This test currently fails on Github Actions specifically.
# Possible reason : TTY emulation.
# Note that the same test works fine locally and on travisCI.
From d6fcdd123cbf05735c894c81599a0926c3427d4f Mon Sep 17 00:00:00 2001
From: "H.J. Lu"
Date: Tue, 11 Jan 2022 10:36:44 -0800
Subject: [PATCH 021/472] x86: Append -z cet-report=error to LDFLAGS
Append -z cet-report=error to LDFLAGS if -fcf-protection is enabled by
default in compiler to catch the missing Intel CET marker:
compiling multi-threaded dynamic library 1.5.1
/usr/local/bin/ld: obj/conf_f408b4c825de923ffc88f7f21b6884b1/dynamic/huf_decompress_amd64.o: error: missing IBT and SHSTK properties
collect2: error: ld returned 1 exit status
...
LINK obj/conf_dbc0b41e36c44111bb0bb918e093d7c1/zstd
/usr/local/bin/ld: obj/conf_dbc0b41e36c44111bb0bb918e093d7c1/huf_decompress_amd64.o: error: missing IBT and SHSTK properties
collect2: error: ld returned 1 exit status
---
lib/libzstd.mk | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/lib/libzstd.mk b/lib/libzstd.mk
index 6e9a643954b..df298d78978 100644
--- a/lib/libzstd.mk
+++ b/lib/libzstd.mk
@@ -114,6 +114,10 @@ CFLAGS += -Qunused-arguments -Wa,--noexecstack
endif
endif
+ifeq ($(shell echo "int main(int argc, char* argv[]) { (void)argc; (void)argv; return 0; }" | $(CC) $(FLAGS) -z cet-report=error -x c -Werror - -o $(VOID) 2>$(VOID) && echo 1 || echo 0),1)
+LDFLAGS += -z cet-report=error
+endif
+
HAVE_COLORNEVER = $(shell echo a | grep --color=never a > /dev/null 2> /dev/null && echo 1 || echo 0)
GREP_OPTIONS ?=
ifeq ($HAVE_COLORNEVER, 1)
From f936dd89cb6fcf5dd2b03db0ca93dc76181838a9 Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Thu, 20 Jan 2022 11:46:09 -0700
Subject: [PATCH 022/472] Minor lint fix
---
lib/compress/zstdmt_compress.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c
index 6bc14b035e1..b0b337e4299 100644
--- a/lib/compress/zstdmt_compress.c
+++ b/lib/compress/zstdmt_compress.c
@@ -266,11 +266,11 @@ static void ZSTDMT_releaseBuffer(ZSTDMT_bufferPool* bufPool, buffer_t buf)
* 1 buffer for input loading
* 1 buffer for "next input" when submitting current one
* 1 buffer stuck in queue */
-#define BUF_POOL_MAX_NB_BUFFERS(nbWorkers) 2*nbWorkers + 3
+#define BUF_POOL_MAX_NB_BUFFERS(nbWorkers) (2*(nbWorkers) + 3)
/* After a worker releases its rawSeqStore, it is immediately ready for reuse.
* So we only need one seq buffer per worker. */
-#define SEQ_POOL_MAX_NB_BUFFERS(nbWorkers) nbWorkers
+#define SEQ_POOL_MAX_NB_BUFFERS(nbWorkers) (nbWorkers)
/* ===== Seq Pool Wrapper ====== */
From fa9cb4510ac26cdad607e19c72133e4c4961b189 Mon Sep 17 00:00:00 2001
From: "W. Felix Handte"
Date: Thu, 20 Jan 2022 17:36:28 -0500
Subject: [PATCH 023/472] Trigger Release Artifact Generation on Publish
We previously triggered release artifact generation on release creation. We
sometimes observed that the action failed to run. I hypothesized that we were
hitting rate limiting or something. I just stumbled across [this documentat-
ion](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#release), which says:
> Note: Workflows are not triggered for the `created`, `edited`, or `deleted`
> activity types for draft releases. When you create your release through the
> GitHub browser UI, your release may automatically be saved as a draft.
This must have been what was happening. This commit therefore changes the
trigger to the `published` activity. This should be more reliable.
This does have the unfortunate side effect that artifacts won't be generated
or attached until *after* the release has been published, which is what I was
trying to avoid by using the `created` activity. Oh well.
---
.github/workflows/publish-release-artifacts.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/publish-release-artifacts.yml b/.github/workflows/publish-release-artifacts.yml
index 952cb26b7e9..a87a75efa49 100644
--- a/.github/workflows/publish-release-artifacts.yml
+++ b/.github/workflows/publish-release-artifacts.yml
@@ -3,7 +3,7 @@ name: publish-release-artifacts
on:
release:
types:
- - created
+ - published
jobs:
publish-release-artifacts:
From 1598e6c634ac041c1928c1be00dfa3484d282397 Mon Sep 17 00:00:00 2001
From: Yonatan Komornik <11005061+yoniko@users.noreply.github.com>
Date: Fri, 21 Jan 2022 13:55:41 -0800
Subject: [PATCH 024/472] Async write for decompression (#2975)
* Async IO decompression:
- Added --[no-]asyncio flag for CLI decompression.
- Replaced dstBuffer in decompression with a pool of write jobs.
- Added an ability to execute write jobs in a separate thread.
- Added an ability to wait (join) on all jobs in a thread pool (queued and running).
---
build/meson/programs/meson.build | 23 +-
lib/common/pool.c | 20 +-
lib/common/pool.h | 6 +
programs/fileio.c | 390 ++++++++++++++++++++++++-------
programs/fileio.h | 1 +
programs/zstdcli.c | 11 +-
tests/playTests.sh | 38 +++
7 files changed, 390 insertions(+), 99 deletions(-)
diff --git a/build/meson/programs/meson.build b/build/meson/programs/meson.build
index 4181030c2ee..0ae93fc107c 100644
--- a/build/meson/programs/meson.build
+++ b/build/meson/programs/meson.build
@@ -20,14 +20,24 @@ zstd_programs_sources = [join_paths(zstd_rootdir, 'programs/zstdcli.c'),
join_paths(zstd_rootdir, 'programs/dibio.c'),
join_paths(zstd_rootdir, 'programs/zstdcli_trace.c'),
# needed due to use of private symbol + -fvisibility=hidden
- join_paths(zstd_rootdir, 'lib/common/xxhash.c')]
+ join_paths(zstd_rootdir, 'lib/common/xxhash.c'),
+ join_paths(zstd_rootdir, 'lib/common/pool.c'),
+ join_paths(zstd_rootdir, 'lib/common/zstd_common.c'),
+ join_paths(zstd_rootdir, 'lib/common/error_private.c')]
+zstd_deps = [ libzstd_dep ]
zstd_c_args = libzstd_debug_cflags
+
+zstd_frugal_deps = [ libzstd_dep ]
+zstd_frugal_c_args = [ '-DZSTD_NOBENCH', '-DZSTD_NODICT', '-DZSTD_NOTRACE' ]
+
if use_multi_thread
+ zstd_deps += [ thread_dep ]
zstd_c_args += [ '-DZSTD_MULTITHREAD' ]
+ zstd_frugal_deps += [ thread_dep ]
+ zstd_frugal_c_args += [ '-DZSTD_MULTITHREAD' ]
endif
-zstd_deps = [ libzstd_dep ]
if use_zlib
zstd_deps += [ zlib_dep ]
zstd_c_args += [ '-DZSTD_GZCOMPRESS', '-DZSTD_GZDECOMPRESS' ]
@@ -69,14 +79,17 @@ zstd = executable('zstd',
zstd_frugal_sources = [join_paths(zstd_rootdir, 'programs/zstdcli.c'),
join_paths(zstd_rootdir, 'programs/timefn.c'),
join_paths(zstd_rootdir, 'programs/util.c'),
- join_paths(zstd_rootdir, 'programs/fileio.c')]
+ join_paths(zstd_rootdir, 'programs/fileio.c'),
+ join_paths(zstd_rootdir, 'lib/common/pool.c'),
+ join_paths(zstd_rootdir, 'lib/common/zstd_common.c'),
+ join_paths(zstd_rootdir, 'lib/common/error_private.c')]
# Minimal target, with only zstd compression and decompression.
# No bench. No legacy.
executable('zstd-frugal',
zstd_frugal_sources,
- dependencies: libzstd_dep,
- c_args: [ '-DZSTD_NOBENCH', '-DZSTD_NODICT', '-DZSTD_NOTRACE' ],
+ dependencies: zstd_frugal_deps,
+ c_args: zstd_frugal_c_args,
install: true)
install_data(join_paths(zstd_rootdir, 'programs/zstdgrep'),
diff --git a/lib/common/pool.c b/lib/common/pool.c
index 2e37cdd73c8..5c1d07d356e 100644
--- a/lib/common/pool.c
+++ b/lib/common/pool.c
@@ -96,9 +96,7 @@ static void* POOL_thread(void* opaque) {
/* If the intended queue size was 0, signal after finishing job */
ZSTD_pthread_mutex_lock(&ctx->queueMutex);
ctx->numThreadsBusy--;
- if (ctx->queueSize == 1) {
- ZSTD_pthread_cond_signal(&ctx->queuePushCond);
- }
+ ZSTD_pthread_cond_signal(&ctx->queuePushCond);
ZSTD_pthread_mutex_unlock(&ctx->queueMutex);
}
} /* for (;;) */
@@ -190,6 +188,17 @@ void POOL_free(POOL_ctx *ctx) {
ZSTD_customFree(ctx, ctx->customMem);
}
+/*! POOL_joinJobs() :
+ * Waits for all queued jobs to finish executing.
+ */
+void POOL_joinJobs(POOL_ctx* ctx) {
+ ZSTD_pthread_mutex_lock(&ctx->queueMutex);
+ while(!ctx->queueEmpty || ctx->numThreadsBusy > 0) {
+ ZSTD_pthread_cond_wait(&ctx->queuePushCond, &ctx->queueMutex);
+ }
+ ZSTD_pthread_mutex_unlock(&ctx->queueMutex);
+}
+
void ZSTD_freeThreadPool (ZSTD_threadPool* pool) {
POOL_free (pool);
}
@@ -330,6 +339,11 @@ void POOL_free(POOL_ctx* ctx) {
(void)ctx;
}
+void POOL_joinJobs(POOL_ctx* ctx){
+ assert(!ctx || ctx == &g_poolCtx);
+ (void)ctx;
+}
+
int POOL_resize(POOL_ctx* ctx, size_t numThreads) {
(void)ctx; (void)numThreads;
return 0;
diff --git a/lib/common/pool.h b/lib/common/pool.h
index 0ebde1805db..b86a3452e5c 100644
--- a/lib/common/pool.h
+++ b/lib/common/pool.h
@@ -38,6 +38,12 @@ POOL_ctx* POOL_create_advanced(size_t numThreads, size_t queueSize,
*/
void POOL_free(POOL_ctx* ctx);
+
+/*! POOL_joinJobs() :
+ * Waits for all queued jobs to finish executing.
+ */
+void POOL_joinJobs(POOL_ctx* ctx);
+
/*! POOL_resize() :
* Expands or shrinks pool's number of threads.
* This is more efficient than releasing + creating a new context,
diff --git a/programs/fileio.c b/programs/fileio.c
index 5338fa62955..b85e0806bcf 100644
--- a/programs/fileio.c
+++ b/programs/fileio.c
@@ -34,6 +34,8 @@
#include /* INT_MAX */
#include
#include "timefn.h" /* UTIL_getTime, UTIL_clockSpanMicro */
+#include "../lib/common/pool.h"
+#include "../lib/common/threading.h"
#if defined (_MSC_VER)
# include
@@ -325,6 +327,7 @@ struct FIO_prefs_s {
/* IO preferences */
U32 removeSrcFile;
U32 overwrite;
+ U32 asyncIO;
/* Computation resources preferences */
unsigned memLimit;
@@ -395,6 +398,7 @@ FIO_prefs_t* FIO_createPreferences(void)
ret->literalCompressionMode = ZSTD_ps_auto;
ret->excludeCompressedFiles = 0;
ret->allowBlockDevices = 0;
+ ret->asyncIO = 0;
return ret;
}
@@ -558,6 +562,10 @@ void FIO_setContentSize(FIO_prefs_t* const prefs, int value)
prefs->contentSize = value != 0;
}
+void FIO_setAsyncIOFlag(FIO_prefs_t* const prefs, unsigned value) {
+ prefs->asyncIO = value;
+}
+
/* FIO_ctx_t functions */
void FIO_setHasStdoutOutput(FIO_ctx_t* const fCtx, int value) {
@@ -1798,7 +1806,7 @@ FIO_compressFilename_srcFile(FIO_ctx_t* const fCtx,
static const char* checked_index(const char* options[], size_t length, size_t index) {
assert(index < length);
- // Necessary to avoid warnings since -O3 will omit the above `assert`
+ /* Necessary to avoid warnings since -O3 will omit the above `assert` */
(void) length;
return options[index];
}
@@ -2000,16 +2008,124 @@ int FIO_compressMultipleFilenames(FIO_ctx_t* const fCtx,
/* **************************************************************************
* Decompression
***************************************************************************/
+#define DECOMPRESSION_MAX_WRITE_JOBS (10)
+
+typedef struct {
+ /* These struct fields should be set only on creation and not changed afterwards */
+ POOL_ctx* writerPool;
+ int totalWriteJobs;
+ FIO_prefs_t* prefs;
+
+ /* Controls the file we currently write to, make changes only by using provided utility functions */
+ FILE* dstFile;
+ unsigned storedSkips;
+
+ /* The jobs and availableWriteJobs fields are access by both the main and writer threads and should
+ * only be mutated after locking the mutex */
+ ZSTD_pthread_mutex_t writeJobsMutex;
+ void* jobs[DECOMPRESSION_MAX_WRITE_JOBS];
+ int availableWriteJobs;
+} write_pool_ctx_t;
+
+typedef struct {
+ /* These fields are automaically set and shouldn't be changed by non WritePool code. */
+ write_pool_ctx_t *ctx;
+ FILE* dstFile;
+ void *buffer;
+ size_t bufferSize;
+
+ /* This field should be changed before a job is queued for execution and should contain the number
+ * of bytes to write from the buffer. */
+ size_t usedBufferSize;
+} write_job_t;
+
typedef struct {
void* srcBuffer;
size_t srcBufferSize;
size_t srcBufferLoaded;
- void* dstBuffer;
- size_t dstBufferSize;
ZSTD_DStream* dctx;
- FILE* dstFile;
+ write_pool_ctx_t *writePoolCtx;
} dRess_t;
+static write_job_t *FIO_createWriteJob(write_pool_ctx_t *ctx) {
+ void *buffer;
+ write_job_t *job;
+ job = (write_job_t*) malloc(sizeof(write_job_t));
+ buffer = malloc(ZSTD_DStreamOutSize());
+ if(!job || !buffer)
+ EXM_THROW(101, "Allocation error : not enough memory");
+ job->buffer = buffer;
+ job->bufferSize = ZSTD_DStreamOutSize();
+ job->usedBufferSize = 0;
+ job->dstFile = NULL;
+ job->ctx = ctx;
+ return job;
+}
+
+/* WritePool_createThreadPool:
+ * Creates a thread pool and a mutex for threaded write pool.
+ * Displays warning if asyncio is requested but MT isn't available. */
+static void WritePool_createThreadPool(write_pool_ctx_t *ctx, const FIO_prefs_t *prefs) {
+ ctx->writerPool = NULL;
+ if(prefs->asyncIO) {
+#ifdef ZSTD_MULTITHREAD
+ if (ZSTD_pthread_mutex_init(&ctx->writeJobsMutex, NULL))
+ EXM_THROW(102, "Failed creating write jobs mutex");
+ /* We want DECOMPRESSION_MAX_WRITE_JOBS-2 queue items because we need to always have 1 free buffer to
+ * decompress into and 1 buffer that's actively written to disk and owned by the writing thread. */
+ assert(DECOMPRESSION_MAX_WRITE_JOBS >= 2);
+ ctx->writerPool = POOL_create(1, DECOMPRESSION_MAX_WRITE_JOBS - 2);
+ if (!ctx->writerPool)
+ EXM_THROW(103, "Failed creating writer thread pool");
+#else
+ DISPLAYLEVEL(2, "Note : asyncio decompression is disabled (lack of multithreading support) \n");
+#endif
+ }
+}
+
+/* WritePool_create:
+ * Allocates and sets and a new write pool including its included jobs. */
+static write_pool_ctx_t* WritePool_create(FIO_prefs_t* const prefs) {
+ write_pool_ctx_t *ctx;
+ int i;
+ ctx = (write_pool_ctx_t*) malloc(sizeof(write_pool_ctx_t));
+ if(!ctx)
+ EXM_THROW(100, "Allocation error : not enough memory");
+ WritePool_createThreadPool(ctx, prefs);
+ ctx->prefs = prefs;
+ ctx->totalWriteJobs = ctx->writerPool ? DECOMPRESSION_MAX_WRITE_JOBS : 1;
+ ctx->availableWriteJobs = ctx->totalWriteJobs;
+ for(i=0; i < ctx->availableWriteJobs; i++) {
+ ctx->jobs[i] = FIO_createWriteJob(ctx);
+ }
+ ctx->storedSkips = 0;
+ ctx->dstFile = NULL;
+ return ctx;
+}
+
+/* WritePool_free:
+ * Release a previously allocated write thread pool. Makes sure all takss are done and released. */
+static void WritePool_free(write_pool_ctx_t* ctx) {
+ int i=0;
+ if(ctx->writerPool) {
+ /* Make sure we finish all tasks and then free the resources */
+ POOL_joinJobs(ctx->writerPool);
+ /* Make sure we are not leaking jobs */
+ assert(ctx->availableWriteJobs==ctx->totalWriteJobs);
+ POOL_free(ctx->writerPool);
+ ZSTD_pthread_mutex_destroy(&ctx->writeJobsMutex);
+ }
+ assert(ctx->dstFile==NULL);
+ assert(ctx->storedSkips==0);
+ for(i=0; iavailableWriteJobs; i++) {
+ write_job_t* job = (write_job_t*) ctx->jobs[i];
+ free(job->buffer);
+ free(job);
+ }
+ free(ctx);
+}
+
+
static dRess_t FIO_createDResources(FIO_prefs_t* const prefs, const char* dictFileName)
{
dRess_t ress;
@@ -2027,9 +2143,7 @@ static dRess_t FIO_createDResources(FIO_prefs_t* const prefs, const char* dictFi
ress.srcBufferSize = ZSTD_DStreamInSize();
ress.srcBuffer = malloc(ress.srcBufferSize);
- ress.dstBufferSize = ZSTD_DStreamOutSize();
- ress.dstBuffer = malloc(ress.dstBufferSize);
- if (!ress.srcBuffer || !ress.dstBuffer)
+ if (!ress.srcBuffer)
EXM_THROW(61, "Allocation error : not enough memory");
/* dictionary */
@@ -2039,6 +2153,8 @@ static dRess_t FIO_createDResources(FIO_prefs_t* const prefs, const char* dictFi
free(dictBuffer);
}
+ ress.writePoolCtx = WritePool_create(prefs);
+
return ress;
}
@@ -2046,9 +2162,16 @@ static void FIO_freeDResources(dRess_t ress)
{
CHECK( ZSTD_freeDStream(ress.dctx) );
free(ress.srcBuffer);
- free(ress.dstBuffer);
+ WritePool_free(ress.writePoolCtx);
}
+/* FIO_consumeDSrcBuffer:
+ * Consumes len bytes from srcBuffer's start and moves the remaining data and srcBufferLoaded accordingly. */
+static void FIO_consumeDSrcBuffer(dRess_t *ress, size_t len) {
+ assert(ress->srcBufferLoaded >= len);
+ ress->srcBufferLoaded -= len;
+ memmove(ress->srcBuffer, (char *)ress->srcBuffer + len, ress->srcBufferLoaded);
+}
/** FIO_fwriteSparse() :
* @return : storedSkips,
@@ -2148,6 +2271,106 @@ FIO_fwriteSparseEnd(const FIO_prefs_t* const prefs, FILE* file, unsigned storedS
} }
}
+/* WritePool_releaseWriteJob:
+ * Releases an acquired job back to the pool. Doesn't execute the job. */
+static void WritePool_releaseWriteJob(write_job_t *job) {
+ write_pool_ctx_t *ctx = job->ctx;
+ if(ctx->writerPool) {
+ ZSTD_pthread_mutex_lock(&ctx->writeJobsMutex);
+ assert(ctx->availableWriteJobs < DECOMPRESSION_MAX_WRITE_JOBS);
+ ctx->jobs[ctx->availableWriteJobs++] = job;
+ ZSTD_pthread_mutex_unlock(&ctx->writeJobsMutex);
+ } else {
+ ctx->availableWriteJobs++;
+ }
+}
+
+/* WritePool_acquireWriteJob:
+ * Returns an available write job to be used for a future write. */
+static write_job_t* WritePool_acquireWriteJob(write_pool_ctx_t *ctx) {
+ write_job_t *job;
+ assert(ctx->dstFile!=NULL || ctx->prefs->testMode);
+ if(ctx->writerPool) {
+ ZSTD_pthread_mutex_lock(&ctx->writeJobsMutex);
+ assert(ctx->availableWriteJobs > 0);
+ job = (write_job_t*) ctx->jobs[--ctx->availableWriteJobs];
+ ZSTD_pthread_mutex_unlock(&ctx->writeJobsMutex);
+ } else {
+ assert(ctx->availableWriteJobs==1);
+ ctx->availableWriteJobs--;
+ job = (write_job_t*)ctx->jobs[0];
+ }
+ job->usedBufferSize = 0;
+ job->dstFile = ctx->dstFile;
+ return job;
+}
+
+/* WritePool_executeWriteJob:
+ * Executes a write job synchronously. Can be used as a function for a thread pool. */
+static void WritePool_executeWriteJob(void* opaque){
+ write_job_t* job = (write_job_t*) opaque;
+ write_pool_ctx_t* ctx = job->ctx;
+ ctx->storedSkips = FIO_fwriteSparse(job->dstFile, job->buffer, job->usedBufferSize, ctx->prefs, ctx->storedSkips);
+ WritePool_releaseWriteJob(job);
+}
+
+/* WritePool_queueWriteJob:
+ * Queues a write job for execution.
+ * Make sure to set `usedBufferSize` to the wanted length before call.
+ * The queued job shouldn't be used directly after queueing it. */
+static void WritePool_queueWriteJob(write_job_t *job) {
+ write_pool_ctx_t* ctx = job->ctx;
+ if(ctx->writerPool)
+ POOL_add(ctx->writerPool, WritePool_executeWriteJob, job);
+ else
+ WritePool_executeWriteJob(job);
+}
+
+/* WritePool_queueAndReacquireWriteJob:
+ * Queues a write job for execution and acquires a new one.
+ * After execution `job`'s pointed value would change to the newly acquired job.
+ * Make sure to set `usedBufferSize` to the wanted length before call.
+ * The queued job shouldn't be used directly after queueing it. */
+static void WritePool_queueAndReacquireWriteJob(write_job_t **job) {
+ WritePool_queueWriteJob(*job);
+ *job = WritePool_acquireWriteJob((*job)->ctx);
+}
+
+/* WritePool_sparseWriteEnd:
+ * Ends sparse writes to the current dstFile.
+ * Blocks on completion of all current write jobs before executing. */
+static void WritePool_sparseWriteEnd(write_pool_ctx_t* ctx) {
+ assert(ctx != NULL);
+ if(ctx->writerPool)
+ POOL_joinJobs(ctx->writerPool);
+ FIO_fwriteSparseEnd(ctx->prefs, ctx->dstFile, ctx->storedSkips);
+ ctx->storedSkips = 0;
+}
+
+/* WritePool_setDstFile:
+ * Sets the destination file for future files in the pool.
+ * Requires completion of all queues write jobs and release of all otherwise acquired jobs.
+ * Also requires ending of sparse write if a previous file was used in sparse mode. */
+static void WritePool_setDstFile(write_pool_ctx_t *ctx, FILE* dstFile) {
+ assert(ctx!=NULL);
+ /* We can change the dst file only if we have finished writing */
+ if(ctx->writerPool)
+ POOL_joinJobs(ctx->writerPool);
+ assert(ctx->storedSkips == 0);
+ assert(ctx->availableWriteJobs == ctx->totalWriteJobs);
+ ctx->dstFile = dstFile;
+}
+
+/* WritePool_closeDstFile:
+ * Ends sparse write and closes the writePool's current dstFile and sets the dstFile to NULL.
+ * Requires completion of all queues write jobs and release of all otherwise acquired jobs. */
+static int WritePool_closeDstFile(write_pool_ctx_t *ctx) {
+ FILE *dstFile = ctx->dstFile;
+ assert(dstFile!=NULL || ctx->prefs->testMode!=0);
+ WritePool_sparseWriteEnd(ctx);
+ WritePool_setDstFile(ctx, NULL);
+ return fclose(dstFile);
+}
/** FIO_passThrough() : just copy input into output, for compatibility with gzip -df mode
@return : 0 (no error) */
@@ -2224,7 +2447,7 @@ FIO_decompressZstdFrame(FIO_ctx_t* const fCtx, dRess_t* ress, FILE* finput,
U64 alreadyDecoded) /* for multi-frames streams */
{
U64 frameSize = 0;
- U32 storedSkips = 0;
+ write_job_t *writeJob = WritePool_acquireWriteJob(ress->writePoolCtx);
/* display last 20 characters only */
{ size_t const srcFileLength = strlen(srcFileName);
@@ -2244,7 +2467,7 @@ FIO_decompressZstdFrame(FIO_ctx_t* const fCtx, dRess_t* ress, FILE* finput,
/* Main decompression Loop */
while (1) {
ZSTD_inBuffer inBuff = { ress->srcBuffer, ress->srcBufferLoaded, 0 };
- ZSTD_outBuffer outBuff= { ress->dstBuffer, ress->dstBufferSize, 0 };
+ ZSTD_outBuffer outBuff= { writeJob->buffer, writeJob->bufferSize, 0 };
size_t const readSizeHint = ZSTD_decompressStream(ress->dctx, &outBuff, &inBuff);
const int displayLevel = (g_display_prefs.progressSetting == FIO_ps_always) ? 1 : 2;
UTIL_HumanReadableSize_t const hrs = UTIL_makeHumanReadableSize(alreadyDecoded+frameSize);
@@ -2256,7 +2479,8 @@ FIO_decompressZstdFrame(FIO_ctx_t* const fCtx, dRess_t* ress, FILE* finput,
}
/* Write block */
- storedSkips = FIO_fwriteSparse(ress->dstFile, ress->dstBuffer, outBuff.pos, prefs, storedSkips);
+ writeJob->usedBufferSize = outBuff.pos;
+ WritePool_queueAndReacquireWriteJob(&writeJob);
frameSize += outBuff.pos;
if (fCtx->nbFilesTotal > 1) {
size_t srcFileNameSize = strlen(srcFileName);
@@ -2273,10 +2497,7 @@ FIO_decompressZstdFrame(FIO_ctx_t* const fCtx, dRess_t* ress, FILE* finput,
srcFileName, hrs.precision, hrs.value, hrs.suffix);
}
- if (inBuff.pos > 0) {
- memmove(ress->srcBuffer, (char*)ress->srcBuffer + inBuff.pos, inBuff.size - inBuff.pos);
- ress->srcBufferLoaded -= inBuff.pos;
- }
+ FIO_consumeDSrcBuffer(ress, inBuff.pos);
if (readSizeHint == 0) break; /* end of frame */
@@ -2294,7 +2515,8 @@ FIO_decompressZstdFrame(FIO_ctx_t* const fCtx, dRess_t* ress, FILE* finput,
ress->srcBufferLoaded += readSize;
} } }
- FIO_fwriteSparseEnd(prefs, ress->dstFile, storedSkips);
+ WritePool_releaseWriteJob(writeJob);
+ WritePool_sparseWriteEnd(ress->writePoolCtx);
return frameSize;
}
@@ -2302,15 +2524,13 @@ FIO_decompressZstdFrame(FIO_ctx_t* const fCtx, dRess_t* ress, FILE* finput,
#ifdef ZSTD_GZDECOMPRESS
static unsigned long long
-FIO_decompressGzFrame(dRess_t* ress, FILE* srcFile,
- const FIO_prefs_t* const prefs,
- const char* srcFileName)
+FIO_decompressGzFrame(dRess_t* ress, FILE* srcFile, const char* srcFileName)
{
unsigned long long outFileSize = 0;
z_stream strm;
int flush = Z_NO_FLUSH;
int decodingError = 0;
- unsigned storedSkips = 0;
+ write_job_t *writeJob = NULL;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
@@ -2321,8 +2541,9 @@ FIO_decompressGzFrame(dRess_t* ress, FILE* srcFile,
if (inflateInit2(&strm, 15 /* maxWindowLogSize */ + 16 /* gzip only */) != Z_OK)
return FIO_ERROR_FRAME_DECODING;
- strm.next_out = (Bytef*)ress->dstBuffer;
- strm.avail_out = (uInt)ress->dstBufferSize;
+ writeJob = WritePool_acquireWriteJob(ress->writePoolCtx);
+ strm.next_out = (Bytef*)writeJob->buffer;
+ strm.avail_out = (uInt)writeJob->bufferSize;
strm.avail_in = (uInt)ress->srcBufferLoaded;
strm.next_in = (z_const unsigned char*)ress->srcBuffer;
@@ -2343,35 +2564,34 @@ FIO_decompressGzFrame(dRess_t* ress, FILE* srcFile,
DISPLAYLEVEL(1, "zstd: %s: inflate error %d \n", srcFileName, ret);
decodingError = 1; break;
}
- { size_t const decompBytes = ress->dstBufferSize - strm.avail_out;
+ { size_t const decompBytes = writeJob->bufferSize - strm.avail_out;
if (decompBytes) {
- storedSkips = FIO_fwriteSparse(ress->dstFile, ress->dstBuffer, decompBytes, prefs, storedSkips);
+ writeJob->usedBufferSize = decompBytes;
+ WritePool_queueAndReacquireWriteJob(&writeJob);
outFileSize += decompBytes;
- strm.next_out = (Bytef*)ress->dstBuffer;
- strm.avail_out = (uInt)ress->dstBufferSize;
+ strm.next_out = (Bytef*)writeJob->buffer;
+ strm.avail_out = (uInt)writeJob->bufferSize;
}
}
if (ret == Z_STREAM_END) break;
}
- if (strm.avail_in > 0)
- memmove(ress->srcBuffer, strm.next_in, strm.avail_in);
- ress->srcBufferLoaded = strm.avail_in;
+ FIO_consumeDSrcBuffer(ress, ress->srcBufferLoaded - strm.avail_in);
+
if ( (inflateEnd(&strm) != Z_OK) /* release resources ; error detected */
&& (decodingError==0) ) {
DISPLAYLEVEL(1, "zstd: %s: inflateEnd error \n", srcFileName);
decodingError = 1;
}
- FIO_fwriteSparseEnd(prefs, ress->dstFile, storedSkips);
+ WritePool_releaseWriteJob(writeJob);
+ WritePool_sparseWriteEnd(ress->writePoolCtx);
return decodingError ? FIO_ERROR_FRAME_DECODING : outFileSize;
}
#endif
-
#ifdef ZSTD_LZMADECOMPRESS
static unsigned long long
FIO_decompressLzmaFrame(dRess_t* ress, FILE* srcFile,
- const FIO_prefs_t* const prefs,
const char* srcFileName, int plain_lzma)
{
unsigned long long outFileSize = 0;
@@ -2379,7 +2599,7 @@ FIO_decompressLzmaFrame(dRess_t* ress, FILE* srcFile,
lzma_action action = LZMA_RUN;
lzma_ret initRet;
int decodingError = 0;
- unsigned storedSkips = 0;
+ write_job_t *writeJob = NULL;
strm.next_in = 0;
strm.avail_in = 0;
@@ -2396,8 +2616,9 @@ FIO_decompressLzmaFrame(dRess_t* ress, FILE* srcFile,
return FIO_ERROR_FRAME_DECODING;
}
- strm.next_out = (BYTE*)ress->dstBuffer;
- strm.avail_out = ress->dstBufferSize;
+ writeJob = WritePool_acquireWriteJob(ress->writePoolCtx);
+ strm.next_out = (Bytef*)writeJob->buffer;
+ strm.avail_out = (uInt)writeJob->bufferSize;
strm.next_in = (BYTE const*)ress->srcBuffer;
strm.avail_in = ress->srcBufferLoaded;
@@ -2420,21 +2641,21 @@ FIO_decompressLzmaFrame(dRess_t* ress, FILE* srcFile,
srcFileName, ret);
decodingError = 1; break;
}
- { size_t const decompBytes = ress->dstBufferSize - strm.avail_out;
+ { size_t const decompBytes = writeJob->bufferSize - strm.avail_out;
if (decompBytes) {
- storedSkips = FIO_fwriteSparse(ress->dstFile, ress->dstBuffer, decompBytes, prefs, storedSkips);
+ writeJob->usedBufferSize = decompBytes;
+ WritePool_queueAndReacquireWriteJob(&writeJob);
outFileSize += decompBytes;
- strm.next_out = (BYTE*)ress->dstBuffer;
- strm.avail_out = ress->dstBufferSize;
+ strm.next_out = (Bytef*)writeJob->buffer;
+ strm.avail_out = writeJob->bufferSize;
} }
if (ret == LZMA_STREAM_END) break;
}
- if (strm.avail_in > 0)
- memmove(ress->srcBuffer, strm.next_in, strm.avail_in);
- ress->srcBufferLoaded = strm.avail_in;
+ FIO_consumeDSrcBuffer(ress, ress->srcBufferLoaded - strm.avail_in);
lzma_end(&strm);
- FIO_fwriteSparseEnd(prefs, ress->dstFile, storedSkips);
+ WritePool_releaseWriteJob(writeJob);
+ WritePool_sparseWriteEnd(ress->writePoolCtx);
return decodingError ? FIO_ERROR_FRAME_DECODING : outFileSize;
}
#endif
@@ -2442,60 +2663,57 @@ FIO_decompressLzmaFrame(dRess_t* ress, FILE* srcFile,
#ifdef ZSTD_LZ4DECOMPRESS
static unsigned long long
FIO_decompressLz4Frame(dRess_t* ress, FILE* srcFile,
- const FIO_prefs_t* const prefs,
const char* srcFileName)
{
unsigned long long filesize = 0;
- LZ4F_errorCode_t nextToLoad;
+ LZ4F_errorCode_t nextToLoad = 4;
LZ4F_decompressionContext_t dCtx;
LZ4F_errorCode_t const errorCode = LZ4F_createDecompressionContext(&dCtx, LZ4F_VERSION);
int decodingError = 0;
- unsigned storedSkips = 0;
+ write_job_t *writeJob = WritePool_acquireWriteJob(ress->writePoolCtx);
if (LZ4F_isError(errorCode)) {
DISPLAYLEVEL(1, "zstd: failed to create lz4 decompression context \n");
return FIO_ERROR_FRAME_DECODING;
}
- /* Init feed with magic number (already consumed from FILE* sFile) */
- { size_t inSize = 4;
- size_t outSize= 0;
- MEM_writeLE32(ress->srcBuffer, LZ4_MAGICNUMBER);
- nextToLoad = LZ4F_decompress(dCtx, ress->dstBuffer, &outSize, ress->srcBuffer, &inSize, NULL);
- if (LZ4F_isError(nextToLoad)) {
- DISPLAYLEVEL(1, "zstd: %s: lz4 header error : %s \n",
- srcFileName, LZ4F_getErrorName(nextToLoad));
- LZ4F_freeDecompressionContext(dCtx);
- return FIO_ERROR_FRAME_DECODING;
- } }
-
/* Main Loop */
for (;nextToLoad;) {
size_t readSize;
size_t pos = 0;
- size_t decodedBytes = ress->dstBufferSize;
+ size_t decodedBytes = writeJob->bufferSize;
+ int fullBufferDecoded = 0;
/* Read input */
- if (nextToLoad > ress->srcBufferSize) nextToLoad = ress->srcBufferSize;
- readSize = fread(ress->srcBuffer, 1, nextToLoad, srcFile);
- if (!readSize) break; /* reached end of file or stream */
+ nextToLoad = MIN(nextToLoad, ress->srcBufferSize-ress->srcBufferLoaded);
+ readSize = fread((char *)ress->srcBuffer + ress->srcBufferLoaded, 1, nextToLoad, srcFile);
+ if(!readSize && ferror(srcFile)) {
+ DISPLAYLEVEL(1, "zstd: %s: read error \n", srcFileName);
+ decodingError=1;
+ break;
+ }
+ if(!readSize && !ress->srcBufferLoaded) break; /* reached end of file */
+ ress->srcBufferLoaded += readSize;
- while ((pos < readSize) || (decodedBytes == ress->dstBufferSize)) { /* still to read, or still to flush */
+ while ((pos < ress->srcBufferLoaded) || fullBufferDecoded) { /* still to read, or still to flush */
/* Decode Input (at least partially) */
- size_t remaining = readSize - pos;
- decodedBytes = ress->dstBufferSize;
- nextToLoad = LZ4F_decompress(dCtx, ress->dstBuffer, &decodedBytes, (char*)(ress->srcBuffer)+pos, &remaining, NULL);
+ size_t remaining = ress->srcBufferLoaded - pos;
+ decodedBytes = writeJob->bufferSize;
+ nextToLoad = LZ4F_decompress(dCtx, writeJob->buffer, &decodedBytes, (char*)(ress->srcBuffer)+pos, &remaining, NULL);
if (LZ4F_isError(nextToLoad)) {
DISPLAYLEVEL(1, "zstd: %s: lz4 decompression error : %s \n",
srcFileName, LZ4F_getErrorName(nextToLoad));
decodingError = 1; nextToLoad = 0; break;
}
pos += remaining;
+ assert(pos <= ress->srcBufferLoaded);
+ fullBufferDecoded = decodedBytes == writeJob->bufferSize;
/* Write Block */
if (decodedBytes) {
UTIL_HumanReadableSize_t hrs;
- storedSkips = FIO_fwriteSparse(ress->dstFile, ress->dstBuffer, decodedBytes, prefs, storedSkips);
+ writeJob->usedBufferSize = decodedBytes;
+ WritePool_queueAndReacquireWriteJob(&writeJob);
filesize += decodedBytes;
hrs = UTIL_makeHumanReadableSize(filesize);
DISPLAYUPDATE(2, "\rDecompressed : %.*f%s ", hrs.precision, hrs.value, hrs.suffix);
@@ -2503,21 +2721,16 @@ FIO_decompressLz4Frame(dRess_t* ress, FILE* srcFile,
if (!nextToLoad) break;
}
+ FIO_consumeDSrcBuffer(ress, pos);
}
- /* can be out because readSize == 0, which could be an fread() error */
- if (ferror(srcFile)) {
- DISPLAYLEVEL(1, "zstd: %s: read error \n", srcFileName);
- decodingError=1;
- }
-
if (nextToLoad!=0) {
DISPLAYLEVEL(1, "zstd: %s: unfinished lz4 stream \n", srcFileName);
decodingError=1;
}
LZ4F_freeDecompressionContext(dCtx);
- ress->srcBufferLoaded = 0; /* LZ4F will reach exact frame boundary */
- FIO_fwriteSparseEnd(prefs, ress->dstFile, storedSkips);
+ WritePool_releaseWriteJob(writeJob);
+ WritePool_sparseWriteEnd(ress->writePoolCtx);
return decodingError ? FIO_ERROR_FRAME_DECODING : filesize;
}
@@ -2566,7 +2779,7 @@ static int FIO_decompressFrames(FIO_ctx_t* const fCtx,
filesize += frameSize;
} else if (buf[0] == 31 && buf[1] == 139) { /* gz magic number */
#ifdef ZSTD_GZDECOMPRESS
- unsigned long long const frameSize = FIO_decompressGzFrame(&ress, srcFile, prefs, srcFileName);
+ unsigned long long const frameSize = FIO_decompressGzFrame(&ress, srcFile, srcFileName);
if (frameSize == FIO_ERROR_FRAME_DECODING) return 1;
filesize += frameSize;
#else
@@ -2576,7 +2789,7 @@ static int FIO_decompressFrames(FIO_ctx_t* const fCtx,
} else if ((buf[0] == 0xFD && buf[1] == 0x37) /* xz magic number */
|| (buf[0] == 0x5D && buf[1] == 0x00)) { /* lzma header (no magic number) */
#ifdef ZSTD_LZMADECOMPRESS
- unsigned long long const frameSize = FIO_decompressLzmaFrame(&ress, srcFile, prefs, srcFileName, buf[0] != 0xFD);
+ unsigned long long const frameSize = FIO_decompressLzmaFrame(&ress, srcFile, srcFileName, buf[0] != 0xFD);
if (frameSize == FIO_ERROR_FRAME_DECODING) return 1;
filesize += frameSize;
#else
@@ -2585,7 +2798,7 @@ static int FIO_decompressFrames(FIO_ctx_t* const fCtx,
#endif
} else if (MEM_readLE32(buf) == LZ4_MAGICNUMBER) {
#ifdef ZSTD_LZ4DECOMPRESS
- unsigned long long const frameSize = FIO_decompressLz4Frame(&ress, srcFile, prefs, srcFileName);
+ unsigned long long const frameSize = FIO_decompressLz4Frame(&ress, srcFile, srcFileName);
if (frameSize == FIO_ERROR_FRAME_DECODING) return 1;
filesize += frameSize;
#else
@@ -2594,7 +2807,7 @@ static int FIO_decompressFrames(FIO_ctx_t* const fCtx,
#endif
} else if ((prefs->overwrite) && !strcmp (dstFileName, stdoutmark)) { /* pass-through mode */
return FIO_passThrough(prefs,
- ress.dstFile, srcFile,
+ ress.writePoolCtx->dstFile, srcFile,
ress.srcBuffer, ress.srcBufferSize,
ress.srcBufferLoaded);
} else {
@@ -2632,7 +2845,8 @@ static int FIO_decompressDstFile(FIO_ctx_t* const fCtx,
int releaseDstFile = 0;
int transferMTime = 0;
- if ((ress.dstFile == NULL) && (prefs->testMode==0)) {
+ if ((ress.writePoolCtx->dstFile == NULL) && (prefs->testMode==0)) {
+ FILE *dstFile;
int dstFilePermissions = DEFAULT_FILE_PERMISSIONS;
if ( strcmp(srcFileName, stdinmark) /* special case : don't transfer permissions from stdin */
&& strcmp(dstFileName, stdoutmark)
@@ -2644,8 +2858,9 @@ static int FIO_decompressDstFile(FIO_ctx_t* const fCtx,
releaseDstFile = 1;
- ress.dstFile = FIO_openDstFile(fCtx, prefs, srcFileName, dstFileName, dstFilePermissions);
- if (ress.dstFile==NULL) return 1;
+ dstFile = FIO_openDstFile(fCtx, prefs, srcFileName, dstFileName, dstFilePermissions);
+ if (dstFile==NULL) return 1;
+ WritePool_setDstFile(ress.writePoolCtx, dstFile);
/* Must only be added after FIO_openDstFile() succeeds.
* Otherwise we may delete the destination file if it already exists,
@@ -2657,10 +2872,8 @@ static int FIO_decompressDstFile(FIO_ctx_t* const fCtx,
result = FIO_decompressFrames(fCtx, ress, srcFile, prefs, dstFileName, srcFileName);
if (releaseDstFile) {
- FILE* const dstFile = ress.dstFile;
clearHandler();
- ress.dstFile = NULL;
- if (fclose(dstFile)) {
+ if (WritePool_closeDstFile(ress.writePoolCtx)) {
DISPLAYLEVEL(1, "zstd: %s: %s \n", dstFileName, strerror(errno));
result = 1;
}
@@ -2874,15 +3087,16 @@ FIO_decompressMultipleFilenames(FIO_ctx_t* const fCtx,
return 1;
}
if (!prefs->testMode) {
- ress.dstFile = FIO_openDstFile(fCtx, prefs, NULL, outFileName, DEFAULT_FILE_PERMISSIONS);
- if (ress.dstFile == 0) EXM_THROW(19, "cannot open %s", outFileName);
+ FILE* dstFile = FIO_openDstFile(fCtx, prefs, NULL, outFileName, DEFAULT_FILE_PERMISSIONS);
+ if (dstFile == 0) EXM_THROW(19, "cannot open %s", outFileName);
+ WritePool_setDstFile(ress.writePoolCtx, dstFile);
}
for (; fCtx->currFileIdx < fCtx->nbFilesTotal; fCtx->currFileIdx++) {
status = FIO_decompressSrcFile(fCtx, prefs, ress, outFileName, srcNamesTable[fCtx->currFileIdx]);
if (!status) fCtx->nbFilesProcessed++;
error |= status;
}
- if ((!prefs->testMode) && (fclose(ress.dstFile)))
+ if ((!prefs->testMode) && (WritePool_closeDstFile(ress.writePoolCtx)))
EXM_THROW(72, "Write error : %s : cannot properly close output file",
strerror(errno));
} else {
diff --git a/programs/fileio.h b/programs/fileio.h
index 61094db83cb..398937a64e8 100644
--- a/programs/fileio.h
+++ b/programs/fileio.h
@@ -109,6 +109,7 @@ void FIO_setAllowBlockDevices(FIO_prefs_t* const prefs, int allowBlockDevices);
void FIO_setPatchFromMode(FIO_prefs_t* const prefs, int value);
void FIO_setContentSize(FIO_prefs_t* const prefs, int value);
void FIO_displayCompressionParameters(const FIO_prefs_t* prefs);
+void FIO_setAsyncIOFlag(FIO_prefs_t* const prefs, unsigned value);
/* FIO_ctx_t functions */
void FIO_setNbFilesTotal(FIO_ctx_t* const fCtx, int value);
diff --git a/programs/zstdcli.c b/programs/zstdcli.c
index bfe18c0c1ba..fd563e1c24d 100644
--- a/programs/zstdcli.c
+++ b/programs/zstdcli.c
@@ -239,9 +239,12 @@ static void usage_advanced(const char* programName)
#ifndef ZSTD_NODECOMPRESS
DISPLAYOUT( "\n");
DISPLAYOUT( "Advanced decompression arguments : \n");
- DISPLAYOUT( " -l : print information about zstd compressed files \n");
- DISPLAYOUT( "--test : test compressed file integrity \n");
- DISPLAYOUT( " -M# : Set a memory usage limit for decompression \n");
+ DISPLAYOUT( " -l : print information about zstd compressed files \n");
+ DISPLAYOUT( "--test : test compressed file integrity \n");
+ DISPLAYOUT( " -M# : Set a memory usage limit for decompression \n");
+#ifdef ZSTD_MULTITHREAD
+ DISPLAYOUT( "--[no-]asyncio : use threaded asynchronous IO for output (default: disabled) \n");
+#endif
# if ZSTD_SPARSE_DEFAULT
DISPLAYOUT( "--[no-]sparse : sparse mode (default: enabled on file, disabled on stdout) \n");
# else
@@ -912,6 +915,8 @@ int main(int argCount, const char* argv[])
if (!strcmp(argument, "--sparse")) { FIO_setSparseWrite(prefs, 2); continue; }
if (!strcmp(argument, "--no-sparse")) { FIO_setSparseWrite(prefs, 0); continue; }
if (!strcmp(argument, "--test")) { operation=zom_test; continue; }
+ if (!strcmp(argument, "--asyncio")) { FIO_setAsyncIOFlag(prefs, 1); continue;}
+ if (!strcmp(argument, "--no-asyncio")) { FIO_setAsyncIOFlag(prefs, 0); continue;}
if (!strcmp(argument, "--train")) { operation=zom_train; if (outFileName==NULL) outFileName=g_defaultDictName; continue; }
if (!strcmp(argument, "--no-dictID")) { FIO_setDictIDFlag(prefs, 0); continue; }
if (!strcmp(argument, "--keep")) { FIO_setRemoveSrcFile(prefs, 0); continue; }
diff --git a/tests/playTests.sh b/tests/playTests.sh
index b7a3d88a817..78d8e742aa3 100755
--- a/tests/playTests.sh
+++ b/tests/playTests.sh
@@ -1575,6 +1575,44 @@ elif [ "$longCSize19wlog23" -gt "$optCSize19wlog23" ]; then
exit 1
fi
+println "\n===> zstd asyncio decompression tests "
+
+addFrame() {
+ datagen -g2M -s$2 >> tmp_uncompressed
+ datagen -g2M -s$2 | zstd --format=$1 >> tmp_compressed.zst
+}
+
+addTwoFrames() {
+ addFrame $1 1
+ addFrame $1 2
+}
+
+testAsyncIO() {
+ roundTripTest -g2M "3 --asyncio --format=$1"
+ roundTripTest -g2M "3 --no-asyncio --format=$1"
+}
+
+rm -f tmp_compressed tmp_uncompressed
+testAsyncIO zstd
+addTwoFrames zstd
+if [ $GZIPMODE -eq 1 ]; then
+ testAsyncIO gzip
+ addTwoFrames gzip
+fi
+if [ $LZMAMODE -eq 1 ]; then
+ testAsyncIO lzma
+ addTwoFrames lzma
+fi
+if [ $LZ4MODE -eq 1 ]; then
+ testAsyncIO lz4
+ addTwoFrames lz4
+fi
+cat tmp_uncompressed | $MD5SUM > tmp2
+zstd -d tmp_compressed.zst --asyncio -c | $MD5SUM > tmp1
+$DIFF -q tmp1 tmp2
+rm tmp1
+zstd -d tmp_compressed.zst --no-asyncio -c | $MD5SUM > tmp1
+$DIFF -q tmp1 tmp2
if [ "$1" != "--test-large-data" ]; then
println "Skipping large data tests"
From 24318093cce0ae0fc55922a55761bf5389d7384d Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Fri, 21 Jan 2022 14:08:46 -0800
Subject: [PATCH 025/472] slightly shortened compression status update line
to fit within 80 columns limit.
---
programs/fileio.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/programs/fileio.c b/programs/fileio.c
index f5e2e488ca4..3f6df112db7 100644
--- a/programs/fileio.c
+++ b/programs/fileio.c
@@ -1449,7 +1449,7 @@ FIO_compressZstdFrame(FIO_ctx_t* const fCtx,
/* display progress notifications */
if (g_display_prefs.displayLevel >= 3) {
- DISPLAYUPDATE(3, "\r(L%i) Buffered :%6.*f%4s - Consumed :%6.*f%4s - Compressed :%6.*f%4s => %.2f%% ",
+ DISPLAYUPDATE(3, "\r(L%i) Buffered:%5.*f%4s - Consumed:%5.*f%4s - Compressed:%5.*f%4s => %.2f%% ",
compressionLevel,
buffered_hrs.precision, buffered_hrs.value, buffered_hrs.suffix,
consumed_hrs.precision, consumed_hrs.value, consumed_hrs.suffix,
From 17017ac8db47e93cd656d15c6cf5c414b3355394 Mon Sep 17 00:00:00 2001
From: binhdvo
Date: Fri, 21 Jan 2022 19:57:19 -0500
Subject: [PATCH 026/472] Change zstdless behavior to align with zless (#2909)
* Change zstdless behavior to align with zless
---
programs/zstdless | 8 +++++++-
tests/playTests.sh | 15 +++++++++++++++
2 files changed, 22 insertions(+), 1 deletion(-)
diff --git a/programs/zstdless b/programs/zstdless
index 893799e7d95..17726a4f6e1 100755
--- a/programs/zstdless
+++ b/programs/zstdless
@@ -1,2 +1,8 @@
#!/bin/sh
-zstdcat "$@" | less
+
+zstd=${ZSTD:-zstd}
+
+# TODO: Address quirks and bugs tied to old versions of less, provide a mechanism to pass flags directly to zstd
+
+export LESSOPEN="|-${zstd} -cdfq %s"
+exec less "$@"
diff --git a/tests/playTests.sh b/tests/playTests.sh
index 78d8e742aa3..f19291a6063 100755
--- a/tests/playTests.sh
+++ b/tests/playTests.sh
@@ -89,6 +89,7 @@ PRGDIR="$SCRIPT_DIR/../programs"
TESTDIR="$SCRIPT_DIR/../tests"
UNAME=$(uname)
ZSTDGREP="$PRGDIR/zstdgrep"
+ZSTDLESS="$PRGDIR/zstdless"
detectedTerminal=false
if [ -t 0 ] && [ -t 1 ]
@@ -322,6 +323,20 @@ ZCAT=./zstdcat "$ZSTDGREP" 2>&1 "1234" tmp_grep_bad.zst && die "Should have fail
ZCAT=./zstdcat "$ZSTDGREP" 2>&1 "1234" tmp_grep_bad.zst | grep "No such file or directory" || true
rm -f tmp_grep*
+println "\n===> zstdless tests"
+if [ -n "$(which less)" ]; then
+ ln -sf "$ZSTD_BIN" zstd
+ rm -f tmp_less*
+ echo "1234" > tmp_less
+ zstd -f tmp_less
+ lines=$(ZSTD=./zstd "$ZSTDLESS" 2>&1 tmp_less.zst | wc -l)
+ test 1 -eq $lines
+ ZSTD=./zstd "$ZSTDLESS" -f tmp_less.zst > tmp_less_regenerated
+ $DIFF tmp_less tmp_less_regenerated
+ ZSTD=./zstd "$ZSTDLESS" 2>&1 tmp_less_bad.zst | grep "No such file or directory" || die
+ rm -f tmp_less*
+fi
+
println "\n===> --exclude-compressed flag"
rm -rf precompressedFilterTestDir
mkdir -p precompressedFilterTestDir
From feaaf7a6b1d1301e99634951d6ab3e20625e9a4a Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Fri, 21 Jan 2022 21:38:35 -0800
Subject: [PATCH 027/472] slightly shortened status and summary lines in very
verbose mode
---
programs/fileio.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/programs/fileio.c b/programs/fileio.c
index 3f6df112db7..d40ebbc1e04 100644
--- a/programs/fileio.c
+++ b/programs/fileio.c
@@ -1449,7 +1449,7 @@ FIO_compressZstdFrame(FIO_ctx_t* const fCtx,
/* display progress notifications */
if (g_display_prefs.displayLevel >= 3) {
- DISPLAYUPDATE(3, "\r(L%i) Buffered:%5.*f%4s - Consumed:%5.*f%4s - Compressed:%5.*f%4s => %.2f%% ",
+ DISPLAYUPDATE(3, "\r(L%i) Buffered:%5.*f%s - Consumed:%5.*f%s - Compressed:%5.*f%s => %.2f%% ",
compressionLevel,
buffered_hrs.precision, buffered_hrs.value, buffered_hrs.suffix,
consumed_hrs.precision, consumed_hrs.value, consumed_hrs.suffix,
@@ -1646,13 +1646,13 @@ FIO_compressFilename_internal(FIO_ctx_t* const fCtx,
UTIL_HumanReadableSize_t hr_isize = UTIL_makeHumanReadableSize((U64) readsize);
UTIL_HumanReadableSize_t hr_osize = UTIL_makeHumanReadableSize((U64) compressedfilesize);
if (readsize == 0) {
- DISPLAYLEVEL(2,"%-20s : (%6.*f%4s => %6.*f%4s, %s) \n",
+ DISPLAYLEVEL(2,"%-20s : (%6.*f%s => %6.*f%s, %s) \n",
srcFileName,
hr_isize.precision, hr_isize.value, hr_isize.suffix,
hr_osize.precision, hr_osize.value, hr_osize.suffix,
dstFileName);
} else {
- DISPLAYLEVEL(2,"%-20s :%6.2f%% (%6.*f%4s => %6.*f%4s, %s) \n",
+ DISPLAYLEVEL(2,"%-20s :%6.2f%% (%6.*f%s => %6.*f%s, %s) \n",
srcFileName,
(double)compressedfilesize / (double)readsize * 100,
hr_isize.precision, hr_isize.value, hr_isize.suffix,
From 2b957afec7b43afa2884441856c0d7277bdecb49 Mon Sep 17 00:00:00 2001
From: Tom Rix
Date: Mon, 24 Jan 2022 12:43:39 -0800
Subject: [PATCH 028/472] cleanup double word in comment.
Remove the second 'a' and 'into'
Signed-off-by: Tom Rix
---
lib/zstd.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lib/zstd.h b/lib/zstd.h
index a88ae7bf8ed..349d8017f84 100644
--- a/lib/zstd.h
+++ b/lib/zstd.h
@@ -1395,7 +1395,7 @@ ZSTDLIB_STATIC_API size_t ZSTD_generateSequences(ZSTD_CCtx* zc, ZSTD_Sequence* o
/*! ZSTD_mergeBlockDelimiters() :
* Given an array of ZSTD_Sequence, remove all sequences that represent block delimiters/last literals
- * by merging them into into the literals of the next sequence.
+ * by merging them into the literals of the next sequence.
*
* As such, the final generated result has no explicit representation of block boundaries,
* and the final last literals segment is not represented in the sequences.
@@ -1442,7 +1442,7 @@ ZSTDLIB_STATIC_API size_t ZSTD_compressSequences(ZSTD_CCtx* const cctx, void* ds
/*! ZSTD_writeSkippableFrame() :
* Generates a zstd skippable frame containing data given by src, and writes it to dst buffer.
*
- * Skippable frames begin with a a 4-byte magic number. There are 16 possible choices of magic number,
+ * Skippable frames begin with a 4-byte magic number. There are 16 possible choices of magic number,
* ranging from ZSTD_MAGIC_SKIPPABLE_START to ZSTD_MAGIC_SKIPPABLE_START+15.
* As such, the parameter magicVariant controls the exact skippable frame magic number variant used, so
* the magic number used will be ZSTD_MAGIC_SKIPPABLE_START + magicVariant.
From 70df5de1b2fc291a58c1b3199dde11b353ccad8f Mon Sep 17 00:00:00 2001
From: Yonatan Komornik <11005061+yoniko@users.noreply.github.com>
Date: Mon, 24 Jan 2022 14:43:02 -0800
Subject: [PATCH 029/472] AsyncIO compression part 1 - refactor of existing
asyncio code (#3021)
* Refactored fileio.c:
- Extracted asyncio code to fileio_asyncio.c/.h
- Moved type definitions to fileio_types.h
- Moved common macro definitions needed by both fileio.c and fileio_asyncio.c to fileio_common.h
* Bugfix - rename fileio_asycio to fileio_asyncio
* Added copyrights & license to new files
* CR fixes
---
build/VS2008/zstd/zstd.vcproj | 4 +
build/VS2010/zstd/zstd.vcxproj | 1 +
build/cmake/programs/CMakeLists.txt | 4 +-
build/meson/programs/meson.build | 2 +
contrib/VS2005/zstd/zstd.vcproj | 4 +
programs/Makefile | 8 +-
programs/fileio.c | 528 +++-------------------------
programs/fileio.h | 8 +-
programs/fileio_asyncio.c | 365 +++++++++++++++++++
programs/fileio_asyncio.h | 120 +++++++
programs/fileio_common.h | 117 ++++++
programs/fileio_types.h | 73 ++++
12 files changed, 736 insertions(+), 498 deletions(-)
create mode 100644 programs/fileio_asyncio.c
create mode 100644 programs/fileio_asyncio.h
create mode 100644 programs/fileio_common.h
create mode 100644 programs/fileio_types.h
diff --git a/build/VS2008/zstd/zstd.vcproj b/build/VS2008/zstd/zstd.vcproj
index c7eec577db3..91f2bda536c 100644
--- a/build/VS2008/zstd/zstd.vcproj
+++ b/build/VS2008/zstd/zstd.vcproj
@@ -384,6 +384,10 @@
RelativePath="..\..\..\programs\fileio.c"
>
+
+
diff --git a/build/VS2010/zstd/zstd.vcxproj b/build/VS2010/zstd/zstd.vcxproj
index 46e22f42e9b..8ab239dd814 100644
--- a/build/VS2010/zstd/zstd.vcxproj
+++ b/build/VS2010/zstd/zstd.vcxproj
@@ -62,6 +62,7 @@
+
diff --git a/build/cmake/programs/CMakeLists.txt b/build/cmake/programs/CMakeLists.txt
index 490030783d3..28b1e1d166b 100644
--- a/build/cmake/programs/CMakeLists.txt
+++ b/build/cmake/programs/CMakeLists.txt
@@ -32,7 +32,7 @@ if (MSVC)
set(PlatformDependResources ${MSVC_RESOURCE_DIR}/zstd.rc)
endif ()
-add_executable(zstd ${PROGRAMS_DIR}/zstdcli.c ${PROGRAMS_DIR}/util.c ${PROGRAMS_DIR}/timefn.c ${PROGRAMS_DIR}/fileio.c ${PROGRAMS_DIR}/benchfn.c ${PROGRAMS_DIR}/benchzstd.c ${PROGRAMS_DIR}/datagen.c ${PROGRAMS_DIR}/dibio.c ${PROGRAMS_DIR}/zstdcli_trace.c ${PlatformDependResources})
+add_executable(zstd ${PROGRAMS_DIR}/zstdcli.c ${PROGRAMS_DIR}/util.c ${PROGRAMS_DIR}/timefn.c ${PROGRAMS_DIR}/fileio.c ${PROGRAMS_DIR}/fileio_asyncio.c ${PROGRAMS_DIR}/benchfn.c ${PROGRAMS_DIR}/benchzstd.c ${PROGRAMS_DIR}/datagen.c ${PROGRAMS_DIR}/dibio.c ${PROGRAMS_DIR}/zstdcli_trace.c ${PlatformDependResources})
target_link_libraries(zstd ${PROGRAMS_ZSTD_LINK_TARGET})
if (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
target_link_libraries(zstd rt)
@@ -75,7 +75,7 @@ if (UNIX)
${CMAKE_CURRENT_BINARY_DIR}/zstdless.1
DESTINATION "${MAN_INSTALL_DIR}")
- add_executable(zstd-frugal ${PROGRAMS_DIR}/zstdcli.c ${PROGRAMS_DIR}/util.c ${PROGRAMS_DIR}/timefn.c ${PROGRAMS_DIR}/fileio.c)
+ add_executable(zstd-frugal ${PROGRAMS_DIR}/zstdcli.c ${PROGRAMS_DIR}/util.c ${PROGRAMS_DIR}/timefn.c ${PROGRAMS_DIR}/fileio.c ${PROGRAMS_DIR}/fileio_asyncio.c)
target_link_libraries(zstd-frugal ${PROGRAMS_ZSTD_LINK_TARGET})
set_property(TARGET zstd-frugal APPEND PROPERTY COMPILE_DEFINITIONS "ZSTD_NOBENCH;ZSTD_NODICT;ZSTD_NOTRACE")
endif ()
diff --git a/build/meson/programs/meson.build b/build/meson/programs/meson.build
index 0ae93fc107c..5ccd679a167 100644
--- a/build/meson/programs/meson.build
+++ b/build/meson/programs/meson.build
@@ -14,6 +14,7 @@ zstd_programs_sources = [join_paths(zstd_rootdir, 'programs/zstdcli.c'),
join_paths(zstd_rootdir, 'programs/util.c'),
join_paths(zstd_rootdir, 'programs/timefn.c'),
join_paths(zstd_rootdir, 'programs/fileio.c'),
+ join_paths(zstd_rootdir, 'programs/fileio_asyncio.c'),
join_paths(zstd_rootdir, 'programs/benchfn.c'),
join_paths(zstd_rootdir, 'programs/benchzstd.c'),
join_paths(zstd_rootdir, 'programs/datagen.c'),
@@ -80,6 +81,7 @@ zstd_frugal_sources = [join_paths(zstd_rootdir, 'programs/zstdcli.c'),
join_paths(zstd_rootdir, 'programs/timefn.c'),
join_paths(zstd_rootdir, 'programs/util.c'),
join_paths(zstd_rootdir, 'programs/fileio.c'),
+ join_paths(zstd_rootdir, 'programs/fileio_asyncio.c'),
join_paths(zstd_rootdir, 'lib/common/pool.c'),
join_paths(zstd_rootdir, 'lib/common/zstd_common.c'),
join_paths(zstd_rootdir, 'lib/common/error_private.c')]
diff --git a/contrib/VS2005/zstd/zstd.vcproj b/contrib/VS2005/zstd/zstd.vcproj
index 78645d18a36..e37ebee3911 100644
--- a/contrib/VS2005/zstd/zstd.vcproj
+++ b/contrib/VS2005/zstd/zstd.vcproj
@@ -363,6 +363,10 @@
RelativePath="..\..\..\programs\fileio.c"
>
+
+
diff --git a/programs/Makefile b/programs/Makefile
index f77e1b7f10f..16763e49365 100644
--- a/programs/Makefile
+++ b/programs/Makefile
@@ -243,17 +243,17 @@ zstd-pgo :
## zstd-small: minimal target, supporting only zstd compression and decompression. no bench. no legacy. no other format.
zstd-small: CFLAGS = -Os -s
-zstd-frugal zstd-small: $(ZSTDLIB_CORE_SRC) zstdcli.c util.c timefn.c fileio.c
+zstd-frugal zstd-small: $(ZSTDLIB_CORE_SRC) zstdcli.c util.c timefn.c fileio.c fileio_asyncio.c
$(CC) $(FLAGS) -DZSTD_NOBENCH -DZSTD_NODICT -DZSTD_NOTRACE -UZSTD_LEGACY_SUPPORT -DZSTD_LEGACY_SUPPORT=0 $^ -o $@$(EXT)
-zstd-decompress: $(ZSTDLIB_COMMON_SRC) $(ZSTDLIB_DECOMPRESS_SRC) zstdcli.c util.c timefn.c fileio.c
+zstd-decompress: $(ZSTDLIB_COMMON_SRC) $(ZSTDLIB_DECOMPRESS_SRC) zstdcli.c util.c timefn.c fileio.c fileio_asyncio.c
$(CC) $(FLAGS) -DZSTD_NOBENCH -DZSTD_NODICT -DZSTD_NOCOMPRESS -DZSTD_NOTRACE -UZSTD_LEGACY_SUPPORT -DZSTD_LEGACY_SUPPORT=0 $^ -o $@$(EXT)
-zstd-compress: $(ZSTDLIB_COMMON_SRC) $(ZSTDLIB_COMPRESS_SRC) zstdcli.c util.c timefn.c fileio.c
+zstd-compress: $(ZSTDLIB_COMMON_SRC) $(ZSTDLIB_COMPRESS_SRC) zstdcli.c util.c timefn.c fileio.c fileio_asyncio.c
$(CC) $(FLAGS) -DZSTD_NOBENCH -DZSTD_NODICT -DZSTD_NODECOMPRESS -DZSTD_NOTRACE -UZSTD_LEGACY_SUPPORT -DZSTD_LEGACY_SUPPORT=0 $^ -o $@$(EXT)
## zstd-dictBuilder: executable supporting dictionary creation and compression (only)
-zstd-dictBuilder: $(ZSTDLIB_COMMON_SRC) $(ZSTDLIB_COMPRESS_SRC) $(ZDICT_SRC) zstdcli.c util.c timefn.c fileio.c dibio.c
+zstd-dictBuilder: $(ZSTDLIB_COMMON_SRC) $(ZSTDLIB_COMPRESS_SRC) $(ZDICT_SRC) zstdcli.c util.c timefn.c fileio.c fileio_asyncio.c dibio.c
$(CC) $(FLAGS) -DZSTD_NOBENCH -DZSTD_NODECOMPRESS -DZSTD_NOTRACE $^ -o $@$(EXT)
zstdmt: zstd
diff --git a/programs/fileio.c b/programs/fileio.c
index d40ebbc1e04..2066096d233 100644
--- a/programs/fileio.c
+++ b/programs/fileio.c
@@ -34,16 +34,18 @@
#include /* INT_MAX */
#include
#include "timefn.h" /* UTIL_getTime, UTIL_clockSpanMicro */
-#include "../lib/common/pool.h"
-#include "../lib/common/threading.h"
#if defined (_MSC_VER)
# include
# include
#endif
-#include "../lib/common/mem.h" /* U32, U64 */
#include "fileio.h"
+#include "fileio_asyncio.h"
+#include "fileio_common.h"
+
+FIO_display_prefs_t g_display_prefs = {2, FIO_ps_auto};
+UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER;
#define ZSTD_STATIC_LINKING_ONLY /* ZSTD_magicNumber, ZSTD_frameHeaderSize_max */
#include "../lib/zstd.h"
@@ -84,62 +86,6 @@
#define DEFAULT_FILE_PERMISSIONS (0666)
#endif
-/*-*************************************
-* Macros
-***************************************/
-#define KB *(1 <<10)
-#define MB *(1 <<20)
-#define GB *(1U<<30)
-#undef MAX
-#define MAX(a,b) ((a)>(b) ? (a) : (b))
-
-struct FIO_display_prefs_s {
- int displayLevel; /* 0 : no display; 1: errors; 2: + result + interaction + warnings; 3: + progression; 4: + information */
- FIO_progressSetting_e progressSetting;
-};
-
-static FIO_display_prefs_t g_display_prefs = {2, FIO_ps_auto};
-
-#define DISPLAY(...) fprintf(stderr, __VA_ARGS__)
-#define DISPLAYOUT(...) fprintf(stdout, __VA_ARGS__)
-#define DISPLAYLEVEL(l, ...) { if (g_display_prefs.displayLevel>=l) { DISPLAY(__VA_ARGS__); } }
-
-static const U64 g_refreshRate = SEC_TO_MICRO / 6;
-static UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER;
-
-#define READY_FOR_UPDATE() ((g_display_prefs.progressSetting != FIO_ps_never) && UTIL_clockSpanMicro(g_displayClock) > g_refreshRate)
-#define DELAY_NEXT_UPDATE() { g_displayClock = UTIL_getTime(); }
-#define DISPLAYUPDATE(l, ...) { \
- if (g_display_prefs.displayLevel>=l && (g_display_prefs.progressSetting != FIO_ps_never)) { \
- if (READY_FOR_UPDATE() || (g_display_prefs.displayLevel>=4)) { \
- DELAY_NEXT_UPDATE(); \
- DISPLAY(__VA_ARGS__); \
- if (g_display_prefs.displayLevel>=4) fflush(stderr); \
- } } }
-
-#undef MIN /* in case it would be already defined */
-#define MIN(a,b) ((a) < (b) ? (a) : (b))
-
-
-#define EXM_THROW(error, ...) \
-{ \
- DISPLAYLEVEL(1, "zstd: "); \
- DISPLAYLEVEL(5, "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_V(v, f) \
- v = f; \
- if (ZSTD_isError(v)) { \
- DISPLAYLEVEL(5, "%s \n", #f); \
- EXM_THROW(11, "%s", ZSTD_getErrorName(v)); \
- }
-#define CHECK(f) { size_t err; CHECK_V(err, f); }
-
-
/*-************************************
* Signal (Ctrl-C trapping)
**************************************/
@@ -250,95 +196,6 @@ void FIO_addAbortHandler()
#endif
}
-
-/*-************************************************************
-* Avoid fseek()'s 2GiB barrier with MSVC, macOS, *BSD, MinGW
-***************************************************************/
-#if defined(_MSC_VER) && _MSC_VER >= 1400
-# define LONG_SEEK _fseeki64
-# define LONG_TELL _ftelli64
-#elif !defined(__64BIT__) && (PLATFORM_POSIX_VERSION >= 200112L) /* No point defining Large file for 64 bit */
-# define LONG_SEEK fseeko
-# define LONG_TELL ftello
-#elif defined(__MINGW32__) && !defined(__STRICT_ANSI__) && !defined(__NO_MINGW_LFS) && defined(__MSVCRT__)
-# define LONG_SEEK fseeko64
-# define LONG_TELL ftello64
-#elif defined(_WIN32) && !defined(__DJGPP__)
-# include
- static int LONG_SEEK(FILE* file, __int64 offset, int origin) {
- LARGE_INTEGER off;
- DWORD method;
- off.QuadPart = offset;
- if (origin == SEEK_END)
- method = FILE_END;
- else if (origin == SEEK_CUR)
- method = FILE_CURRENT;
- else
- method = FILE_BEGIN;
-
- if (SetFilePointerEx((HANDLE) _get_osfhandle(_fileno(file)), off, NULL, method))
- return 0;
- else
- return -1;
- }
- static __int64 LONG_TELL(FILE* file) {
- LARGE_INTEGER off, newOff;
- off.QuadPart = 0;
- newOff.QuadPart = 0;
- SetFilePointerEx((HANDLE) _get_osfhandle(_fileno(file)), off, &newOff, FILE_CURRENT);
- return newOff.QuadPart;
- }
-#else
-# define LONG_SEEK fseek
-# define LONG_TELL ftell
-#endif
-
-
-/*-*************************************
-* Parameters: FIO_prefs_t
-***************************************/
-
-/* typedef'd to FIO_prefs_t within fileio.h */
-struct FIO_prefs_s {
-
- /* Algorithm preferences */
- FIO_compressionType_t compressionType;
- U32 sparseFileSupport; /* 0: no sparse allowed; 1: auto (file yes, stdout no); 2: force sparse */
- int dictIDFlag;
- int checksumFlag;
- int blockSize;
- int overlapLog;
- U32 adaptiveMode;
- U32 useRowMatchFinder;
- int rsyncable;
- int minAdaptLevel;
- int maxAdaptLevel;
- int ldmFlag;
- int ldmHashLog;
- int ldmMinMatch;
- int ldmBucketSizeLog;
- int ldmHashRateLog;
- size_t streamSrcSize;
- size_t targetCBlockSize;
- int srcSizeHint;
- int testMode;
- ZSTD_paramSwitch_e literalCompressionMode;
-
- /* IO preferences */
- U32 removeSrcFile;
- U32 overwrite;
- U32 asyncIO;
-
- /* Computation resources preferences */
- unsigned memLimit;
- int nbWorkers;
-
- int excludeCompressedFiles;
- int patchFromMode;
- int contentSize;
- int allowBlockDevices;
-};
-
/*-*************************************
* Parameters: FIO_ctx_t
***************************************/
@@ -563,7 +420,13 @@ void FIO_setContentSize(FIO_prefs_t* const prefs, int value)
}
void FIO_setAsyncIOFlag(FIO_prefs_t* const prefs, unsigned value) {
+#ifdef ZSTD_MULTITHREAD
prefs->asyncIO = value;
+#else
+ (void) prefs;
+ (void) value;
+ DISPLAYLEVEL(2, "Note : asyncio is disabled (lack of multithreading support) \n");
+#endif
}
/* FIO_ctx_t functions */
@@ -2019,124 +1882,15 @@ int FIO_compressMultipleFilenames(FIO_ctx_t* const fCtx,
/* **************************************************************************
* Decompression
***************************************************************************/
-#define DECOMPRESSION_MAX_WRITE_JOBS (10)
-
-typedef struct {
- /* These struct fields should be set only on creation and not changed afterwards */
- POOL_ctx* writerPool;
- int totalWriteJobs;
- FIO_prefs_t* prefs;
-
- /* Controls the file we currently write to, make changes only by using provided utility functions */
- FILE* dstFile;
- unsigned storedSkips;
-
- /* The jobs and availableWriteJobs fields are access by both the main and writer threads and should
- * only be mutated after locking the mutex */
- ZSTD_pthread_mutex_t writeJobsMutex;
- void* jobs[DECOMPRESSION_MAX_WRITE_JOBS];
- int availableWriteJobs;
-} write_pool_ctx_t;
-
-typedef struct {
- /* These fields are automaically set and shouldn't be changed by non WritePool code. */
- write_pool_ctx_t *ctx;
- FILE* dstFile;
- void *buffer;
- size_t bufferSize;
-
- /* This field should be changed before a job is queued for execution and should contain the number
- * of bytes to write from the buffer. */
- size_t usedBufferSize;
-} write_job_t;
typedef struct {
void* srcBuffer;
size_t srcBufferSize;
size_t srcBufferLoaded;
ZSTD_DStream* dctx;
- write_pool_ctx_t *writePoolCtx;
+ WritePoolCtx_t *writeCtx;
} dRess_t;
-static write_job_t *FIO_createWriteJob(write_pool_ctx_t *ctx) {
- void *buffer;
- write_job_t *job;
- job = (write_job_t*) malloc(sizeof(write_job_t));
- buffer = malloc(ZSTD_DStreamOutSize());
- if(!job || !buffer)
- EXM_THROW(101, "Allocation error : not enough memory");
- job->buffer = buffer;
- job->bufferSize = ZSTD_DStreamOutSize();
- job->usedBufferSize = 0;
- job->dstFile = NULL;
- job->ctx = ctx;
- return job;
-}
-
-/* WritePool_createThreadPool:
- * Creates a thread pool and a mutex for threaded write pool.
- * Displays warning if asyncio is requested but MT isn't available. */
-static void WritePool_createThreadPool(write_pool_ctx_t *ctx, const FIO_prefs_t *prefs) {
- ctx->writerPool = NULL;
- if(prefs->asyncIO) {
-#ifdef ZSTD_MULTITHREAD
- if (ZSTD_pthread_mutex_init(&ctx->writeJobsMutex, NULL))
- EXM_THROW(102, "Failed creating write jobs mutex");
- /* We want DECOMPRESSION_MAX_WRITE_JOBS-2 queue items because we need to always have 1 free buffer to
- * decompress into and 1 buffer that's actively written to disk and owned by the writing thread. */
- assert(DECOMPRESSION_MAX_WRITE_JOBS >= 2);
- ctx->writerPool = POOL_create(1, DECOMPRESSION_MAX_WRITE_JOBS - 2);
- if (!ctx->writerPool)
- EXM_THROW(103, "Failed creating writer thread pool");
-#else
- DISPLAYLEVEL(2, "Note : asyncio decompression is disabled (lack of multithreading support) \n");
-#endif
- }
-}
-
-/* WritePool_create:
- * Allocates and sets and a new write pool including its included jobs. */
-static write_pool_ctx_t* WritePool_create(FIO_prefs_t* const prefs) {
- write_pool_ctx_t *ctx;
- int i;
- ctx = (write_pool_ctx_t*) malloc(sizeof(write_pool_ctx_t));
- if(!ctx)
- EXM_THROW(100, "Allocation error : not enough memory");
- WritePool_createThreadPool(ctx, prefs);
- ctx->prefs = prefs;
- ctx->totalWriteJobs = ctx->writerPool ? DECOMPRESSION_MAX_WRITE_JOBS : 1;
- ctx->availableWriteJobs = ctx->totalWriteJobs;
- for(i=0; i < ctx->availableWriteJobs; i++) {
- ctx->jobs[i] = FIO_createWriteJob(ctx);
- }
- ctx->storedSkips = 0;
- ctx->dstFile = NULL;
- return ctx;
-}
-
-/* WritePool_free:
- * Release a previously allocated write thread pool. Makes sure all takss are done and released. */
-static void WritePool_free(write_pool_ctx_t* ctx) {
- int i=0;
- if(ctx->writerPool) {
- /* Make sure we finish all tasks and then free the resources */
- POOL_joinJobs(ctx->writerPool);
- /* Make sure we are not leaking jobs */
- assert(ctx->availableWriteJobs==ctx->totalWriteJobs);
- POOL_free(ctx->writerPool);
- ZSTD_pthread_mutex_destroy(&ctx->writeJobsMutex);
- }
- assert(ctx->dstFile==NULL);
- assert(ctx->storedSkips==0);
- for(i=0; iavailableWriteJobs; i++) {
- write_job_t* job = (write_job_t*) ctx->jobs[i];
- free(job->buffer);
- free(job);
- }
- free(ctx);
-}
-
-
static dRess_t FIO_createDResources(FIO_prefs_t* const prefs, const char* dictFileName)
{
dRess_t ress;
@@ -2164,7 +1918,7 @@ static dRess_t FIO_createDResources(FIO_prefs_t* const prefs, const char* dictFi
free(dictBuffer);
}
- ress.writePoolCtx = WritePool_create(prefs);
+ ress.writeCtx = AIO_WritePool_create(prefs, ZSTD_DStreamOutSize());
return ress;
}
@@ -2173,7 +1927,7 @@ static void FIO_freeDResources(dRess_t ress)
{
CHECK( ZSTD_freeDStream(ress.dctx) );
free(ress.srcBuffer);
- WritePool_free(ress.writePoolCtx);
+ AIO_WritePool_free(ress.writeCtx);
}
/* FIO_consumeDSrcBuffer:
@@ -2184,205 +1938,6 @@ static void FIO_consumeDSrcBuffer(dRess_t *ress, size_t len) {
memmove(ress->srcBuffer, (char *)ress->srcBuffer + len, ress->srcBufferLoaded);
}
-/** FIO_fwriteSparse() :
-* @return : storedSkips,
-* argument for next call to FIO_fwriteSparse() or FIO_fwriteSparseEnd() */
-static unsigned
-FIO_fwriteSparse(FILE* file,
- const void* buffer, size_t bufferSize,
- const FIO_prefs_t* const prefs,
- unsigned storedSkips)
-{
- const size_t* const bufferT = (const size_t*)buffer; /* Buffer is supposed malloc'ed, hence aligned on size_t */
- size_t bufferSizeT = bufferSize / sizeof(size_t);
- const size_t* const bufferTEnd = bufferT + bufferSizeT;
- const size_t* ptrT = bufferT;
- static const size_t segmentSizeT = (32 KB) / sizeof(size_t); /* check every 32 KB */
-
- if (prefs->testMode) return 0; /* do not output anything in test mode */
-
- if (!prefs->sparseFileSupport) { /* normal write */
- size_t const sizeCheck = fwrite(buffer, 1, bufferSize, file);
- if (sizeCheck != bufferSize)
- EXM_THROW(70, "Write error : cannot write decoded block : %s",
- strerror(errno));
- return 0;
- }
-
- /* avoid int overflow */
- if (storedSkips > 1 GB) {
- if (LONG_SEEK(file, 1 GB, SEEK_CUR) != 0)
- EXM_THROW(91, "1 GB skip error (sparse file support)");
- storedSkips -= 1 GB;
- }
-
- while (ptrT < bufferTEnd) {
- size_t nb0T;
-
- /* adjust last segment if < 32 KB */
- size_t seg0SizeT = segmentSizeT;
- if (seg0SizeT > bufferSizeT) seg0SizeT = bufferSizeT;
- bufferSizeT -= seg0SizeT;
-
- /* count leading zeroes */
- for (nb0T=0; (nb0T < seg0SizeT) && (ptrT[nb0T] == 0); nb0T++) ;
- storedSkips += (unsigned)(nb0T * sizeof(size_t));
-
- if (nb0T != seg0SizeT) { /* not all 0s */
- size_t const nbNon0ST = seg0SizeT - nb0T;
- /* skip leading zeros */
- if (LONG_SEEK(file, storedSkips, SEEK_CUR) != 0)
- EXM_THROW(92, "Sparse skip error ; try --no-sparse");
- storedSkips = 0;
- /* write the rest */
- if (fwrite(ptrT + nb0T, sizeof(size_t), nbNon0ST, file) != nbNon0ST)
- EXM_THROW(93, "Write error : cannot write decoded block : %s",
- strerror(errno));
- }
- ptrT += seg0SizeT;
- }
-
- { static size_t const maskT = sizeof(size_t)-1;
- if (bufferSize & maskT) {
- /* size not multiple of sizeof(size_t) : implies end of block */
- const char* const restStart = (const char*)bufferTEnd;
- const char* restPtr = restStart;
- const char* const restEnd = (const char*)buffer + bufferSize;
- assert(restEnd > restStart && restEnd < restStart + sizeof(size_t));
- for ( ; (restPtr < restEnd) && (*restPtr == 0); restPtr++) ;
- storedSkips += (unsigned) (restPtr - restStart);
- if (restPtr != restEnd) {
- /* not all remaining bytes are 0 */
- size_t const restSize = (size_t)(restEnd - restPtr);
- if (LONG_SEEK(file, storedSkips, SEEK_CUR) != 0)
- EXM_THROW(92, "Sparse skip error ; try --no-sparse");
- if (fwrite(restPtr, 1, restSize, file) != restSize)
- EXM_THROW(95, "Write error : cannot write end of decoded block : %s",
- strerror(errno));
- storedSkips = 0;
- } } }
-
- return storedSkips;
-}
-
-static void
-FIO_fwriteSparseEnd(const FIO_prefs_t* const prefs, FILE* file, unsigned storedSkips)
-{
- if (prefs->testMode) assert(storedSkips == 0);
- if (storedSkips>0) {
- assert(prefs->sparseFileSupport > 0); /* storedSkips>0 implies sparse support is enabled */
- (void)prefs; /* assert can be disabled, in which case prefs becomes unused */
- if (LONG_SEEK(file, storedSkips-1, SEEK_CUR) != 0)
- EXM_THROW(69, "Final skip error (sparse file support)");
- /* last zero must be explicitly written,
- * so that skipped ones get implicitly translated as zero by FS */
- { const char lastZeroByte[1] = { 0 };
- if (fwrite(lastZeroByte, 1, 1, file) != 1)
- EXM_THROW(69, "Write error : cannot write last zero : %s", strerror(errno));
- } }
-}
-
-/* WritePool_releaseWriteJob:
- * Releases an acquired job back to the pool. Doesn't execute the job. */
-static void WritePool_releaseWriteJob(write_job_t *job) {
- write_pool_ctx_t *ctx = job->ctx;
- if(ctx->writerPool) {
- ZSTD_pthread_mutex_lock(&ctx->writeJobsMutex);
- assert(ctx->availableWriteJobs < DECOMPRESSION_MAX_WRITE_JOBS);
- ctx->jobs[ctx->availableWriteJobs++] = job;
- ZSTD_pthread_mutex_unlock(&ctx->writeJobsMutex);
- } else {
- ctx->availableWriteJobs++;
- }
-}
-
-/* WritePool_acquireWriteJob:
- * Returns an available write job to be used for a future write. */
-static write_job_t* WritePool_acquireWriteJob(write_pool_ctx_t *ctx) {
- write_job_t *job;
- assert(ctx->dstFile!=NULL || ctx->prefs->testMode);
- if(ctx->writerPool) {
- ZSTD_pthread_mutex_lock(&ctx->writeJobsMutex);
- assert(ctx->availableWriteJobs > 0);
- job = (write_job_t*) ctx->jobs[--ctx->availableWriteJobs];
- ZSTD_pthread_mutex_unlock(&ctx->writeJobsMutex);
- } else {
- assert(ctx->availableWriteJobs==1);
- ctx->availableWriteJobs--;
- job = (write_job_t*)ctx->jobs[0];
- }
- job->usedBufferSize = 0;
- job->dstFile = ctx->dstFile;
- return job;
-}
-
-/* WritePool_executeWriteJob:
- * Executes a write job synchronously. Can be used as a function for a thread pool. */
-static void WritePool_executeWriteJob(void* opaque){
- write_job_t* job = (write_job_t*) opaque;
- write_pool_ctx_t* ctx = job->ctx;
- ctx->storedSkips = FIO_fwriteSparse(job->dstFile, job->buffer, job->usedBufferSize, ctx->prefs, ctx->storedSkips);
- WritePool_releaseWriteJob(job);
-}
-
-/* WritePool_queueWriteJob:
- * Queues a write job for execution.
- * Make sure to set `usedBufferSize` to the wanted length before call.
- * The queued job shouldn't be used directly after queueing it. */
-static void WritePool_queueWriteJob(write_job_t *job) {
- write_pool_ctx_t* ctx = job->ctx;
- if(ctx->writerPool)
- POOL_add(ctx->writerPool, WritePool_executeWriteJob, job);
- else
- WritePool_executeWriteJob(job);
-}
-
-/* WritePool_queueAndReacquireWriteJob:
- * Queues a write job for execution and acquires a new one.
- * After execution `job`'s pointed value would change to the newly acquired job.
- * Make sure to set `usedBufferSize` to the wanted length before call.
- * The queued job shouldn't be used directly after queueing it. */
-static void WritePool_queueAndReacquireWriteJob(write_job_t **job) {
- WritePool_queueWriteJob(*job);
- *job = WritePool_acquireWriteJob((*job)->ctx);
-}
-
-/* WritePool_sparseWriteEnd:
- * Ends sparse writes to the current dstFile.
- * Blocks on completion of all current write jobs before executing. */
-static void WritePool_sparseWriteEnd(write_pool_ctx_t* ctx) {
- assert(ctx != NULL);
- if(ctx->writerPool)
- POOL_joinJobs(ctx->writerPool);
- FIO_fwriteSparseEnd(ctx->prefs, ctx->dstFile, ctx->storedSkips);
- ctx->storedSkips = 0;
-}
-
-/* WritePool_setDstFile:
- * Sets the destination file for future files in the pool.
- * Requires completion of all queues write jobs and release of all otherwise acquired jobs.
- * Also requires ending of sparse write if a previous file was used in sparse mode. */
-static void WritePool_setDstFile(write_pool_ctx_t *ctx, FILE* dstFile) {
- assert(ctx!=NULL);
- /* We can change the dst file only if we have finished writing */
- if(ctx->writerPool)
- POOL_joinJobs(ctx->writerPool);
- assert(ctx->storedSkips == 0);
- assert(ctx->availableWriteJobs == ctx->totalWriteJobs);
- ctx->dstFile = dstFile;
-}
-
-/* WritePool_closeDstFile:
- * Ends sparse write and closes the writePool's current dstFile and sets the dstFile to NULL.
- * Requires completion of all queues write jobs and release of all otherwise acquired jobs. */
-static int WritePool_closeDstFile(write_pool_ctx_t *ctx) {
- FILE *dstFile = ctx->dstFile;
- assert(dstFile!=NULL || ctx->prefs->testMode!=0);
- WritePool_sparseWriteEnd(ctx);
- WritePool_setDstFile(ctx, NULL);
- return fclose(dstFile);
-}
-
/** FIO_passThrough() : just copy input into output, for compatibility with gzip -df mode
@return : 0 (no error) */
static int FIO_passThrough(const FIO_prefs_t* const prefs,
@@ -2403,7 +1958,7 @@ static int FIO_passThrough(const FIO_prefs_t* const prefs,
do {
readFromInput = fread(buffer, 1, blockSize, finput);
- storedSkips = FIO_fwriteSparse(foutput, buffer, readFromInput, prefs, storedSkips);
+ storedSkips = AIO_fwriteSparse(foutput, buffer, readFromInput, prefs, storedSkips);
} while (readFromInput == blockSize);
if (ferror(finput)) {
DISPLAYLEVEL(1, "Pass-through read error : %s\n", strerror(errno));
@@ -2411,7 +1966,7 @@ static int FIO_passThrough(const FIO_prefs_t* const prefs,
}
assert(feof(finput));
- FIO_fwriteSparseEnd(prefs, foutput, storedSkips);
+ AIO_fwriteSparseEnd(prefs, foutput, storedSkips);
return 0;
}
@@ -2458,7 +2013,7 @@ FIO_decompressZstdFrame(FIO_ctx_t* const fCtx, dRess_t* ress, FILE* finput,
U64 alreadyDecoded) /* for multi-frames streams */
{
U64 frameSize = 0;
- write_job_t *writeJob = WritePool_acquireWriteJob(ress->writePoolCtx);
+ IOJob_t *writeJob = AIO_WritePool_acquireJob(ress->writeCtx);
/* display last 20 characters only */
{ size_t const srcFileLength = strlen(srcFileName);
@@ -2486,12 +2041,13 @@ FIO_decompressZstdFrame(FIO_ctx_t* const fCtx, dRess_t* ress, FILE* finput,
DISPLAYLEVEL(1, "%s : Decoding error (36) : %s \n",
srcFileName, ZSTD_getErrorName(readSizeHint));
FIO_zstdErrorHelp(prefs, ress, readSizeHint, srcFileName);
+ AIO_WritePool_releaseIoJob(writeJob);
return FIO_ERROR_FRAME_DECODING;
}
/* Write block */
writeJob->usedBufferSize = outBuff.pos;
- WritePool_queueAndReacquireWriteJob(&writeJob);
+ AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
frameSize += outBuff.pos;
if (fCtx->nbFilesTotal > 1) {
size_t srcFileNameSize = strlen(srcFileName);
@@ -2526,8 +2082,8 @@ FIO_decompressZstdFrame(FIO_ctx_t* const fCtx, dRess_t* ress, FILE* finput,
ress->srcBufferLoaded += readSize;
} } }
- WritePool_releaseWriteJob(writeJob);
- WritePool_sparseWriteEnd(ress->writePoolCtx);
+ AIO_WritePool_releaseIoJob(writeJob);
+ AIO_WritePool_sparseWriteEnd(ress->writeCtx);
return frameSize;
}
@@ -2541,7 +2097,7 @@ FIO_decompressGzFrame(dRess_t* ress, FILE* srcFile, const char* srcFileName)
z_stream strm;
int flush = Z_NO_FLUSH;
int decodingError = 0;
- write_job_t *writeJob = NULL;
+ IOJob_t *writeJob = NULL;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
@@ -2552,7 +2108,7 @@ FIO_decompressGzFrame(dRess_t* ress, FILE* srcFile, const char* srcFileName)
if (inflateInit2(&strm, 15 /* maxWindowLogSize */ + 16 /* gzip only */) != Z_OK)
return FIO_ERROR_FRAME_DECODING;
- writeJob = WritePool_acquireWriteJob(ress->writePoolCtx);
+ writeJob = AIO_WritePool_acquireJob(ress->writeCtx);
strm.next_out = (Bytef*)writeJob->buffer;
strm.avail_out = (uInt)writeJob->bufferSize;
strm.avail_in = (uInt)ress->srcBufferLoaded;
@@ -2578,7 +2134,7 @@ FIO_decompressGzFrame(dRess_t* ress, FILE* srcFile, const char* srcFileName)
{ size_t const decompBytes = writeJob->bufferSize - strm.avail_out;
if (decompBytes) {
writeJob->usedBufferSize = decompBytes;
- WritePool_queueAndReacquireWriteJob(&writeJob);
+ AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
outFileSize += decompBytes;
strm.next_out = (Bytef*)writeJob->buffer;
strm.avail_out = (uInt)writeJob->bufferSize;
@@ -2594,8 +2150,8 @@ FIO_decompressGzFrame(dRess_t* ress, FILE* srcFile, const char* srcFileName)
DISPLAYLEVEL(1, "zstd: %s: inflateEnd error \n", srcFileName);
decodingError = 1;
}
- WritePool_releaseWriteJob(writeJob);
- WritePool_sparseWriteEnd(ress->writePoolCtx);
+ AIO_WritePool_releaseIoJob(writeJob);
+ AIO_WritePool_sparseWriteEnd(ress->writeCtx);
return decodingError ? FIO_ERROR_FRAME_DECODING : outFileSize;
}
#endif
@@ -2610,7 +2166,7 @@ FIO_decompressLzmaFrame(dRess_t* ress, FILE* srcFile,
lzma_action action = LZMA_RUN;
lzma_ret initRet;
int decodingError = 0;
- write_job_t *writeJob = NULL;
+ IOJob_t *writeJob = NULL;
strm.next_in = 0;
strm.avail_in = 0;
@@ -2627,7 +2183,7 @@ FIO_decompressLzmaFrame(dRess_t* ress, FILE* srcFile,
return FIO_ERROR_FRAME_DECODING;
}
- writeJob = WritePool_acquireWriteJob(ress->writePoolCtx);
+ writeJob = AIO_WritePool_acquireJob(ress->writeCtx);
strm.next_out = (Bytef*)writeJob->buffer;
strm.avail_out = (uInt)writeJob->bufferSize;
strm.next_in = (BYTE const*)ress->srcBuffer;
@@ -2655,7 +2211,7 @@ FIO_decompressLzmaFrame(dRess_t* ress, FILE* srcFile,
{ size_t const decompBytes = writeJob->bufferSize - strm.avail_out;
if (decompBytes) {
writeJob->usedBufferSize = decompBytes;
- WritePool_queueAndReacquireWriteJob(&writeJob);
+ AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
outFileSize += decompBytes;
strm.next_out = (Bytef*)writeJob->buffer;
strm.avail_out = writeJob->bufferSize;
@@ -2665,8 +2221,8 @@ FIO_decompressLzmaFrame(dRess_t* ress, FILE* srcFile,
FIO_consumeDSrcBuffer(ress, ress->srcBufferLoaded - strm.avail_in);
lzma_end(&strm);
- WritePool_releaseWriteJob(writeJob);
- WritePool_sparseWriteEnd(ress->writePoolCtx);
+ AIO_WritePool_releaseIoJob(writeJob);
+ AIO_WritePool_sparseWriteEnd(ress->writeCtx);
return decodingError ? FIO_ERROR_FRAME_DECODING : outFileSize;
}
#endif
@@ -2681,13 +2237,15 @@ FIO_decompressLz4Frame(dRess_t* ress, FILE* srcFile,
LZ4F_decompressionContext_t dCtx;
LZ4F_errorCode_t const errorCode = LZ4F_createDecompressionContext(&dCtx, LZ4F_VERSION);
int decodingError = 0;
- write_job_t *writeJob = WritePool_acquireWriteJob(ress->writePoolCtx);
+ IOJob_t *writeJob = NULL;
if (LZ4F_isError(errorCode)) {
DISPLAYLEVEL(1, "zstd: failed to create lz4 decompression context \n");
return FIO_ERROR_FRAME_DECODING;
}
+ writeJob = AIO_WritePool_acquireJob(ress->writeCtx);
+
/* Main Loop */
for (;nextToLoad;) {
size_t readSize;
@@ -2724,7 +2282,7 @@ FIO_decompressLz4Frame(dRess_t* ress, FILE* srcFile,
if (decodedBytes) {
UTIL_HumanReadableSize_t hrs;
writeJob->usedBufferSize = decodedBytes;
- WritePool_queueAndReacquireWriteJob(&writeJob);
+ AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
filesize += decodedBytes;
hrs = UTIL_makeHumanReadableSize(filesize);
DISPLAYUPDATE(2, "\rDecompressed : %.*f%s ", hrs.precision, hrs.value, hrs.suffix);
@@ -2740,8 +2298,8 @@ FIO_decompressLz4Frame(dRess_t* ress, FILE* srcFile,
}
LZ4F_freeDecompressionContext(dCtx);
- WritePool_releaseWriteJob(writeJob);
- WritePool_sparseWriteEnd(ress->writePoolCtx);
+ AIO_WritePool_releaseIoJob(writeJob);
+ AIO_WritePool_sparseWriteEnd(ress->writeCtx);
return decodingError ? FIO_ERROR_FRAME_DECODING : filesize;
}
@@ -2818,7 +2376,7 @@ static int FIO_decompressFrames(FIO_ctx_t* const fCtx,
#endif
} else if ((prefs->overwrite) && !strcmp (dstFileName, stdoutmark)) { /* pass-through mode */
return FIO_passThrough(prefs,
- ress.writePoolCtx->dstFile, srcFile,
+ AIO_WritePool_getFile(ress.writeCtx), srcFile,
ress.srcBuffer, ress.srcBufferSize,
ress.srcBufferLoaded);
} else {
@@ -2856,7 +2414,7 @@ static int FIO_decompressDstFile(FIO_ctx_t* const fCtx,
int releaseDstFile = 0;
int transferMTime = 0;
- if ((ress.writePoolCtx->dstFile == NULL) && (prefs->testMode==0)) {
+ if ((AIO_WritePool_getFile(ress.writeCtx) == NULL) && (prefs->testMode == 0)) {
FILE *dstFile;
int dstFilePermissions = DEFAULT_FILE_PERMISSIONS;
if ( strcmp(srcFileName, stdinmark) /* special case : don't transfer permissions from stdin */
@@ -2871,7 +2429,7 @@ static int FIO_decompressDstFile(FIO_ctx_t* const fCtx,
dstFile = FIO_openDstFile(fCtx, prefs, srcFileName, dstFileName, dstFilePermissions);
if (dstFile==NULL) return 1;
- WritePool_setDstFile(ress.writePoolCtx, dstFile);
+ AIO_WritePool_setFile(ress.writeCtx, dstFile);
/* Must only be added after FIO_openDstFile() succeeds.
* Otherwise we may delete the destination file if it already exists,
@@ -2884,7 +2442,7 @@ static int FIO_decompressDstFile(FIO_ctx_t* const fCtx,
if (releaseDstFile) {
clearHandler();
- if (WritePool_closeDstFile(ress.writePoolCtx)) {
+ if (AIO_WritePool_closeFile(ress.writeCtx)) {
DISPLAYLEVEL(1, "zstd: %s: %s \n", dstFileName, strerror(errno));
result = 1;
}
@@ -3100,14 +2658,14 @@ FIO_decompressMultipleFilenames(FIO_ctx_t* const fCtx,
if (!prefs->testMode) {
FILE* dstFile = FIO_openDstFile(fCtx, prefs, NULL, outFileName, DEFAULT_FILE_PERMISSIONS);
if (dstFile == 0) EXM_THROW(19, "cannot open %s", outFileName);
- WritePool_setDstFile(ress.writePoolCtx, dstFile);
+ AIO_WritePool_setFile(ress.writeCtx, dstFile);
}
for (; fCtx->currFileIdx < fCtx->nbFilesTotal; fCtx->currFileIdx++) {
status = FIO_decompressSrcFile(fCtx, prefs, ress, outFileName, srcNamesTable[fCtx->currFileIdx]);
if (!status) fCtx->nbFilesProcessed++;
error |= status;
}
- if ((!prefs->testMode) && (WritePool_closeDstFile(ress.writePoolCtx)))
+ if ((!prefs->testMode) && (AIO_WritePool_closeFile(ress.writeCtx)))
EXM_THROW(72, "Write error : %s : cannot properly close output file",
strerror(errno));
} else {
diff --git a/programs/fileio.h b/programs/fileio.h
index 398937a64e8..9d6ebb1accb 100644
--- a/programs/fileio.h
+++ b/programs/fileio.h
@@ -13,6 +13,7 @@
#define FILEIO_H_23981798732
#define ZSTD_STATIC_LINKING_ONLY /* ZSTD_compressionParameters */
+#include "fileio_types.h"
#include "../lib/zstd.h" /* ZSTD_* */
#if defined (__cplusplus)
@@ -53,10 +54,6 @@ extern "C" {
/*-*************************************
* Types
***************************************/
-typedef enum { FIO_zstdCompression, FIO_gzipCompression, FIO_xzCompression, FIO_lzmaCompression, FIO_lz4Compression } FIO_compressionType_t;
-
-typedef struct FIO_prefs_s FIO_prefs_t;
-
FIO_prefs_t* FIO_createPreferences(void);
void FIO_freePreferences(FIO_prefs_t* const prefs);
@@ -66,9 +63,6 @@ typedef struct FIO_ctx_s FIO_ctx_t;
FIO_ctx_t* FIO_createContext(void);
void FIO_freeContext(FIO_ctx_t* const fCtx);
-typedef struct FIO_display_prefs_s FIO_display_prefs_t;
-
-typedef enum { FIO_ps_auto, FIO_ps_never, FIO_ps_always } FIO_progressSetting_e;
/*-*************************************
* Parameters
diff --git a/programs/fileio_asyncio.c b/programs/fileio_asyncio.c
new file mode 100644
index 00000000000..868720a1da2
--- /dev/null
+++ b/programs/fileio_asyncio.c
@@ -0,0 +1,365 @@
+/*
+ * Copyright (c) Yann Collet, 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 "platform.h"
+#include /* fprintf, open, fdopen, fread, _fileno, stdin, stdout */
+#include /* malloc, free */
+#include
+#include /* errno */
+
+#if defined (_MSC_VER)
+# include
+# include
+#endif
+
+#include "fileio_asyncio.h"
+#include "fileio_common.h"
+
+/* **********************************************************************
+ * Sparse write
+ ************************************************************************/
+
+/** AIO_fwriteSparse() :
+* @return : storedSkips,
+* argument for next call to AIO_fwriteSparse() or AIO_fwriteSparseEnd() */
+unsigned AIO_fwriteSparse(FILE* file,
+ const void* buffer, size_t bufferSize,
+ const FIO_prefs_t* const prefs,
+ unsigned storedSkips)
+{
+ const size_t* const bufferT = (const size_t*)buffer; /* Buffer is supposed malloc'ed, hence aligned on size_t */
+ size_t bufferSizeT = bufferSize / sizeof(size_t);
+ const size_t* const bufferTEnd = bufferT + bufferSizeT;
+ const size_t* ptrT = bufferT;
+ static const size_t segmentSizeT = (32 KB) / sizeof(size_t); /* check every 32 KB */
+
+ if (prefs->testMode) return 0; /* do not output anything in test mode */
+
+ if (!prefs->sparseFileSupport) { /* normal write */
+ size_t const sizeCheck = fwrite(buffer, 1, bufferSize, file);
+ if (sizeCheck != bufferSize)
+ EXM_THROW(70, "Write error : cannot write decoded block : %s",
+ strerror(errno));
+ return 0;
+ }
+
+ /* avoid int overflow */
+ if (storedSkips > 1 GB) {
+ if (LONG_SEEK(file, 1 GB, SEEK_CUR) != 0)
+ EXM_THROW(91, "1 GB skip error (sparse file support)");
+ storedSkips -= 1 GB;
+ }
+
+ while (ptrT < bufferTEnd) {
+ size_t nb0T;
+
+ /* adjust last segment if < 32 KB */
+ size_t seg0SizeT = segmentSizeT;
+ if (seg0SizeT > bufferSizeT) seg0SizeT = bufferSizeT;
+ bufferSizeT -= seg0SizeT;
+
+ /* count leading zeroes */
+ for (nb0T=0; (nb0T < seg0SizeT) && (ptrT[nb0T] == 0); nb0T++) ;
+ storedSkips += (unsigned)(nb0T * sizeof(size_t));
+
+ if (nb0T != seg0SizeT) { /* not all 0s */
+ size_t const nbNon0ST = seg0SizeT - nb0T;
+ /* skip leading zeros */
+ if (LONG_SEEK(file, storedSkips, SEEK_CUR) != 0)
+ EXM_THROW(92, "Sparse skip error ; try --no-sparse");
+ storedSkips = 0;
+ /* write the rest */
+ if (fwrite(ptrT + nb0T, sizeof(size_t), nbNon0ST, file) != nbNon0ST)
+ EXM_THROW(93, "Write error : cannot write decoded block : %s",
+ strerror(errno));
+ }
+ ptrT += seg0SizeT;
+ }
+
+ { static size_t const maskT = sizeof(size_t)-1;
+ if (bufferSize & maskT) {
+ /* size not multiple of sizeof(size_t) : implies end of block */
+ const char* const restStart = (const char*)bufferTEnd;
+ const char* restPtr = restStart;
+ const char* const restEnd = (const char*)buffer + bufferSize;
+ assert(restEnd > restStart && restEnd < restStart + sizeof(size_t));
+ for ( ; (restPtr < restEnd) && (*restPtr == 0); restPtr++) ;
+ storedSkips += (unsigned) (restPtr - restStart);
+ if (restPtr != restEnd) {
+ /* not all remaining bytes are 0 */
+ size_t const restSize = (size_t)(restEnd - restPtr);
+ if (LONG_SEEK(file, storedSkips, SEEK_CUR) != 0)
+ EXM_THROW(92, "Sparse skip error ; try --no-sparse");
+ if (fwrite(restPtr, 1, restSize, file) != restSize)
+ EXM_THROW(95, "Write error : cannot write end of decoded block : %s",
+ strerror(errno));
+ storedSkips = 0;
+ } } }
+
+ return storedSkips;
+}
+
+void AIO_fwriteSparseEnd(const FIO_prefs_t* const prefs, FILE* file, unsigned storedSkips)
+{
+ if (prefs->testMode) assert(storedSkips == 0);
+ if (storedSkips>0) {
+ assert(prefs->sparseFileSupport > 0); /* storedSkips>0 implies sparse support is enabled */
+ (void)prefs; /* assert can be disabled, in which case prefs becomes unused */
+ if (LONG_SEEK(file, storedSkips-1, SEEK_CUR) != 0)
+ EXM_THROW(69, "Final skip error (sparse file support)");
+ /* last zero must be explicitly written,
+ * so that skipped ones get implicitly translated as zero by FS */
+ { const char lastZeroByte[1] = { 0 };
+ if (fwrite(lastZeroByte, 1, 1, file) != 1)
+ EXM_THROW(69, "Write error : cannot write last zero : %s", strerror(errno));
+ } }
+}
+
+
+/* **********************************************************************
+ * AsyncIO functionality
+ ************************************************************************/
+
+/* ***********************************
+ * General IoPool implementation
+ *************************************/
+
+static IOJob_t *AIO_IOPool_createIoJob(IOPoolCtx_t *ctx, size_t bufferSize) {
+ void *buffer;
+ IOJob_t *job;
+ job = (IOJob_t*) malloc(sizeof(IOJob_t));
+ buffer = malloc(bufferSize);
+ if(!job || !buffer)
+ EXM_THROW(101, "Allocation error : not enough memory");
+ job->buffer = buffer;
+ job->bufferSize = bufferSize;
+ job->usedBufferSize = 0;
+ job->file = NULL;
+ job->ctx = ctx;
+ job->offset = 0;
+ return job;
+}
+
+
+/* AIO_IOPool_createThreadPool:
+ * Creates a thread pool and a mutex for threaded IO pool.
+ * Displays warning if asyncio is requested but MT isn't available. */
+static void AIO_IOPool_createThreadPool(IOPoolCtx_t *ctx, const FIO_prefs_t *prefs) {
+ ctx->threadPool = NULL;
+ if(prefs->asyncIO) {
+ if (ZSTD_pthread_mutex_init(&ctx->ioJobsMutex, NULL))
+ EXM_THROW(102,"Failed creating write availableJobs mutex");
+ /* We want MAX_IO_JOBS-2 queue items because we need to always have 1 free buffer to
+ * decompress into and 1 buffer that's actively written to disk and owned by the writing thread. */
+ assert(MAX_IO_JOBS >= 2);
+ ctx->threadPool = POOL_create(1, MAX_IO_JOBS - 2);
+ if (!ctx->threadPool)
+ EXM_THROW(104, "Failed creating writer thread pool");
+ }
+}
+
+/* AIO_IOPool_init:
+ * Allocates and sets and a new write pool including its included availableJobs. */
+static void AIO_IOPool_init(IOPoolCtx_t *ctx, FIO_prefs_t* const prefs, POOL_function poolFunction, size_t bufferSize) {
+ int i;
+ AIO_IOPool_createThreadPool(ctx, prefs);
+ ctx->prefs = prefs;
+ ctx->poolFunction = poolFunction;
+ ctx->totalIoJobs = ctx->threadPool ? MAX_IO_JOBS : 1;
+ ctx->availableJobsCount = ctx->totalIoJobs;
+ for(i=0; i < ctx->availableJobsCount; i++) {
+ ctx->availableJobs[i] = AIO_IOPool_createIoJob(ctx, bufferSize);
+ }
+ ctx->file = NULL;
+}
+
+
+/* AIO_IOPool_releaseIoJob:
+ * Releases an acquired job back to the pool. Doesn't execute the job. */
+static void AIO_IOPool_releaseIoJob(IOJob_t *job) {
+ IOPoolCtx_t *ctx = (IOPoolCtx_t *) job->ctx;
+ if(ctx->threadPool) {
+ ZSTD_pthread_mutex_lock(&ctx->ioJobsMutex);
+ assert(ctx->availableJobsCount < MAX_IO_JOBS);
+ ctx->availableJobs[ctx->availableJobsCount++] = job;
+ ZSTD_pthread_mutex_unlock(&ctx->ioJobsMutex);
+ } else {
+ assert(ctx->availableJobsCount == 0);
+ ctx->availableJobsCount++;
+ }
+}
+
+/* AIO_IOPool_join:
+ * Waits for all tasks in the pool to finish executing. */
+static void AIO_IOPool_join(IOPoolCtx_t* ctx) {
+ if(ctx->threadPool)
+ POOL_joinJobs(ctx->threadPool);
+}
+
+/* AIO_IOPool_free:
+ * Release a previously allocated write thread pool. Makes sure all takss are done and released. */
+static void AIO_IOPool_destroy(IOPoolCtx_t* ctx) {
+ int i;
+ if(ctx->threadPool) {
+ /* Make sure we finish all tasks and then free the resources */
+ AIO_IOPool_join(ctx);
+ /* Make sure we are not leaking availableJobs */
+ assert(ctx->availableJobsCount == ctx->totalIoJobs);
+ POOL_free(ctx->threadPool);
+ ZSTD_pthread_mutex_destroy(&ctx->ioJobsMutex);
+ }
+ assert(ctx->file == NULL);
+ for(i=0; iavailableJobsCount; i++) {
+ IOJob_t* job = (IOJob_t*) ctx->availableJobs[i];
+ free(job->buffer);
+ free(job);
+ }
+}
+
+/* AIO_IOPool_acquireJob:
+ * Returns an available io job to be used for a future io. */
+static IOJob_t* AIO_IOPool_acquireJob(IOPoolCtx_t *ctx) {
+ IOJob_t *job;
+ assert(ctx->file != NULL || ctx->prefs->testMode);
+ if(ctx->threadPool) {
+ ZSTD_pthread_mutex_lock(&ctx->ioJobsMutex);
+ assert(ctx->availableJobsCount > 0);
+ job = (IOJob_t*) ctx->availableJobs[--ctx->availableJobsCount];
+ ZSTD_pthread_mutex_unlock(&ctx->ioJobsMutex);
+ } else {
+ assert(ctx->availableJobsCount == 1);
+ ctx->availableJobsCount--;
+ job = (IOJob_t*)ctx->availableJobs[0];
+ }
+ job->usedBufferSize = 0;
+ job->file = ctx->file;
+ job->offset = 0;
+ return job;
+}
+
+
+/* AIO_IOPool_setFile:
+ * Sets the destination file for future files in the pool.
+ * Requires completion of all queues write jobs and release of all otherwise acquired jobs.
+ * Also requires ending of sparse write if a previous file was used in sparse mode. */
+static void AIO_IOPool_setFile(IOPoolCtx_t *ctx, FILE* file) {
+ assert(ctx!=NULL);
+ AIO_IOPool_join(ctx);
+ assert(ctx->availableJobsCount == ctx->totalIoJobs);
+ ctx->file = file;
+}
+
+static FILE* AIO_IOPool_getFile(IOPoolCtx_t *ctx) {
+ return ctx->file;
+}
+
+/* AIO_IOPool_enqueueJob:
+ * Enqueues an io job for execution.
+ * The queued job shouldn't be used directly after queueing it. */
+static void AIO_IOPool_enqueueJob(IOJob_t *job) {
+ IOPoolCtx_t* ctx = (IOPoolCtx_t *)job->ctx;
+ if(ctx->threadPool)
+ POOL_add(ctx->threadPool, ctx->poolFunction, job);
+ else
+ ctx->poolFunction(job);
+}
+
+/* ***********************************
+ * WritePool implementation
+ *************************************/
+
+/* AIO_WritePool_acquireJob:
+ * Returns an available write job to be used for a future write. */
+IOJob_t* AIO_WritePool_acquireJob(WritePoolCtx_t *ctx) {
+ return AIO_IOPool_acquireJob(&ctx->base);
+}
+
+/* AIO_WritePool_enqueueAndReacquireWriteJob:
+ * Queues a write job for execution and acquires a new one.
+ * After execution `job`'s pointed value would change to the newly acquired job.
+ * Make sure to set `usedBufferSize` to the wanted length before call.
+ * The queued job shouldn't be used directly after queueing it. */
+void AIO_WritePool_enqueueAndReacquireWriteJob(IOJob_t **job) {
+ AIO_IOPool_enqueueJob(*job);
+ *job = AIO_IOPool_acquireJob((IOPoolCtx_t *)(*job)->ctx);
+}
+
+/* AIO_WritePool_sparseWriteEnd:
+ * Ends sparse writes to the current file.
+ * Blocks on completion of all current write jobs before executing. */
+void AIO_WritePool_sparseWriteEnd(WritePoolCtx_t *ctx) {
+ assert(ctx != NULL);
+ if(ctx->base.threadPool)
+ POOL_joinJobs(ctx->base.threadPool);
+ AIO_fwriteSparseEnd(ctx->base.prefs, ctx->base.file, ctx->storedSkips);
+ ctx->storedSkips = 0;
+}
+
+/* AIO_WritePool_setFile:
+ * Sets the destination file for future writes in the pool.
+ * Requires completion of all queues write jobs and release of all otherwise acquired jobs.
+ * Also requires ending of sparse write if a previous file was used in sparse mode. */
+void AIO_WritePool_setFile(WritePoolCtx_t *ctx, FILE* file) {
+ AIO_IOPool_setFile(&ctx->base, file);
+ assert(ctx->storedSkips == 0);
+}
+
+/* AIO_WritePool_getFile:
+ * Returns the file the writePool is currently set to write to. */
+FILE* AIO_WritePool_getFile(WritePoolCtx_t *ctx) {
+ return AIO_IOPool_getFile(&ctx->base);
+}
+
+/* AIO_WritePool_releaseIoJob:
+ * Releases an acquired job back to the pool. Doesn't execute the job. */
+void AIO_WritePool_releaseIoJob(IOJob_t *job) {
+ AIO_IOPool_releaseIoJob(job);
+}
+
+/* AIO_WritePool_closeFile:
+ * Ends sparse write and closes the writePool's current file and sets the file to NULL.
+ * Requires completion of all queues write jobs and release of all otherwise acquired jobs. */
+int AIO_WritePool_closeFile(WritePoolCtx_t *ctx) {
+ FILE *dstFile = ctx->base.file;
+ assert(dstFile!=NULL || ctx->base.prefs->testMode!=0);
+ AIO_WritePool_sparseWriteEnd(ctx);
+ AIO_IOPool_setFile(&ctx->base, NULL);
+ return fclose(dstFile);
+}
+
+/* AIO_WritePool_executeWriteJob:
+ * Executes a write job synchronously. Can be used as a function for a thread pool. */
+static void AIO_WritePool_executeWriteJob(void* opaque){
+ IOJob_t* job = (IOJob_t*) opaque;
+ WritePoolCtx_t* ctx = (WritePoolCtx_t*) job->ctx;
+ ctx->storedSkips = AIO_fwriteSparse(job->file, job->buffer, job->usedBufferSize, ctx->base.prefs, ctx->storedSkips);
+ AIO_IOPool_releaseIoJob(job);
+}
+
+/* AIO_WritePool_create:
+ * Allocates and sets and a new write pool including its included jobs. */
+WritePoolCtx_t* AIO_WritePool_create(FIO_prefs_t* const prefs, size_t bufferSize) {
+ WritePoolCtx_t* ctx = (WritePoolCtx_t*) malloc(sizeof(WritePoolCtx_t));
+ if(!ctx) EXM_THROW(100, "Allocation error : not enough memory");
+ AIO_IOPool_init(&ctx->base, prefs, AIO_WritePool_executeWriteJob, bufferSize);
+ ctx->storedSkips = 0;
+ return ctx;
+}
+
+/* AIO_WritePool_free:
+ * Frees and releases a writePool and its resources. Closes destination file if needs to. */
+void AIO_WritePool_free(WritePoolCtx_t* ctx) {
+ /* Make sure we finish all tasks and then free the resources */
+ if(AIO_WritePool_getFile(ctx))
+ AIO_WritePool_closeFile(ctx);
+ AIO_IOPool_destroy(&ctx->base);
+ assert(ctx->storedSkips==0);
+ free(ctx);
+}
diff --git a/programs/fileio_asyncio.h b/programs/fileio_asyncio.h
new file mode 100644
index 00000000000..3e91164c558
--- /dev/null
+++ b/programs/fileio_asyncio.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) Yann Collet, 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 ZSTD_FILEIO_ASYNCIO_H
+#define ZSTD_FILEIO_ASYNCIO_H
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+#include "../lib/common/mem.h" /* U32, U64 */
+#include "fileio_types.h"
+#include "platform.h"
+#include "util.h"
+#include "../lib/common/pool.h"
+#include "../lib/common/threading.h"
+
+#define MAX_IO_JOBS (10)
+
+typedef struct {
+ /* These struct fields should be set only on creation and not changed afterwards */
+ POOL_ctx* threadPool;
+ int totalIoJobs;
+ FIO_prefs_t* prefs;
+ POOL_function poolFunction;
+
+ /* Controls the file we currently write to, make changes only by using provided utility functions */
+ FILE* file;
+
+ /* The jobs and availableJobsCount fields are accessed by both the main and worker threads and should
+ * only be mutated after locking the mutex */
+ ZSTD_pthread_mutex_t ioJobsMutex;
+ void* availableJobs[MAX_IO_JOBS];
+ int availableJobsCount;
+} IOPoolCtx_t;
+
+typedef struct {
+ IOPoolCtx_t base;
+ unsigned storedSkips;
+} WritePoolCtx_t;
+
+typedef struct {
+ /* These fields are automatically set and shouldn't be changed by non WritePool code. */
+ void *ctx;
+ FILE* file;
+ void *buffer;
+ size_t bufferSize;
+
+ /* This field should be changed before a job is queued for execution and should contain the number
+ * of bytes to write from the buffer. */
+ size_t usedBufferSize;
+ U64 offset;
+} IOJob_t;
+
+/** AIO_fwriteSparse() :
+* @return : storedSkips,
+* argument for next call to AIO_fwriteSparse() or AIO_fwriteSparseEnd() */
+unsigned AIO_fwriteSparse(FILE* file,
+ const void* buffer, size_t bufferSize,
+ const FIO_prefs_t* const prefs,
+ unsigned storedSkips);
+
+void AIO_fwriteSparseEnd(const FIO_prefs_t* const prefs, FILE* file, unsigned storedSkips);
+
+/* AIO_WritePool_releaseIoJob:
+ * Releases an acquired job back to the pool. Doesn't execute the job. */
+void AIO_WritePool_releaseIoJob(IOJob_t *job);
+
+/* AIO_WritePool_acquireJob:
+ * Returns an available write job to be used for a future write. */
+IOJob_t* AIO_WritePool_acquireJob(WritePoolCtx_t *ctx);
+
+/* AIO_WritePool_enqueueAndReacquireWriteJob:
+ * Enqueues a write job for execution and acquires a new one.
+ * After execution `job`'s pointed value would change to the newly acquired job.
+ * Make sure to set `usedBufferSize` to the wanted length before call.
+ * The queued job shouldn't be used directly after queueing it. */
+void AIO_WritePool_enqueueAndReacquireWriteJob(IOJob_t **job);
+
+/* AIO_WritePool_sparseWriteEnd:
+ * Ends sparse writes to the current file.
+ * Blocks on completion of all current write jobs before executing. */
+void AIO_WritePool_sparseWriteEnd(WritePoolCtx_t *ctx);
+
+/* AIO_WritePool_setFile:
+ * Sets the destination file for future writes in the pool.
+ * Requires completion of all queues write jobs and release of all otherwise acquired jobs.
+ * Also requires ending of sparse write if a previous file was used in sparse mode. */
+void AIO_WritePool_setFile(WritePoolCtx_t *ctx, FILE* file);
+
+/* AIO_WritePool_getFile:
+ * Returns the file the writePool is currently set to write to. */
+FILE* AIO_WritePool_getFile(WritePoolCtx_t *ctx);
+
+/* AIO_WritePool_closeFile:
+ * Ends sparse write and closes the writePool's current file and sets the file to NULL.
+ * Requires completion of all queues write jobs and release of all otherwise acquired jobs. */
+int AIO_WritePool_closeFile(WritePoolCtx_t *ctx);
+
+/* AIO_WritePool_create:
+ * Allocates and sets and a new write pool including its included jobs.
+ * bufferSize should be set to the maximal buffer we want to write to at a time. */
+WritePoolCtx_t* AIO_WritePool_create(FIO_prefs_t* const prefs, size_t bufferSize);
+
+/* AIO_WritePool_free:
+ * Frees and releases a writePool and its resources. Closes destination file. */
+void AIO_WritePool_free(WritePoolCtx_t* ctx);
+
+#if defined (__cplusplus)
+}
+#endif
+
+#endif /* ZSTD_FILEIO_ASYNCIO_H */
diff --git a/programs/fileio_common.h b/programs/fileio_common.h
new file mode 100644
index 00000000000..d33c19d7bd1
--- /dev/null
+++ b/programs/fileio_common.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) Yann Collet, 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 ZSTD_FILEIO_COMMON_H
+#define ZSTD_FILEIO_COMMON_H
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+#include "../lib/common/mem.h" /* U32, U64 */
+#include "fileio_types.h"
+#include "platform.h"
+#include "timefn.h" /* UTIL_getTime, UTIL_clockSpanMicro */
+
+/*-*************************************
+* Macros
+***************************************/
+#define KB *(1 <<10)
+#define MB *(1 <<20)
+#define GB *(1U<<30)
+#undef MAX
+#define MAX(a,b) ((a)>(b) ? (a) : (b))
+
+extern FIO_display_prefs_t g_display_prefs;
+
+#define DISPLAY(...) fprintf(stderr, __VA_ARGS__)
+#define DISPLAYOUT(...) fprintf(stdout, __VA_ARGS__)
+#define DISPLAYLEVEL(l, ...) { if (g_display_prefs.displayLevel>=l) { DISPLAY(__VA_ARGS__); } }
+
+extern UTIL_time_t g_displayClock;
+
+#define REFRESH_RATE ((U64)(SEC_TO_MICRO / 6))
+#define READY_FOR_UPDATE() ((g_display_prefs.progressSetting != FIO_ps_never) && UTIL_clockSpanMicro(g_displayClock) > REFRESH_RATE)
+#define DELAY_NEXT_UPDATE() { g_displayClock = UTIL_getTime(); }
+#define DISPLAYUPDATE(l, ...) { \
+ if (g_display_prefs.displayLevel>=l && (g_display_prefs.progressSetting != FIO_ps_never)) { \
+ if (READY_FOR_UPDATE() || (g_display_prefs.displayLevel>=4)) { \
+ DELAY_NEXT_UPDATE(); \
+ DISPLAY(__VA_ARGS__); \
+ if (g_display_prefs.displayLevel>=4) fflush(stderr); \
+ } } }
+
+#undef MIN /* in case it would be already defined */
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+
+#define EXM_THROW(error, ...) \
+{ \
+ DISPLAYLEVEL(1, "zstd: "); \
+ DISPLAYLEVEL(5, "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_V(v, f) \
+ v = f; \
+ if (ZSTD_isError(v)) { \
+ DISPLAYLEVEL(5, "%s \n", #f); \
+ EXM_THROW(11, "%s", ZSTD_getErrorName(v)); \
+ }
+#define CHECK(f) { size_t err; CHECK_V(err, f); }
+
+
+/* Avoid fseek()'s 2GiB barrier with MSVC, macOS, *BSD, MinGW */
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+# define LONG_SEEK _fseeki64
+# define LONG_TELL _ftelli64
+#elif !defined(__64BIT__) && (PLATFORM_POSIX_VERSION >= 200112L) /* No point defining Large file for 64 bit */
+# define LONG_SEEK fseeko
+# define LONG_TELL ftello
+#elif defined(__MINGW32__) && !defined(__STRICT_ANSI__) && !defined(__NO_MINGW_LFS) && defined(__MSVCRT__)
+# define LONG_SEEK fseeko64
+# define LONG_TELL ftello64
+#elif defined(_WIN32) && !defined(__DJGPP__)
+# include
+ static int LONG_SEEK(FILE* file, __int64 offset, int origin) {
+ LARGE_INTEGER off;
+ DWORD method;
+ off.QuadPart = offset;
+ if (origin == SEEK_END)
+ method = FILE_END;
+ else if (origin == SEEK_CUR)
+ method = FILE_CURRENT;
+ else
+ method = FILE_BEGIN;
+
+ if (SetFilePointerEx((HANDLE) _get_osfhandle(_fileno(file)), off, NULL, method))
+ return 0;
+ else
+ return -1;
+ }
+ static __int64 LONG_TELL(FILE* file) {
+ LARGE_INTEGER off, newOff;
+ off.QuadPart = 0;
+ newOff.QuadPart = 0;
+ SetFilePointerEx((HANDLE) _get_osfhandle(_fileno(file)), off, &newOff, FILE_CURRENT);
+ return newOff.QuadPart;
+ }
+#else
+# define LONG_SEEK fseek
+# define LONG_TELL ftell
+#endif
+
+#if defined (__cplusplus)
+}
+#endif
+#endif //ZSTD_FILEIO_COMMON_H
diff --git a/programs/fileio_types.h b/programs/fileio_types.h
new file mode 100644
index 00000000000..1909ab1ab5a
--- /dev/null
+++ b/programs/fileio_types.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) Yann Collet, 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 FILEIO_TYPES_HEADER
+#define FILEIO_TYPES_HEADER
+
+#define ZSTD_STATIC_LINKING_ONLY /* ZSTD_compressionParameters */
+#include "../lib/zstd.h" /* ZSTD_* */
+
+/*-*************************************
+* Parameters: FIO_prefs_t
+***************************************/
+
+typedef struct FIO_display_prefs_s FIO_display_prefs_t;
+
+typedef enum { FIO_ps_auto, FIO_ps_never, FIO_ps_always } FIO_progressSetting_e;
+
+struct FIO_display_prefs_s {
+ int displayLevel; /* 0 : no display; 1: errors; 2: + result + interaction + warnings; 3: + progression; 4: + information */
+ FIO_progressSetting_e progressSetting;
+};
+
+
+typedef enum { FIO_zstdCompression, FIO_gzipCompression, FIO_xzCompression, FIO_lzmaCompression, FIO_lz4Compression } FIO_compressionType_t;
+
+typedef struct FIO_prefs_s {
+
+ /* Algorithm preferences */
+ FIO_compressionType_t compressionType;
+ U32 sparseFileSupport; /* 0: no sparse allowed; 1: auto (file yes, stdout no); 2: force sparse */
+ int dictIDFlag;
+ int checksumFlag;
+ int blockSize;
+ int overlapLog;
+ U32 adaptiveMode;
+ U32 useRowMatchFinder;
+ int rsyncable;
+ int minAdaptLevel;
+ int maxAdaptLevel;
+ int ldmFlag;
+ int ldmHashLog;
+ int ldmMinMatch;
+ int ldmBucketSizeLog;
+ int ldmHashRateLog;
+ size_t streamSrcSize;
+ size_t targetCBlockSize;
+ int srcSizeHint;
+ int testMode;
+ ZSTD_paramSwitch_e literalCompressionMode;
+
+ /* IO preferences */
+ U32 removeSrcFile;
+ U32 overwrite;
+ U32 asyncIO;
+
+ /* Computation resources preferences */
+ unsigned memLimit;
+ int nbWorkers;
+
+ int excludeCompressedFiles;
+ int patchFromMode;
+ int contentSize;
+ int allowBlockDevices;
+} FIO_prefs_t;
+
+#endif /* FILEIO_TYPES_HEADER */
\ No newline at end of file
From 4021b784376c3790c077e9b8deedbb6a4f016687 Mon Sep 17 00:00:00 2001
From: brailovich <91924341+brailovich@users.noreply.github.com>
Date: Mon, 24 Jan 2022 17:42:21 -0800
Subject: [PATCH 030/472] fix for error message in recursive mode for an empty
folder
-r on empty directory resulted in zstd waiting input from stdin. now zstd exits without error and prints a warning message explaining why no processing happened (no files or directories to process).
---
programs/zstdcli.c | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/programs/zstdcli.c b/programs/zstdcli.c
index fd563e1c24d..34d2fa6e0d9 100644
--- a/programs/zstdcli.c
+++ b/programs/zstdcli.c
@@ -834,6 +834,7 @@ int main(int argCount, const char* argv[])
size_t streamSrcSize = 0;
size_t targetCBlockSize = 0;
size_t srcSizeHint = 0;
+ size_t nbInputFileNames = 0;
int dictCLevel = g_defaultDictCLevel;
unsigned dictSelect = g_defaultSelectivityLevel;
#ifndef ZSTD_NODICT
@@ -1256,6 +1257,8 @@ int main(int argCount, const char* argv[])
}
}
+ nbInputFileNames = filenames->tableSize; /* saving number of input files */
+
if (recursive) { /* at this stage, filenameTable is a list of paths, which can contain both files and directories */
UTIL_expandFNT(&filenames, followLinks);
}
@@ -1358,7 +1361,17 @@ int main(int argCount, const char* argv[])
#endif
/* No input filename ==> use stdin and stdout */
- if (filenames->tableSize == 0) UTIL_refFilename(filenames, stdinmark);
+ if (filenames->tableSize == 0) {
+ /* It is possible that the input
+ was a number of empty directories. In this case
+ stdin and stdout should not be used */
+ if (nbInputFileNames > 0 ){
+ DISPLAYLEVEL(2, "please provide correct input file(s) or non-empty directories -- ignored \n");
+ CLEAN_RETURN(2);
+ }
+ UTIL_refFilename(filenames, stdinmark);
+ }
+
if (!strcmp(filenames->fileNames[0], stdinmark) && !outFileName)
outFileName = stdoutmark; /* when input is stdin, default output is stdout */
From 87dcd3326a587c7e9d61c2910f38337aa014355d Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Sat, 22 Jan 2022 02:30:08 -0800
Subject: [PATCH 031/472] fix sequence compression API in Explicit Delimiter
mode
---
lib/common/error_private.c | 2 +-
lib/compress/zstd_compress.c | 73 +++++++++++++++++++++++++++++++-----
lib/zstd.h | 21 ++++++-----
3 files changed, 77 insertions(+), 19 deletions(-)
diff --git a/lib/common/error_private.c b/lib/common/error_private.c
index 6d1135f8c37..38e419d358b 100644
--- a/lib/common/error_private.c
+++ b/lib/common/error_private.c
@@ -27,7 +27,7 @@ const char* ERR_getErrorString(ERR_enum code)
case PREFIX(version_unsupported): return "Version not supported";
case PREFIX(frameParameter_unsupported): return "Unsupported frame parameter";
case PREFIX(frameParameter_windowTooLarge): return "Frame requires too much memory for decoding";
- case PREFIX(corruption_detected): return "Corrupted block detected";
+ case PREFIX(corruption_detected): return "Input corruption detected";
case PREFIX(checksum_wrong): return "Restored data doesn't match checksum";
case PREFIX(parameter_unsupported): return "Unsupported parameter";
case PREFIX(parameter_outOfBound): return "Parameter is out of bound";
diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
index 1cb229f7aa9..fc25d054139 100644
--- a/lib/compress/zstd_compress.c
+++ b/lib/compress/zstd_compress.c
@@ -5752,9 +5752,9 @@ size_t ZSTD_compress2(ZSTD_CCtx* cctx,
}
typedef struct {
- U32 idx; /* Index in array of ZSTD_Sequence */
- U32 posInSequence; /* Position within sequence at idx */
- size_t posInSrc; /* Number of bytes given by sequences provided so far */
+ U32 idx; /* Index in array of ZSTD_Sequence */
+ U32 posInSequence; /* Position within sequence at idx */
+ size_t posInSrc; /* Number of bytes given by sequences provided so far */
} ZSTD_sequencePosition;
/* ZSTD_validateSequence() :
@@ -5809,6 +5809,8 @@ ZSTD_copySequencesToSeqStoreExplicitBlockDelim(ZSTD_CCtx* cctx,
repcodes_t updatedRepcodes;
U32 dictSize;
+ DEBUGLOG(5, "ZSTD_copySequencesToSeqStoreExplicitBlockDelim (blockSize = %zu)", blockSize);
+
if (cctx->cdict) {
dictSize = (U32)cctx->cdict->dictContentSize;
} else if (cctx->prefixDict.dict) {
@@ -5995,6 +5997,56 @@ static ZSTD_sequenceCopier ZSTD_selectSequenceCopier(ZSTD_sequenceFormat_e mode)
return sequenceCopier;
}
+/* Discover the size of next by searching for the block delimiter.
+ * Note that a block delimiter must exist in this mode,
+ * otherwise it's an input error.
+ * The value retrieved will be later compared to ensure it remains within bounds */
+static size_t
+blockSize_explicitDelimiter(const ZSTD_Sequence* inSeqs, size_t inSeqsSize, ZSTD_sequencePosition seqPos)
+{
+ int end = 0;
+ size_t blockSize = 0;
+ size_t spos = seqPos.idx;
+ assert(spos <= inSeqsSize);
+ while (spos < inSeqsSize) {
+ end = (inSeqs[spos].offset == 0);
+ blockSize += inSeqs[spos].litLength + inSeqs[spos].matchLength;
+ if (end) {
+ if (inSeqs[spos].matchLength != 0)
+ RETURN_ERROR(corruption_detected, "delimiter format error : both matchlength and offset must be == 0");
+ break;
+ }
+ spos++;
+ }
+ if (!end)
+ RETURN_ERROR(corruption_detected, "Reached end of sequences without finding a block delimiter");
+ return blockSize;
+}
+
+/* More a "target" block size */
+static size_t blockSize_noDelimiter(size_t blockSize, size_t remaining)
+{
+ int const lastBlock = (remaining <= blockSize);
+ return lastBlock ? remaining : blockSize;
+}
+
+static size_t determine_blockSize(ZSTD_sequenceFormat_e mode,
+ size_t blockSize, size_t remaining,
+ const ZSTD_Sequence* inSeqs, size_t inSeqsSize, ZSTD_sequencePosition seqPos)
+{
+ DEBUGLOG(6, "determine_blockSize : remainingSize = %zu", remaining);
+ if (mode == ZSTD_sf_noBlockDelimiters)
+ return blockSize_noDelimiter(blockSize, remaining);
+ { size_t const explicitBlockSize = blockSize_explicitDelimiter(inSeqs, inSeqsSize, seqPos);
+ FORWARD_IF_ERROR(explicitBlockSize, "Error while determining block size with explicit delimiters");
+ if (explicitBlockSize > blockSize)
+ RETURN_ERROR(corruption_detected, "sequences incorrectly define a too large block");
+ if (explicitBlockSize > remaining)
+ RETURN_ERROR(srcSize_wrong, "sequences define a frame longer than source");
+ return explicitBlockSize;
+ }
+}
+
/* Compress, block-by-block, all of the sequences given.
*
* Returns the cumulative size of all compressed blocks (including their headers),
@@ -6007,9 +6059,6 @@ ZSTD_compressSequences_internal(ZSTD_CCtx* cctx,
const void* src, size_t srcSize)
{
size_t cSize = 0;
- U32 lastBlock;
- size_t blockSize;
- size_t compressedSeqsSize;
size_t remaining = srcSize;
ZSTD_sequencePosition seqPos = {0, 0, 0};
@@ -6029,10 +6078,15 @@ ZSTD_compressSequences_internal(ZSTD_CCtx* cctx,
}
while (remaining) {
+ size_t compressedSeqsSize;
size_t cBlockSize;
size_t additionalByteAdjustment;
- lastBlock = remaining <= cctx->blockSize;
- blockSize = lastBlock ? (U32)remaining : (U32)cctx->blockSize;
+ size_t blockSize = determine_blockSize(cctx->appliedParams.blockDelimiters,
+ cctx->blockSize, remaining,
+ inSeqs, inSeqsSize, seqPos);
+ U32 const lastBlock = (blockSize == remaining);
+ assert(blockSize <= remaining);
+ FORWARD_IF_ERROR(blockSize, "Error while trying to determine block size");
ZSTD_resetSeqStore(&cctx->seqStore);
DEBUGLOG(4, "Working on new block. Blocksize: %zu", blockSize);
@@ -6113,7 +6167,8 @@ ZSTD_compressSequences_internal(ZSTD_CCtx* cctx,
return cSize;
}
-size_t ZSTD_compressSequences(ZSTD_CCtx* const cctx, void* dst, size_t dstCapacity,
+size_t ZSTD_compressSequences(ZSTD_CCtx* cctx,
+ void* dst, size_t dstCapacity,
const ZSTD_Sequence* inSeqs, size_t inSeqsSize,
const void* src, size_t srcSize)
{
diff --git a/lib/zstd.h b/lib/zstd.h
index 349d8017f84..8e90ae5f95c 100644
--- a/lib/zstd.h
+++ b/lib/zstd.h
@@ -1375,23 +1375,25 @@ typedef enum {
} ZSTD_sequenceFormat_e;
/*! ZSTD_generateSequences() :
- * Generate sequences using ZSTD_compress2, given a source buffer.
+ * Generate sequences using ZSTD_compress2(), given a source buffer.
*
* Each block will end with a dummy sequence
* with offset == 0, matchLength == 0, and litLength == length of last literals.
* litLength may be == 0, and if so, then the sequence of (of: 0 ml: 0 ll: 0)
* simply acts as a block delimiter.
*
- * zc can be used to insert custom compression params.
- * This function invokes ZSTD_compress2
+ * @zc can be used to insert custom compression params.
+ * This function invokes ZSTD_compress2().
*
* The output of this function can be fed into ZSTD_compressSequences() with CCtx
* setting of ZSTD_c_blockDelimiters as ZSTD_sf_explicitBlockDelimiters
* @return : number of sequences generated
*/
-ZSTDLIB_STATIC_API size_t ZSTD_generateSequences(ZSTD_CCtx* zc, ZSTD_Sequence* outSeqs,
- size_t outSeqsSize, const void* src, size_t srcSize);
+ZSTDLIB_STATIC_API size_t
+ZSTD_generateSequences( ZSTD_CCtx* zc,
+ ZSTD_Sequence* outSeqs, size_t outSeqsSize,
+ const void* src, size_t srcSize);
/*! ZSTD_mergeBlockDelimiters() :
* Given an array of ZSTD_Sequence, remove all sequences that represent block delimiters/last literals
@@ -1432,11 +1434,12 @@ ZSTDLIB_STATIC_API size_t ZSTD_mergeBlockDelimiters(ZSTD_Sequence* sequences, si
* Note: Repcodes are, as of now, always re-calculated within this function, so ZSTD_Sequence::rep is unused.
* Note 2: Once we integrate ability to ingest repcodes, the explicit block delims mode must respect those repcodes exactly,
* and cannot emit an RLE block that disagrees with the repcode history
- * @return : final compressed size or a ZSTD error.
+ * @return : final compressed size, or a ZSTD error code.
*/
-ZSTDLIB_STATIC_API size_t ZSTD_compressSequences(ZSTD_CCtx* const cctx, void* dst, size_t dstSize,
- const ZSTD_Sequence* inSeqs, size_t inSeqsSize,
- const void* src, size_t srcSize);
+ZSTDLIB_STATIC_API size_t
+ZSTD_compressSequences( ZSTD_CCtx* cctx, void* dst, size_t dstSize,
+ const ZSTD_Sequence* inSeqs, size_t inSeqsSize,
+ const void* src, size_t srcSize);
/*! ZSTD_writeSkippableFrame() :
From fc2ea97442460158a92d1e7b7c26e7486e45a605 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Sun, 23 Jan 2022 22:08:20 -0800
Subject: [PATCH 032/472] refactored fuzzer tests for sequence compression api
add explicit delimiter mode to libfuzzer test
---
lib/common/error_private.c | 2 +-
lib/compress/zstd_compress.c | 33 +++---
tests/fuzz/sequence_compression_api.c | 148 ++++++++++++++++----------
tests/fuzzer.c | 68 ++++++------
4 files changed, 144 insertions(+), 107 deletions(-)
diff --git a/lib/common/error_private.c b/lib/common/error_private.c
index 38e419d358b..94e9d4c484e 100644
--- a/lib/common/error_private.c
+++ b/lib/common/error_private.c
@@ -27,7 +27,7 @@ const char* ERR_getErrorString(ERR_enum code)
case PREFIX(version_unsupported): return "Version not supported";
case PREFIX(frameParameter_unsupported): return "Unsupported frame parameter";
case PREFIX(frameParameter_windowTooLarge): return "Frame requires too much memory for decoding";
- case PREFIX(corruption_detected): return "Input corruption detected";
+ case PREFIX(corruption_detected): return "Data corruption detected";
case PREFIX(checksum_wrong): return "Restored data doesn't match checksum";
case PREFIX(parameter_unsupported): return "Unsupported parameter";
case PREFIX(parameter_outOfBound): return "Parameter is out of bound";
diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
index fc25d054139..c369c84e281 100644
--- a/lib/compress/zstd_compress.c
+++ b/lib/compress/zstd_compress.c
@@ -2598,7 +2598,7 @@ ZSTD_entropyCompressSeqStore_internal(seqStore_t* seqStorePtr,
entropyWorkspace = count + (MaxSeq + 1);
entropyWkspSize -= (MaxSeq + 1) * sizeof(*count);
- DEBUGLOG(4, "ZSTD_entropyCompressSeqStore_internal (nbSeq=%zu)", nbSeq);
+ DEBUGLOG(5, "ZSTD_entropyCompressSeqStore_internal (nbSeq=%zu)", nbSeq);
ZSTD_STATIC_ASSERT(HUF_WORKSPACE_SIZE >= (1<= HUF_WORKSPACE_SIZE);
@@ -2642,11 +2642,10 @@ ZSTD_entropyCompressSeqStore_internal(seqStore_t* seqStorePtr,
ZSTD_memcpy(&nextEntropy->fse, &prevEntropy->fse, sizeof(prevEntropy->fse));
return (size_t)(op - ostart);
}
- {
- ZSTD_symbolEncodingTypeStats_t stats;
- BYTE* seqHead = op++;
+ { BYTE* seqHead = op++;
/* build stats for sequences */
- stats = ZSTD_buildSequencesStatistics(seqStorePtr, nbSeq,
+ const ZSTD_symbolEncodingTypeStats_t stats =
+ ZSTD_buildSequencesStatistics(seqStorePtr, nbSeq,
&prevEntropy->fse, &nextEntropy->fse,
op, oend,
strategy, count,
@@ -5997,16 +5996,17 @@ static ZSTD_sequenceCopier ZSTD_selectSequenceCopier(ZSTD_sequenceFormat_e mode)
return sequenceCopier;
}
-/* Discover the size of next by searching for the block delimiter.
- * Note that a block delimiter must exist in this mode,
+/* Discover the size of next block by searching for the delimiter.
+ * Note that a block delimiter **must** exist in this mode,
* otherwise it's an input error.
- * The value retrieved will be later compared to ensure it remains within bounds */
+ * The block size retrieved will be later compared to ensure it remains within bounds */
static size_t
blockSize_explicitDelimiter(const ZSTD_Sequence* inSeqs, size_t inSeqsSize, ZSTD_sequencePosition seqPos)
{
int end = 0;
size_t blockSize = 0;
size_t spos = seqPos.idx;
+ DEBUGLOG(6, "blockSize_explicitDelimiter : seq %zu / %zu", spos, inSeqsSize);
assert(spos <= inSeqsSize);
while (spos < inSeqsSize) {
end = (inSeqs[spos].offset == 0);
@@ -6085,10 +6085,10 @@ ZSTD_compressSequences_internal(ZSTD_CCtx* cctx,
cctx->blockSize, remaining,
inSeqs, inSeqsSize, seqPos);
U32 const lastBlock = (blockSize == remaining);
- assert(blockSize <= remaining);
FORWARD_IF_ERROR(blockSize, "Error while trying to determine block size");
+ assert(blockSize <= remaining);
ZSTD_resetSeqStore(&cctx->seqStore);
- DEBUGLOG(4, "Working on new block. Blocksize: %zu", blockSize);
+ DEBUGLOG(5, "Working on new block. Blocksize: %zu", blockSize);
additionalByteAdjustment = sequenceCopier(cctx, &seqPos, inSeqs, inSeqsSize, ip, blockSize);
FORWARD_IF_ERROR(additionalByteAdjustment, "Bad sequence copy");
@@ -6098,7 +6098,7 @@ ZSTD_compressSequences_internal(ZSTD_CCtx* cctx,
if (blockSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1) {
cBlockSize = ZSTD_noCompressBlock(op, dstCapacity, ip, blockSize, lastBlock);
FORWARD_IF_ERROR(cBlockSize, "Nocompress block failed");
- DEBUGLOG(4, "Block too small, writing out nocompress block: cSize: %zu", cBlockSize);
+ DEBUGLOG(5, "Block too small, writing out nocompress block: cSize: %zu", cBlockSize);
cSize += cBlockSize;
ip += blockSize;
op += cBlockSize;
@@ -6115,7 +6115,7 @@ ZSTD_compressSequences_internal(ZSTD_CCtx* cctx,
cctx->entropyWorkspace, ENTROPY_WORKSPACE_SIZE /* statically allocated in resetCCtx */,
cctx->bmi2);
FORWARD_IF_ERROR(compressedSeqsSize, "Compressing sequences of block failed");
- DEBUGLOG(4, "Compressed sequences size: %zu", compressedSeqsSize);
+ DEBUGLOG(5, "Compressed sequences size: %zu", compressedSeqsSize);
if (!cctx->isFirstBlock &&
ZSTD_maybeRLE(&cctx->seqStore) &&
@@ -6131,11 +6131,11 @@ ZSTD_compressSequences_internal(ZSTD_CCtx* cctx,
/* ZSTD_noCompressBlock writes the block header as well */
cBlockSize = ZSTD_noCompressBlock(op, dstCapacity, ip, blockSize, lastBlock);
FORWARD_IF_ERROR(cBlockSize, "Nocompress block failed");
- DEBUGLOG(4, "Writing out nocompress block, size: %zu", cBlockSize);
+ DEBUGLOG(5, "Writing out nocompress block, size: %zu", cBlockSize);
} else if (compressedSeqsSize == 1) {
cBlockSize = ZSTD_rleCompressBlock(op, dstCapacity, *ip, blockSize, lastBlock);
FORWARD_IF_ERROR(cBlockSize, "RLE compress block failed");
- DEBUGLOG(4, "Writing out RLE block, size: %zu", cBlockSize);
+ DEBUGLOG(5, "Writing out RLE block, size: %zu", cBlockSize);
} else {
U32 cBlockHeader;
/* Error checking and repcodes update */
@@ -6147,11 +6147,11 @@ ZSTD_compressSequences_internal(ZSTD_CCtx* cctx,
cBlockHeader = lastBlock + (((U32)bt_compressed)<<1) + (U32)(compressedSeqsSize << 3);
MEM_writeLE24(op, cBlockHeader);
cBlockSize = ZSTD_blockHeaderSize + compressedSeqsSize;
- DEBUGLOG(4, "Writing out compressed block, size: %zu", cBlockSize);
+ DEBUGLOG(5, "Writing out compressed block, size: %zu", cBlockSize);
}
cSize += cBlockSize;
- DEBUGLOG(4, "cSize running total: %zu", cSize);
+ DEBUGLOG(5, "cSize running total: %zu", cSize);
if (lastBlock) {
break;
@@ -6164,6 +6164,7 @@ ZSTD_compressSequences_internal(ZSTD_CCtx* cctx,
}
}
+ DEBUGLOG(4, "cSize final total: %zu", cSize);
return cSize;
}
diff --git a/tests/fuzz/sequence_compression_api.c b/tests/fuzz/sequence_compression_api.c
index a2959e1aca6..b35f5a79563 100644
--- a/tests/fuzz/sequence_compression_api.c
+++ b/tests/fuzz/sequence_compression_api.c
@@ -26,8 +26,8 @@
#include "zstd_helpers.h"
#include "fuzz_data_producer.h"
-static ZSTD_CCtx *cctx = NULL;
-static ZSTD_DCtx *dctx = NULL;
+static ZSTD_CCtx* cctx = NULL;
+static ZSTD_DCtx* dctx = NULL;
static void* literalsBuffer = NULL;
static void* generatedSrc = NULL;
static ZSTD_Sequence* generatedSequences = NULL;
@@ -55,7 +55,7 @@ static uint32_t FUZZ_RDG_rand(uint32_t* src)
/* Make a pseudorandom string - this simple function exists to avoid
* taking a dependency on datagen.h to have RDG_genBuffer().
*/
-static char *generatePseudoRandomString(char *str, size_t size) {
+static char* generatePseudoRandomString(char* str, size_t size) {
const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJK1234567890!@#$^&*()_";
uint32_t seed = 0;
if (size) {
@@ -69,7 +69,10 @@ static char *generatePseudoRandomString(char *str, size_t size) {
/* Returns size of source buffer */
static size_t decodeSequences(void* dst, size_t nbSequences,
- size_t literalsSize, const void* dict, size_t dictSize) {
+ size_t literalsSize,
+ const void* dict, size_t dictSize,
+ ZSTD_sequenceFormat_e mode)
+{
const uint8_t* litPtr = literalsBuffer;
const uint8_t* const litBegin = literalsBuffer;
const uint8_t* const litEnd = litBegin + literalsSize;
@@ -78,21 +81,20 @@ static size_t decodeSequences(void* dst, size_t nbSequences,
const uint8_t* const oend = (uint8_t*)dst + ZSTD_FUZZ_GENERATED_SRC_MAXSIZE;
size_t generatedSrcBufferSize = 0;
size_t bytesWritten = 0;
- uint32_t lastLLSize;
for (size_t i = 0; i < nbSequences; ++i) {
- FUZZ_ASSERT(generatedSequences[i].matchLength != 0);
- FUZZ_ASSERT(generatedSequences[i].offset != 0);
+ /* block boundary */
+ if (generatedSequences[i].offset == 0)
+ FUZZ_ASSERT(generatedSequences[i].matchLength == 0);
if (litPtr + generatedSequences[i].litLength > litEnd) {
litPtr = litBegin;
}
- ZSTD_memcpy(op, litPtr, generatedSequences[i].litLength);
+ memcpy(op, litPtr, generatedSequences[i].litLength);
bytesWritten += generatedSequences[i].litLength;
op += generatedSequences[i].litLength;
litPtr += generatedSequences[i].litLength;
- FUZZ_ASSERT(generatedSequences[i].offset != 0);
/* Copy over the match */
{ size_t matchLength = generatedSequences[i].matchLength;
size_t j = 0;
@@ -109,7 +111,7 @@ static size_t decodeSequences(void* dst, size_t nbSequences,
}
}
for (; j < matchLength; ++j) {
- op[j] = op[j-(int)generatedSequences[i].offset];
+ op[j] = op[j - generatedSequences[i].offset];
}
op += j;
FUZZ_ASSERT(generatedSequences[i].matchLength == j + k);
@@ -118,55 +120,65 @@ static size_t decodeSequences(void* dst, size_t nbSequences,
}
generatedSrcBufferSize = bytesWritten;
FUZZ_ASSERT(litPtr <= litEnd);
- lastLLSize = (uint32_t)(litEnd - litPtr);
- if (lastLLSize <= oend - op) {
- ZSTD_memcpy(op, litPtr, lastLLSize);
- generatedSrcBufferSize += lastLLSize;
- }
+ if (mode == ZSTD_sf_noBlockDelimiters) {
+ const uint32_t lastLLSize = (uint32_t)(litEnd - litPtr);
+ if (lastLLSize <= oend - op) {
+ memcpy(op, litPtr, lastLLSize);
+ generatedSrcBufferSize += lastLLSize;
+ } }
return generatedSrcBufferSize;
}
/* Returns nb sequences generated
- * TODO: Add repcode fuzzing once we support repcode match splits
+ * Note : random sequences are always valid in ZSTD_sf_noBlockDelimiters mode.
+ * However, it can fail with ZSTD_sf_explicitBlockDelimiters,
+ * due to potential lack of space in
*/
static size_t generateRandomSequences(FUZZ_dataProducer_t* producer,
size_t literalsSizeLimit, size_t dictSize,
- size_t windowLog) {
+ size_t windowLog, ZSTD_sequenceFormat_e mode)
+{
+ const uint32_t repCode = 0; /* not used by sequence ingestion api */
+ const uint32_t windowSize = 1 << windowLog;
+ const uint32_t blockSizeMax = MIN(128 << 10, 1 << windowLog);
+ uint32_t matchLengthMax = ZSTD_FUZZ_MATCHLENGTH_MAXSIZE;
uint32_t bytesGenerated = 0;
uint32_t nbSeqGenerated = 0;
- uint32_t litLength;
- uint32_t matchLength;
- uint32_t matchBound;
- uint32_t offset;
- uint32_t offsetBound;
- uint32_t repCode = 0;
uint32_t isFirstSequence = 1;
- uint32_t windowSize = 1 << windowLog;
+ uint32_t blockSize = 0;
- while (nbSeqGenerated < ZSTD_FUZZ_MAX_NBSEQ
+ if (mode == ZSTD_sf_explicitBlockDelimiters) {
+ /* ensure that no sequence can be larger than one block */
+ literalsSizeLimit = MIN(literalsSizeLimit, blockSizeMax/2);
+ matchLengthMax = MIN(matchLengthMax, blockSizeMax/2);
+ }
+
+ while ( nbSeqGenerated < ZSTD_FUZZ_MAX_NBSEQ-1
&& bytesGenerated < ZSTD_FUZZ_GENERATED_SRC_MAXSIZE
&& !FUZZ_dataProducer_empty(producer)) {
- matchBound = ZSTD_FUZZ_MATCHLENGTH_MAXSIZE;
- litLength = isFirstSequence && dictSize == 0 ? FUZZ_dataProducer_uint32Range(producer, 1, literalsSizeLimit)
- : FUZZ_dataProducer_uint32Range(producer, 0, literalsSizeLimit);
+ uint32_t matchLength;
+ uint32_t matchBound = matchLengthMax;
+ uint32_t offset;
+ uint32_t offsetBound;
+ const uint32_t minLitLength = (isFirstSequence && (dictSize == 0));
+ const uint32_t litLength = FUZZ_dataProducer_uint32Range(producer, minLitLength, (uint32_t)literalsSizeLimit);
bytesGenerated += litLength;
if (bytesGenerated > ZSTD_FUZZ_GENERATED_SRC_MAXSIZE) {
break;
}
- offsetBound = bytesGenerated > windowSize ? windowSize : bytesGenerated + dictSize;
+ offsetBound = (bytesGenerated > windowSize) ? windowSize : bytesGenerated + (uint32_t)dictSize;
offset = FUZZ_dataProducer_uint32Range(producer, 1, offsetBound);
if (dictSize > 0 && bytesGenerated <= windowSize) {
/* Prevent match length from being such that it would be associated with an offset too large
* from the decoder's perspective. If not possible (match would be too small),
* then reduce the offset if necessary.
*/
- size_t bytesToReachWindowSize = windowSize - bytesGenerated;
+ const size_t bytesToReachWindowSize = windowSize - bytesGenerated;
if (bytesToReachWindowSize < ZSTD_MINMATCH_MIN) {
- uint32_t newOffsetBound = offsetBound > windowSize ? windowSize : offsetBound;
+ const uint32_t newOffsetBound = offsetBound > windowSize ? windowSize : offsetBound;
offset = FUZZ_dataProducer_uint32Range(producer, 1, newOffsetBound);
} else {
- matchBound = bytesToReachWindowSize > ZSTD_FUZZ_MATCHLENGTH_MAXSIZE ?
- ZSTD_FUZZ_MATCHLENGTH_MAXSIZE : bytesToReachWindowSize;
+ matchBound = MIN(matchLengthMax, (uint32_t)bytesToReachWindowSize);
}
}
matchLength = FUZZ_dataProducer_uint32Range(producer, ZSTD_MINMATCH_MIN, matchBound);
@@ -174,9 +186,35 @@ static size_t generateRandomSequences(FUZZ_dataProducer_t* producer,
if (bytesGenerated > ZSTD_FUZZ_GENERATED_SRC_MAXSIZE) {
break;
}
- ZSTD_Sequence seq = {offset, litLength, matchLength, repCode};
- generatedSequences[nbSeqGenerated++] = seq;
- isFirstSequence = 0;
+ { ZSTD_Sequence seq = {offset, litLength, matchLength, repCode};
+ const uint32_t lastLits = FUZZ_dataProducer_uint32Range(producer, 0, litLength);
+ #define SPLITPROB 6000
+ #define SPLITMARK 5234
+ const int split = (FUZZ_dataProducer_uint32Range(producer, 0, SPLITPROB) == SPLITMARK);
+ if (mode == ZSTD_sf_explicitBlockDelimiters) {
+ const size_t seqSize = seq.litLength + seq.matchLength;
+ if (blockSize + seqSize > blockSizeMax) { /* reaching limit : must end block now */
+ const ZSTD_Sequence endBlock = {0, 0, 0, 0};
+ generatedSequences[nbSeqGenerated++] = endBlock;
+ blockSize = seqSize;
+ }
+ if (split) {
+ const ZSTD_Sequence endBlock = {0, lastLits, 0, 0};
+ generatedSequences[nbSeqGenerated++] = endBlock;
+ assert(lastLits <= seq.litLength);
+ seq.litLength -= lastLits;
+ blockSize = seqSize - lastLits;
+ } else {
+ blockSize += seqSize;
+ }
+ }
+ generatedSequences[nbSeqGenerated++] = seq;
+ isFirstSequence = 0;
+ } }
+ if (mode == ZSTD_sf_explicitBlockDelimiters) {
+ /* always end sequences with a block delimiter */
+ const ZSTD_Sequence endBlock = {0, 0, 0, 0};
+ generatedSequences[nbSeqGenerated++] = endBlock;
}
return nbSeqGenerated;
@@ -187,12 +225,11 @@ static size_t roundTripTest(void *result, size_t resultCapacity,
size_t srcSize,
const void *dict, size_t dictSize,
size_t generatedSequencesSize,
- size_t wLog, unsigned cLevel, unsigned hasDict)
+ int wLog, int cLevel, unsigned hasDict,
+ ZSTD_sequenceFormat_e mode)
{
size_t cSize;
size_t dSize;
- ZSTD_CDict* cdict = NULL;
- ZSTD_DDict* ddict = NULL;
ZSTD_CCtx_reset(cctx, ZSTD_reset_session_and_parameters);
ZSTD_CCtx_setParameter(cctx, ZSTD_c_nbWorkers, 0);
@@ -200,8 +237,7 @@ static size_t roundTripTest(void *result, size_t resultCapacity,
ZSTD_CCtx_setParameter(cctx, ZSTD_c_windowLog, wLog);
ZSTD_CCtx_setParameter(cctx, ZSTD_c_minMatch, ZSTD_MINMATCH_MIN);
ZSTD_CCtx_setParameter(cctx, ZSTD_c_validateSequences, 1);
- /* TODO: Add block delim mode fuzzing */
- ZSTD_CCtx_setParameter(cctx, ZSTD_c_blockDelimiters, ZSTD_sf_noBlockDelimiters);
+ ZSTD_CCtx_setParameter(cctx, ZSTD_c_blockDelimiters, mode);
if (hasDict) {
FUZZ_ZASSERT(ZSTD_CCtx_loadDictionary(cctx, dict, dictSize));
FUZZ_ZASSERT(ZSTD_DCtx_loadDictionary(dctx, dict, dictSize));
@@ -214,16 +250,10 @@ static size_t roundTripTest(void *result, size_t resultCapacity,
dSize = ZSTD_decompressDCtx(dctx, result, resultCapacity, compressed, cSize);
FUZZ_ZASSERT(dSize);
- if (cdict) {
- ZSTD_freeCDict(cdict);
- }
- if (ddict) {
- ZSTD_freeDDict(ddict);
- }
return dSize;
}
-int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
+int LLVMFuzzerTestOneInput(const uint8_t* src, size_t size)
{
void* rBuf;
size_t rBufSize;
@@ -231,15 +261,18 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
size_t cBufSize;
size_t generatedSrcSize;
size_t nbSequences;
- void* dictBuffer;
+ void* dictBuffer = NULL;
size_t dictSize = 0;
unsigned hasDict;
unsigned wLog;
int cLevel;
+ ZSTD_sequenceFormat_e mode;
- FUZZ_dataProducer_t *producer = FUZZ_dataProducer_create(src, size);
+ FUZZ_dataProducer_t* const producer = FUZZ_dataProducer_create(src, size);
+ FUZZ_ASSERT(producer);
if (literalsBuffer == NULL) {
literalsBuffer = FUZZ_malloc(ZSTD_FUZZ_GENERATED_LITERALS_SIZE);
+ FUZZ_ASSERT(literalsBuffer);
literalsBuffer = generatePseudoRandomString(literalsBuffer, ZSTD_FUZZ_GENERATED_LITERALS_SIZE);
}
@@ -247,11 +280,13 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
if (hasDict) {
dictSize = FUZZ_dataProducer_uint32Range(producer, 1, ZSTD_FUZZ_GENERATED_DICT_MAXSIZE);
dictBuffer = FUZZ_malloc(dictSize);
+ FUZZ_ASSERT(dictBuffer);
dictBuffer = generatePseudoRandomString(dictBuffer, dictSize);
}
/* Generate window log first so we dont generate offsets too large */
wLog = FUZZ_dataProducer_uint32Range(producer, ZSTD_WINDOWLOG_MIN, ZSTD_WINDOWLOG_MAX_32);
cLevel = FUZZ_dataProducer_int32Range(producer, -3, 22);
+ mode = (ZSTD_sequenceFormat_e)FUZZ_dataProducer_int32Range(producer, 0, 1);
if (!generatedSequences) {
generatedSequences = FUZZ_malloc(sizeof(ZSTD_Sequence)*ZSTD_FUZZ_MAX_NBSEQ);
@@ -259,8 +294,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
if (!generatedSrc) {
generatedSrc = FUZZ_malloc(ZSTD_FUZZ_GENERATED_SRC_MAXSIZE);
}
- nbSequences = generateRandomSequences(producer, ZSTD_FUZZ_GENERATED_LITERALS_SIZE, dictSize, wLog);
- generatedSrcSize = decodeSequences(generatedSrc, nbSequences, ZSTD_FUZZ_GENERATED_LITERALS_SIZE, dictBuffer, dictSize);
+ nbSequences = generateRandomSequences(producer, ZSTD_FUZZ_GENERATED_LITERALS_SIZE, dictSize, wLog, mode);
+ generatedSrcSize = decodeSequences(generatedSrc, nbSequences, ZSTD_FUZZ_GENERATED_LITERALS_SIZE, dictBuffer, dictSize, mode);
cBufSize = ZSTD_compressBound(generatedSrcSize);
cBuf = FUZZ_malloc(cBufSize);
@@ -276,14 +311,15 @@ int LLVMFuzzerTestOneInput(const uint8_t *src, size_t size)
FUZZ_ASSERT(dctx);
}
- size_t const result = roundTripTest(rBuf, rBufSize,
+ { const size_t result = roundTripTest(rBuf, rBufSize,
cBuf, cBufSize,
generatedSrcSize,
dictBuffer, dictSize,
nbSequences,
- wLog, cLevel, hasDict);
- FUZZ_ZASSERT(result);
- FUZZ_ASSERT_MSG(result == generatedSrcSize, "Incorrect regenerated size");
+ (int)wLog, cLevel, hasDict, mode);
+ FUZZ_ZASSERT(result);
+ FUZZ_ASSERT_MSG(result == generatedSrcSize, "Incorrect regenerated size");
+ }
FUZZ_ASSERT_MSG(!FUZZ_memcmp(generatedSrc, rBuf, generatedSrcSize), "Corruption!");
free(rBuf);
diff --git a/tests/fuzzer.c b/tests/fuzzer.c
index 823db775f35..134a40468b1 100644
--- a/tests/fuzzer.c
+++ b/tests/fuzzer.c
@@ -3114,18 +3114,17 @@ static int basicUnitTests(U32 const seed, double compressibility)
DISPLAYLEVEL(3, "test%3i : ZSTD_getSequences followed by ZSTD_compressSequences : ", testNb++);
{
- size_t srcSize = 500 KB;
- BYTE* src = (BYTE*)CNBuffer;
- BYTE* dst = (BYTE*)compressedBuffer;
- size_t dstSize = ZSTD_compressBound(srcSize);
- size_t decompressSize = srcSize;
- char* decompressBuffer = (char*)malloc(decompressSize);
+ const size_t srcSize = 500 KB;
+ const BYTE* const src = (BYTE*)CNBuffer;
+ BYTE* const dst = (BYTE*)compressedBuffer;
+ const size_t dstCapacity = ZSTD_compressBound(srcSize);
+ const size_t decompressSize = srcSize;
+ char* const decompressBuffer = (char*)malloc(decompressSize);
size_t compressedSize;
- size_t dSize;
- ZSTD_CCtx* cctx = ZSTD_createCCtx();
- ZSTD_Sequence* seqs = (ZSTD_Sequence*)malloc(srcSize * sizeof(ZSTD_Sequence));
- size_t seqsSize;
+ ZSTD_CCtx* const cctx = ZSTD_createCCtx();
+ ZSTD_Sequence* const seqs = (ZSTD_Sequence*)malloc(srcSize * sizeof(ZSTD_Sequence));
+ size_t nbSeqs;
if (seqs == NULL) goto _output_error;
assert(cctx != NULL);
@@ -3133,36 +3132,37 @@ static int basicUnitTests(U32 const seed, double compressibility)
/* Populate src with random data */
RDG_genBuffer(CNBuffer, srcSize, compressibility, 0., seed);
- /* Test with block delimiters roundtrip */
- seqsSize = ZSTD_generateSequences(cctx, seqs, srcSize, src, srcSize);
+ /* Roundtrip Test with block delimiters generated by ZSTD_generateSequences() */
+ nbSeqs = ZSTD_generateSequences(cctx, seqs, srcSize, src, srcSize);
ZSTD_CCtx_reset(cctx, ZSTD_reset_session_and_parameters);
ZSTD_CCtx_setParameter(cctx, ZSTD_c_blockDelimiters, ZSTD_sf_explicitBlockDelimiters);
- compressedSize = ZSTD_compressSequences(cctx, dst, dstSize, seqs, seqsSize, src, srcSize);
+ compressedSize = ZSTD_compressSequences(cctx, dst, dstCapacity, seqs, nbSeqs, src, srcSize);
if (ZSTD_isError(compressedSize)) {
DISPLAY("Error in sequence compression with block delims\n");
goto _output_error;
}
- dSize = ZSTD_decompress(decompressBuffer, decompressSize, dst, compressedSize);
- if (ZSTD_isError(dSize)) {
- DISPLAY("Error in sequence compression roundtrip with block delims\n");
- goto _output_error;
- }
+ { size_t const dSize = ZSTD_decompress(decompressBuffer, decompressSize, dst, compressedSize);
+ if (ZSTD_isError(dSize)) {
+ DISPLAY("Error in sequence compression roundtrip with block delims\n");
+ goto _output_error;
+ } }
assert(!memcmp(decompressBuffer, src, srcSize));
- /* Test with no block delimiters roundtrip */
- seqsSize = ZSTD_mergeBlockDelimiters(seqs, seqsSize);
- ZSTD_CCtx_reset(cctx, ZSTD_reset_session_and_parameters);
- ZSTD_CCtx_setParameter(cctx, ZSTD_c_blockDelimiters, ZSTD_sf_noBlockDelimiters);
- compressedSize = ZSTD_compressSequences(cctx, dst, dstSize, seqs, seqsSize, src, srcSize);
+ /* Roundtrip Test with no block delimiters */
+ { size_t const nbSeqsAfterMerge = ZSTD_mergeBlockDelimiters(seqs, nbSeqs);
+ ZSTD_CCtx_reset(cctx, ZSTD_reset_session_and_parameters);
+ ZSTD_CCtx_setParameter(cctx, ZSTD_c_blockDelimiters, ZSTD_sf_noBlockDelimiters);
+ compressedSize = ZSTD_compressSequences(cctx, dst, dstCapacity, seqs, nbSeqsAfterMerge, src, srcSize);
+ }
if (ZSTD_isError(compressedSize)) {
DISPLAY("Error in sequence compression with no block delims\n");
goto _output_error;
}
- dSize = ZSTD_decompress(decompressBuffer, decompressSize, dst, compressedSize);
- if (ZSTD_isError(dSize)) {
- DISPLAY("Error in sequence compression roundtrip with no block delims\n");
- goto _output_error;
- }
+ { size_t const dSize = ZSTD_decompress(decompressBuffer, decompressSize, dst, compressedSize);
+ if (ZSTD_isError(dSize)) {
+ DISPLAY("Error in sequence compression roundtrip with no block delims\n");
+ goto _output_error;
+ } }
assert(!memcmp(decompressBuffer, src, srcSize));
ZSTD_freeCCtx(cctx);
@@ -3968,9 +3968,9 @@ static int fuzzerTests(U32 seed, unsigned nbTests, unsigned startTest, U32 const
DISPLAYLEVEL(5, "fuzzer t%u: Bufferless streaming compression test \n", testNb);
{ U32 const testLog = FUZ_rand(&lseed) % maxSrcLog;
U32 const dictLog = FUZ_rand(&lseed) % maxSrcLog;
- int const cLevel = (FUZ_rand(&lseed) %
- (ZSTD_maxCLevel() -
- (MAX(testLog, dictLog) / cLevelLimiter))) +
+ int const cLevel = (int)(FUZ_rand(&lseed) %
+ ((U32)ZSTD_maxCLevel() -
+ (MAX(testLog, dictLog) / (U32)cLevelLimiter))) +
1;
maxTestSize = FUZ_rLogLength(&lseed, testLog);
if (maxTestSize >= dstBufferSize) maxTestSize = dstBufferSize-1;
@@ -4066,7 +4066,7 @@ static int fuzzerTests(U32 seed, unsigned nbTests, unsigned startTest, U32 const
free(cBuffer);
free(dstBuffer);
free(mirrorBuffer);
- return result;
+ return (int)result;
_output_error:
result = 1;
@@ -4103,7 +4103,7 @@ static unsigned readU32FromChar(const char** stringPtr)
{
unsigned result = 0;
while ((**stringPtr >='0') && (**stringPtr <='9'))
- result *= 10, result += **stringPtr - '0', (*stringPtr)++ ;
+ result *= 10, result += (unsigned)(**stringPtr - '0'), (*stringPtr)++ ;
if ((**stringPtr=='K') || (**stringPtr=='M')) {
result <<= 10;
if (**stringPtr=='M') result <<= 10;
@@ -4245,7 +4245,7 @@ int main(int argc, const char** argv)
}
}
if (!result)
- result = fuzzerTests(seed, nbTests, testNb, maxDuration, ((double)proba) / 100, bigTests);
+ result = fuzzerTests(seed, (unsigned)nbTests, (unsigned)testNb, maxDuration, ((double)proba) / 100, bigTests);
if (mainPause) {
int unused;
DISPLAY("Press Enter \n");
From 5684bae4f666f2730f2121048b0aa8472ac30457 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Fri, 31 Dec 2021 16:25:12 -0800
Subject: [PATCH 033/472] minor refactoring
on streaming compression implementation.
---
lib/compress/zstd_compress.c | 27 ++++++++++++++-------------
1 file changed, 14 insertions(+), 13 deletions(-)
diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
index 1cb229f7aa9..74f0cbfb510 100644
--- a/lib/compress/zstd_compress.c
+++ b/lib/compress/zstd_compress.c
@@ -5325,19 +5325,18 @@ static size_t ZSTD_nextInputSizeHint(const ZSTD_CCtx* cctx)
/** ZSTD_compressStream_generic():
* internal function for all *compressStream*() variants
- * non-static, because can be called from zstdmt_compress.c
- * @return : hint size for next input */
+ * @return : hint size for next input to complete ongoing block */
static size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs,
ZSTD_outBuffer* output,
ZSTD_inBuffer* input,
ZSTD_EndDirective const flushMode)
{
const char* const istart = (const char*)input->src;
- const char* const iend = input->size != 0 ? istart + input->size : istart;
- const char* ip = input->pos != 0 ? istart + input->pos : istart;
+ const char* const iend = (istart != NULL) ? istart + input->size : istart;
+ const char* ip = (istart != NULL) ? istart + input->pos : istart;
char* const ostart = (char*)output->dst;
- char* const oend = output->size != 0 ? ostart + output->size : ostart;
- char* op = output->pos != 0 ? ostart + output->pos : ostart;
+ char* const oend = (ostart != NULL) ? ostart + output->size : ostart;
+ char* op = (ostart != NULL) ? ostart + output->pos : ostart;
U32 someMoreWork = 1;
/* check expectations */
@@ -5515,7 +5514,8 @@ size_t ZSTD_compressStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output, ZSTD_inBuf
/* After a compression call set the expected input/output buffer.
* This is validated at the start of the next compression call.
*/
-static void ZSTD_setBufferExpectations(ZSTD_CCtx* cctx, ZSTD_outBuffer const* output, ZSTD_inBuffer const* input)
+static void
+ZSTD_setBufferExpectations(ZSTD_CCtx* cctx, const ZSTD_outBuffer* output, const ZSTD_inBuffer* input)
{
if (cctx->appliedParams.inBufferMode == ZSTD_bm_stable) {
cctx->expectedInBuffer = *input;
@@ -5550,7 +5550,8 @@ static size_t ZSTD_checkBufferStability(ZSTD_CCtx const* cctx,
static size_t ZSTD_CCtx_init_compressStream2(ZSTD_CCtx* cctx,
ZSTD_EndDirective endOp,
- size_t inSize) {
+ size_t inSize)
+{
ZSTD_CCtx_params params = cctx->requestedParams;
ZSTD_prefixDict const prefixDict = cctx->prefixDict;
FORWARD_IF_ERROR( ZSTD_initLocalDict(cctx) , ""); /* Init the local dict if present. */
@@ -5564,9 +5565,9 @@ static size_t ZSTD_CCtx_init_compressStream2(ZSTD_CCtx* cctx,
params.compressionLevel = cctx->cdict->compressionLevel;
}
DEBUGLOG(4, "ZSTD_compressStream2 : transparent init stage");
- if (endOp == ZSTD_e_end) cctx->pledgedSrcSizePlusOne = inSize + 1; /* auto-fix pledgedSrcSize */
- {
- size_t const dictSize = prefixDict.dict
+ if (endOp == ZSTD_e_end) cctx->pledgedSrcSizePlusOne = inSize + 1; /* auto-determine pledgedSrcSize */
+
+ { size_t const dictSize = prefixDict.dict
? prefixDict.dictSize
: (cctx->cdict ? cctx->cdict->dictContentSize : 0);
ZSTD_cParamMode_e const mode = ZSTD_getCParamMode(cctx->cdict, ¶ms, cctx->pledgedSrcSizePlusOne - 1);
@@ -5607,7 +5608,7 @@ static size_t ZSTD_CCtx_init_compressStream2(ZSTD_CCtx* cctx,
cctx->streamStage = zcss_load;
cctx->appliedParams = params;
} else
-#endif
+#endif /* ZSTD_MULTITHREAD */
{ U64 const pledgedSrcSize = cctx->pledgedSrcSizePlusOne - 1;
assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams)));
FORWARD_IF_ERROR( ZSTD_compressBegin_internal(cctx,
@@ -5699,7 +5700,7 @@ size_t ZSTD_compressStream2( ZSTD_CCtx* cctx,
ZSTD_setBufferExpectations(cctx, output, input);
return flushMin;
}
-#endif
+#endif /* ZSTD_MULTITHREAD */
FORWARD_IF_ERROR( ZSTD_compressStream_generic(cctx, output, input, endOp) , "");
DEBUGLOG(5, "completed ZSTD_compressStream2");
ZSTD_setBufferExpectations(cctx, output, input);
From c0c5ffa97385063ce9b24432e7c0c4130ed26e27 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Fri, 31 Dec 2021 18:20:36 -0800
Subject: [PATCH 034/472] streaming compression : lazy parameter adaptation
with stable input
effectively makes ZSTD_c_stableInput compatible ZSTD_compressStream()
and zstd_e_continue operation mode.
---
lib/compress/zstd_compress.c | 25 ++++++++++++++++---------
lib/zstd.h | 6 +++---
2 files changed, 19 insertions(+), 12 deletions(-)
diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
index 74f0cbfb510..724ac3887e1 100644
--- a/lib/compress/zstd_compress.c
+++ b/lib/compress/zstd_compress.c
@@ -177,12 +177,9 @@ size_t ZSTD_freeCCtx(ZSTD_CCtx* cctx)
if (cctx==NULL) return 0; /* support free on NULL */
RETURN_ERROR_IF(cctx->staticSize, memory_allocation,
"not compatible with static CCtx");
- {
- int cctxInWorkspace = ZSTD_cwksp_owns_buffer(&cctx->workspace, cctx);
+ { int cctxInWorkspace = ZSTD_cwksp_owns_buffer(&cctx->workspace, cctx);
ZSTD_freeCCtxContent(cctx);
- if (!cctxInWorkspace) {
- ZSTD_customFree(cctx, cctx->customMem);
- }
+ if (!cctxInWorkspace) ZSTD_customFree(cctx, cctx->customMem);
}
return 0;
}
@@ -4487,6 +4484,7 @@ ZSTD_compress_insertDictionary(ZSTD_compressedBlockState_t* bs,
#define ZSTD_USE_CDICT_PARAMS_DICTSIZE_MULTIPLIER (6ULL)
/*! ZSTD_compressBegin_internal() :
+ * Assumption : either @dict OR @cdict (or none) is non-NULL, never both
* @return : 0, or an error code */
static size_t ZSTD_compressBegin_internal(ZSTD_CCtx* cctx,
const void* dict, size_t dictSize,
@@ -4567,11 +4565,11 @@ size_t ZSTD_compressBegin_advanced(ZSTD_CCtx* cctx,
&cctxParams, pledgedSrcSize);
}
-size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel)
+size_t
+ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel)
{
ZSTD_CCtx_params cctxParams;
- {
- ZSTD_parameters const params = ZSTD_getParams_internal(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, dictSize, ZSTD_cpm_noAttachDict);
+ { ZSTD_parameters const params = ZSTD_getParams_internal(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, dictSize, ZSTD_cpm_noAttachDict);
ZSTD_CCtxParams_init_internal(&cctxParams, ¶ms, (compressionLevel == 0) ? ZSTD_CLEVEL_DEFAULT : compressionLevel);
}
DEBUGLOG(4, "ZSTD_compressBegin_usingDict (dictSize=%u)", (unsigned)dictSize);
@@ -5648,6 +5646,12 @@ size_t ZSTD_compressStream2( ZSTD_CCtx* cctx,
/* transparent initialization stage */
if (cctx->streamStage == zcss_init) {
+ if ( (cctx->requestedParams.inBufferMode == ZSTD_bm_stable) /* input is presumed stable, across invocations */
+ && (endOp == ZSTD_e_continue) /* more to come */
+ && (input->pos < ZSTD_BLOCKSIZE_MAX) ) { /* not even reached one block yet */
+ cctx->expectedInBuffer = *input;
+ return (ZSTD_BLOCKSIZE_MAX - input->pos); /* don't do anything : allows lazy compression parameters adaptation */
+ }
FORWARD_IF_ERROR(ZSTD_CCtx_init_compressStream2(cctx, endOp, input->size), "CompressStream2 initialization failed");
ZSTD_setBufferExpectations(cctx, output, input); /* Set initial buffer expectations now that we've initialized */
}
@@ -6159,6 +6163,7 @@ size_t ZSTD_compressSequences(ZSTD_CCtx* const cctx, void* dst, size_t dstCapaci
/*====== Finalize ======*/
/*! ZSTD_flushStream() :
+* Note : not compatible with ZSTD_c_stableInBuffer
* @return : amount of data remaining to flush */
size_t ZSTD_flushStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output)
{
@@ -6169,7 +6174,9 @@ size_t ZSTD_flushStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output)
size_t ZSTD_endStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output)
{
- ZSTD_inBuffer input = { NULL, 0, 0 };
+ ZSTD_inBuffer const nullInput = { NULL, 0, 0 };
+ int const stableInput = (zcs->appliedParams.inBufferMode == ZSTD_bm_stable);
+ ZSTD_inBuffer input = stableInput ? zcs->expectedInBuffer : nullInput;
size_t const remainingToFlush = ZSTD_compressStream2(zcs, output, &input, ZSTD_e_end);
FORWARD_IF_ERROR( remainingToFlush , "ZSTD_compressStream2 failed");
if (zcs->appliedParams.nbWorkers > 0) return remainingToFlush; /* minimal estimation */
diff --git a/lib/zstd.h b/lib/zstd.h
index 349d8017f84..f25e17d4b6b 100644
--- a/lib/zstd.h
+++ b/lib/zstd.h
@@ -1843,14 +1843,14 @@ ZSTDLIB_STATIC_API size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const vo
* large enough to fit a block (see ZSTD_c_stableOutBuffer). This will also
* avoid the memcpy() from the input buffer to the input window buffer.
*
- * NOTE: ZSTD_compressStream2() will error if ZSTD_e_end is not used.
- * That means this flag cannot be used with ZSTD_compressStream().
+ * NOTE: ZSTD_compressStream2() will error if ZSTD_e_flush is used.
+ * That means this flag cannot be used with ZSTD_flushStream().
*
* NOTE: So long as the ZSTD_inBuffer always points to valid memory, using
* this flag is ALWAYS memory safe, and will never access out-of-bounds
* memory. However, compression WILL fail if you violate the preconditions.
*
- * WARNING: The data in the ZSTD_inBuffer in the range [dst, dst + pos) MUST
+ * WARNING: The data in the ZSTD_inBuffer in the range [src, src + pos) MUST
* not be modified during compression or you will get data corruption. This
* is because zstd needs to reference data in the ZSTD_inBuffer to find
* matches. Normally zstd maintains its own window buffer for this purpose,
From 37b87add7a158deee06a12743aac11c36c352340 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Sat, 1 Jan 2022 23:15:34 -0800
Subject: [PATCH 035/472] make stableSrc compatible with regular streaming API
including flushStream().
Now the only condition is for `input.size` to continuously grow.
---
lib/compress/zstd_compress.c | 17 +++++++------
lib/zstd.h | 28 ++++++++++-----------
tests/zstreamtest.c | 47 +++++++++++++++++++-----------------
3 files changed, 48 insertions(+), 44 deletions(-)
diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
index 724ac3887e1..c1cd53fe721 100644
--- a/lib/compress/zstd_compress.c
+++ b/lib/compress/zstd_compress.c
@@ -5338,7 +5338,7 @@ static size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs,
U32 someMoreWork = 1;
/* check expectations */
- DEBUGLOG(5, "ZSTD_compressStream_generic, flush=%u", (unsigned)flushMode);
+ DEBUGLOG(5, "ZSTD_compressStream_generic, flush=%i", (int)flushMode);
if (zcs->appliedParams.inBufferMode == ZSTD_bm_buffered) {
assert(zcs->inBuff != NULL);
assert(zcs->inBuffSize > 0);
@@ -5424,9 +5424,8 @@ static size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs,
if (!lastBlock)
assert(zcs->inBuffTarget <= zcs->inBuffSize);
zcs->inToCompress = zcs->inBuffPos;
- } else {
+ } else { /* !inputBuffered, hence ZSTD_bm_stable */
unsigned const lastBlock = (ip + iSize == iend);
- assert(flushMode == ZSTD_e_end /* Already validated */);
cSize = lastBlock ?
ZSTD_compressEnd(zcs, cDst, oSize, ip, iSize) :
ZSTD_compressContinue(zcs, cDst, oSize, ip, iSize);
@@ -5533,11 +5532,10 @@ static size_t ZSTD_checkBufferStability(ZSTD_CCtx const* cctx,
{
if (cctx->appliedParams.inBufferMode == ZSTD_bm_stable) {
ZSTD_inBuffer const expect = cctx->expectedInBuffer;
- if (expect.src != input->src || expect.pos != input->pos || expect.size != input->size)
+ if (expect.src != input->src || expect.pos != input->pos)
RETURN_ERROR(srcBuffer_wrong, "ZSTD_c_stableInBuffer enabled but input differs!");
- if (endOp != ZSTD_e_end)
- RETURN_ERROR(srcBuffer_wrong, "ZSTD_c_stableInBuffer can only be used with ZSTD_e_end!");
}
+ (void)endOp;
if (cctx->appliedParams.outBufferMode == ZSTD_bm_stable) {
size_t const outBufferSize = output->size - output->pos;
if (cctx->expectedOutBufferSize != outBufferSize)
@@ -5650,7 +5648,7 @@ size_t ZSTD_compressStream2( ZSTD_CCtx* cctx,
&& (endOp == ZSTD_e_continue) /* more to come */
&& (input->pos < ZSTD_BLOCKSIZE_MAX) ) { /* not even reached one block yet */
cctx->expectedInBuffer = *input;
- return (ZSTD_BLOCKSIZE_MAX - input->pos); /* don't do anything : allows lazy compression parameters adaptation */
+ return (ZSTD_BLOCKSIZE_MAX - input->pos); /* don't do anything : allows lazy adaptation of compression parameters */
}
FORWARD_IF_ERROR(ZSTD_CCtx_init_compressStream2(cctx, endOp, input->size), "CompressStream2 initialization failed");
ZSTD_setBufferExpectations(cctx, output, input); /* Set initial buffer expectations now that we've initialized */
@@ -6167,7 +6165,10 @@ size_t ZSTD_compressSequences(ZSTD_CCtx* const cctx, void* dst, size_t dstCapaci
* @return : amount of data remaining to flush */
size_t ZSTD_flushStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output)
{
- ZSTD_inBuffer input = { NULL, 0, 0 };
+ ZSTD_inBuffer const nullInput = { NULL, 0, 0 };
+ int const stableInput = (zcs->appliedParams.inBufferMode == ZSTD_bm_stable);
+ ZSTD_inBuffer input = stableInput ? zcs->expectedInBuffer : nullInput;
+ input.size = input.pos; /* do not ingest more input during flush */
return ZSTD_compressStream2(zcs, output, &input, ZSTD_e_flush);
}
diff --git a/lib/zstd.h b/lib/zstd.h
index f25e17d4b6b..f503c1708af 100644
--- a/lib/zstd.h
+++ b/lib/zstd.h
@@ -1829,13 +1829,16 @@ ZSTDLIB_STATIC_API size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const vo
* Experimental parameter.
* Default is 0 == disabled. Set to 1 to enable.
*
- * Tells the compressor that the ZSTD_inBuffer will ALWAYS be the same
- * between calls, except for the modifications that zstd makes to pos (the
- * caller must not modify pos). This is checked by the compressor, and
- * compression will fail if it ever changes. This means the only flush
- * mode that makes sense is ZSTD_e_end, so zstd will error if ZSTD_e_end
- * is not used. The data in the ZSTD_inBuffer in the range [src, src + pos)
- * MUST not be modified during compression or you will get data corruption.
+ * Tells the compressor that input data presented with ZSTD_inBuffer
+ * will ALWAYS be the same between calls.
+ * Technically, the @src pointer must never be changed,
+ * and the @pos field can only be updated by zstd.
+ * However, it's possible to increase the @size field,
+ * allowing scenarios where more data can be appended after compressions starts.
+ * These conditions are checked by the compressor,
+ * and compression will fail if they are not respected.
+ * Also, data in the ZSTD_inBuffer within the range [src, src + pos)
+ * MUST not be modified during compression or it will result in data corruption.
*
* When this flag is enabled zstd won't allocate an input window buffer,
* because the user guarantees it can reference the ZSTD_inBuffer until
@@ -1843,18 +1846,15 @@ ZSTDLIB_STATIC_API size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const vo
* large enough to fit a block (see ZSTD_c_stableOutBuffer). This will also
* avoid the memcpy() from the input buffer to the input window buffer.
*
- * NOTE: ZSTD_compressStream2() will error if ZSTD_e_flush is used.
- * That means this flag cannot be used with ZSTD_flushStream().
- *
* NOTE: So long as the ZSTD_inBuffer always points to valid memory, using
* this flag is ALWAYS memory safe, and will never access out-of-bounds
- * memory. However, compression WILL fail if you violate the preconditions.
+ * memory. However, compression WILL fail if conditions are not respected.
*
* WARNING: The data in the ZSTD_inBuffer in the range [src, src + pos) MUST
- * not be modified during compression or you will get data corruption. This
- * is because zstd needs to reference data in the ZSTD_inBuffer to find
+ * not be modified during compression or it sill result in data corruption.
+ * This is because zstd needs to reference data in the ZSTD_inBuffer to find
* matches. Normally zstd maintains its own window buffer for this purpose,
- * but passing this flag tells zstd to use the user provided buffer.
+ * but passing this flag tells zstd to rely on user provided buffer instead.
*/
#define ZSTD_c_stableInBuffer ZSTD_c_experimentalParam9
diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c
index 72fd72ea368..66e017c3d3d 100644
--- a/tests/zstreamtest.c
+++ b/tests/zstreamtest.c
@@ -613,7 +613,7 @@ static int basicUnitTests(U32 seed, double compressibility)
DISPLAYLEVEL(3, "OK (error detected : %s) \n", ZSTD_getErrorName(r));
} }
- /* Complex context re-use scenario */
+ /* Compression state re-use scenario */
DISPLAYLEVEL(3, "test%3i : context re-use : ", testNb++);
ZSTD_freeCStream(zc);
zc = ZSTD_createCStream();
@@ -634,8 +634,7 @@ static int basicUnitTests(U32 seed, double compressibility)
CHECK_Z( ZSTD_compressStream(zc, &outBuff, &inBuff) );
if (inBuff.pos != inBuff.size) goto _output_error; /* entire input should be consumed */
DISPLAYLEVEL(5, "end1 ");
- { size_t const r = ZSTD_endStream(zc, &outBuff);
- if (r != 0) goto _output_error; } /* error, or some data not flushed */
+ if (ZSTD_endStream(zc, &outBuff) != 0) goto _output_error; /* error, or some data not flushed */
}
/* use 2 */
{ size_t const inSize = 1025; /* will not continue, because tables auto-adjust and are therefore different size */
@@ -653,8 +652,7 @@ static int basicUnitTests(U32 seed, double compressibility)
CHECK_Z( ZSTD_compressStream(zc, &outBuff, &inBuff) );
if (inBuff.pos != inBuff.size) goto _output_error; /* entire input should be consumed */
DISPLAYLEVEL(5, "end2 ");
- { size_t const r = ZSTD_endStream(zc, &outBuff);
- if (r != 0) goto _output_error; } /* error, or some data not flushed */
+ if (ZSTD_endStream(zc, &outBuff) != 0) goto _output_error; /* error, or some data not flushed */
}
DISPLAYLEVEL(3, "OK \n");
@@ -786,16 +784,18 @@ static int basicUnitTests(U32 seed, double compressibility)
CHECK(!(cSize < ZSTD_compressBound(CNBufferSize)), "cSize too large for test");
CHECK_Z(cSize = ZSTD_compress2(cctx, compressedBuffer, cSize + 4, CNBuffer, CNBufferSize));
CHECK_Z(cctxSize1 = ZSTD_sizeof_CCtx(cctx));
- { ZSTD_CCtx* cctx2 = ZSTD_createCCtx();
+ { ZSTD_CCtx* const cctx2 = ZSTD_createCCtx();
+ assert(cctx2 != NULL);
in.pos = out.pos = 0;
CHECK_Z(ZSTD_compressStream2(cctx2, &out, &in, ZSTD_e_continue));
CHECK(!(ZSTD_compressStream2(cctx2, &out, &in, ZSTD_e_end) == 0), "Not finished");
CHECK_Z(cctxSize2 = ZSTD_sizeof_CCtx(cctx2));
ZSTD_freeCCtx(cctx2);
}
- { ZSTD_CCtx* cctx3 = ZSTD_createCCtx();
+ { ZSTD_CCtx* const cctx3 = ZSTD_createCCtx();
ZSTD_parameters params = ZSTD_getParams(0, CNBufferSize, 0);
size_t cSize3;
+ assert(cctx3 != NULL);
params.fParams.checksumFlag = 1;
cSize3 = ZSTD_compress_advanced(cctx3, compressedBuffer, compressedBufferSize, CNBuffer, CNBufferSize, NULL, 0, params);
CHECK_Z(cSize3);
@@ -808,8 +808,7 @@ static int basicUnitTests(U32 seed, double compressibility)
DISPLAYLEVEL(3, "OK \n");
DISPLAYLEVEL(3, "test%3i : ZSTD_compress2() doesn't modify user parameters : ", testNb++);
- {
- int stableInBuffer;
+ { int stableInBuffer;
int stableOutBuffer;
CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_stableInBuffer, &stableInBuffer));
CHECK_Z(ZSTD_CCtx_getParameter(cctx, ZSTD_c_stableOutBuffer, &stableOutBuffer));
@@ -870,21 +869,24 @@ static int basicUnitTests(U32 seed, double compressibility)
}
DISPLAYLEVEL(3, "OK \n");
- DISPLAYLEVEL(3, "test%3i : ZSTD_compressStream2() ZSTD_c_stableInBuffer with continue and flush : ", testNb++);
+ /* stableSrc + streaming */
+ DISPLAYLEVEL(3, "test%3i : ZSTD_c_stableInBuffer compatibility with compressStream, flushStream and endStream : ", testNb++);
+ CHECK_Z( ZSTD_initCStream(cctx, 1) );
+ CHECK_Z( ZSTD_CCtx_setParameter(cctx, ZSTD_c_stableInBuffer, 1) );
in.src = CNBuffer;
- in.size = CNBufferSize;
+ in.size = 100;
in.pos = 0;
- out.pos = 0;
- out.size = compressedBufferSize;
- CHECK_Z(ZSTD_CCtx_reset(cctx, ZSTD_reset_session_only));
- { size_t const ret = ZSTD_compressStream2(cctx, &out, &in, ZSTD_e_continue);
- CHECK(!ZSTD_isError(ret), "Must error");
- CHECK(!(ZSTD_getErrorCode(ret) == ZSTD_error_srcBuffer_wrong), "Must be this error");
- }
- CHECK_Z(ZSTD_CCtx_reset(cctx, ZSTD_reset_session_only));
- { size_t const ret = ZSTD_compressStream2(cctx, &out, &in, ZSTD_e_flush);
- CHECK(!ZSTD_isError(ret), "Must error");
- CHECK(!(ZSTD_getErrorCode(ret) == ZSTD_error_srcBuffer_wrong), "Must be this error");
+ { ZSTD_outBuffer outBuf;
+ outBuf.dst = (char*)(compressedBuffer)+cSize;
+ outBuf.size = ZSTD_compressBound(500);
+ outBuf.pos = 0;
+ CHECK_Z( ZSTD_compressStream(cctx, &outBuf, &in) );
+ in.size = 200;
+ CHECK_Z( ZSTD_compressStream(cctx, &outBuf, &in) );
+ CHECK_Z( ZSTD_flushStream(cctx, &outBuf) );
+ in.size = 300;
+ CHECK_Z( ZSTD_compressStream(cctx, &outBuf, &in) );
+ if (ZSTD_endStream(cctx, &outBuf) != 0) goto _output_error; /* error, or some data not flushed */
}
DISPLAYLEVEL(3, "OK \n");
@@ -902,6 +904,7 @@ static int basicUnitTests(U32 seed, double compressibility)
CHECK_Z(ZSTD_CCtx_setParameter(cctx, ZSTD_c_stableOutBuffer, 1));
in.pos = out.pos = 0;
in.size = MIN(CNBufferSize, 10);
+ out.size = compressedBufferSize;
CHECK_Z(ZSTD_compressStream2(cctx, &out, &in, ZSTD_e_flush));
in.pos = 0;
in.size = CNBufferSize - in.size;
From 27d336b099d3e4b19969a24e7425a14af86d4879 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Sun, 2 Jan 2022 20:06:46 -0800
Subject: [PATCH 036/472] minor behavior refinements
specifically, there is no obligation to start streaming compression with pos=0.
stableSrc mode is now compatible with this setup.
---
lib/compress/zstd_compress.c | 12 ++++++++----
lib/zstd.h | 2 +-
tests/zstreamtest.c | 22 ++++++++++++----------
3 files changed, 21 insertions(+), 15 deletions(-)
diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
index c1cd53fe721..015fd23c9d8 100644
--- a/lib/compress/zstd_compress.c
+++ b/lib/compress/zstd_compress.c
@@ -5630,6 +5630,8 @@ static size_t ZSTD_CCtx_init_compressStream2(ZSTD_CCtx* cctx,
return 0;
}
+/* @return provides a minimum amount of data remaining to be flushed from internal buffers
+ */
size_t ZSTD_compressStream2( ZSTD_CCtx* cctx,
ZSTD_outBuffer* output,
ZSTD_inBuffer* input,
@@ -5644,13 +5646,15 @@ size_t ZSTD_compressStream2( ZSTD_CCtx* cctx,
/* transparent initialization stage */
if (cctx->streamStage == zcss_init) {
+ size_t const inputSize = input->size - input->pos; /* no obligation to start from pos==0 */
if ( (cctx->requestedParams.inBufferMode == ZSTD_bm_stable) /* input is presumed stable, across invocations */
- && (endOp == ZSTD_e_continue) /* more to come */
- && (input->pos < ZSTD_BLOCKSIZE_MAX) ) { /* not even reached one block yet */
+ && (endOp == ZSTD_e_continue) /* no flush requested, more input to come */
+ && (inputSize < ZSTD_BLOCKSIZE_MAX) ) { /* not even reached one block yet */
+ /* just wait, allows lazy adaptation of compression parameters */
cctx->expectedInBuffer = *input;
- return (ZSTD_BLOCKSIZE_MAX - input->pos); /* don't do anything : allows lazy adaptation of compression parameters */
+ return ZSTD_FRAMEHEADERSIZE_MIN(cctx->requestedParams.format); /* at least some header to produce */
}
- FORWARD_IF_ERROR(ZSTD_CCtx_init_compressStream2(cctx, endOp, input->size), "CompressStream2 initialization failed");
+ FORWARD_IF_ERROR(ZSTD_CCtx_init_compressStream2(cctx, endOp, inputSize), "CompressStream2 initialization failed");
ZSTD_setBufferExpectations(cctx, output, input); /* Set initial buffer expectations now that we've initialized */
}
/* end of transparent initialization stage */
diff --git a/lib/zstd.h b/lib/zstd.h
index f503c1708af..c3847a864f8 100644
--- a/lib/zstd.h
+++ b/lib/zstd.h
@@ -1851,7 +1851,7 @@ ZSTDLIB_STATIC_API size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const vo
* memory. However, compression WILL fail if conditions are not respected.
*
* WARNING: The data in the ZSTD_inBuffer in the range [src, src + pos) MUST
- * not be modified during compression or it sill result in data corruption.
+ * not be modified during compression or it will result in data corruption.
* This is because zstd needs to reference data in the ZSTD_inBuffer to find
* matches. Normally zstd maintains its own window buffer for this purpose,
* but passing this flag tells zstd to rely on user provided buffer instead.
diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c
index 66e017c3d3d..15a5cdb7c2e 100644
--- a/tests/zstreamtest.c
+++ b/tests/zstreamtest.c
@@ -873,20 +873,21 @@ static int basicUnitTests(U32 seed, double compressibility)
DISPLAYLEVEL(3, "test%3i : ZSTD_c_stableInBuffer compatibility with compressStream, flushStream and endStream : ", testNb++);
CHECK_Z( ZSTD_initCStream(cctx, 1) );
CHECK_Z( ZSTD_CCtx_setParameter(cctx, ZSTD_c_stableInBuffer, 1) );
- in.src = CNBuffer;
- in.size = 100;
- in.pos = 0;
- { ZSTD_outBuffer outBuf;
+ { ZSTD_inBuffer inBuf;
+ ZSTD_outBuffer outBuf;
+ inBuf.src = CNBuffer;
+ inBuf.size = 100;
+ inBuf.pos = 0;
outBuf.dst = (char*)(compressedBuffer)+cSize;
outBuf.size = ZSTD_compressBound(500);
outBuf.pos = 0;
- CHECK_Z( ZSTD_compressStream(cctx, &outBuf, &in) );
- in.size = 200;
- CHECK_Z( ZSTD_compressStream(cctx, &outBuf, &in) );
+ CHECK_Z( ZSTD_compressStream(cctx, &outBuf, &inBuf) );
+ inBuf.size = 200;
+ CHECK_Z( ZSTD_compressStream(cctx, &outBuf, &inBuf) );
CHECK_Z( ZSTD_flushStream(cctx, &outBuf) );
- in.size = 300;
- CHECK_Z( ZSTD_compressStream(cctx, &outBuf, &in) );
- if (ZSTD_endStream(cctx, &outBuf) != 0) goto _output_error; /* error, or some data not flushed */
+ inBuf.size = 300;
+ CHECK_Z( ZSTD_compressStream(cctx, &outBuf, &inBuf) );
+ CHECK(ZSTD_endStream(cctx, &outBuf) != 0, "compression should be successful and fully flushed");
}
DISPLAYLEVEL(3, "OK \n");
@@ -902,6 +903,7 @@ static int basicUnitTests(U32 seed, double compressibility)
CHECK_Z(ZSTD_CCtx_reset(cctx, ZSTD_reset_session_and_parameters));
CHECK_Z(ZSTD_CCtx_setParameter(cctx, ZSTD_c_checksumFlag, 1));
CHECK_Z(ZSTD_CCtx_setParameter(cctx, ZSTD_c_stableOutBuffer, 1));
+ in.src = CNBuffer;
in.pos = out.pos = 0;
in.size = MIN(CNBufferSize, 10);
out.size = compressedBufferSize;
From 4b9d1dd9ffc882ff9ac57e2363a411591a27b886 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Wed, 5 Jan 2022 13:21:43 -0800
Subject: [PATCH 037/472] fixed incorrect comment
---
lib/compress/zstd_compress.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
index 015fd23c9d8..3e20818c682 100644
--- a/lib/compress/zstd_compress.c
+++ b/lib/compress/zstd_compress.c
@@ -6165,7 +6165,6 @@ size_t ZSTD_compressSequences(ZSTD_CCtx* const cctx, void* dst, size_t dstCapaci
/*====== Finalize ======*/
/*! ZSTD_flushStream() :
-* Note : not compatible with ZSTD_c_stableInBuffer
* @return : amount of data remaining to flush */
size_t ZSTD_flushStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output)
{
From 8296be4a0a25b6e2f74f1cdd7bd4ae04981f6a45 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Wed, 5 Jan 2022 16:34:10 -0800
Subject: [PATCH 038/472] pretend consuming input to provide a sense of forward
progress
---
lib/compress/zstd_compress.c | 21 +++++++++++++++++----
lib/compress/zstd_compress_internal.h | 1 +
2 files changed, 18 insertions(+), 4 deletions(-)
diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
index 3e20818c682..553a0765de1 100644
--- a/lib/compress/zstd_compress.c
+++ b/lib/compress/zstd_compress.c
@@ -5647,15 +5647,28 @@ size_t ZSTD_compressStream2( ZSTD_CCtx* cctx,
/* transparent initialization stage */
if (cctx->streamStage == zcss_init) {
size_t const inputSize = input->size - input->pos; /* no obligation to start from pos==0 */
+ size_t const totalInputSize = (cctx->savedInPosPlusOne == 0) ? inputSize : input->size - (cctx->savedInPosPlusOne - 1);
if ( (cctx->requestedParams.inBufferMode == ZSTD_bm_stable) /* input is presumed stable, across invocations */
&& (endOp == ZSTD_e_continue) /* no flush requested, more input to come */
- && (inputSize < ZSTD_BLOCKSIZE_MAX) ) { /* not even reached one block yet */
- /* just wait, allows lazy adaptation of compression parameters */
+ && (totalInputSize < ZSTD_BLOCKSIZE_MAX) ) { /* not even reached one block yet */
+ if (cctx->savedInPosPlusOne) { /* not the first time */
+ /* check stable source guarantees */
+ assert(input->src == cctx->expectedInBuffer.src);
+ assert(input->pos == cctx->expectedInBuffer.size);
+ }
+ /* keep track of first position */
+ if (cctx->savedInPosPlusOne == 0) cctx->savedInPosPlusOne = input->pos + 1;
cctx->expectedInBuffer = *input;
+ /* pretend input was consumed, to give a sense forward progress */
+ input[0].pos = input[0].size;
+ /* but actually input wasn't consumed, so keep track of position from where compression shall resume */
+ cctx->expectedInBuffer.pos = cctx->savedInPosPlusOne - 1;
+ /* don't initialize yet, wait for the first block of flush() order, for better parameters adaptation */
return ZSTD_FRAMEHEADERSIZE_MIN(cctx->requestedParams.format); /* at least some header to produce */
}
- FORWARD_IF_ERROR(ZSTD_CCtx_init_compressStream2(cctx, endOp, inputSize), "CompressStream2 initialization failed");
- ZSTD_setBufferExpectations(cctx, output, input); /* Set initial buffer expectations now that we've initialized */
+ FORWARD_IF_ERROR(ZSTD_CCtx_init_compressStream2(cctx, endOp, totalInputSize), "compressStream2 initialization failed");
+ cctx->savedInPosPlusOne = 0;
+ ZSTD_setBufferExpectations(cctx, output, input); /* Set initial buffer expectations now that we've initialized */
}
/* end of transparent initialization stage */
diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h
index 9fe3affab6b..8cc2f81d6d0 100644
--- a/lib/compress/zstd_compress_internal.h
+++ b/lib/compress/zstd_compress_internal.h
@@ -411,6 +411,7 @@ struct ZSTD_CCtx_s {
/* Stable in/out buffer verification */
ZSTD_inBuffer expectedInBuffer;
size_t expectedOutBufferSize;
+ size_t savedInPosPlusOne; /* 0 == no savedInPos */
/* Dictionary */
ZSTD_localDict localDict;
From 270f9bf00595160933652d92a809a2dab61cfeed Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Mon, 24 Jan 2022 14:45:22 -0800
Subject: [PATCH 039/472] better consistency in accessing @input
as suggested by @terrelln.
Also : commented zstreamtest more
to ensure ZSTD_stableInBuffer is tested/
---
lib/compress/zstd_compress.c | 2 +-
tests/zstreamtest.c | 21 +++++++++++++--------
2 files changed, 14 insertions(+), 9 deletions(-)
diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
index 553a0765de1..7be5d87a751 100644
--- a/lib/compress/zstd_compress.c
+++ b/lib/compress/zstd_compress.c
@@ -5660,7 +5660,7 @@ size_t ZSTD_compressStream2( ZSTD_CCtx* cctx,
if (cctx->savedInPosPlusOne == 0) cctx->savedInPosPlusOne = input->pos + 1;
cctx->expectedInBuffer = *input;
/* pretend input was consumed, to give a sense forward progress */
- input[0].pos = input[0].size;
+ input->pos = input->size;
/* but actually input wasn't consumed, so keep track of position from where compression shall resume */
cctx->expectedInBuffer.pos = cctx->savedInPosPlusOne - 1;
/* don't initialize yet, wait for the first block of flush() order, for better parameters adaptation */
diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c
index 15a5cdb7c2e..1444d27df9a 100644
--- a/tests/zstreamtest.c
+++ b/tests/zstreamtest.c
@@ -769,11 +769,12 @@ static int basicUnitTests(U32 seed, double compressibility)
}
/* Compression with ZSTD_c_stable{In,Out}Buffer */
- { ZSTD_CCtx* cctx = ZSTD_createCCtx();
+ { ZSTD_CCtx* const cctx = ZSTD_createCCtx();
ZSTD_inBuffer in;
ZSTD_outBuffer out;
size_t cctxSize1;
size_t cctxSize2;
+ assert(cctx != NULL);
in.src = CNBuffer;
in.size = CNBufferSize;
out.dst = compressedBuffer;
@@ -784,6 +785,7 @@ static int basicUnitTests(U32 seed, double compressibility)
CHECK(!(cSize < ZSTD_compressBound(CNBufferSize)), "cSize too large for test");
CHECK_Z(cSize = ZSTD_compress2(cctx, compressedBuffer, cSize + 4, CNBuffer, CNBufferSize));
CHECK_Z(cctxSize1 = ZSTD_sizeof_CCtx(cctx));
+ /* @cctxSize2 : sizeof_CCtx when doing full streaming (no stable in/out) */
{ ZSTD_CCtx* const cctx2 = ZSTD_createCCtx();
assert(cctx2 != NULL);
in.pos = out.pos = 0;
@@ -792,16 +794,17 @@ static int basicUnitTests(U32 seed, double compressibility)
CHECK_Z(cctxSize2 = ZSTD_sizeof_CCtx(cctx2));
ZSTD_freeCCtx(cctx2);
}
- { ZSTD_CCtx* const cctx3 = ZSTD_createCCtx();
+ /* @cctxSize1 : sizeof_CCtx when doing single-shot compression (no streaming) */
+ { ZSTD_CCtx* const cctx1 = ZSTD_createCCtx();
ZSTD_parameters params = ZSTD_getParams(0, CNBufferSize, 0);
size_t cSize3;
- assert(cctx3 != NULL);
+ assert(cctx1 != NULL);
params.fParams.checksumFlag = 1;
- cSize3 = ZSTD_compress_advanced(cctx3, compressedBuffer, compressedBufferSize, CNBuffer, CNBufferSize, NULL, 0, params);
+ cSize3 = ZSTD_compress_advanced(cctx1, compressedBuffer, compressedBufferSize, CNBuffer, CNBufferSize, NULL, 0, params);
CHECK_Z(cSize3);
CHECK(!(cSize == cSize3), "Must be same compressed size");
- CHECK(!(cctxSize1 == ZSTD_sizeof_CCtx(cctx3)), "Must be same CCtx size");
- ZSTD_freeCCtx(cctx3);
+ CHECK(!(cctxSize1 == ZSTD_sizeof_CCtx(cctx1)), "Must be same CCtx size");
+ ZSTD_freeCCtx(cctx1);
}
CHECK(!(cctxSize1 < cctxSize2), "Stable buffers means less allocated size");
CHECK_Z(ZSTD_decompress(decodedBuffer, CNBufferSize, compressedBuffer, cSize));
@@ -891,8 +894,9 @@ static int basicUnitTests(U32 seed, double compressibility)
}
DISPLAYLEVEL(3, "OK \n");
- DISPLAYLEVEL(3, "test%3i : ZSTD_compressStream2() ZSTD_c_stableInBuffer allocated size : ", testNb++);
+ DISPLAYLEVEL(3, "test%3i : ZSTD_compressStream2() with ZSTD_c_stableInBuffer: context size : ", testNb++);
{ size_t const cctxSize = ZSTD_sizeof_CCtx(cctx);
+ DISPLAYLEVEL(4, "cctxSize1=%zu; cctxSize=%zu; cctxSize2=%zu : ", cctxSize1, cctxSize, cctxSize2);
CHECK(!(cctxSize1 < cctxSize), "Must be bigger than single-pass");
CHECK(!(cctxSize < cctxSize2), "Must be smaller than streaming");
cctxSize1 = cctxSize;
@@ -925,8 +929,9 @@ static int basicUnitTests(U32 seed, double compressibility)
}
DISPLAYLEVEL(3, "OK \n");
- DISPLAYLEVEL(3, "test%3i : ZSTD_compressStream2() ZSTD_c_stableOutBuffer allocated size : ", testNb++);
+ DISPLAYLEVEL(3, "test%3i : ZSTD_compressStream2() with ZSTD_c_stableOutBuffer: context size : ", testNb++);
{ size_t const cctxSize = ZSTD_sizeof_CCtx(cctx);
+ DISPLAYLEVEL(4, "cctxSize1=%zu; cctxSize=%zu; cctxSize2=%zu : ", cctxSize1, cctxSize, cctxSize2);
CHECK(!(cctxSize1 < cctxSize), "Must be bigger than single-pass and stableInBuffer");
CHECK(!(cctxSize < cctxSize2), "Must be smaller than streaming");
}
From c1668a00d2d71915582a22c3c0082f59cfee53bd Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Mon, 24 Jan 2022 22:57:55 -0800
Subject: [PATCH 040/472] fix extended case combining stableInBuffer with
continue() and flush() modes
---
lib/compress/zstd_compress.c | 78 ++++++++++++++++++---------
lib/compress/zstd_compress_internal.h | 2 +-
tests/fuzzer.c | 2 +-
tests/zstreamtest.c | 12 +++--
4 files changed, 65 insertions(+), 29 deletions(-)
diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
index 7be5d87a751..e2a6d9675c5 100644
--- a/lib/compress/zstd_compress.c
+++ b/lib/compress/zstd_compress.c
@@ -5316,9 +5316,14 @@ size_t ZSTD_initCStream(ZSTD_CStream* zcs, int compressionLevel)
static size_t ZSTD_nextInputSizeHint(const ZSTD_CCtx* cctx)
{
- size_t hintInSize = cctx->inBuffTarget - cctx->inBuffPos;
- if (hintInSize==0) hintInSize = cctx->blockSize;
- return hintInSize;
+ if (cctx->appliedParams.inBufferMode == ZSTD_bm_stable) {
+ return cctx->blockSize - cctx->stableIn_notConsumed;
+ }
+ assert(cctx->appliedParams.inBufferMode == ZSTD_bm_buffered);
+ { size_t hintInSize = cctx->inBuffTarget - cctx->inBuffPos;
+ if (hintInSize==0) hintInSize = cctx->blockSize;
+ return hintInSize;
+ }
}
/** ZSTD_compressStream_generic():
@@ -5329,16 +5334,23 @@ static size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs,
ZSTD_inBuffer* input,
ZSTD_EndDirective const flushMode)
{
- const char* const istart = (const char*)input->src;
+ const char* const istart = (assert(input != NULL), (const char*)input->src);
const char* const iend = (istart != NULL) ? istart + input->size : istart;
const char* ip = (istart != NULL) ? istart + input->pos : istart;
- char* const ostart = (char*)output->dst;
+ char* const ostart = (assert(output != NULL), (char*)output->dst);
char* const oend = (ostart != NULL) ? ostart + output->size : ostart;
char* op = (ostart != NULL) ? ostart + output->pos : ostart;
U32 someMoreWork = 1;
/* check expectations */
- DEBUGLOG(5, "ZSTD_compressStream_generic, flush=%i", (int)flushMode);
+ DEBUGLOG(5, "ZSTD_compressStream_generic, flush=%i, srcSize = %zu", (int)flushMode, input->size - input->pos);
+ assert(zcs != NULL);
+ if (zcs->appliedParams.inBufferMode == ZSTD_bm_stable) {
+ assert(input->pos >= zcs->stableIn_notConsumed);
+ input->pos -= zcs->stableIn_notConsumed;
+ ip -= zcs->stableIn_notConsumed;
+ zcs->stableIn_notConsumed = 0;
+ }
if (zcs->appliedParams.inBufferMode == ZSTD_bm_buffered) {
assert(zcs->inBuff != NULL);
assert(zcs->inBuffSize > 0);
@@ -5347,8 +5359,10 @@ static size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs,
assert(zcs->outBuff != NULL);
assert(zcs->outBuffSize > 0);
}
- assert(output->pos <= output->size);
+ if (input->src == NULL) assert(input->size == 0);
assert(input->pos <= input->size);
+ if (output->dst == NULL) assert(output->size == 0);
+ assert(output->pos <= output->size);
assert((U32)flushMode <= (U32)ZSTD_e_end);
while (someMoreWork) {
@@ -5380,8 +5394,7 @@ static size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs,
zcs->inBuff + zcs->inBuffPos, toLoad,
ip, iend-ip);
zcs->inBuffPos += loaded;
- if (loaded != 0)
- ip += loaded;
+ if (ip) ip += loaded;
if ( (flushMode == ZSTD_e_continue)
&& (zcs->inBuffPos < zcs->inBuffTarget) ) {
/* not enough input to fill full block : stop here */
@@ -5392,6 +5405,20 @@ static size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs,
/* empty */
someMoreWork = 0; break;
}
+ } else {
+ assert(zcs->appliedParams.inBufferMode == ZSTD_bm_stable);
+ if ( (flushMode == ZSTD_e_continue)
+ && ( (size_t)(iend - ip) < zcs->blockSize) ) {
+ /* can't compress a full block : stop here */
+ zcs->stableIn_notConsumed = (size_t)(iend - ip);
+ ip = iend; /* pretend to have consumed input */
+ someMoreWork = 0; break;
+ }
+ if ( (flushMode == ZSTD_e_flush)
+ && (ip == iend) ) {
+ /* empty */
+ someMoreWork = 0; break;
+ }
}
/* compress current block (note : this stage cannot be stopped in the middle) */
DEBUGLOG(5, "stream compression stage (flushMode==%u)", flushMode);
@@ -5399,9 +5426,8 @@ static size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs,
void* cDst;
size_t cSize;
size_t oSize = oend-op;
- size_t const iSize = inputBuffered
- ? zcs->inBuffPos - zcs->inToCompress
- : MIN((size_t)(iend - ip), zcs->blockSize);
+ size_t const iSize = inputBuffered ? zcs->inBuffPos - zcs->inToCompress
+ : MIN((size_t)(iend - ip), zcs->blockSize);
if (oSize >= ZSTD_compressBound(iSize) || zcs->appliedParams.outBufferMode == ZSTD_bm_stable)
cDst = op; /* compress into output buffer, to skip flush stage */
else
@@ -5425,17 +5451,15 @@ static size_t ZSTD_compressStream_generic(ZSTD_CStream* zcs,
assert(zcs->inBuffTarget <= zcs->inBuffSize);
zcs->inToCompress = zcs->inBuffPos;
} else { /* !inputBuffered, hence ZSTD_bm_stable */
- unsigned const lastBlock = (ip + iSize == iend);
+ unsigned const lastBlock = (flushMode == ZSTD_e_end) && (ip + iSize == iend);
cSize = lastBlock ?
ZSTD_compressEnd(zcs, cDst, oSize, ip, iSize) :
ZSTD_compressContinue(zcs, cDst, oSize, ip, iSize);
/* Consume the input prior to error checking to mirror buffered mode. */
- if (iSize > 0)
- ip += iSize;
+ if (ip) ip += iSize;
FORWARD_IF_ERROR(cSize, "%s", lastBlock ? "ZSTD_compressEnd failed" : "ZSTD_compressContinue failed");
zcs->frameEnded = lastBlock;
- if (lastBlock)
- assert(ip == iend);
+ if (lastBlock) assert(ip == iend);
}
if (cDst == op) { /* no need to flush */
op += cSize;
@@ -5514,6 +5538,7 @@ size_t ZSTD_compressStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output, ZSTD_inBuf
static void
ZSTD_setBufferExpectations(ZSTD_CCtx* cctx, const ZSTD_outBuffer* output, const ZSTD_inBuffer* input)
{
+ DEBUGLOG(5, "ZSTD_setBufferExpectations (for advanced stable in/out modes)");
if (cctx->appliedParams.inBufferMode == ZSTD_bm_stable) {
cctx->expectedInBuffer = *input;
}
@@ -5647,27 +5672,25 @@ size_t ZSTD_compressStream2( ZSTD_CCtx* cctx,
/* transparent initialization stage */
if (cctx->streamStage == zcss_init) {
size_t const inputSize = input->size - input->pos; /* no obligation to start from pos==0 */
- size_t const totalInputSize = (cctx->savedInPosPlusOne == 0) ? inputSize : input->size - (cctx->savedInPosPlusOne - 1);
+ size_t const totalInputSize = inputSize + cctx->stableIn_notConsumed;
if ( (cctx->requestedParams.inBufferMode == ZSTD_bm_stable) /* input is presumed stable, across invocations */
&& (endOp == ZSTD_e_continue) /* no flush requested, more input to come */
&& (totalInputSize < ZSTD_BLOCKSIZE_MAX) ) { /* not even reached one block yet */
- if (cctx->savedInPosPlusOne) { /* not the first time */
+ if (cctx->stableIn_notConsumed) { /* not the first time */
/* check stable source guarantees */
assert(input->src == cctx->expectedInBuffer.src);
assert(input->pos == cctx->expectedInBuffer.size);
}
- /* keep track of first position */
- if (cctx->savedInPosPlusOne == 0) cctx->savedInPosPlusOne = input->pos + 1;
- cctx->expectedInBuffer = *input;
/* pretend input was consumed, to give a sense forward progress */
input->pos = input->size;
+ /* save stable inBuffer, for later control, and flush/end */
+ cctx->expectedInBuffer = *input;
/* but actually input wasn't consumed, so keep track of position from where compression shall resume */
- cctx->expectedInBuffer.pos = cctx->savedInPosPlusOne - 1;
+ cctx->stableIn_notConsumed += inputSize;
/* don't initialize yet, wait for the first block of flush() order, for better parameters adaptation */
return ZSTD_FRAMEHEADERSIZE_MIN(cctx->requestedParams.format); /* at least some header to produce */
}
FORWARD_IF_ERROR(ZSTD_CCtx_init_compressStream2(cctx, endOp, totalInputSize), "compressStream2 initialization failed");
- cctx->savedInPosPlusOne = 0;
ZSTD_setBufferExpectations(cctx, output, input); /* Set initial buffer expectations now that we've initialized */
}
/* end of transparent initialization stage */
@@ -5681,6 +5704,13 @@ size_t ZSTD_compressStream2( ZSTD_CCtx* cctx,
ZSTDMT_updateCParams_whileCompressing(cctx->mtctx, &cctx->requestedParams);
cctx->cParamsChanged = 0;
}
+ if (cctx->stableIn_notConsumed) {
+ assert(cctx->appliedParams.inBufferMode == ZSTD_bm_stable);
+ /* some early data was skipped - make it available for consumption */
+ assert(input->pos >= cctx->stableIn_notConsumed);
+ input->pos -= cctx->stableIn_notConsumed;
+ cctx->stableIn_notConsumed = 0;
+ }
for (;;) {
size_t const ipos = input->pos;
size_t const opos = output->pos;
diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h
index 8cc2f81d6d0..efbf89ae37b 100644
--- a/lib/compress/zstd_compress_internal.h
+++ b/lib/compress/zstd_compress_internal.h
@@ -410,8 +410,8 @@ struct ZSTD_CCtx_s {
/* Stable in/out buffer verification */
ZSTD_inBuffer expectedInBuffer;
+ size_t stableIn_notConsumed; /* nb bytes within stable input buffer that are said to be consumed but are not */
size_t expectedOutBufferSize;
- size_t savedInPosPlusOne; /* 0 == no savedInPos */
/* Dictionary */
ZSTD_localDict localDict;
diff --git a/tests/fuzzer.c b/tests/fuzzer.c
index 823db775f35..ddb2ad3938b 100644
--- a/tests/fuzzer.c
+++ b/tests/fuzzer.c
@@ -1206,7 +1206,7 @@ static int basicUnitTests(U32 const seed, double compressibility)
DISPLAYLEVEL(3, "test%3i : compress a NULL input with each level : ", testNb++);
{ int level = -1;
- ZSTD_CCtx* cctx = ZSTD_createCCtx();
+ ZSTD_CCtx* const cctx = ZSTD_createCCtx();
if (!cctx) goto _output_error;
for (level = -1; level <= ZSTD_maxCLevel(); ++level) {
CHECK_Z( ZSTD_compress(compressedBuffer, compressedBufferSize, NULL, 0, level) );
diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c
index 1444d27df9a..e084924b2d0 100644
--- a/tests/zstreamtest.c
+++ b/tests/zstreamtest.c
@@ -878,20 +878,26 @@ static int basicUnitTests(U32 seed, double compressibility)
CHECK_Z( ZSTD_CCtx_setParameter(cctx, ZSTD_c_stableInBuffer, 1) );
{ ZSTD_inBuffer inBuf;
ZSTD_outBuffer outBuf;
+ const size_t inputSize = 500;
inBuf.src = CNBuffer;
inBuf.size = 100;
inBuf.pos = 0;
outBuf.dst = (char*)(compressedBuffer)+cSize;
- outBuf.size = ZSTD_compressBound(500);
+ outBuf.size = ZSTD_compressBound(inputSize);
outBuf.pos = 0;
CHECK_Z( ZSTD_compressStream(cctx, &outBuf, &inBuf) );
inBuf.size = 200;
CHECK_Z( ZSTD_compressStream(cctx, &outBuf, &inBuf) );
CHECK_Z( ZSTD_flushStream(cctx, &outBuf) );
- inBuf.size = 300;
+ inBuf.size = inputSize;
CHECK_Z( ZSTD_compressStream(cctx, &outBuf, &inBuf) );
CHECK(ZSTD_endStream(cctx, &outBuf) != 0, "compression should be successful and fully flushed");
- }
+ { void* const verifBuf = (char*)outBuf.dst + outBuf.pos;
+ const size_t decSize = ZSTD_decompress(verifBuf, inputSize, outBuf.dst, outBuf.pos);
+ CHECK_Z(decSize);
+ CHECK(decSize != inputSize, "regenerated %zu bytes, instead of %zu", decSize, inputSize);
+ CHECK(memcmp(inBuf.src, verifBuf, inputSize) != 0, "regenerated data different from original");
+ } }
DISPLAYLEVEL(3, "OK \n");
DISPLAYLEVEL(3, "test%3i : ZSTD_compressStream2() with ZSTD_c_stableInBuffer: context size : ", testNb++);
From af3d9c506e5bbfdbf78ce35fe62501c4ae0e19a7 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Wed, 26 Jan 2022 10:30:33 -0800
Subject: [PATCH 041/472] added streaming test starting from non-0 pos
---
tests/zstreamtest.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c
index e084924b2d0..a88ba7ac868 100644
--- a/tests/zstreamtest.c
+++ b/tests/zstreamtest.c
@@ -878,10 +878,11 @@ static int basicUnitTests(U32 seed, double compressibility)
CHECK_Z( ZSTD_CCtx_setParameter(cctx, ZSTD_c_stableInBuffer, 1) );
{ ZSTD_inBuffer inBuf;
ZSTD_outBuffer outBuf;
+ const size_t nonZeroStartPos = 18;
const size_t inputSize = 500;
inBuf.src = CNBuffer;
inBuf.size = 100;
- inBuf.pos = 0;
+ inBuf.pos = nonZeroStartPos;
outBuf.dst = (char*)(compressedBuffer)+cSize;
outBuf.size = ZSTD_compressBound(inputSize);
outBuf.pos = 0;
@@ -889,14 +890,15 @@ static int basicUnitTests(U32 seed, double compressibility)
inBuf.size = 200;
CHECK_Z( ZSTD_compressStream(cctx, &outBuf, &inBuf) );
CHECK_Z( ZSTD_flushStream(cctx, &outBuf) );
- inBuf.size = inputSize;
+ inBuf.size = nonZeroStartPos + inputSize;
CHECK_Z( ZSTD_compressStream(cctx, &outBuf, &inBuf) );
CHECK(ZSTD_endStream(cctx, &outBuf) != 0, "compression should be successful and fully flushed");
- { void* const verifBuf = (char*)outBuf.dst + outBuf.pos;
+ { const void* const realSrcStart = (const char*)inBuf.src + nonZeroStartPos;
+ void* const verifBuf = (char*)outBuf.dst + outBuf.pos;
const size_t decSize = ZSTD_decompress(verifBuf, inputSize, outBuf.dst, outBuf.pos);
CHECK_Z(decSize);
CHECK(decSize != inputSize, "regenerated %zu bytes, instead of %zu", decSize, inputSize);
- CHECK(memcmp(inBuf.src, verifBuf, inputSize) != 0, "regenerated data different from original");
+ CHECK(memcmp(realSrcStart, verifBuf, inputSize) != 0, "regenerated data different from original");
} }
DISPLAYLEVEL(3, "OK \n");
From b99ece96b99b7015169d5e11d45078f21bfa6bd4 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Wed, 26 Jan 2022 10:43:50 -0800
Subject: [PATCH 042/472] converted checks into user validation generating
error codes
had to create a new error code for this condition,
none of the existing ones were fitting enough.
---
lib/common/error_private.c | 1 +
lib/compress/zstd_compress.c | 4 ++--
lib/zstd_errors.h | 1 +
3 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/lib/common/error_private.c b/lib/common/error_private.c
index 6d1135f8c37..f171e0dce38 100644
--- a/lib/common/error_private.c
+++ b/lib/common/error_private.c
@@ -38,6 +38,7 @@ const char* ERR_getErrorString(ERR_enum code)
case PREFIX(tableLog_tooLarge): return "tableLog requires too much memory : unsupported";
case PREFIX(maxSymbolValue_tooLarge): return "Unsupported max Symbol Value : too large";
case PREFIX(maxSymbolValue_tooSmall): return "Specified maxSymbolValue is too small";
+ case PREFIX(stabilityCondition_notRespected): return "pledged buffer stability condition is not respected";
case PREFIX(dictionary_corrupted): return "Dictionary is corrupted";
case PREFIX(dictionary_wrong): return "Dictionary mismatch";
case PREFIX(dictionaryCreation_failed): return "Cannot create Dictionary from provided samples";
diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
index e2a6d9675c5..57f41a3b343 100644
--- a/lib/compress/zstd_compress.c
+++ b/lib/compress/zstd_compress.c
@@ -5678,8 +5678,8 @@ size_t ZSTD_compressStream2( ZSTD_CCtx* cctx,
&& (totalInputSize < ZSTD_BLOCKSIZE_MAX) ) { /* not even reached one block yet */
if (cctx->stableIn_notConsumed) { /* not the first time */
/* check stable source guarantees */
- assert(input->src == cctx->expectedInBuffer.src);
- assert(input->pos == cctx->expectedInBuffer.size);
+ RETURN_ERROR_IF(input->src != cctx->expectedInBuffer.src, stabilityCondition_notRespected, "stableInBuffer condition not respected: wrong src pointer");
+ RETURN_ERROR_IF(input->pos != cctx->expectedInBuffer.size, stabilityCondition_notRespected, "stableInBuffer condition not respected: externally modified pos");
}
/* pretend input was consumed, to give a sense forward progress */
input->pos = input->size;
diff --git a/lib/zstd_errors.h b/lib/zstd_errors.h
index fa3686b7724..2ec0b0ab168 100644
--- a/lib/zstd_errors.h
+++ b/lib/zstd_errors.h
@@ -66,6 +66,7 @@ typedef enum {
ZSTD_error_tableLog_tooLarge = 44,
ZSTD_error_maxSymbolValue_tooLarge = 46,
ZSTD_error_maxSymbolValue_tooSmall = 48,
+ ZSTD_error_stabilityCondition_notRespected = 50,
ZSTD_error_stage_wrong = 60,
ZSTD_error_init_missing = 62,
ZSTD_error_memory_allocation = 64,
From cbff372d105cb0593af24aba780481c83ef8d2ab Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Wed, 26 Jan 2022 11:05:57 -0800
Subject: [PATCH 043/472] added helper function inBuffer_forEndFlush()
---
lib/compress/zstd_compress.c | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
index 57f41a3b343..6a34beea97c 100644
--- a/lib/compress/zstd_compress.c
+++ b/lib/compress/zstd_compress.c
@@ -6207,13 +6207,18 @@ size_t ZSTD_compressSequences(ZSTD_CCtx* const cctx, void* dst, size_t dstCapaci
/*====== Finalize ======*/
+static ZSTD_inBuffer inBuffer_forEndFlush(const ZSTD_CStream* zcs)
+{
+ const ZSTD_inBuffer nullInput = { NULL, 0, 0 };
+ const int stableInput = (zcs->appliedParams.inBufferMode == ZSTD_bm_stable);
+ return stableInput ? zcs->expectedInBuffer : nullInput;
+}
+
/*! ZSTD_flushStream() :
* @return : amount of data remaining to flush */
size_t ZSTD_flushStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output)
{
- ZSTD_inBuffer const nullInput = { NULL, 0, 0 };
- int const stableInput = (zcs->appliedParams.inBufferMode == ZSTD_bm_stable);
- ZSTD_inBuffer input = stableInput ? zcs->expectedInBuffer : nullInput;
+ ZSTD_inBuffer input = inBuffer_forEndFlush(zcs);
input.size = input.pos; /* do not ingest more input during flush */
return ZSTD_compressStream2(zcs, output, &input, ZSTD_e_flush);
}
@@ -6221,11 +6226,9 @@ size_t ZSTD_flushStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output)
size_t ZSTD_endStream(ZSTD_CStream* zcs, ZSTD_outBuffer* output)
{
- ZSTD_inBuffer const nullInput = { NULL, 0, 0 };
- int const stableInput = (zcs->appliedParams.inBufferMode == ZSTD_bm_stable);
- ZSTD_inBuffer input = stableInput ? zcs->expectedInBuffer : nullInput;
+ ZSTD_inBuffer input = inBuffer_forEndFlush(zcs);
size_t const remainingToFlush = ZSTD_compressStream2(zcs, output, &input, ZSTD_e_end);
- FORWARD_IF_ERROR( remainingToFlush , "ZSTD_compressStream2 failed");
+ FORWARD_IF_ERROR(remainingToFlush , "ZSTD_compressStream2(,,ZSTD_e_end) failed");
if (zcs->appliedParams.nbWorkers > 0) return remainingToFlush; /* minimal estimation */
/* single thread mode : attempt to calculate remaining to flush more precisely */
{ size_t const lastBlockSize = zcs->frameEnded ? 0 : ZSTD_BLOCKHEADERSIZE;
From dda4c10f0710d24685ee8490e9715d18300665f1 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Wed, 26 Jan 2022 13:33:04 -0800
Subject: [PATCH 044/472] added ZSTD_compressStream2() + ZSTD_c_stableInBuffer
test
---
tests/zstreamtest.c | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c
index a88ba7ac868..dbe2b907ae0 100644
--- a/tests/zstreamtest.c
+++ b/tests/zstreamtest.c
@@ -902,6 +902,36 @@ static int basicUnitTests(U32 seed, double compressibility)
} }
DISPLAYLEVEL(3, "OK \n");
+ /* stableSrc + streaming */
+ DISPLAYLEVEL(3, "test%3i : ZSTD_c_stableInBuffer compatibility with compressStream2, using different end directives : ", testNb++);
+ CHECK_Z( ZSTD_initCStream(cctx, 1) );
+ CHECK_Z( ZSTD_CCtx_setParameter(cctx, ZSTD_c_stableInBuffer, 1) );
+ { ZSTD_inBuffer inBuf;
+ ZSTD_outBuffer outBuf;
+ const size_t nonZeroStartPos = 18;
+ const size_t inputSize = 500;
+ inBuf.src = CNBuffer;
+ inBuf.size = 100;
+ inBuf.pos = nonZeroStartPos;
+ outBuf.dst = (char*)(compressedBuffer)+cSize;
+ outBuf.size = ZSTD_compressBound(inputSize);
+ outBuf.pos = 0;
+ CHECK_Z( ZSTD_compressStream2(cctx, &outBuf, &inBuf, ZSTD_e_continue) );
+ inBuf.size = 200;
+ CHECK_Z( ZSTD_compressStream2(cctx, &outBuf, &inBuf, ZSTD_e_continue) );
+ CHECK_Z( ZSTD_compressStream2(cctx, &outBuf, &inBuf, ZSTD_e_flush) );
+ inBuf.size = nonZeroStartPos + inputSize;
+ CHECK_Z( ZSTD_compressStream2(cctx, &outBuf, &inBuf, ZSTD_e_continue) );
+ CHECK( ZSTD_compressStream2(cctx, &outBuf, &inBuf, ZSTD_e_end) != 0, "compression should be successful and fully flushed");
+ { const void* const realSrcStart = (const char*)inBuf.src + nonZeroStartPos;
+ void* const verifBuf = (char*)outBuf.dst + outBuf.pos;
+ const size_t decSize = ZSTD_decompress(verifBuf, inputSize, outBuf.dst, outBuf.pos);
+ CHECK_Z(decSize);
+ CHECK(decSize != inputSize, "regenerated %zu bytes, instead of %zu", decSize, inputSize);
+ CHECK(memcmp(realSrcStart, verifBuf, inputSize) != 0, "regenerated data different from original");
+ } }
+ DISPLAYLEVEL(3, "OK \n");
+
DISPLAYLEVEL(3, "test%3i : ZSTD_compressStream2() with ZSTD_c_stableInBuffer: context size : ", testNb++);
{ size_t const cctxSize = ZSTD_sizeof_CCtx(cctx);
DISPLAYLEVEL(4, "cctxSize1=%zu; cctxSize=%zu; cctxSize2=%zu : ", cctxSize1, cctxSize, cctxSize2);
From 7616e39f3b5618a20d2c8d059f1369283838cbce Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Thu, 20 Jan 2022 21:23:48 -0800
Subject: [PATCH 045/472] adding traces to better track processing of literals
---
lib/compress/huf_compress.c | 135 ++++++++++++++++++-------
lib/compress/zstd_compress.c | 13 +--
lib/compress/zstd_compress_literals.c | 36 +++++--
lib/decompress/zstd_decompress_block.c | 2 +-
lib/zstd.h | 4 +-
5 files changed, 135 insertions(+), 55 deletions(-)
diff --git a/lib/compress/huf_compress.c b/lib/compress/huf_compress.c
index 2b3d6adc2a2..e9652058db5 100644
--- a/lib/compress/huf_compress.c
+++ b/lib/compress/huf_compress.c
@@ -272,7 +272,7 @@ size_t HUF_readCTable (HUF_CElt* CTable, unsigned* maxSymbolValuePtr, const void
U32 HUF_getNbBitsFromCTable(HUF_CElt const* CTable, U32 symbolValue)
{
- const HUF_CElt* ct = CTable + 1;
+ const HUF_CElt* const ct = CTable + 1;
assert(symbolValue <= HUF_SYMBOLVALUE_MAX);
return (U32)HUF_getNbBits(ct[symbolValue]);
}
@@ -287,53 +287,56 @@ typedef struct nodeElt_s {
/**
* HUF_setMaxHeight():
- * Enforces maxNbBits on the Huffman tree described in huffNode.
+ * Try to enforce @targetNbBits on the Huffman tree described in @huffNode.
*
- * It sets all nodes with nbBits > maxNbBits to be maxNbBits. Then it adjusts
- * the tree to so that it is a valid canonical Huffman tree.
+ * It attempts to convert all nodes with nbBits > @targetNbBits
+ * to employ @targetNbBits instead. Then it adjusts the tree
+ * so that it remains a valid canonical Huffman tree.
*
* @pre The sum of the ranks of each symbol == 2^largestBits,
* where largestBits == huffNode[lastNonNull].nbBits.
* @post The sum of the ranks of each symbol == 2^largestBits,
- * where largestBits is the return value <= maxNbBits.
+ * where largestBits is the return value (expected <= targetNbBits).
*
- * @param huffNode The Huffman tree modified in place to enforce maxNbBits.
+ * @param huffNode The Huffman tree modified in place to enforce targetNbBits.
+ * It's presumed sorted, from most frequent to rarest symbol.
* @param lastNonNull The symbol with the lowest count in the Huffman tree.
- * @param maxNbBits The maximum allowed number of bits, which the Huffman tree
+ * @param targetNbBits The allowed number of bits, which the Huffman tree
* may not respect. After this function the Huffman tree will
- * respect maxNbBits.
- * @return The maximum number of bits of the Huffman tree after adjustment,
- * necessarily no more than maxNbBits.
+ * respect targetNbBits.
+ * @return The maximum number of bits of the Huffman tree after adjustment.
*/
-static U32 HUF_setMaxHeight(nodeElt* huffNode, U32 lastNonNull, U32 maxNbBits)
+static U32 HUF_setMaxHeight(nodeElt* huffNode, U32 lastNonNull, U32 targetNbBits)
{
const U32 largestBits = huffNode[lastNonNull].nbBits;
- /* early exit : no elt > maxNbBits, so the tree is already valid. */
- if (largestBits <= maxNbBits) return largestBits;
+ /* early exit : no elt > targetNbBits, so the tree is already valid. */
+ if (largestBits <= targetNbBits) return largestBits;
+
+ DEBUGLOG(5, "HUF_setMaxHeight (targetNbBits = %u)", targetNbBits);
/* there are several too large elements (at least >= 2) */
{ int totalCost = 0;
- const U32 baseCost = 1 << (largestBits - maxNbBits);
+ const U32 baseCost = 1 << (largestBits - targetNbBits);
int n = (int)lastNonNull;
- /* Adjust any ranks > maxNbBits to maxNbBits.
+ /* Adjust any ranks > targetNbBits to targetNbBits.
* Compute totalCost, which is how far the sum of the ranks is
* we are over 2^largestBits after adjust the offending ranks.
*/
- while (huffNode[n].nbBits > maxNbBits) {
+ while (huffNode[n].nbBits > targetNbBits) {
totalCost += baseCost - (1 << (largestBits - huffNode[n].nbBits));
- huffNode[n].nbBits = (BYTE)maxNbBits;
+ huffNode[n].nbBits = (BYTE)targetNbBits;
n--;
}
- /* n stops at huffNode[n].nbBits <= maxNbBits */
- assert(huffNode[n].nbBits <= maxNbBits);
- /* n end at index of smallest symbol using < maxNbBits */
- while (huffNode[n].nbBits == maxNbBits) --n;
+ /* n stops at huffNode[n].nbBits <= targetNbBits */
+ assert(huffNode[n].nbBits <= targetNbBits);
+ /* n end at index of smallest symbol using < targetNbBits */
+ while (huffNode[n].nbBits == targetNbBits) --n;
- /* renorm totalCost from 2^largestBits to 2^maxNbBits
+ /* renorm totalCost from 2^largestBits to 2^targetNbBits
* note : totalCost is necessarily a multiple of baseCost */
assert((totalCost & (baseCost - 1)) == 0);
- totalCost >>= (largestBits - maxNbBits);
+ totalCost >>= (largestBits - targetNbBits);
assert(totalCost > 0);
/* repay normalized cost */
@@ -342,12 +345,12 @@ static U32 HUF_setMaxHeight(nodeElt* huffNode, U32 lastNonNull, U32 maxNbBits)
/* Get pos of last (smallest = lowest cum. count) symbol per rank */
ZSTD_memset(rankLast, 0xF0, sizeof(rankLast));
- { U32 currentNbBits = maxNbBits;
+ { U32 currentNbBits = targetNbBits;
int pos;
for (pos=n ; pos >= 0; pos--) {
if (huffNode[pos].nbBits >= currentNbBits) continue;
- currentNbBits = huffNode[pos].nbBits; /* < maxNbBits */
- rankLast[maxNbBits-currentNbBits] = (U32)pos;
+ currentNbBits = huffNode[pos].nbBits; /* < targetNbBits */
+ rankLast[targetNbBits-currentNbBits] = (U32)pos;
} }
while (totalCost > 0) {
@@ -394,7 +397,7 @@ static U32 HUF_setMaxHeight(nodeElt* huffNode, U32 lastNonNull, U32 maxNbBits)
rankLast[nBitsToDecrease] = noSymbol;
else {
rankLast[nBitsToDecrease]--;
- if (huffNode[rankLast[nBitsToDecrease]].nbBits != maxNbBits-nBitsToDecrease)
+ if (huffNode[rankLast[nBitsToDecrease]].nbBits != targetNbBits-nBitsToDecrease)
rankLast[nBitsToDecrease] = noSymbol; /* this rank is now empty */
}
} /* while (totalCost > 0) */
@@ -406,11 +409,11 @@ static U32 HUF_setMaxHeight(nodeElt* huffNode, U32 lastNonNull, U32 maxNbBits)
* TODO.
*/
while (totalCost < 0) { /* Sometimes, cost correction overshoot */
- /* special case : no rank 1 symbol (using maxNbBits-1);
- * let's create one from largest rank 0 (using maxNbBits).
+ /* special case : no rank 1 symbol (using targetNbBits-1);
+ * let's create one from largest rank 0 (using targetNbBits).
*/
if (rankLast[1] == noSymbol) {
- while (huffNode[n].nbBits == maxNbBits) n--;
+ while (huffNode[n].nbBits == targetNbBits) n--;
huffNode[n+1].nbBits--;
assert(n >= 0);
rankLast[1] = (U32)(n+1);
@@ -424,7 +427,7 @@ static U32 HUF_setMaxHeight(nodeElt* huffNode, U32 lastNonNull, U32 maxNbBits)
} /* repay normalized cost */
} /* there are several too large elements (at least >= 2) */
- return maxNbBits;
+ return targetNbBits;
}
typedef struct {
@@ -594,6 +597,28 @@ static void HUF_sort(nodeElt huffNode[], const unsigned count[], U32 const maxSy
assert(HUF_isSorted(huffNode, maxSymbolValue1));
}
+
+size_t showHNodeSymbols(const nodeElt* hnode, size_t size)
+{
+ size_t u;
+ for (u=0; uhuffNodeTbl;
nodeElt* const huffNode = huffNode0+1;
int nonNullRank;
+ DEBUGLOG(5, "HUF_buildCTable_wksp (alphabet size = %u)", maxSymbolValue+1);
+
/* safety checks */
if (wkspSize < sizeof(HUF_buildCTable_wksp_tables))
- return ERROR(workSpace_tooSmall);
+ return ERROR(workSpace_tooSmall);
if (maxNbBits == 0) maxNbBits = HUF_TABLELOG_DEFAULT;
if (maxSymbolValue > HUF_SYMBOLVALUE_MAX)
- return ERROR(maxSymbolValue_tooLarge);
+ return ERROR(maxSymbolValue_tooLarge);
ZSTD_memset(huffNode0, 0, sizeof(huffNodeTable));
/* sort, decreasing order */
HUF_sort(huffNode, count, maxSymbolValue, wksp_tables->rankPosition);
+ DEBUGLOG(6, "sorted symbols completed (%zu symbols)", showHNodeSymbols(huffNode, maxSymbolValue+1)); (void)showHNodeSymbols;
/* build tree */
nonNullRank = HUF_buildTree(huffNode, maxSymbolValue);
- /* enforce maxTableLog */
+ /* determine and enforce maxTableLog */
maxNbBits = HUF_setMaxHeight(huffNode, (U32)nonNullRank, maxNbBits);
if (maxNbBits > HUF_TABLELOG_MAX) return ERROR(GENERIC); /* check fit into table */
@@ -1158,6 +1192,28 @@ static size_t HUF_compressCTable_internal(
return (size_t)(op-ostart);
}
+static size_t showU32(const U32* arr, size_t size)
+{
+ size_t u;
+ for (u=0; u= 2);
if (suspectUncompressible && srcSize >= (SUSPECT_INCOMPRESSIBLE_SAMPLE_SIZE * SUSPECT_INCOMPRESSIBLE_SAMPLE_RATIO)) {
size_t largestTotal = 0;
+ DEBUGLOG(5, "input suspected incompressible : sampling to check");
{ unsigned maxSymbolValueBegin = maxSymbolValue;
CHECK_V_F(largestBegin, HIST_count_simple (table->count, &maxSymbolValueBegin, (const BYTE*)src, SUSPECT_INCOMPRESSIBLE_SAMPLE_SIZE) );
largestTotal += largestBegin;
@@ -1227,6 +1285,7 @@ HUF_compress_internal (void* dst, size_t dstSize,
if (largest == srcSize) { *ostart = ((const BYTE*)src)[0]; return 1; } /* single symbol, rle */
if (largest <= (srcSize >> 7)+4) return 0; /* heuristic : probably not compressible enough */
}
+ DEBUGLOG(6, "histogram detail completed (%zu symbols)", showU32(table->count, maxSymbolValue+1)); (void)showU32;
/* Check validity of previous table */
if ( repeat
@@ -1248,6 +1307,7 @@ HUF_compress_internal (void* dst, size_t dstSize,
&table->wksps.buildCTable_wksp, sizeof(table->wksps.buildCTable_wksp));
CHECK_F(maxBits);
huffLog = (U32)maxBits;
+ DEBUGLOG(6, "bit distribution completed (%zu symbols)", showCTableBits(table->CTable + 1, maxSymbolValue+1)); (void)showCTableBits;
}
/* Zero unused symbols in CTable, so we can check it for validity */
{
@@ -1300,6 +1360,7 @@ size_t HUF_compress1X_repeat (void* dst, size_t dstSize,
HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat,
int bmi2, unsigned suspectUncompressible)
{
+ DEBUGLOG(5, "HUF_compress1X_repeat (srcSize = %zu)", srcSize);
return HUF_compress_internal(dst, dstSize, src, srcSize,
maxSymbolValue, huffLog, HUF_singleStream,
workSpace, wkspSize, hufTable,
@@ -1314,6 +1375,7 @@ size_t HUF_compress4X_wksp (void* dst, size_t dstSize,
unsigned maxSymbolValue, unsigned huffLog,
void* workSpace, size_t wkspSize)
{
+ DEBUGLOG(5, "HUF_compress4X_wksp (srcSize = %zu)", srcSize);
return HUF_compress_internal(dst, dstSize, src, srcSize,
maxSymbolValue, huffLog, HUF_fourStreams,
workSpace, wkspSize,
@@ -1330,6 +1392,7 @@ size_t HUF_compress4X_repeat (void* dst, size_t dstSize,
void* workSpace, size_t wkspSize,
HUF_CElt* hufTable, HUF_repeat* repeat, int preferRepeat, int bmi2, unsigned suspectUncompressible)
{
+ DEBUGLOG(5, "HUF_compress4X_repeat (srcSize = %zu)", srcSize);
return HUF_compress_internal(dst, dstSize, src, srcSize,
maxSymbolValue, huffLog, HUF_fourStreams,
workSpace, wkspSize,
diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
index c369c84e281..c534eb25eea 100644
--- a/lib/compress/zstd_compress.c
+++ b/lib/compress/zstd_compress.c
@@ -5850,12 +5850,13 @@ ZSTD_copySequencesToSeqStoreExplicitBlockDelim(ZSTD_CCtx* cctx,
return 0;
}
-/* Returns the number of bytes to move the current read position back by. Only non-zero
- * if we ended up splitting a sequence. Otherwise, it may return a ZSTD error if something
- * went wrong.
+/* Returns the number of bytes to move the current read position back by.
+ * Only non-zero if we ended up splitting a sequence.
+ * Otherwise, it may return a ZSTD error if something went wrong.
*
- * This function will attempt to scan through blockSize bytes represented by the sequences
- * in inSeqs, storing any (partial) sequences.
+ * This function will attempt to scan through blockSize bytes
+ * represented by the sequences in @inSeqs,
+ * storing any (partial) sequences.
*
* Occasionally, we may want to change the actual number of bytes we consumed from inSeqs to
* avoid splitting a match, or to avoid splitting a match such that it would produce a match
@@ -5883,7 +5884,7 @@ ZSTD_copySequencesToSeqStoreNoBlockDelim(ZSTD_CCtx* cctx, ZSTD_sequencePosition*
} else {
dictSize = 0;
}
- DEBUGLOG(5, "ZSTD_copySequencesToSeqStore: idx: %u PIS: %u blockSize: %zu", idx, startPosInSequence, blockSize);
+ DEBUGLOG(5, "ZSTD_copySequencesToSeqStoreNoBlockDelim: idx: %u PIS: %u blockSize: %zu", idx, startPosInSequence, blockSize);
DEBUGLOG(5, "Start seq: idx: %u (of: %u ml: %u ll: %u)", idx, inSeqs[idx].offset, inSeqs[idx].matchLength, inSeqs[idx].litLength);
ZSTD_memcpy(updatedRepcodes.rep, cctx->blockState.prevCBlock->rep, sizeof(repcodes_t));
while (endPosInSequence && idx < inSeqsSize && !finalMatchSplit) {
diff --git a/lib/compress/zstd_compress_literals.c b/lib/compress/zstd_compress_literals.c
index 52b0a8059ab..0c1acc407c9 100644
--- a/lib/compress/zstd_compress_literals.c
+++ b/lib/compress/zstd_compress_literals.c
@@ -36,7 +36,7 @@ size_t ZSTD_noCompressLiterals (void* dst, size_t dstCapacity, const void* src,
}
ZSTD_memcpy(ostart + flSize, src, srcSize);
- DEBUGLOG(5, "Raw literals: %u -> %u", (U32)srcSize, (U32)(srcSize + flSize));
+ DEBUGLOG(5, "Raw (uncompressed) literals: %u -> %u", (U32)srcSize, (U32)(srcSize + flSize));
return srcSize + flSize;
}
@@ -67,6 +67,17 @@ size_t ZSTD_compressRleLiteralsBlock (void* dst, size_t dstCapacity, const void*
return flSize+1;
}
+static size_t showHexa(const void* src, size_t srcSize)
+{
+ const BYTE* const ip = (const BYTE*)src;
+ size_t u;
+ for (u=0; urepeatMode;
- int const preferRepeat = strategy < ZSTD_lazy ? srcSize <= 1024 : 0;
+ int const preferRepeat = (strategy < ZSTD_lazy) ? srcSize <= 1024 : 0;
+ typedef size_t (*huf_compress_f)(void*, size_t, const void*, size_t, unsigned, unsigned, void*, size_t, HUF_CElt*, HUF_repeat*, int, int, unsigned);
+ huf_compress_f huf_compress;
if (repeat == HUF_repeat_valid && lhSize == 3) singleStream = 1;
- cLitSize = singleStream ?
- HUF_compress1X_repeat(
- ostart+lhSize, dstCapacity-lhSize, src, srcSize,
- HUF_SYMBOLVALUE_MAX, HUF_TABLELOG_DEFAULT, entropyWorkspace, entropyWorkspaceSize,
- (HUF_CElt*)nextHuf->CTable, &repeat, preferRepeat, bmi2, suspectUncompressible) :
- HUF_compress4X_repeat(
- ostart+lhSize, dstCapacity-lhSize, src, srcSize,
- HUF_SYMBOLVALUE_MAX, HUF_TABLELOG_DEFAULT, entropyWorkspace, entropyWorkspaceSize,
- (HUF_CElt*)nextHuf->CTable, &repeat, preferRepeat, bmi2, suspectUncompressible);
+ huf_compress = singleStream ? HUF_compress1X_repeat : HUF_compress4X_repeat;
+ cLitSize = huf_compress(ostart+lhSize, dstCapacity-lhSize,
+ src, srcSize,
+ HUF_SYMBOLVALUE_MAX, HUF_TABLELOG_DEFAULT,
+ entropyWorkspace, entropyWorkspaceSize,
+ (HUF_CElt*)nextHuf->CTable,
+ &repeat, preferRepeat,
+ bmi2, suspectUncompressible);
if (repeat != HUF_repeat_none) {
/* reused the existing table */
DEBUGLOG(5, "Reusing previous huffman table");
diff --git a/lib/decompress/zstd_decompress_block.c b/lib/decompress/zstd_decompress_block.c
index 2e44d30d2f3..758c7ad99ae 100644
--- a/lib/decompress/zstd_decompress_block.c
+++ b/lib/decompress/zstd_decompress_block.c
@@ -1993,7 +1993,7 @@ ZSTD_decompressBlock_internal(ZSTD_DCtx* dctx,
/* Decode literals section */
{ size_t const litCSize = ZSTD_decodeLiteralsBlock(dctx, src, srcSize, dst, dstCapacity, streaming);
- DEBUGLOG(5, "ZSTD_decodeLiteralsBlock : %u", (U32)litCSize);
+ DEBUGLOG(5, "ZSTD_decodeLiteralsBlock : cSize=%u, nbLiterals=%zu", (U32)litCSize, dctx->litSize);
if (ZSTD_isError(litCSize)) return litCSize;
ip += litCSize;
srcSize -= litCSize;
diff --git a/lib/zstd.h b/lib/zstd.h
index 8e90ae5f95c..2867850534b 100644
--- a/lib/zstd.h
+++ b/lib/zstd.h
@@ -1409,7 +1409,9 @@ ZSTD_generateSequences( ZSTD_CCtx* zc,
ZSTDLIB_STATIC_API size_t ZSTD_mergeBlockDelimiters(ZSTD_Sequence* sequences, size_t seqsSize);
/*! ZSTD_compressSequences() :
- * Compress an array of ZSTD_Sequence, generated from the original source buffer, into dst.
+ * Compress an array of ZSTD_Sequence, associted with @src buffer, into dst.
+ * @src contains the entire input (not just the literals).
+ * If @srcSize > sum(sequence.length), the remaining bytes are considered all literals
* If a dictionary is included, then the cctx should reference the dict. (see: ZSTD_CCtx_refCDict(), ZSTD_CCtx_loadDictionary(), etc.)
* The entire source is compressed into a single frame.
*
From 51da2d2ff245be6bcd719ee1f3d4c23225d4e653 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Thu, 20 Jan 2022 21:24:33 -0800
Subject: [PATCH 046/472] improved compression of literals in specific corner
cases
In rare cases, the default huffman depth selector is a bit too harsh,
requiring brutal adaptations to the tree,
resulting is some loss of compression ratio.
This new heuristic avoids the worse cases, favoring compression ratio.
As an example, compression of a specific distribution of 771 literals
is now improved to 441 bytes, from 601 bytes before.
---
lib/compress/huf_compress.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/lib/compress/huf_compress.c b/lib/compress/huf_compress.c
index e9652058db5..e61b39f60ab 100644
--- a/lib/compress/huf_compress.c
+++ b/lib/compress/huf_compress.c
@@ -705,6 +705,18 @@ static void HUF_buildCTableFromTree(HUF_CElt* CTable, nodeElt const* huffNode, i
CTable[0] = maxNbBits;
}
+static size_t
+HUF_nbSymbolsTooLarge(const nodeElt* hnodes, U32 maxSymbolValue, U32 maxNbBits)
+{
+ size_t nbSTL = 0;
+ int s = (int)maxSymbolValue;
+ for ( ; s > 0; s-- ) {
+ if (hnodes[s].nbBits > maxNbBits) nbSTL++;
+ else break;
+ }
+ return nbSTL;
+}
+
size_t
HUF_buildCTable_wksp(HUF_CElt* CTable, const unsigned* count, U32 maxSymbolValue, U32 maxNbBits,
void* workSpace, size_t wkspSize)
@@ -733,6 +745,16 @@ HUF_buildCTable_wksp(HUF_CElt* CTable, const unsigned* count, U32 maxSymbolValue
nonNullRank = HUF_buildTree(huffNode, maxSymbolValue);
/* determine and enforce maxTableLog */
+ /* Loosen target when maxNbBits is within limits.
+ * A harsh rebalancing can be bad for compression ratio
+ * while a mild one tends to be better */
+ while (maxNbBits < HUF_TABLELOG_MAX) {
+ size_t const nbNodes = HUF_nbSymbolsTooLarge(huffNode, maxSymbolValue, maxNbBits);
+ #define HUF_NB_NODES_TO_FIX_MAX 8
+ if (nbNodes < HUF_NB_NODES_TO_FIX_MAX) /* heuristic */
+ break;
+ maxNbBits++;
+ }
maxNbBits = HUF_setMaxHeight(huffNode, (U32)nonNullRank, maxNbBits);
if (maxNbBits > HUF_TABLELOG_MAX) return ERROR(GENERIC); /* check fit into table */
From 4684836f4fbaea1d5416306173306bac56361d83 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Thu, 20 Jan 2022 21:59:46 -0800
Subject: [PATCH 047/472] update regression tests
minor compression ratio benefits in some cases,
no compression ratio regression in the measured scenarios.
---
lib/compress/huf_compress.c | 18 +++++++++---------
lib/compress/zstd_compress_literals.c | 2 +-
tests/regression/results.csv | 22 +++++++++++-----------
3 files changed, 21 insertions(+), 21 deletions(-)
diff --git a/lib/compress/huf_compress.c b/lib/compress/huf_compress.c
index e61b39f60ab..fee2c0b528a 100644
--- a/lib/compress/huf_compress.c
+++ b/lib/compress/huf_compress.c
@@ -598,21 +598,21 @@ static void HUF_sort(nodeElt huffNode[], const unsigned count[], U32 const maxSy
}
-size_t showHNodeSymbols(const nodeElt* hnode, size_t size)
+static size_t showHNodeSymbols(const nodeElt* hnode, size_t size)
{
size_t u;
for (u=0; u
Date: Thu, 20 Jan 2022 22:19:35 -0800
Subject: [PATCH 048/472] proper max limit to 11
---
lib/compress/huf_compress.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lib/compress/huf_compress.c b/lib/compress/huf_compress.c
index fee2c0b528a..d3b597d9852 100644
--- a/lib/compress/huf_compress.c
+++ b/lib/compress/huf_compress.c
@@ -745,10 +745,10 @@ HUF_buildCTable_wksp(HUF_CElt* CTable, const unsigned* count, U32 maxSymbolValue
nonNullRank = HUF_buildTree(huffNode, maxSymbolValue);
/* determine and enforce maxTableLog */
- /* Loosen target when maxNbBits is within limits.
+ /* Loosen target when maxNbBits is already within limits.
* A harsh rebalancing can be bad for compression ratio
* while a mild one tends to be better */
- while (maxNbBits < HUF_TABLELOG_MAX) {
+ while (maxNbBits < HUF_TABLELOG_DEFAULT) {
size_t const nbSTL = HUF_nbSymbolsTooLarge(huffNode, maxSymbolValue, maxNbBits);
#define HUF_NB_NODES_TO_FIX_MAX 32
if (nbSTL < HUF_NB_NODES_TO_FIX_MAX) /* heuristic */
From e9dd923fa4b87db64ff6c3681194967d5eaaa264 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Fri, 21 Jan 2022 16:14:25 -0800
Subject: [PATCH 049/472] only declare debug functions in debug mode
---
doc/zstd_manual.html | 4 +-
lib/compress/huf_compress.c | 122 ++++++++++++++------------
lib/compress/zstd_compress_internal.h | 8 +-
lib/compress/zstd_compress_literals.c | 38 +++++---
lib/compress/zstd_cwksp.h | 2 +-
5 files changed, 99 insertions(+), 75 deletions(-)
diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html
index 9f73c4c815e..6b45e0a99a8 100644
--- a/doc/zstd_manual.html
+++ b/doc/zstd_manual.html
@@ -1164,7 +1164,9 @@ Streaming decompression functions
ZSTDLIB_STATIC_API size_t ZSTD_compressSequences(ZSTD_CCtx* const cctx, void* dst, size_t dstSize,
const ZSTD_Sequence* inSeqs, size_t inSeqsSize,
const void* src, size_t srcSize);
- Compress an array of ZSTD_Sequence, generated from the original source buffer, into dst.
+
Compress an array of ZSTD_Sequence, associted with @src buffer, into dst.
+ @src contains the entire input (not just the literals).
+ If @srcSize > sum(sequence.length), the remaining bytes are considered all literals
If a dictionary is included, then the cctx should reference the dict. (see: ZSTD_CCtx_refCDict(), ZSTD_CCtx_loadDictionary(), etc.)
The entire source is compressed into a single frame.
diff --git a/lib/compress/huf_compress.c b/lib/compress/huf_compress.c
index d3b597d9852..b533573d158 100644
--- a/lib/compress/huf_compress.c
+++ b/lib/compress/huf_compress.c
@@ -42,13 +42,67 @@
/* **************************************************************
-* Utils
+* Required declarations
****************************************************************/
-unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue)
+typedef struct nodeElt_s {
+ U32 count;
+ U16 parent;
+ BYTE byte;
+ BYTE nbBits;
+} nodeElt;
+
+
+/* **************************************************************
+* Debug Traces
+****************************************************************/
+
+#if DEBUGLEVEL >= 2
+
+static size_t showU32(const U32* arr, size_t size)
{
- return FSE_optimalTableLog_internal(maxTableLog, srcSize, maxSymbolValue, 1);
+ size_t u;
+ for (u=0; u 1) {
assert(bucketStartIdx < maxSymbolValue1);
@@ -598,27 +645,6 @@ static void HUF_sort(nodeElt huffNode[], const unsigned count[], U32 const maxSy
}
-static size_t showHNodeSymbols(const nodeElt* hnode, size_t size)
-{
- size_t u;
- for (u=0; urankPosition);
- DEBUGLOG(6, "sorted symbols completed (%zu symbols)", showHNodeSymbols(huffNode, maxSymbolValue+1)); (void)showHNodeSymbols;
+ DEBUGLOG(6, "sorted symbols completed (%zu symbols)", showHNodeSymbols(huffNode, maxSymbolValue+1));
/* build tree */
nonNullRank = HUF_buildTree(huffNode, maxSymbolValue);
@@ -943,7 +969,7 @@ static size_t HUF_closeCStream(HUF_CStream_t* bitC)
{
size_t const nbBits = bitC->bitPos[0] & 0xFF;
if (bitC->ptr >= bitC->endPtr) return 0; /* overflow detected */
- return (bitC->ptr - bitC->startPtr) + (nbBits > 0);
+ return (size_t)(bitC->ptr - bitC->startPtr) + (nbBits > 0);
}
}
@@ -1214,28 +1240,12 @@ static size_t HUF_compressCTable_internal(
return (size_t)(op-ostart);
}
-static size_t showU32(const U32* arr, size_t size)
-{
- size_t u;
- for (u=0; u> 7)+4) return 0; /* heuristic : probably not compressible enough */
}
- DEBUGLOG(6, "histogram detail completed (%zu symbols)", showU32(table->count, maxSymbolValue+1)); (void)showU32;
+ DEBUGLOG(6, "histogram detail completed (%zu symbols)", showU32(table->count, maxSymbolValue+1));
/* Check validity of previous table */
if ( repeat
@@ -1329,7 +1339,7 @@ HUF_compress_internal (void* dst, size_t dstSize,
&table->wksps.buildCTable_wksp, sizeof(table->wksps.buildCTable_wksp));
CHECK_F(maxBits);
huffLog = (U32)maxBits;
- DEBUGLOG(6, "bit distribution completed (%zu symbols)", showCTableBits(table->CTable + 1, maxSymbolValue+1)); (void)showCTableBits;
+ DEBUGLOG(6, "bit distribution completed (%zu symbols)", showCTableBits(table->CTable + 1, maxSymbolValue+1));
}
/* Zero unused symbols in CTable, so we can check it for validity */
{
diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h
index 9fe3affab6b..e615b10c2fb 100644
--- a/lib/compress/zstd_compress_internal.h
+++ b/lib/compress/zstd_compress_internal.h
@@ -713,7 +713,7 @@ static unsigned ZSTD_NbCommonBytes (size_t val)
}
# endif
# elif defined(__GNUC__) && (__GNUC__ >= 4)
- return (__builtin_ctzll((U64)val) >> 3);
+ return (unsigned)(__builtin_ctzll((U64)val) >> 3);
# else
static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2,
0, 3, 1, 3, 1, 4, 2, 7,
@@ -736,7 +736,7 @@ static unsigned ZSTD_NbCommonBytes (size_t val)
__assume(0);
}
# elif defined(__GNUC__) && (__GNUC__ >= 3)
- return (__builtin_ctz((U32)val) >> 3);
+ return (unsigned)(__builtin_ctz((U32)val) >> 3);
# else
static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0,
3, 2, 2, 1, 3, 2, 0, 1,
@@ -761,7 +761,7 @@ static unsigned ZSTD_NbCommonBytes (size_t val)
}
# endif
# elif defined(__GNUC__) && (__GNUC__ >= 4)
- return (__builtin_clzll(val) >> 3);
+ return (unsigned)(__builtin_clzll(val) >> 3);
# else
unsigned r;
const unsigned n32 = sizeof(size_t)*4; /* calculate this way due to compiler complaining in 32-bits mode */
@@ -781,7 +781,7 @@ static unsigned ZSTD_NbCommonBytes (size_t val)
__assume(0);
}
# elif defined(__GNUC__) && (__GNUC__ >= 3)
- return (__builtin_clz((U32)val) >> 3);
+ return (unsigned)(__builtin_clz((U32)val) >> 3);
# else
unsigned r;
if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; }
diff --git a/lib/compress/zstd_compress_literals.c b/lib/compress/zstd_compress_literals.c
index a114b7d150d..d6d63278ac1 100644
--- a/lib/compress/zstd_compress_literals.c
+++ b/lib/compress/zstd_compress_literals.c
@@ -13,6 +13,29 @@
***************************************/
#include "zstd_compress_literals.h"
+
+/* **************************************************************
+* Debug Traces
+****************************************************************/
+#if DEBUGLEVEL >= 2
+
+static size_t showHexa(const void* src, size_t srcSize)
+{
+ const BYTE* const ip = (const BYTE*)src;
+ size_t u;
+ for (u=0; utableValidEnd >= ws->objectEnd);
assert(ws->tableValidEnd <= ws->allocStart);
if (ws->tableValidEnd < ws->tableEnd) {
- ZSTD_memset(ws->tableValidEnd, 0, (BYTE*)ws->tableEnd - (BYTE*)ws->tableValidEnd);
+ ZSTD_memset(ws->tableValidEnd, 0, (size_t)((BYTE*)ws->tableEnd - (BYTE*)ws->tableValidEnd));
}
ZSTD_cwksp_mark_tables_clean(ws);
}
From 32a5d95dcb873044cb6af4921772510e619ac9e7 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Fri, 21 Jan 2022 16:40:30 -0800
Subject: [PATCH 050/472] moved HufLog to lib/decompress
it's only used to size decompression tables
---
lib/common/zstd_internal.h | 1 -
lib/compress/zstd_compress.c | 12 ++++++------
lib/decompress/zstd_decompress_internal.h | 1 +
3 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h
index e4d36ce0905..0594b7fce03 100644
--- a/lib/common/zstd_internal.h
+++ b/lib/common/zstd_internal.h
@@ -95,7 +95,6 @@ typedef enum { bt_raw, bt_rle, bt_compressed, bt_reserved } blockType_e;
#define MIN_SEQUENCES_SIZE 1 /* nbSeq==0 */
#define MIN_CBLOCK_SIZE (1 /*litCSize*/ + 1 /* RLE or RAW */ + MIN_SEQUENCES_SIZE /* nbSeq==0 */) /* for a non-null block */
-#define HufLog 12
typedef enum { set_basic, set_rle, set_compressed, set_repeat } symbolEncodingType_e;
#define LONGNBSEQ 0x7F00
diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
index c534eb25eea..e80d453e9c8 100644
--- a/lib/compress/zstd_compress.c
+++ b/lib/compress/zstd_compress.c
@@ -746,12 +746,12 @@ size_t ZSTD_CCtxParams_setParameter(ZSTD_CCtx_params* CCtxParams,
case ZSTD_c_minMatch :
if (value!=0) /* 0 => use default */
BOUNDCHECK(ZSTD_c_minMatch, value);
- CCtxParams->cParams.minMatch = value;
+ CCtxParams->cParams.minMatch = (U32)value;
return CCtxParams->cParams.minMatch;
case ZSTD_c_targetLength :
BOUNDCHECK(ZSTD_c_targetLength, value);
- CCtxParams->cParams.targetLength = value;
+ CCtxParams->cParams.targetLength = (U32)value;
return CCtxParams->cParams.targetLength;
case ZSTD_c_strategy :
@@ -764,12 +764,12 @@ size_t ZSTD_CCtxParams_setParameter(ZSTD_CCtx_params* CCtxParams,
/* Content size written in frame header _when known_ (default:1) */
DEBUGLOG(4, "set content size flag = %u", (value!=0));
CCtxParams->fParams.contentSizeFlag = value != 0;
- return CCtxParams->fParams.contentSizeFlag;
+ return (size_t)CCtxParams->fParams.contentSizeFlag;
case ZSTD_c_checksumFlag :
/* A 32-bits content checksum will be calculated and written at end of frame (default:0) */
CCtxParams->fParams.checksumFlag = value != 0;
- return CCtxParams->fParams.checksumFlag;
+ return (size_t)CCtxParams->fParams.checksumFlag;
case ZSTD_c_dictIDFlag : /* When applicable, dictionary's dictID is provided in frame header (default:1) */
DEBUGLOG(4, "set dictIDFlag = %u", (value!=0));
@@ -778,7 +778,7 @@ size_t ZSTD_CCtxParams_setParameter(ZSTD_CCtx_params* CCtxParams,
case ZSTD_c_forceMaxWindow :
CCtxParams->forceWindow = (value != 0);
- return CCtxParams->forceWindow;
+ return (size_t)CCtxParams->forceWindow;
case ZSTD_c_forceAttachDict : {
const ZSTD_dictAttachPref_e pref = (ZSTD_dictAttachPref_e)value;
@@ -3063,7 +3063,7 @@ static size_t ZSTD_buildBlockEntropyStats_literals(void* const src, size_t srcSi
unsigned* const countWksp = (unsigned*)workspace;
const size_t countWkspSize = (HUF_SYMBOLVALUE_MAX + 1) * sizeof(unsigned);
BYTE* const nodeWksp = countWkspStart + countWkspSize;
- const size_t nodeWkspSize = wkspEnd-nodeWksp;
+ const size_t nodeWkspSize = (size_t)(wkspEnd - nodeWksp);
unsigned maxSymbolValue = HUF_SYMBOLVALUE_MAX;
unsigned huffLog = HUF_TABLELOG_DEFAULT;
HUF_repeat repeat = prevHuf->repeatMode;
diff --git a/lib/decompress/zstd_decompress_internal.h b/lib/decompress/zstd_decompress_internal.h
index 2b5a53850ac..17c7df9d0d0 100644
--- a/lib/decompress/zstd_decompress_internal.h
+++ b/lib/decompress/zstd_decompress_internal.h
@@ -75,6 +75,7 @@ static UNUSED_ATTR const U32 ML_base[MaxML+1] = {
#define ZSTD_BUILD_FSE_TABLE_WKSP_SIZE (sizeof(S16) * (MaxSeq + 1) + (1u << MaxFSELog) + sizeof(U64))
#define ZSTD_BUILD_FSE_TABLE_WKSP_SIZE_U32 ((ZSTD_BUILD_FSE_TABLE_WKSP_SIZE + sizeof(U32) - 1) / sizeof(U32))
+#define HufLog 12
typedef struct {
ZSTD_seqSymbol LLTable[SEQSYMBOL_TABLE_SIZE(LLFSELog)]; /* Note : Space reserved for FSE Tables */
From 2d154e627a9961b728ee5943076c639b3942bbaa Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Fri, 21 Jan 2022 17:03:48 -0800
Subject: [PATCH 051/472] renamed HufLog into ZSTD_HUFFDTABLE_CAPACITY_LOG
old name was not descriptive and actually misleading
---
lib/decompress/zstd_ddict.c | 2 +-
lib/decompress/zstd_decompress.c | 2 +-
lib/decompress/zstd_decompress_internal.h | 4 ++--
lib/legacy/zstd_v05.c | 6 +++---
lib/legacy/zstd_v06.c | 6 +++---
lib/legacy/zstd_v07.c | 6 +++---
6 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/lib/decompress/zstd_ddict.c b/lib/decompress/zstd_ddict.c
index ce335477b32..889764a5e87 100644
--- a/lib/decompress/zstd_ddict.c
+++ b/lib/decompress/zstd_ddict.c
@@ -134,7 +134,7 @@ static size_t ZSTD_initDDict_internal(ZSTD_DDict* ddict,
ZSTD_memcpy(internalBuffer, dict, dictSize);
}
ddict->dictSize = dictSize;
- ddict->entropy.hufTable[0] = (HUF_DTable)((HufLog)*0x1000001); /* cover both little and big endian */
+ ddict->entropy.hufTable[0] = (HUF_DTable)((ZSTD_HUFFDTABLE_CAPACITY_LOG)*0x1000001); /* cover both little and big endian */
/* parse dictionary content */
FORWARD_IF_ERROR( ZSTD_loadEntropy_intoDDict(ddict, dictContentType) , "");
diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c
index 0031e98cfb1..44649dd3718 100644
--- a/lib/decompress/zstd_decompress.c
+++ b/lib/decompress/zstd_decompress.c
@@ -1453,7 +1453,7 @@ size_t ZSTD_decompressBegin(ZSTD_DCtx* dctx)
dctx->prefixStart = NULL;
dctx->virtualStart = NULL;
dctx->dictEnd = NULL;
- dctx->entropy.hufTable[0] = (HUF_DTable)((HufLog)*0x1000001); /* cover both little and big endian */
+ dctx->entropy.hufTable[0] = (HUF_DTable)((ZSTD_HUFFDTABLE_CAPACITY_LOG)*0x1000001); /* cover both little and big endian */
dctx->litEntropy = dctx->fseEntropy = 0;
dctx->dictID = 0;
dctx->bType = bt_reserved;
diff --git a/lib/decompress/zstd_decompress_internal.h b/lib/decompress/zstd_decompress_internal.h
index 17c7df9d0d0..91e9dceb5d3 100644
--- a/lib/decompress/zstd_decompress_internal.h
+++ b/lib/decompress/zstd_decompress_internal.h
@@ -75,13 +75,13 @@ static UNUSED_ATTR const U32 ML_base[MaxML+1] = {
#define ZSTD_BUILD_FSE_TABLE_WKSP_SIZE (sizeof(S16) * (MaxSeq + 1) + (1u << MaxFSELog) + sizeof(U64))
#define ZSTD_BUILD_FSE_TABLE_WKSP_SIZE_U32 ((ZSTD_BUILD_FSE_TABLE_WKSP_SIZE + sizeof(U32) - 1) / sizeof(U32))
-#define HufLog 12
+#define ZSTD_HUFFDTABLE_CAPACITY_LOG 12
typedef struct {
ZSTD_seqSymbol LLTable[SEQSYMBOL_TABLE_SIZE(LLFSELog)]; /* Note : Space reserved for FSE Tables */
ZSTD_seqSymbol OFTable[SEQSYMBOL_TABLE_SIZE(OffFSELog)]; /* is also used as temporary workspace while building hufTable during DDict creation */
ZSTD_seqSymbol MLTable[SEQSYMBOL_TABLE_SIZE(MLFSELog)]; /* and therefore must be at least HUF_DECOMPRESS_WORKSPACE_SIZE large */
- HUF_DTable hufTable[HUF_DTABLE_SIZE(HufLog)]; /* can accommodate HUF_decompress4X */
+ HUF_DTable hufTable[HUF_DTABLE_SIZE(ZSTD_HUFFDTABLE_CAPACITY_LOG)]; /* can accommodate HUF_decompress4X */
U32 rep[ZSTD_REP_NUM];
U32 workspace[ZSTD_BUILD_FSE_TABLE_WKSP_SIZE_U32];
} ZSTD_entropyDTables_t;
diff --git a/lib/legacy/zstd_v05.c b/lib/legacy/zstd_v05.c
index 795dfb410c0..fbed0a01531 100644
--- a/lib/legacy/zstd_v05.c
+++ b/lib/legacy/zstd_v05.c
@@ -485,7 +485,7 @@ static const size_t ZSTDv05_frameHeaderSize_min = 5;
#define FSEv05_ENCODING_DYNAMIC 3
-#define HufLog 12
+#define ZSTD_HUFFDTABLE_CAPACITY_LOG 12
#define MIN_SEQUENCES_SIZE 1 /* nbSeq==0 */
#define MIN_CBLOCK_SIZE (1 /*litCSize*/ + 1 /* RLE or RAW */ + MIN_SEQUENCES_SIZE /* nbSeq==0 */) /* for a non-null block */
@@ -2645,7 +2645,7 @@ struct ZSTDv05_DCtx_s
FSEv05_DTable LLTable[FSEv05_DTABLE_SIZE_U32(LLFSEv05Log)];
FSEv05_DTable OffTable[FSEv05_DTABLE_SIZE_U32(OffFSEv05Log)];
FSEv05_DTable MLTable[FSEv05_DTABLE_SIZE_U32(MLFSEv05Log)];
- unsigned hufTableX4[HUFv05_DTABLE_SIZE(HufLog)];
+ unsigned hufTableX4[HUFv05_DTABLE_SIZE(ZSTD_HUFFDTABLE_CAPACITY_LOG)];
const void* previousDstEnd;
const void* base;
const void* vBase;
@@ -2673,7 +2673,7 @@ size_t ZSTDv05_decompressBegin(ZSTDv05_DCtx* dctx)
dctx->base = NULL;
dctx->vBase = NULL;
dctx->dictEnd = NULL;
- dctx->hufTableX4[0] = HufLog;
+ dctx->hufTableX4[0] = ZSTD_HUFFDTABLE_CAPACITY_LOG;
dctx->flagStaticTables = 0;
return 0;
}
diff --git a/lib/legacy/zstd_v06.c b/lib/legacy/zstd_v06.c
index ead213c4849..c486ed0ac93 100644
--- a/lib/legacy/zstd_v06.c
+++ b/lib/legacy/zstd_v06.c
@@ -479,7 +479,7 @@ typedef enum { bt_compressed, bt_raw, bt_rle, bt_end } blockType_t;
#define MIN_SEQUENCES_SIZE 1 /* nbSeq==0 */
#define MIN_CBLOCK_SIZE (1 /*litCSize*/ + 1 /* RLE or RAW */ + MIN_SEQUENCES_SIZE /* nbSeq==0 */) /* for a non-null block */
-#define HufLog 12
+#define ZSTD_HUFFDTABLE_CAPACITY_LOG 12
#define IS_HUF 0
#define IS_PCH 1
@@ -2806,7 +2806,7 @@ struct ZSTDv06_DCtx_s
FSEv06_DTable LLTable[FSEv06_DTABLE_SIZE_U32(LLFSELog)];
FSEv06_DTable OffTable[FSEv06_DTABLE_SIZE_U32(OffFSELog)];
FSEv06_DTable MLTable[FSEv06_DTABLE_SIZE_U32(MLFSELog)];
- unsigned hufTableX4[HUFv06_DTABLE_SIZE(HufLog)];
+ unsigned hufTableX4[HUFv06_DTABLE_SIZE(ZSTD_HUFFDTABLE_CAPACITY_LOG)];
const void* previousDstEnd;
const void* base;
const void* vBase;
@@ -2834,7 +2834,7 @@ size_t ZSTDv06_decompressBegin(ZSTDv06_DCtx* dctx)
dctx->base = NULL;
dctx->vBase = NULL;
dctx->dictEnd = NULL;
- dctx->hufTableX4[0] = HufLog;
+ dctx->hufTableX4[0] = ZSTD_HUFFDTABLE_CAPACITY_LOG;
dctx->flagRepeatTable = 0;
return 0;
}
diff --git a/lib/legacy/zstd_v07.c b/lib/legacy/zstd_v07.c
index 3a0418e526f..20d97da5d58 100644
--- a/lib/legacy/zstd_v07.c
+++ b/lib/legacy/zstd_v07.c
@@ -2717,7 +2717,7 @@ typedef enum { bt_compressed, bt_raw, bt_rle, bt_end } blockType_t;
#define MIN_SEQUENCES_SIZE 1 /* nbSeq==0 */
#define MIN_CBLOCK_SIZE (1 /*litCSize*/ + 1 /* RLE or RAW */ + MIN_SEQUENCES_SIZE /* nbSeq==0 */) /* for a non-null block */
-#define HufLog 12
+#define ZSTD_HUFFDTABLE_CAPACITY_LOG 12
typedef enum { lbt_huffman, lbt_repeat, lbt_raw, lbt_rle } litBlockType_t;
#define LONGNBSEQ 0x7F00
@@ -2931,7 +2931,7 @@ struct ZSTDv07_DCtx_s
FSEv07_DTable LLTable[FSEv07_DTABLE_SIZE_U32(LLFSELog)];
FSEv07_DTable OffTable[FSEv07_DTABLE_SIZE_U32(OffFSELog)];
FSEv07_DTable MLTable[FSEv07_DTABLE_SIZE_U32(MLFSELog)];
- HUFv07_DTable hufTable[HUFv07_DTABLE_SIZE(HufLog)]; /* can accommodate HUFv07_decompress4X */
+ HUFv07_DTable hufTable[HUFv07_DTABLE_SIZE(ZSTD_HUFFDTABLE_CAPACITY_LOG)]; /* can accommodate HUFv07_decompress4X */
const void* previousDstEnd;
const void* base;
const void* vBase;
@@ -2967,7 +2967,7 @@ size_t ZSTDv07_decompressBegin(ZSTDv07_DCtx* dctx)
dctx->base = NULL;
dctx->vBase = NULL;
dctx->dictEnd = NULL;
- dctx->hufTable[0] = (HUFv07_DTable)((HufLog)*0x1000001);
+ dctx->hufTable[0] = (HUFv07_DTable)((ZSTD_HUFFDTABLE_CAPACITY_LOG)*0x1000001);
dctx->litEntropy = dctx->fseEntropy = 0;
dctx->dictID = 0;
{ int i; for (i=0; irep[i] = repStartValue[i]; }
From a66e8bb437203df68910f9d898dd9434bbf8f08a Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Fri, 21 Jan 2022 17:13:33 -0800
Subject: [PATCH 052/472] introduced LitHufLog constant
which properly represents the maximum bit size of compressed literals (11) as defined in the specification.
To be preferred from HUF_TABLELOG_DEFAULT which represents the same value but by accident.
Name selected to keep the same convention as existing width definitions,
MLFSELog, LLFSELog and OffFSELog.
---
lib/common/zstd_internal.h | 7 ++++---
lib/compress/zstd_compress.c | 16 ++++++++--------
lib/compress/zstd_compress_literals.c | 2 +-
3 files changed, 13 insertions(+), 12 deletions(-)
diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h
index 0594b7fce03..ff2c2cd4493 100644
--- a/lib/common/zstd_internal.h
+++ b/lib/common/zstd_internal.h
@@ -102,6 +102,7 @@ typedef enum { set_basic, set_rle, set_compressed, set_repeat } symbolEncodingTy
#define MINMATCH 3
#define Litbits 8
+#define LitHufLog 11
#define MaxLit ((1<= 3) /* GCC Intrinsic */
- return __builtin_clz (val) ^ 31;
+ return (U32)__builtin_clz (val) ^ 31;
# elif defined(__ICCARM__) /* IAR Intrinsic */
return 31 - __CLZ(val);
# else /* Software version */
@@ -416,7 +417,7 @@ MEM_STATIC unsigned ZSTD_countTrailingZeros(size_t val)
}
# endif
# elif defined(__GNUC__) && (__GNUC__ >= 4)
- return __builtin_ctzll((U64)val);
+ return (unsigned)__builtin_ctzll((U64)val);
# else
static const int DeBruijnBytePos[64] = { 0, 1, 2, 7, 3, 13, 8, 19,
4, 25, 14, 28, 9, 34, 20, 56,
@@ -439,7 +440,7 @@ MEM_STATIC unsigned ZSTD_countTrailingZeros(size_t val)
__assume(0);
}
# elif defined(__GNUC__) && (__GNUC__ >= 3)
- return __builtin_ctz((U32)val);
+ return (unsigned)__builtin_ctz((U32)val);
# else
static const int DeBruijnBytePos[32] = { 0, 1, 28, 2, 29, 14, 24, 3,
30, 22, 20, 15, 25, 17, 4, 8,
diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
index e80d453e9c8..ed10d855591 100644
--- a/lib/compress/zstd_compress.c
+++ b/lib/compress/zstd_compress.c
@@ -840,7 +840,7 @@ size_t ZSTD_CCtxParams_setParameter(ZSTD_CCtx_params* CCtxParams,
case ZSTD_c_enableDedicatedDictSearch :
CCtxParams->enableDedicatedDictSearch = (value!=0);
- return CCtxParams->enableDedicatedDictSearch;
+ return (size_t)CCtxParams->enableDedicatedDictSearch;
case ZSTD_c_enableLongDistanceMatching :
CCtxParams->ldmParams.enableLdm = (ZSTD_paramSwitch_e)value;
@@ -849,38 +849,38 @@ size_t ZSTD_CCtxParams_setParameter(ZSTD_CCtx_params* CCtxParams,
case ZSTD_c_ldmHashLog :
if (value!=0) /* 0 ==> auto */
BOUNDCHECK(ZSTD_c_ldmHashLog, value);
- CCtxParams->ldmParams.hashLog = value;
+ CCtxParams->ldmParams.hashLog = (U32)value;
return CCtxParams->ldmParams.hashLog;
case ZSTD_c_ldmMinMatch :
if (value!=0) /* 0 ==> default */
BOUNDCHECK(ZSTD_c_ldmMinMatch, value);
- CCtxParams->ldmParams.minMatchLength = value;
+ CCtxParams->ldmParams.minMatchLength = (U32)value;
return CCtxParams->ldmParams.minMatchLength;
case ZSTD_c_ldmBucketSizeLog :
if (value!=0) /* 0 ==> default */
BOUNDCHECK(ZSTD_c_ldmBucketSizeLog, value);
- CCtxParams->ldmParams.bucketSizeLog = value;
+ CCtxParams->ldmParams.bucketSizeLog = (U32)value;
return CCtxParams->ldmParams.bucketSizeLog;
case ZSTD_c_ldmHashRateLog :
if (value!=0) /* 0 ==> default */
BOUNDCHECK(ZSTD_c_ldmHashRateLog, value);
- CCtxParams->ldmParams.hashRateLog = value;
+ CCtxParams->ldmParams.hashRateLog = (U32)value;
return CCtxParams->ldmParams.hashRateLog;
case ZSTD_c_targetCBlockSize :
if (value!=0) /* 0 ==> default */
BOUNDCHECK(ZSTD_c_targetCBlockSize, value);
- CCtxParams->targetCBlockSize = value;
+ CCtxParams->targetCBlockSize = (U32)value;
return CCtxParams->targetCBlockSize;
case ZSTD_c_srcSizeHint :
if (value!=0) /* 0 ==> default */
BOUNDCHECK(ZSTD_c_srcSizeHint, value);
CCtxParams->srcSizeHint = value;
- return CCtxParams->srcSizeHint;
+ return (size_t)CCtxParams->srcSizeHint;
case ZSTD_c_stableInBuffer:
BOUNDCHECK(ZSTD_c_stableInBuffer, value);
@@ -3065,7 +3065,7 @@ static size_t ZSTD_buildBlockEntropyStats_literals(void* const src, size_t srcSi
BYTE* const nodeWksp = countWkspStart + countWkspSize;
const size_t nodeWkspSize = (size_t)(wkspEnd - nodeWksp);
unsigned maxSymbolValue = HUF_SYMBOLVALUE_MAX;
- unsigned huffLog = HUF_TABLELOG_DEFAULT;
+ unsigned huffLog = LitHufLog;
HUF_repeat repeat = prevHuf->repeatMode;
DEBUGLOG(5, "ZSTD_buildBlockEntropyStats_literals (srcSize=%zu)", srcSize);
diff --git a/lib/compress/zstd_compress_literals.c b/lib/compress/zstd_compress_literals.c
index d6d63278ac1..83f9116487f 100644
--- a/lib/compress/zstd_compress_literals.c
+++ b/lib/compress/zstd_compress_literals.c
@@ -132,7 +132,7 @@ size_t ZSTD_compressLiterals (ZSTD_hufCTables_t const* prevHuf,
huf_compress = singleStream ? HUF_compress1X_repeat : HUF_compress4X_repeat;
cLitSize = huf_compress(ostart+lhSize, dstCapacity-lhSize,
src, srcSize,
- HUF_SYMBOLVALUE_MAX, HUF_TABLELOG_DEFAULT,
+ HUF_SYMBOLVALUE_MAX, LitHufLog,
entropyWorkspace, entropyWorkspaceSize,
(HUF_CElt*)nextHuf->CTable,
&repeat, preferRepeat,
From 8b46895588c6a199d6fa674752b51dd547f0addf Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Wed, 26 Jan 2022 15:22:06 -0800
Subject: [PATCH 053/472] removed new huffman depth heuristic
results are now identical to before this PR
---
lib/compress/huf_compress.c | 27 ++++-----------------------
tests/regression/results.csv | 22 +++++++++++-----------
2 files changed, 15 insertions(+), 34 deletions(-)
diff --git a/lib/compress/huf_compress.c b/lib/compress/huf_compress.c
index b533573d158..d1b98afdb42 100644
--- a/lib/compress/huf_compress.c
+++ b/lib/compress/huf_compress.c
@@ -143,7 +143,10 @@ typedef struct {
S16 norm[HUF_TABLELOG_MAX+1];
} HUF_CompressWeightsWksp;
-static size_t HUF_compressWeights(void* dst, size_t dstSize, const void* weightTable, size_t wtSize, void* workspace, size_t workspaceSize)
+static size_t
+HUF_compressWeights(void* dst, size_t dstSize,
+ const void* weightTable, size_t wtSize,
+ void* workspace, size_t workspaceSize)
{
BYTE* const ostart = (BYTE*) dst;
BYTE* op = ostart;
@@ -731,18 +734,6 @@ static void HUF_buildCTableFromTree(HUF_CElt* CTable, nodeElt const* huffNode, i
CTable[0] = maxNbBits;
}
-static size_t
-HUF_nbSymbolsTooLarge(const nodeElt* hnodes, U32 maxSymbolValue, U32 maxNbBits)
-{
- size_t nbSTL = 0;
- int s = (int)maxSymbolValue;
- for ( ; s > 0; s-- ) {
- if (hnodes[s].nbBits > maxNbBits) nbSTL++;
- else break;
- }
- return nbSTL;
-}
-
size_t
HUF_buildCTable_wksp(HUF_CElt* CTable, const unsigned* count, U32 maxSymbolValue, U32 maxNbBits,
void* workSpace, size_t wkspSize)
@@ -771,16 +762,6 @@ HUF_buildCTable_wksp(HUF_CElt* CTable, const unsigned* count, U32 maxSymbolValue
nonNullRank = HUF_buildTree(huffNode, maxSymbolValue);
/* determine and enforce maxTableLog */
- /* Loosen target when maxNbBits is already within limits.
- * A harsh rebalancing can be bad for compression ratio
- * while a mild one tends to be better */
- while (maxNbBits < HUF_TABLELOG_DEFAULT) {
- size_t const nbSTL = HUF_nbSymbolsTooLarge(huffNode, maxSymbolValue, maxNbBits);
- #define HUF_NB_NODES_TO_FIX_MAX 32
- if (nbSTL < HUF_NB_NODES_TO_FIX_MAX) /* heuristic */
- break;
- maxNbBits++;
- }
maxNbBits = HUF_setMaxHeight(huffNode, (U32)nonNullRank, maxNbBits);
if (maxNbBits > HUF_TABLELOG_MAX) return ERROR(GENERIC); /* check fit into table */
diff --git a/tests/regression/results.csv b/tests/regression/results.csv
index f80da9f4e9c..3385c504931 100644
--- a/tests/regression/results.csv
+++ b/tests/regression/results.csv
@@ -51,7 +51,7 @@ silesia, long distance mode, compress
silesia, multithreaded, compress cctx, 4842075
silesia, multithreaded long distance mode, compress cctx, 4842075
silesia, small window log, compress cctx, 7082951
-silesia, small hash log, compress cctx, 6525895
+silesia, small hash log, compress cctx, 6526141
silesia, small chain log, compress cctx, 4912197
silesia, explicit params, compress cctx, 4794052
silesia, uncompressed literals, compress cctx, 4842075
@@ -115,7 +115,7 @@ silesia, long distance mode, zstdcli,
silesia, multithreaded, zstdcli, 4842123
silesia, multithreaded long distance mode, zstdcli, 4833785
silesia, small window log, zstdcli, 7095048
-silesia, small hash log, zstdcli, 6525943
+silesia, small hash log, zstdcli, 6526189
silesia, small chain log, zstdcli, 4912245
silesia, explicit params, zstdcli, 4795432
silesia, uncompressed literals, zstdcli, 5120614
@@ -141,7 +141,7 @@ silesia.tar, long distance mode, zstdcli,
silesia.tar, multithreaded, zstdcli, 4854164
silesia.tar, multithreaded long distance mode, zstdcli, 4845745
silesia.tar, small window log, zstdcli, 7100701
-silesia.tar, small hash log, zstdcli, 6529041
+silesia.tar, small hash log, zstdcli, 6529289
silesia.tar, small chain log, zstdcli, 4917022
silesia.tar, explicit params, zstdcli, 4820713
silesia.tar, uncompressed literals, zstdcli, 5122571
@@ -255,7 +255,7 @@ silesia, long distance mode, advanced
silesia, multithreaded, advanced one pass, 4842075
silesia, multithreaded long distance mode, advanced one pass, 4833737
silesia, small window log, advanced one pass, 7095000
-silesia, small hash log, advanced one pass, 6525895
+silesia, small hash log, advanced one pass, 6526141
silesia, small chain log, advanced one pass, 4912197
silesia, explicit params, advanced one pass, 4795432
silesia, uncompressed literals, advanced one pass, 5120566
@@ -289,7 +289,7 @@ silesia.tar, long distance mode, advanced
silesia.tar, multithreaded, advanced one pass, 4854160
silesia.tar, multithreaded long distance mode, advanced one pass, 4845741
silesia.tar, small window log, advanced one pass, 7100655
-silesia.tar, small hash log, advanced one pass, 6528983
+silesia.tar, small hash log, advanced one pass, 6529231
silesia.tar, small chain log, advanced one pass, 4917041
silesia.tar, explicit params, advanced one pass, 4806855
silesia.tar, uncompressed literals, advanced one pass, 5122473
@@ -573,7 +573,7 @@ silesia, long distance mode, advanced
silesia, multithreaded, advanced one pass small out, 4842075
silesia, multithreaded long distance mode, advanced one pass small out, 4833737
silesia, small window log, advanced one pass small out, 7095000
-silesia, small hash log, advanced one pass small out, 6525895
+silesia, small hash log, advanced one pass small out, 6526141
silesia, small chain log, advanced one pass small out, 4912197
silesia, explicit params, advanced one pass small out, 4795432
silesia, uncompressed literals, advanced one pass small out, 5120566
@@ -607,7 +607,7 @@ silesia.tar, long distance mode, advanced
silesia.tar, multithreaded, advanced one pass small out, 4854160
silesia.tar, multithreaded long distance mode, advanced one pass small out, 4845741
silesia.tar, small window log, advanced one pass small out, 7100655
-silesia.tar, small hash log, advanced one pass small out, 6528983
+silesia.tar, small hash log, advanced one pass small out, 6529231
silesia.tar, small chain log, advanced one pass small out, 4917041
silesia.tar, explicit params, advanced one pass small out, 4806855
silesia.tar, uncompressed literals, advanced one pass small out, 5122473
@@ -891,7 +891,7 @@ silesia, long distance mode, advanced
silesia, multithreaded, advanced streaming, 4842075
silesia, multithreaded long distance mode, advanced streaming, 4833737
silesia, small window log, advanced streaming, 7111103
-silesia, small hash log, advanced streaming, 6525895
+silesia, small hash log, advanced streaming, 6526141
silesia, small chain log, advanced streaming, 4912197
silesia, explicit params, advanced streaming, 4795452
silesia, uncompressed literals, advanced streaming, 5120566
@@ -925,7 +925,7 @@ silesia.tar, long distance mode, advanced
silesia.tar, multithreaded, advanced streaming, 4854160
silesia.tar, multithreaded long distance mode, advanced streaming, 4845741
silesia.tar, small window log, advanced streaming, 7117559
-silesia.tar, small hash log, advanced streaming, 6528986
+silesia.tar, small hash log, advanced streaming, 6529234
silesia.tar, small chain log, advanced streaming, 4917021
silesia.tar, explicit params, advanced streaming, 4806873
silesia.tar, uncompressed literals, advanced streaming, 5127423
@@ -1303,7 +1303,7 @@ silesia, long distance mode, old stre
silesia, multithreaded, old streaming advanced, 4842075
silesia, multithreaded long distance mode, old streaming advanced, 4842075
silesia, small window log, old streaming advanced, 7111103
-silesia, small hash log, old streaming advanced, 6525895
+silesia, small hash log, old streaming advanced, 6526141
silesia, small chain log, old streaming advanced, 4912197
silesia, explicit params, old streaming advanced, 4795452
silesia, uncompressed literals, old streaming advanced, 4842075
@@ -1329,7 +1329,7 @@ silesia.tar, long distance mode, old stre
silesia.tar, multithreaded, old streaming advanced, 4859271
silesia.tar, multithreaded long distance mode, old streaming advanced, 4859271
silesia.tar, small window log, old streaming advanced, 7117562
-silesia.tar, small hash log, old streaming advanced, 6528986
+silesia.tar, small hash log, old streaming advanced, 6529234
silesia.tar, small chain log, old streaming advanced, 4917021
silesia.tar, explicit params, old streaming advanced, 4806873
silesia.tar, uncompressed literals, old streaming advanced, 4859271
From beb48722411b53f36387c5c88ba9a9c7671c5c12 Mon Sep 17 00:00:00 2001
From: brailovich <91924341+brailovich@users.noreply.github.com>
Date: Wed, 26 Jan 2022 16:51:18 -0800
Subject: [PATCH 054/472] Update zstdcli.c
---
programs/zstdcli.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/programs/zstdcli.c b/programs/zstdcli.c
index 34d2fa6e0d9..a8fc0d0486e 100644
--- a/programs/zstdcli.c
+++ b/programs/zstdcli.c
@@ -1366,8 +1366,8 @@ int main(int argCount, const char* argv[])
was a number of empty directories. In this case
stdin and stdout should not be used */
if (nbInputFileNames > 0 ){
- DISPLAYLEVEL(2, "please provide correct input file(s) or non-empty directories -- ignored \n");
- CLEAN_RETURN(2);
+ DISPLAYLEVEL(1, "please provide correct input file(s) or non-empty directories -- ignored \n");
+ CLEAN_RETURN(0);
}
UTIL_refFilename(filenames, stdinmark);
}
From 5e7523385b98afce31747be2a0569089539ceb8e Mon Sep 17 00:00:00 2001
From: brailovich <91924341+brailovich@users.noreply.github.com>
Date: Wed, 26 Jan 2022 16:53:11 -0800
Subject: [PATCH 055/472] Update playTests.sh
---
tests/playTests.sh | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/tests/playTests.sh b/tests/playTests.sh
index f19291a6063..5a45247b3b4 100755
--- a/tests/playTests.sh
+++ b/tests/playTests.sh
@@ -390,6 +390,18 @@ println "\n===> recursive mode test "
# combination of -r with empty list of input file
zstd -c -r < tmp > tmp.zst
+# combination of -r with empty folder
+mkdir -p tmpEmptyDir
+zstd -r tmpEmptyDir 2>tmplog2
+if [grep "aborting" tmplog2]; then
+ println "Should not abort on empty directory"
+ rm -rf tmplog2
+ rm -rf tmpEmptyDir
+ die
+fi
+rm -rf tmplog2
+rm -rf tmpEmptyDir
+
println "\n===> file removal"
zstd -f --rm tmp
From e60eba58bf83979e92de487a87c0829a7bd60f92 Mon Sep 17 00:00:00 2001
From: Nick Terrell
Date: Wed, 26 Jan 2022 17:55:26 -0800
Subject: [PATCH 056/472] Print zlib/lz4/lzma library versions in verbose
version output
Knowing the version of zlib/lz4/lzma we're linking against is very
useful for debugging issues with those libraries, so print it out in the
verbosity 4 version output.
Also print this information at the top of `playTests.sh`.
---
programs/fileio.c | 34 ++++++++++++++++++++++++++++++++++
programs/fileio.h | 3 +++
programs/zstdcli.c | 5 +++++
tests/playTests.sh | 1 +
4 files changed, 43 insertions(+)
diff --git a/programs/fileio.c b/programs/fileio.c
index 2066096d233..64909b96401 100644
--- a/programs/fileio.c
+++ b/programs/fileio.c
@@ -69,6 +69,40 @@ UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER;
# include
#endif
+char const* FIO_zlibVersion(void)
+{
+#if defined(ZSTD_GZCOMPRESS) || defined(ZSTD_GZDECOMPRESS)
+ return zlibVersion();
+#else
+ return "Unsupported";
+#endif
+}
+
+char const* FIO_lz4Version(void)
+{
+#if defined(ZSTD_LZ4COMPRESS) || defined(ZSTD_LZ4DECOMPRESS)
+ /* LZ4_versionString() added in v1.7.3 */
+# if LZ4_VERSION_NUMBER >= 10703
+ return LZ4_versionString();
+# else
+# define ZSTD_LZ4_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE
+# define ZSTD_LZ4_VERSION_STRING ZSTD_EXPAND_AND_QUOTE(ZSTD_LZ4_VERSION)
+ return ZSTD_LZ4_VERSION_STRING;
+# endif
+#else
+ return "Unsupported";
+#endif
+}
+
+char const* FIO_lzmaVersion(void)
+{
+#if defined(ZSTD_LZMACOMPRESS) || defined(ZSTD_LZMADECOMPRESS)
+ return lzma_version_string();
+#else
+ return "Unsupported";
+#endif
+}
+
/*-*************************************
* Constants
diff --git a/programs/fileio.h b/programs/fileio.h
index 9d6ebb1accb..0920f722884 100644
--- a/programs/fileio.h
+++ b/programs/fileio.h
@@ -166,6 +166,9 @@ int FIO_checkFilenameCollisions(const char** filenameTable, unsigned nbFiles);
/* custom crash signal handler */
void FIO_addAbortHandler(void);
+char const* FIO_zlibVersion(void);
+char const* FIO_lz4Version(void);
+char const* FIO_lzmaVersion(void);
#if defined (__cplusplus)
diff --git a/programs/zstdcli.c b/programs/zstdcli.c
index fd563e1c24d..a187ffafd90 100644
--- a/programs/zstdcli.c
+++ b/programs/zstdcli.c
@@ -637,6 +637,11 @@ static void printVersion(void)
#endif
DISPLAYOUT("\n");
if (g_displayLevel >= 4) {
+ /* library versions */
+ DISPLAYOUT("zlib version %s\n", FIO_zlibVersion());
+ DISPLAYOUT("lz4 version %s\n", FIO_lz4Version());
+ DISPLAYOUT("lzma version %s\n", FIO_lzmaVersion());
+
/* posix support */
#ifdef _POSIX_C_SOURCE
DISPLAYOUT("_POSIX_C_SOURCE defined: %ldL\n", (long) _POSIX_C_SOURCE);
diff --git a/tests/playTests.sh b/tests/playTests.sh
index f19291a6063..edf344f5b10 100755
--- a/tests/playTests.sh
+++ b/tests/playTests.sh
@@ -183,6 +183,7 @@ else
fi
+zstd -vvV
println "\n===> simple tests "
From f2d9652ad82c5ead0665bea428215eaf027de933 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Wed, 26 Jan 2022 18:04:52 -0800
Subject: [PATCH 057/472] more usage of new error code
stabilityCondition_notRespected
as suggested by @terrelln
---
lib/compress/zstd_compress.c | 4 ++--
tests/zstreamtest.c | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
index 6a34beea97c..4a073cf10bb 100644
--- a/lib/compress/zstd_compress.c
+++ b/lib/compress/zstd_compress.c
@@ -5558,13 +5558,13 @@ static size_t ZSTD_checkBufferStability(ZSTD_CCtx const* cctx,
if (cctx->appliedParams.inBufferMode == ZSTD_bm_stable) {
ZSTD_inBuffer const expect = cctx->expectedInBuffer;
if (expect.src != input->src || expect.pos != input->pos)
- RETURN_ERROR(srcBuffer_wrong, "ZSTD_c_stableInBuffer enabled but input differs!");
+ RETURN_ERROR(stabilityCondition_notRespected, "ZSTD_c_stableInBuffer enabled but input differs!");
}
(void)endOp;
if (cctx->appliedParams.outBufferMode == ZSTD_bm_stable) {
size_t const outBufferSize = output->size - output->pos;
if (cctx->expectedOutBufferSize != outBufferSize)
- RETURN_ERROR(dstBuffer_wrong, "ZSTD_c_stableOutBuffer enabled but output size differs!");
+ RETURN_ERROR(stabilityCondition_notRespected, "ZSTD_c_stableOutBuffer enabled but output size differs!");
}
return 0;
}
diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c
index dbe2b907ae0..20a05a75c9f 100644
--- a/tests/zstreamtest.c
+++ b/tests/zstreamtest.c
@@ -868,7 +868,7 @@ static int basicUnitTests(U32 seed, double compressibility)
in.pos = 0;
{ size_t const ret = ZSTD_compressStream2(cctx, &out, &in, ZSTD_e_end);
CHECK(!ZSTD_isError(ret), "Must error");
- CHECK(!(ZSTD_getErrorCode(ret) == ZSTD_error_srcBuffer_wrong), "Must be this error");
+ CHECK(!(ZSTD_getErrorCode(ret) == ZSTD_error_stabilityCondition_notRespected), "Must be this error");
}
DISPLAYLEVEL(3, "OK \n");
@@ -963,7 +963,7 @@ static int basicUnitTests(U32 seed, double compressibility)
in.pos = out.pos = 0;
{ size_t const ret = ZSTD_compressStream2(cctx, &out, &in, ZSTD_e_continue);
CHECK(!ZSTD_isError(ret), "Must have errored");
- CHECK(!(ZSTD_getErrorCode(ret) == ZSTD_error_dstBuffer_wrong), "Must be this error");
+ CHECK(!(ZSTD_getErrorCode(ret) == ZSTD_error_stabilityCondition_notRespected), "Must be this error");
}
DISPLAYLEVEL(3, "OK \n");
From 501a353b9154e6885031fe14e2ded5760c97f854 Mon Sep 17 00:00:00 2001
From: brailovich <91924341+brailovich@users.noreply.github.com>
Date: Wed, 26 Jan 2022 18:56:52 -0800
Subject: [PATCH 058/472] Update playTests.sh
---
tests/playTests.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/playTests.sh b/tests/playTests.sh
index 5a45247b3b4..ba287403406 100755
--- a/tests/playTests.sh
+++ b/tests/playTests.sh
@@ -393,7 +393,7 @@ zstd -c -r < tmp > tmp.zst
# combination of -r with empty folder
mkdir -p tmpEmptyDir
zstd -r tmpEmptyDir 2>tmplog2
-if [grep "aborting" tmplog2]; then
+if [ grep "aborting" tmplog2 ]; then
println "Should not abort on empty directory"
rm -rf tmplog2
rm -rf tmpEmptyDir
From 8df1257c3cd70341307b87bffccbb334a10db5a1 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Thu, 27 Jan 2022 05:13:45 -0800
Subject: [PATCH 059/472] fix issue 44108
credit to oss-fuzz
In rare circumstances, the block-splitter might cut a block at the exact beginning of a repcode.
In which case, since litlength=0, if the repcode expected 1+ literals in front, its signification changes.
This scenario is controlled in ZSTD_seqStore_resolveOffCodes(),
and the repcode is transformed into a raw offset when its new meaning is incorrect.
In more complex scenarios, the previous block might be emitted as uncompressed after all,
thus modifying the expected repcode history.
In the case discovered by oss-fuzz, the first block is emitted as uncompressed,
so the repcode history remains at default values: 1,4,8.
But since the starting repcode is repcode3, and the literal length is == 0,
its meaning is : = repcode1 - 1.
Since repcode1==1, it results in an offset value of 0, which is invalid.
So that's what the `assert()` was verifying : the result of the repcode translation should be a valid offset.
But actually, it doesn't matter, because this result will then be compared to reality,
and since it's an invalid offset, it will necessarily be discarded if incorrect,
then the repcode will be replaced by a raw offset.
So the `assert()` is not useful.
Furthermore, it's incorrect, because it assumes this situation cannot happen, but it does, as described in above scenario.
---
lib/compress/zstd_compress.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
index ed10d855591..c222ed2c4a7 100644
--- a/lib/compress/zstd_compress.c
+++ b/lib/compress/zstd_compress.c
@@ -3443,8 +3443,11 @@ ZSTD_resolveRepcodeToRawOffset(const U32 rep[ZSTD_REP_NUM], const U32 offBase, c
if (adjustedRepCode == ZSTD_REP_NUM) {
/* litlength == 0 and offCode == 2 implies selection of first repcode - 1
* This is only valid if it results in a valid offset value, aka > 0.
+ * Note : it may happen that `rep[0]==1` in exceptional circumstances.
+ * In which case this function will return 0, which is an invalid offset.
+ * It's not an issue though, since this value will be
+ * compared and discarded within ZSTD_seqStore_resolveOffCodes().
*/
- assert(rep[0] > 1);
return rep[0] - 1;
}
return rep[adjustedRepCode];
From 9f37d1fede5a3be4b16c10194306b1baa8a084f4 Mon Sep 17 00:00:00 2001
From: brailovich <91924341+brailovich@users.noreply.github.com>
Date: Thu, 27 Jan 2022 08:22:05 -0800
Subject: [PATCH 060/472] Update playTests.sh
combination of -r with empty folder simplified to comply with sh compatibility tests
---
tests/playTests.sh | 9 +--------
1 file changed, 1 insertion(+), 8 deletions(-)
diff --git a/tests/playTests.sh b/tests/playTests.sh
index ba287403406..e0d802f6ee3 100755
--- a/tests/playTests.sh
+++ b/tests/playTests.sh
@@ -392,14 +392,7 @@ zstd -c -r < tmp > tmp.zst
# combination of -r with empty folder
mkdir -p tmpEmptyDir
-zstd -r tmpEmptyDir 2>tmplog2
-if [ grep "aborting" tmplog2 ]; then
- println "Should not abort on empty directory"
- rm -rf tmplog2
- rm -rf tmpEmptyDir
- die
-fi
-rm -rf tmplog2
+zstd -r tmpEmptyDir
rm -rf tmpEmptyDir
From 246982e782849d8646b2d5df6648319935669228 Mon Sep 17 00:00:00 2001
From: Nick Terrell
Date: Thu, 20 Jan 2022 22:41:47 -0800
Subject: [PATCH 061/472] [dibio] Fix assertion triggered by no inputs
Passing 0 inputs to `DiB_shuffle()` caused an assertion failure where
it should just return.
A test is added in a later commit, with the initial introduction of the
new testing framework.
Fixes #3007.
---
programs/dibio.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/programs/dibio.c b/programs/dibio.c
index d19f954486f..147d1e7bfdf 100644
--- a/programs/dibio.c
+++ b/programs/dibio.c
@@ -27,9 +27,9 @@
#include /* memset */
#include /* fprintf, fopen, ftello64 */
#include /* errno */
-#include
#include "timefn.h" /* UTIL_time_t, UTIL_clockSpanMicro, UTIL_getTime */
+#include "../lib/common/debug.h" /* assert */
#include "../lib/common/mem.h" /* read */
#include "dibio.h"
@@ -193,7 +193,8 @@ static U32 DiB_rand(U32* src)
static void DiB_shuffle(const char** fileNamesTable, unsigned nbFiles) {
U32 seed = 0xFD2FB528;
unsigned i;
- assert(nbFiles >= 1);
+ if (nbFiles == 0)
+ return;
for (i = nbFiles - 1; i > 0; --i) {
unsigned const j = DiB_rand(&seed) % (i + 1);
const char* const tmp = fileNamesTable[j];
From 495dcb839ab6ab40c4156b99be16b010389f2214 Mon Sep 17 00:00:00 2001
From: Nick Terrell
Date: Thu, 20 Jan 2022 22:45:03 -0800
Subject: [PATCH 062/472] [zstdcli] Fix option detection for --auto-threads
The option `--auto-threads` should still be accepted and parsed, even if
`ZSTD_MULTITHREAD` is not defined. It doesn't mean anything, but we
should still accept the option. Since we want scripts to be able to work
generically.
This bug was caught by tests I added to the new testing framework.
---
programs/zstdcli.c | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/programs/zstdcli.c b/programs/zstdcli.c
index fd563e1c24d..53b47f874ff 100644
--- a/programs/zstdcli.c
+++ b/programs/zstdcli.c
@@ -802,9 +802,7 @@ int main(int argCount, const char* argv[])
separateFiles = 0,
setRealTimePrio = 0,
singleThread = 0,
-#ifdef ZSTD_MULTITHREAD
defaultLogicalCores = 0,
-#endif
showDefaultCParams = 0,
ultra=0,
contentSize=1;
@@ -996,7 +994,6 @@ int main(int argCount, const char* argv[])
if (longCommandWArg(&argument, "--target-compressed-block-size=")) { targetCBlockSize = readSizeTFromChar(&argument); continue; }
if (longCommandWArg(&argument, "--size-hint=")) { srcSizeHint = readSizeTFromChar(&argument); continue; }
if (longCommandWArg(&argument, "--output-dir-flat")) { NEXT_FIELD(outDirName); continue; }
-#ifdef ZSTD_MULTITHREAD
if (longCommandWArg(&argument, "--auto-threads")) {
const char* threadDefault = NULL;
NEXT_FIELD(threadDefault);
@@ -1004,7 +1001,6 @@ int main(int argCount, const char* argv[])
defaultLogicalCores = 1;
continue;
}
-#endif
#ifdef UTIL_HAS_MIRRORFILELIST
if (longCommandWArg(&argument, "--output-dir-mirror")) { NEXT_FIELD(outMirroredDirName); continue; }
#endif
@@ -1220,7 +1216,7 @@ int main(int argCount, const char* argv[])
}
}
#else
- (void)singleThread; (void)nbWorkers;
+ (void)singleThread; (void)nbWorkers; (void)defaultLogicalCores;
#endif
g_utilDisplayLevel = g_displayLevel;
From f088c430e35d8b97d11aa38c5c78a72931ec7bad Mon Sep 17 00:00:00 2001
From: Nick Terrell
Date: Thu, 20 Jan 2022 22:46:45 -0800
Subject: [PATCH 063/472] [datagen] Remove extra newline printed
`datagen` was printing a `\n` even when it had no other output. Raise
the output level for the final `\n` to the minimum output level used.
This minor bug was caught by the new testing framework.
---
tests/datagencli.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/datagencli.c b/tests/datagencli.c
index ecc05f95f19..7300fdb766b 100644
--- a/tests/datagencli.c
+++ b/tests/datagencli.c
@@ -124,7 +124,7 @@ int main(int argc, const char** argv)
DISPLAYLEVEL(3, "Seed = %u \n", (unsigned)seed);
RDG_genStdout(size, (double)probaU32/100, litProba, seed);
- DISPLAYLEVEL(1, "\n");
+ DISPLAYLEVEL(3, "\n");
return 0;
}
From f3096ff6d1fcf87eeec876da13c06a97343ed6cf Mon Sep 17 00:00:00 2001
From: Nick Terrell
Date: Tue, 18 Jan 2022 13:31:27 -0800
Subject: [PATCH 064/472] [test] Add new CLI testing platform
Adds the new CLI testing platform that I'm proposing.
See the added `README.md` for details.
---
tests/cli-tests/.gitignore | 4 +
tests/cli-tests/README.md | 248 +++++++
tests/cli-tests/basic/help.sh | 7 +
tests/cli-tests/basic/help.sh.stdout.glob | 34 +
tests/cli-tests/basic/version.sh | 3 +
tests/cli-tests/basic/version.sh.stdout.glob | 2 +
tests/cli-tests/bin/cmp_size | 46 ++
tests/cli-tests/bin/datagen | 3 +
tests/cli-tests/bin/die | 4 +
tests/cli-tests/bin/println | 2 +
tests/cli-tests/bin/unzstd | 1 +
tests/cli-tests/bin/zstd | 7 +
tests/cli-tests/bin/zstdcat | 1 +
tests/cli-tests/bin/zstdgrep | 2 +
tests/cli-tests/common/format.sh | 19 +
tests/cli-tests/common/mtime.sh | 13 +
tests/cli-tests/common/permissions.sh | 18 +
tests/cli-tests/common/platform.sh | 37 +
tests/cli-tests/compression/adapt.sh | 6 +
tests/cli-tests/compression/basic.sh | 28 +
.../compression/compress-literals.sh | 10 +
tests/cli-tests/compression/format.sh | 16 +
tests/cli-tests/compression/levels.sh | 64 ++
.../compression/levels.sh.stderr.exact | 75 ++
.../compression/long-distance-matcher.sh | 7 +
tests/cli-tests/compression/multi-threaded.sh | 11 +
.../cli-tests/compression/row-match-finder.sh | 7 +
tests/cli-tests/compression/setup | 7 +
tests/cli-tests/compression/stream-size.sh | 7 +
tests/cli-tests/dict-builder/no-inputs | 3 +
tests/cli-tests/dict-builder/no-inputs.exit | 1 +
.../dict-builder/no-inputs.stderr.exact | 5 +
.../dictionaries/dictionary-mismatch.sh | 29 +
.../dictionary-mismatch.sh.stderr.exact | 8 +
tests/cli-tests/dictionaries/setup | 6 +
tests/cli-tests/dictionaries/setup_once | 24 +
tests/cli-tests/run.py | 687 ++++++++++++++++++
37 files changed, 1452 insertions(+)
create mode 100644 tests/cli-tests/.gitignore
create mode 100644 tests/cli-tests/README.md
create mode 100755 tests/cli-tests/basic/help.sh
create mode 100644 tests/cli-tests/basic/help.sh.stdout.glob
create mode 100755 tests/cli-tests/basic/version.sh
create mode 100644 tests/cli-tests/basic/version.sh.stdout.glob
create mode 100755 tests/cli-tests/bin/cmp_size
create mode 100755 tests/cli-tests/bin/datagen
create mode 100755 tests/cli-tests/bin/die
create mode 100755 tests/cli-tests/bin/println
create mode 120000 tests/cli-tests/bin/unzstd
create mode 100755 tests/cli-tests/bin/zstd
create mode 120000 tests/cli-tests/bin/zstdcat
create mode 100755 tests/cli-tests/bin/zstdgrep
create mode 100644 tests/cli-tests/common/format.sh
create mode 100644 tests/cli-tests/common/mtime.sh
create mode 100644 tests/cli-tests/common/permissions.sh
create mode 100644 tests/cli-tests/common/platform.sh
create mode 100755 tests/cli-tests/compression/adapt.sh
create mode 100755 tests/cli-tests/compression/basic.sh
create mode 100755 tests/cli-tests/compression/compress-literals.sh
create mode 100755 tests/cli-tests/compression/format.sh
create mode 100755 tests/cli-tests/compression/levels.sh
create mode 100644 tests/cli-tests/compression/levels.sh.stderr.exact
create mode 100755 tests/cli-tests/compression/long-distance-matcher.sh
create mode 100755 tests/cli-tests/compression/multi-threaded.sh
create mode 100755 tests/cli-tests/compression/row-match-finder.sh
create mode 100755 tests/cli-tests/compression/setup
create mode 100755 tests/cli-tests/compression/stream-size.sh
create mode 100755 tests/cli-tests/dict-builder/no-inputs
create mode 100644 tests/cli-tests/dict-builder/no-inputs.exit
create mode 100644 tests/cli-tests/dict-builder/no-inputs.stderr.exact
create mode 100755 tests/cli-tests/dictionaries/dictionary-mismatch.sh
create mode 100644 tests/cli-tests/dictionaries/dictionary-mismatch.sh.stderr.exact
create mode 100755 tests/cli-tests/dictionaries/setup
create mode 100755 tests/cli-tests/dictionaries/setup_once
create mode 100755 tests/cli-tests/run.py
diff --git a/tests/cli-tests/.gitignore b/tests/cli-tests/.gitignore
new file mode 100644
index 00000000000..4bb425b6139
--- /dev/null
+++ b/tests/cli-tests/.gitignore
@@ -0,0 +1,4 @@
+scratch/
+!bin/
+!datagen
+!zstdcat
diff --git a/tests/cli-tests/README.md b/tests/cli-tests/README.md
new file mode 100644
index 00000000000..3098f466f2c
--- /dev/null
+++ b/tests/cli-tests/README.md
@@ -0,0 +1,248 @@
+# CLI tests
+
+The CLI tests are focused on testing the zstd CLI.
+They are intended to be simple tests that the CLI and arguments work as advertised.
+They are not intended to test the library, only the code in `programs/`.
+The library will get incidental coverage, but if you find yourself trying to trigger a specific condition in the library, this is the wrong tool.
+
+## Test runner usage
+
+The test runner `run.py` will run tests against the in-tree build of `zstd` and `datagen` by default. Which means that `zstd` and `datagen` must be built.
+
+The `zstd` binary used can be passed with `--zstd /path/to/zstd`.
+Additionally, to run `zstd` through a tool like `valgrind` or `qemu`, set the `--exec-prefix 'valgrind -q'` flag.
+
+Similarly, the `--datagen`, and `--zstdgrep` flags can be set to specify
+the paths to their respective binaries. However, these tools do not use
+the `EXEC_PREFIX`.
+
+Each test executes in its own scratch directory under `scratch/test/name`. E.g. `scratch/basic/help.sh/`. Normally these directories are removed after the test executes. However, the `--preserve` flag will preserve these directories after execution, and save the tests exit code, stdout, and stderr in the scratch directory to `exit`, `stderr`, and `stdout` respectively. This can be useful for debugging/editing a test and updating the expected output.
+
+### Running all the tests
+
+By default the test runner `run.py` will run all the tests, and report the results.
+
+Examples:
+
+```
+./run.py
+./run.py --preserve
+./run.py --zstd ../../build/programs/zstd --datagen ../../build/tests/datagen
+```
+
+### Running specific tests
+
+A set of test names can be passed to the test runner `run.py` to only execute those tests.
+This can be useful for writing or debugging a test, especially with `--preserve`.
+
+The test name can either be the path to the test file, or the test name, which is the path relative to the test directory.
+
+Examples:
+
+```
+./run.py basic/help.sh
+./run.py --preserve basic/help.sh basic/version.sh
+./run.py --preserve --verbose basic/help.sh
+```
+
+## Writing a test
+
+Test cases are arbitrary executables, and can be written in any language, but are generally shell scripts.
+After the script executes, the exit code, stderr, and stdout are compared against the expectations.
+
+Each test is run in a clean directory that the test can use for intermediate files. This directory will be cleaned up at the end of the test, unless `--preserve` is passed to the test runner. Additionally, the `setup` script can prepare the directory before the test runs.
+
+### Calling zstd, utilities, and environment variables
+
+The `$PATH` for tests is prepended with the `bin/` sub-directory, which contains helper scripts for ease of testing.
+The `zstd` binary will call the zstd binary specified by `run.py` with the correct `$EXEC_PREFIX`.
+Similarly, `datagen`, `unzstd`, `zstdgrep`, `zstdcat`, etc, are provided.
+
+Helper utilities like `cmp_size`, `println`, and `die` are provided here too. See their scripts for details.
+
+Common shell script libraries are provided under `common/`, with helper variables and functions. They can be sourced with `source "$COMMON/library.sh`.
+
+Lastly, environment variables are provided for testing, which can be listed when calling `run.py` with `--verbose`.
+They are generally used by the helper scripts in `bin/` to coordinate everything.
+
+### Basic test case
+
+When executing your `$TEST` executable, by default the exit code is expected to be `0`. However, you can provide an alterate expected exit code in a `$TEST.exit` file.
+
+When executing your `$TEST` exectuable, by default the expected stderr and stdout are empty. However, you can override the default by providing one of three files:
+
+* `$TEST.{stdout,stderr}.exact`
+* `$TEST.{stdout,stderr}.glob`
+* `$TEST.{stdout,stderr}.ignore`
+
+If you provide a `.exact` file, the output is expected to exactly match, byte-for-byte.
+
+If you provide a `.glob` file, the output is expected to match the expected file, where each line is interpreted as a glob syntax. Additionally, a line containing only `...` matches all lines until the next expected line matches.
+
+If you provide a `.ignore` file, the output is ignored.
+
+#### Passing examples
+
+All these examples pass.
+
+Exit 1, and change the expectation to be 1.
+
+```
+exit-1.sh
+---
+#!/bin/sh
+exit 1
+---
+
+exit-1.sh.exit
+---
+1
+---
+```
+
+Check the stdout output exactly matches.
+
+```
+echo.sh
+---
+#!/bin/sh
+echo "hello world"
+---
+
+echo.sh.stdout.exact
+---
+hello world
+---
+```
+
+Check the stderr output using a glob.
+
+```
+random.sh
+---
+#!/bin/sh
+head -c 10 < /dev/urandom | xxd >&2
+---
+
+random.sh.stderr.glob
+---
+00000000: * * * * * *
+```
+
+Multiple lines can be matched with ...
+
+```
+random-num-lines.sh
+---
+#!/bin/sh
+echo hello
+seq 0 $RANDOM
+echo world
+---
+
+random-num-lines.sh.stdout.glob
+---
+hello
+0
+...
+world
+---
+```
+
+#### Failing examples
+
+Exit code is expected to be 0, but is 1.
+
+```
+exit-1.sh
+---
+#!/bin/sh
+exit 1
+---
+```
+
+Stdout is expected to be empty, but isn't.
+
+```
+echo.sh
+---
+#!/bin/sh
+echo hello world
+```
+
+Stderr is expected to be hello but is world.
+
+```
+hello.sh
+---
+#!/bin/sh
+echo world >&2
+---
+
+hello.sh.stderr.exact
+---
+hello
+---
+```
+
+### Setup & teardown scripts
+
+Finally, test writing can be eased with setup and teardown scripts.
+Each directory in the test directory is a test-suite consisting of all tests within that directory (but not sub-directories).
+This test suite can come with 4 scripts to help test writing:
+
+* `setup_once`
+* `teardown_once`
+* `setup`
+* `teardown`
+
+The `setup_once` and `teardown_once` are run once before and after all the tests in the suite respectively.
+They operate in the scratch directory for the test suite, which is the parent directory of each scratch directory for each test case.
+They can do work that is shared between tests to improve test efficiency.
+For example, the `dictionaries/setup_once` script builds several dictionaries, for use in the `dictionaries` tests.
+
+The `setup` and `teardown` scripts run before and after each test case respectively, in the test case's scratch directory.
+These scripts can do work that is shared between test cases to make tests more succinct.
+For example, the `dictionaries/setup` script copies the dictionaries built by the `dictionaries/setup_once` script into the test's scratch directory, to make them easier to use, and make sure they aren't accidentally modified.
+
+#### Examples
+
+```
+basic/setup
+---
+#!/bin/sh
+# Create some files for testing with
+datagen > file
+datagen > file0
+datagen > file1
+---
+
+basic/test.sh
+---
+#!/bin/sh
+zstd file file0 file1
+---
+
+dictionaries/setup_once
+---
+#!/bin/sh
+set -e
+
+mkdir files/ dicts/
+for i in $(seq 10); do
+ datagen -g1000 > files/$i
+done
+
+zstd --train -r files/ -o dicts/0
+---
+
+dictionaries/setup
+---
+#!/bin/sh
+
+# Runs in the test case's scratch directory.
+# The test suite's scratch directory that
+# `setup_once` operates in is the parent directory.
+cp -r ../files ../dicts .
+---
+```
diff --git a/tests/cli-tests/basic/help.sh b/tests/cli-tests/basic/help.sh
new file mode 100755
index 00000000000..c683b6a6a72
--- /dev/null
+++ b/tests/cli-tests/basic/help.sh
@@ -0,0 +1,7 @@
+#!/bin/sh -e
+println "+ zstd -h"
+zstd -h
+println "+ zstd -H"
+zstd -H
+println "+ zstd --help"
+zstd --help
diff --git a/tests/cli-tests/basic/help.sh.stdout.glob b/tests/cli-tests/basic/help.sh.stdout.glob
new file mode 100644
index 00000000000..5b2f8e4581d
--- /dev/null
+++ b/tests/cli-tests/basic/help.sh.stdout.glob
@@ -0,0 +1,34 @@
++ zstd -h
+*** zstd command line interface *-bits v1.*.*, by Yann Collet ***
+Usage :
+ zstd *args* *FILE(s)* *-o file*
+
+FILE : a filename
+ with no FILE, or when FILE is - , read standard input
+Arguments :
+ -# : # compression level*
+ -d : decompression
+ -D DICT: use DICT as Dictionary for compression or decompression
+ -o file: result stored into `file` (only 1 output file)
+ -f : disable input and output checks. Allows overwriting existing files,
+ input from console, output to stdout, operating on links,
+ block devices, etc.
+--rm : remove source file(s) after successful de/compression
+ -k : preserve source file(s) (default)
+ -h/-H : display help/long help and exit
+
+Advanced arguments :
+ -V : display Version number and exit
+...
++ zstd -H
+...
+Arguments :
+...
+Advanced arguments :
+...
++ zstd --help
+...
+Arguments :
+...
+Advanced arguments :
+...
diff --git a/tests/cli-tests/basic/version.sh b/tests/cli-tests/basic/version.sh
new file mode 100755
index 00000000000..d50de0f3cac
--- /dev/null
+++ b/tests/cli-tests/basic/version.sh
@@ -0,0 +1,3 @@
+#!/bin/sh -e
+zstd -V
+zstd --version
diff --git a/tests/cli-tests/basic/version.sh.stdout.glob b/tests/cli-tests/basic/version.sh.stdout.glob
new file mode 100644
index 00000000000..54968fa4191
--- /dev/null
+++ b/tests/cli-tests/basic/version.sh.stdout.glob
@@ -0,0 +1,2 @@
+*** zstd command line interface *-bits v1.*.*, by Yann Collet ***
+*** zstd command line interface *-bits v1.*.*, by Yann Collet ***
diff --git a/tests/cli-tests/bin/cmp_size b/tests/cli-tests/bin/cmp_size
new file mode 100755
index 00000000000..5afa1c590ad
--- /dev/null
+++ b/tests/cli-tests/bin/cmp_size
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+# Small utility to
+
+set -e
+
+usage()
+{
+ printf "USAGE:\n\t$0 [-eq|-ne|-lt|-le|-gt|-ge] FILE1 FILE2\n"
+}
+
+help()
+{
+ printf "Small utility to compare file sizes without printing them with set -x.\n\n"
+ usage
+}
+
+case "$1" in
+ -h) help; exit 0 ;;
+ --help) help; exit 0 ;;
+esac
+
+if ! test -f $2; then
+ printf "FILE1='%b' is not a file\n\n" "$2"
+ usage
+ exit 1
+fi
+
+if ! test -f $3; then
+ printf "FILE2='%b' is not a file\n\n" "$3"
+ usage
+ exit 1
+fi
+
+
+size1=$(wc -c < $2)
+size2=$(wc -c < $3)
+
+case "$1" in
+ -eq) [ "$size1" -eq "$size2" ] ;;
+ -ne) [ "$size1" -ne "$size2" ] ;;
+ -lt) [ "$size1" -lt "$size2" ] ;;
+ -le) [ "$size1" -le "$size2" ] ;;
+ -gt) [ "$size1" -gt "$size2" ] ;;
+ -ge) [ "$size1" -ge "$size2" ] ;;
+esac
diff --git a/tests/cli-tests/bin/datagen b/tests/cli-tests/bin/datagen
new file mode 100755
index 00000000000..8c60cbcc9e7
--- /dev/null
+++ b/tests/cli-tests/bin/datagen
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+"$DATAGEN_BIN" $@
diff --git a/tests/cli-tests/bin/die b/tests/cli-tests/bin/die
new file mode 100755
index 00000000000..8633bc975b0
--- /dev/null
+++ b/tests/cli-tests/bin/die
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+println "${*}" 1>&2
+exit 1
diff --git a/tests/cli-tests/bin/println b/tests/cli-tests/bin/println
new file mode 100755
index 00000000000..1da2460471b
--- /dev/null
+++ b/tests/cli-tests/bin/println
@@ -0,0 +1,2 @@
+#!/bin/env sh
+printf '%b\n' "${*}"
diff --git a/tests/cli-tests/bin/unzstd b/tests/cli-tests/bin/unzstd
new file mode 120000
index 00000000000..613f917fd7c
--- /dev/null
+++ b/tests/cli-tests/bin/unzstd
@@ -0,0 +1 @@
+zstd
\ No newline at end of file
diff --git a/tests/cli-tests/bin/zstd b/tests/cli-tests/bin/zstd
new file mode 100755
index 00000000000..198fc6d2d93
--- /dev/null
+++ b/tests/cli-tests/bin/zstd
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+if [ -z "$EXEC_PREFIX" ]; then
+ "$ZSTD_BIN" $@
+else
+ $EXEC_PREFIX "$ZSTD_BIN" $@
+fi
diff --git a/tests/cli-tests/bin/zstdcat b/tests/cli-tests/bin/zstdcat
new file mode 120000
index 00000000000..613f917fd7c
--- /dev/null
+++ b/tests/cli-tests/bin/zstdcat
@@ -0,0 +1 @@
+zstd
\ No newline at end of file
diff --git a/tests/cli-tests/bin/zstdgrep b/tests/cli-tests/bin/zstdgrep
new file mode 100755
index 00000000000..8821ebb5be5
--- /dev/null
+++ b/tests/cli-tests/bin/zstdgrep
@@ -0,0 +1,2 @@
+#!/bin/sh
+"$ZSTDGREP_BIN" $@
diff --git a/tests/cli-tests/common/format.sh b/tests/cli-tests/common/format.sh
new file mode 100644
index 00000000000..20ff0f05672
--- /dev/null
+++ b/tests/cli-tests/common/format.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+source "$COMMON/platform.sh"
+
+zstd_supports_format()
+{
+ zstd -h | grep > $INTOVOID -- "--format=$1"
+}
+
+format_extension()
+{
+ if [ "$1" = "zstd" ]; then
+ printf "zst"
+ elif [ "$1" = "gzip" ]; then
+ printf "gz"
+ else
+ printf "$1"
+ fi
+}
diff --git a/tests/cli-tests/common/mtime.sh b/tests/cli-tests/common/mtime.sh
new file mode 100644
index 00000000000..7ce931a96c0
--- /dev/null
+++ b/tests/cli-tests/common/mtime.sh
@@ -0,0 +1,13 @@
+source "$COMMON/platform.sh"
+
+MTIME="stat -c %Y"
+case "$UNAME" in
+ Darwin | FreeBSD | OpenBSD | NetBSD) MTIME="stat -f %m" ;;
+esac
+
+assertSameMTime() {
+ MT1=$($MTIME "$1")
+ MT2=$($MTIME "$2")
+ echo MTIME $MT1 $MT2
+ [ "$MT1" = "$MT2" ] || die "mtime on $1 doesn't match mtime on $2 ($MT1 != $MT2)"
+}
diff --git a/tests/cli-tests/common/permissions.sh b/tests/cli-tests/common/permissions.sh
new file mode 100644
index 00000000000..b1f6ea3ba70
--- /dev/null
+++ b/tests/cli-tests/common/permissions.sh
@@ -0,0 +1,18 @@
+source "$COMMON/platform.sh"
+
+GET_PERMS="stat -c %a"
+case "$UNAME" in
+ Darwin | FreeBSD | OpenBSD | NetBSD) GET_PERMS="stat -f %Lp" ;;
+esac
+
+assertFilePermissions() {
+ STAT1=$($GET_PERMS "$1")
+ STAT2=$2
+ [ "$STAT1" = "$STAT2" ] || die "permissions on $1 don't match expected ($STAT1 != $STAT2)"
+}
+
+assertSamePermissions() {
+ STAT1=$($GET_PERMS "$1")
+ STAT2=$($GET_PERMS "$2")
+ [ "$STAT1" = "$STAT2" ] || die "permissions on $1 don't match those on $2 ($STAT1 != $STAT2)"
+}
diff --git a/tests/cli-tests/common/platform.sh b/tests/cli-tests/common/platform.sh
new file mode 100644
index 00000000000..6eb45eab99e
--- /dev/null
+++ b/tests/cli-tests/common/platform.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+UNAME=$(uname)
+
+isWindows=false
+INTOVOID="/dev/null"
+case "$UNAME" in
+ GNU) DEVDEVICE="/dev/random" ;;
+ *) DEVDEVICE="/dev/zero" ;;
+esac
+case "$OS" in
+ Windows*)
+ isWindows=true
+ INTOVOID="NUL"
+ DEVDEVICE="NUL"
+ ;;
+esac
+
+case "$UNAME" in
+ Darwin) MD5SUM="md5 -r" ;;
+ FreeBSD) MD5SUM="gmd5sum" ;;
+ NetBSD) MD5SUM="md5 -n" ;;
+ OpenBSD) MD5SUM="md5" ;;
+ *) MD5SUM="md5sum" ;;
+esac
+
+DIFF="diff"
+case "$UNAME" in
+ SunOS) DIFF="gdiff" ;;
+esac
+
+if echo hello | zstd -v -T2 2>&1 > $INTOVOID | grep -q 'multi-threading is disabled'
+then
+ hasMT=""
+else
+ hasMT="true"
+fi
diff --git a/tests/cli-tests/compression/adapt.sh b/tests/cli-tests/compression/adapt.sh
new file mode 100755
index 00000000000..564e955b5ea
--- /dev/null
+++ b/tests/cli-tests/compression/adapt.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+set -e
+
+# Test --adapt
+zstd -f file --adapt -c | zstd -t
diff --git a/tests/cli-tests/compression/basic.sh b/tests/cli-tests/compression/basic.sh
new file mode 100755
index 00000000000..6f0f87932fb
--- /dev/null
+++ b/tests/cli-tests/compression/basic.sh
@@ -0,0 +1,28 @@
+#!/bin/sh -e
+
+# Uncomment the set -x line for debugging
+# set -x
+
+# Test compression flags and check that they work
+zstd file ; zstd -t file.zst
+zstd -f file ; zstd -t file.zst
+zstd -f -z file ; zstd -t file.zst
+zstd -f -k file ; zstd -t file.zst
+zstd -f -C file ; zstd -t file.zst
+zstd -f --check file ; zstd -t file.zst
+zstd -f --no-check file ; zstd -t file.zst
+zstd -f -- file ; zstd -t file.zst
+
+# Test output file compression
+zstd -o file-out.zst ; zstd -t file-out.zst
+zstd -fo file-out.zst; zstd -t file-out.zst
+
+# Test compression to stdout
+zstd -c file | zstd -t
+zstd --stdout file | zstd -t
+println bob | zstd | zstd -t
+
+# Test --rm
+cp file file-rm
+zstd --rm file-rm; zstd -t file-rm.zst
+test ! -f file-rm
diff --git a/tests/cli-tests/compression/compress-literals.sh b/tests/cli-tests/compression/compress-literals.sh
new file mode 100755
index 00000000000..573481a3f5b
--- /dev/null
+++ b/tests/cli-tests/compression/compress-literals.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+set -e
+
+# Test --[no-]compress-literals
+zstd file --no-compress-literals -1 -c | zstd -t
+zstd file --no-compress-literals -19 -c | zstd -t
+zstd file --no-compress-literals --fast=1 -c | zstd -t
+zstd file --compress-literals -1 -c | zstd -t
+zstd file --compress-literals --fast=1 -c | zstd -t
diff --git a/tests/cli-tests/compression/format.sh b/tests/cli-tests/compression/format.sh
new file mode 100755
index 00000000000..86fb400809c
--- /dev/null
+++ b/tests/cli-tests/compression/format.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+source "$COMMON/format.sh"
+
+set -e
+
+# Test --format
+zstd --format=zstd file -f
+zstd -t file.zst
+for format in "gzip" "lz4" "xz" "lzma"; do
+ if zstd_supports_format $format; then
+ zstd --format=$format file
+ zstd -t file.$(format_extension $format)
+ zstd -c --format=$format file | zstd -t --format=$format
+ fi
+done
diff --git a/tests/cli-tests/compression/levels.sh b/tests/cli-tests/compression/levels.sh
new file mode 100755
index 00000000000..6bd0aca0078
--- /dev/null
+++ b/tests/cli-tests/compression/levels.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+
+set -e
+set -x
+
+datagen > file
+
+# Compress with various levels and ensure that their sizes are ordered
+zstd --fast=10 file -o file-f10.zst
+zstd --fast=1 file -o file-f1.zst
+zstd -1 file -o file-1.zst
+zstd -19 file -o file-19.zst
+zstd -22 --ultra file -o file-22.zst
+
+zstd -t file-{f10,f1,1,19,22}.zst
+
+cmp_size -ne file-19.zst file-22.zst
+cmp_size -lt file-19.zst file-1.zst
+cmp_size -lt file-1.zst file-f1.zst
+cmp_size -lt file-f1.zst file-f10.zst
+
+# Test default levels
+zstd --fast file -f
+cmp file.zst file-f1.zst || die "--fast is not level -1"
+
+zstd -0 file -o file-0.zst
+zstd -f file
+cmp file.zst file-0.zst || die "Level 0 is not the default level"
+
+# Test level clamping
+zstd -99 file -o file-99.zst
+cmp file-19.zst file-99.zst || die "Level 99 is clamped to 19"
+zstd --fast=200000 file -c | zstd -t
+
+zstd -5000000000 -f file && die "Level too large, must fail" ||:
+zstd --fast=5000000000 -f file && die "Level too large, must fail" ||:
+
+# Test setting a level through the environment variable
+ZSTD_CLEVEL=-10 zstd file -o file-f10-env.zst
+ZSTD_CLEVEL=1 zstd file -o file-1-env.zst
+ZSTD_CLEVEL=+19 zstd file -o file-19-env.zst
+ZSTD_CLEVEL=+99 zstd file -o file-99-env.zst
+
+cmp file-f10{,-env}.zst || die "Environment variable failed to set level"
+cmp file-1{,-env}.zst || die "Environment variable failed to set level"
+cmp file-19{,-env}.zst || die "Environment variable failed to set level"
+cmp file-99{,-env}.zst || die "Environment variable failed to set level"
+
+# Test invalid environment clevel is the default level
+zstd -f file
+ZSTD_CLEVEL=- zstd -f file -o file-env.zst ; cmp file.zst file-env.zst
+ZSTD_CLEVEL=+ zstd -f file -o file-env.zst ; cmp file.zst file-env.zst
+ZSTD_CLEVEL=a zstd -f file -o file-env.zst ; cmp file.zst file-env.zst
+ZSTD_CLEVEL=-a zstd -f file -o file-env.zst ; cmp file.zst file-env.zst
+ZSTD_CLEVEL=+a zstd -f file -o file-env.zst ; cmp file.zst file-env.zst
+ZSTD_CLEVEL=3a7 zstd -f file -o file-env.zst ; cmp file.zst file-env.zst
+ZSTD_CLEVEL=5000000000 zstd -f file -o file-env.zst; cmp file.zst file-env.zst
+
+# Test environment clevel is overridden by command line
+ZSTD_CLEVEL=10 zstd -f file -1 -o file-1-env.zst
+ZSTD_CLEVEL=10 zstd -f file --fast=1 -o file-f1-env.zst
+
+cmp file-1{,-env}.zst || die "Environment variable not overridden"
+cmp file-f1{,-env}.zst || die "Environment variable not overridden"
diff --git a/tests/cli-tests/compression/levels.sh.stderr.exact b/tests/cli-tests/compression/levels.sh.stderr.exact
new file mode 100644
index 00000000000..c0b7066fcba
--- /dev/null
+++ b/tests/cli-tests/compression/levels.sh.stderr.exact
@@ -0,0 +1,75 @@
++ datagen
++ zstd --fast=10 file -o file-f10.zst
++ zstd --fast=1 file -o file-f1.zst
++ zstd -1 file -o file-1.zst
++ zstd -19 file -o file-19.zst
++ zstd -22 --ultra file -o file-22.zst
++ zstd -t file-f10.zst file-f1.zst file-1.zst file-19.zst file-22.zst
++ cmp_size -ne file-19.zst file-22.zst
++ cmp_size -lt file-19.zst file-1.zst
++ cmp_size -lt file-1.zst file-f1.zst
++ cmp_size -lt file-f1.zst file-f10.zst
++ zstd --fast file -f
++ cmp file.zst file-f1.zst
++ zstd -0 file -o file-0.zst
++ zstd -f file
++ cmp file.zst file-0.zst
++ zstd -99 file -o file-99.zst
+Warning : compression level higher than max, reduced to 19
++ cmp file-19.zst file-99.zst
++ zstd --fast=200000 file -c
++ zstd -t
++ zstd -5000000000 -f file
+error: numeric value overflows 32-bit unsigned int
++ :
++ zstd --fast=5000000000 -f file
+error: numeric value overflows 32-bit unsigned int
++ :
++ ZSTD_CLEVEL=-10
++ zstd file -o file-f10-env.zst
++ ZSTD_CLEVEL=1
++ zstd file -o file-1-env.zst
++ ZSTD_CLEVEL=+19
++ zstd file -o file-19-env.zst
++ ZSTD_CLEVEL=+99
++ zstd file -o file-99-env.zst
+Warning : compression level higher than max, reduced to 19
++ cmp file-f10.zst file-f10-env.zst
++ cmp file-1.zst file-1-env.zst
++ cmp file-19.zst file-19-env.zst
++ cmp file-99.zst file-99-env.zst
++ zstd -f file
++ ZSTD_CLEVEL=-
++ zstd -f file -o file-env.zst
+Ignore environment variable setting ZSTD_CLEVEL=-: not a valid integer value
++ cmp file.zst file-env.zst
++ ZSTD_CLEVEL=+
++ zstd -f file -o file-env.zst
+Ignore environment variable setting ZSTD_CLEVEL=+: not a valid integer value
++ cmp file.zst file-env.zst
++ ZSTD_CLEVEL=a
++ zstd -f file -o file-env.zst
+Ignore environment variable setting ZSTD_CLEVEL=a: not a valid integer value
++ cmp file.zst file-env.zst
++ ZSTD_CLEVEL=-a
++ zstd -f file -o file-env.zst
+Ignore environment variable setting ZSTD_CLEVEL=-a: not a valid integer value
++ cmp file.zst file-env.zst
++ ZSTD_CLEVEL=+a
++ zstd -f file -o file-env.zst
+Ignore environment variable setting ZSTD_CLEVEL=+a: not a valid integer value
++ cmp file.zst file-env.zst
++ ZSTD_CLEVEL=3a7
++ zstd -f file -o file-env.zst
+Ignore environment variable setting ZSTD_CLEVEL=3a7: not a valid integer value
++ cmp file.zst file-env.zst
++ ZSTD_CLEVEL=5000000000
++ zstd -f file -o file-env.zst
+Ignore environment variable setting ZSTD_CLEVEL=5000000000: numeric value too large
++ cmp file.zst file-env.zst
++ ZSTD_CLEVEL=10
++ zstd -f file -1 -o file-1-env.zst
++ ZSTD_CLEVEL=10
++ zstd -f file --fast=1 -o file-f1-env.zst
++ cmp file-1.zst file-1-env.zst
++ cmp file-f1.zst file-f1-env.zst
diff --git a/tests/cli-tests/compression/long-distance-matcher.sh b/tests/cli-tests/compression/long-distance-matcher.sh
new file mode 100755
index 00000000000..8f2c61bf75c
--- /dev/null
+++ b/tests/cli-tests/compression/long-distance-matcher.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+set -e
+
+# Test --long
+zstd -f file --long ; zstd -t file.zst
+zstd -f file --long=20; zstd -t file.zst
diff --git a/tests/cli-tests/compression/multi-threaded.sh b/tests/cli-tests/compression/multi-threaded.sh
new file mode 100755
index 00000000000..e3961330a0a
--- /dev/null
+++ b/tests/cli-tests/compression/multi-threaded.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+set -e
+
+# Test multi-threaded flags
+zstd --single-thread file -f ; zstd -t file.zst
+zstd -T2 -f file ; zstd -t file.zst
+zstd --rsyncable -f file ; zstd -t file.zst
+zstd -T0 -f file ; zstd -t file.zst
+zstd -T0 --auto-threads=logical -f file ; zstd -t file.zst
+zstd -T0 --auto-threads=physical -f file; zstd -t file.zst
diff --git a/tests/cli-tests/compression/row-match-finder.sh b/tests/cli-tests/compression/row-match-finder.sh
new file mode 100755
index 00000000000..5b36017a0ca
--- /dev/null
+++ b/tests/cli-tests/compression/row-match-finder.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+set -e
+
+# Test --[no-]row-match-finder
+zstd file -7f --row-match-finder
+zstd file -7f --no-row-match-finder
diff --git a/tests/cli-tests/compression/setup b/tests/cli-tests/compression/setup
new file mode 100755
index 00000000000..96e2309b6a1
--- /dev/null
+++ b/tests/cli-tests/compression/setup
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+set -e
+
+datagen > file
+datagen > file0
+datagen > file1
diff --git a/tests/cli-tests/compression/stream-size.sh b/tests/cli-tests/compression/stream-size.sh
new file mode 100755
index 00000000000..7344769a253
--- /dev/null
+++ b/tests/cli-tests/compression/stream-size.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+set -e
+
+# Test stream size & hint
+datagen -g7654 | zstd --stream-size=7654 | zstd -t
+datagen -g7654 | zstd --size-hint=7000 | zstd -t
diff --git a/tests/cli-tests/dict-builder/no-inputs b/tests/cli-tests/dict-builder/no-inputs
new file mode 100755
index 00000000000..d37fdce5a20
--- /dev/null
+++ b/tests/cli-tests/dict-builder/no-inputs
@@ -0,0 +1,3 @@
+#!/bin/sh
+set -x
+zstd --train
diff --git a/tests/cli-tests/dict-builder/no-inputs.exit b/tests/cli-tests/dict-builder/no-inputs.exit
new file mode 100644
index 00000000000..8351c19397f
--- /dev/null
+++ b/tests/cli-tests/dict-builder/no-inputs.exit
@@ -0,0 +1 @@
+14
diff --git a/tests/cli-tests/dict-builder/no-inputs.stderr.exact b/tests/cli-tests/dict-builder/no-inputs.stderr.exact
new file mode 100644
index 00000000000..b3b5599d5ae
--- /dev/null
+++ b/tests/cli-tests/dict-builder/no-inputs.stderr.exact
@@ -0,0 +1,5 @@
++ zstd --train
+! Warning : nb of samples too low for proper processing !
+! Please provide _one file per sample_.
+! Alternatively, split files into fixed-size blocks representative of samples, with -B#
+Error 14 : nb of samples too low
diff --git a/tests/cli-tests/dictionaries/dictionary-mismatch.sh b/tests/cli-tests/dictionaries/dictionary-mismatch.sh
new file mode 100755
index 00000000000..2b5d5a0d227
--- /dev/null
+++ b/tests/cli-tests/dictionaries/dictionary-mismatch.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+source "$COMMON/platform.sh"
+
+set -e
+
+if [ false ]; then
+ for seed in $(seq 100); do
+ datagen -g1000 -s$seed > file$seed
+ done
+
+ zstd --train -r . -o dict0 -qq
+
+ for seed in $(seq 101 200); do
+ datagen -g1000 -s$seed > file$seed
+ done
+
+ zstd --train -r . -o dict1 -qq
+
+ [ "$($MD5SUM < dict0)" != "$($MD5SUM < dict1)" ] || die "dictionaries must not match"
+
+ datagen -g1000 -s0 > file0
+fi
+
+set -x
+zstd files/0 -D dicts/0
+zstd -t files/0.zst -D dicts/0
+zstd -t files/0.zst -D dicts/1 && die "Must fail" ||:
+zstd -t files/0.zst && die "Must fail" ||:
diff --git a/tests/cli-tests/dictionaries/dictionary-mismatch.sh.stderr.exact b/tests/cli-tests/dictionaries/dictionary-mismatch.sh.stderr.exact
new file mode 100644
index 00000000000..399a3207d26
--- /dev/null
+++ b/tests/cli-tests/dictionaries/dictionary-mismatch.sh.stderr.exact
@@ -0,0 +1,8 @@
++ zstd files/0 -D dicts/0
++ zstd -t files/0.zst -D dicts/0
++ zstd -t files/0.zst -D dicts/1
+files/0.zst : Decoding error (36) : Dictionary mismatch
++ :
++ zstd -t files/0.zst
+files/0.zst : Decoding error (36) : Dictionary mismatch
++ :
diff --git a/tests/cli-tests/dictionaries/setup b/tests/cli-tests/dictionaries/setup
new file mode 100755
index 00000000000..616c73eb804
--- /dev/null
+++ b/tests/cli-tests/dictionaries/setup
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+set -e
+
+cp -r ../files .
+cp -r ../dicts .
diff --git a/tests/cli-tests/dictionaries/setup_once b/tests/cli-tests/dictionaries/setup_once
new file mode 100755
index 00000000000..6316df165fa
--- /dev/null
+++ b/tests/cli-tests/dictionaries/setup_once
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+set -e
+
+source "$COMMON/platform.sh"
+
+
+mkdir files/ dicts/
+
+for seed in $(seq 50); do
+ datagen -g1000 -s$seed > files/$seed
+done
+
+zstd --train -r files -o dicts/0 -qq
+
+for seed in $(seq 51 100); do
+ datagen -g1000 -s$seed > files/$seed
+done
+
+zstd --train -r files -o dicts/1 -qq
+
+cmp dicts/0 dicts/1 && die "dictionaries must not match!"
+
+datagen -g1000 > files/0
diff --git a/tests/cli-tests/run.py b/tests/cli-tests/run.py
new file mode 100755
index 00000000000..6791918a5bd
--- /dev/null
+++ b/tests/cli-tests/run.py
@@ -0,0 +1,687 @@
+#!/usr/bin/env python3
+# ################################################################
+# Copyright (c) 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.
+# ##########################################################################
+
+import argparse
+import contextlib
+import copy
+import fnmatch
+import os
+import shutil
+import subprocess
+import sys
+import tempfile
+import typing
+
+
+EXCLUDED_DIRS = {
+ "bin",
+ "common",
+ "scratch",
+}
+
+
+EXCLUDED_BASENAMES = {
+ "setup",
+ "setup_once",
+ "teardown",
+ "teardown_once",
+ "README.md",
+ "run.py",
+ ".gitignore",
+}
+
+EXCLUDED_SUFFIXES = [
+ ".exact",
+ ".glob",
+ ".ignore",
+ ".exit",
+]
+
+
+def exclude_dir(dirname: str) -> bool:
+ """
+ Should files under the directory :dirname: be excluded from the test runner?
+ """
+ if dirname in EXCLUDED_DIRS:
+ return True
+ return False
+
+
+def exclude_file(filename: str) -> bool:
+ """Should the file :filename: be excluded from the test runner?"""
+ if filename in EXCLUDED_BASENAMES:
+ return True
+ for suffix in EXCLUDED_SUFFIXES:
+ if filename.endswith(suffix):
+ return True
+ return False
+
+def read_file(filename: str) -> bytes:
+ """Reads the file :filename: and returns the contents as bytes."""
+ with open(filename, "rb") as f:
+ return f.read()
+
+
+def diff(a: bytes, b: bytes) -> str:
+ """Returns a diff between two different byte-strings :a: and :b:."""
+ assert a != b
+ with tempfile.NamedTemporaryFile("wb") as fa:
+ fa.write(a)
+ fa.flush()
+ with tempfile.NamedTemporaryFile("wb") as fb:
+ fb.write(b)
+ fb.flush()
+
+ diff_bytes = subprocess.run(["diff", fa.name, fb.name], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL).stdout
+ return diff_bytes.decode("utf8")
+
+
+def pop_line(data: bytes) -> typing.Tuple[typing.Optional[bytes], bytes]:
+ """
+ Pop the first line from :data: and returns the first line and the remainder
+ of the data as a tuple. If :data: is empty, returns :(None, data):. Otherwise
+ the first line always ends in a :\n:, even if it is the last line and :data:
+ doesn't end in :\n:.
+ """
+ NEWLINE = b"\n"[0]
+
+ if data == b'':
+ return (None, data)
+
+ newline_idx = data.find(b"\n")
+ if newline_idx == -1:
+ end_idx = len(data)
+ else:
+ end_idx = newline_idx + 1
+
+ line = data[:end_idx]
+ data = data[end_idx:]
+
+ assert len(line) != 0
+ if line[-1] != NEWLINE:
+ line += NEWLINE
+
+ return (line, data)
+
+
+def glob_line_matches(actual: bytes, expect: bytes) -> bool:
+ """
+ Does the `actual` line match the expected glob line `expect`?
+ """
+ return fnmatch.fnmatchcase(actual.strip(), expect.strip())
+
+
+def glob_diff(actual: bytes, expect: bytes) -> bytes:
+ """
+ Returns None if the :actual: content matches the expected glob :expect:,
+ otherwise returns the diff bytes.
+ """
+ diff = b''
+ actual_line, actual = pop_line(actual)
+ expect_line, expect = pop_line(expect)
+ while True:
+ # Handle end of file conditions - allow extra newlines
+ while expect_line is None and actual_line == b"\n":
+ actual_line, actual = pop_line(actual)
+ while actual_line is None and expect_line == b"\n":
+ expect_line, expect = pop_line(expect)
+
+ if expect_line is None and actual_line is None:
+ if diff == b'':
+ return None
+ return diff
+ elif expect_line is None:
+ diff += b"---\n"
+ while actual_line != None:
+ diff += b"> "
+ diff += actual_line
+ actual_line, actual = pop_line(actual)
+ return diff
+ elif actual_line is None:
+ diff += b"---\n"
+ while expect_line != None:
+ diff += b"< "
+ diff += expect_line
+ expect_line, expect = pop_line(expect)
+ return diff
+
+ assert expect_line is not None
+ assert actual_line is not None
+
+ if expect_line == b'...\n':
+ next_expect_line, expect = pop_line(expect)
+ if next_expect_line is None:
+ if diff == b'':
+ return None
+ return diff
+ while not glob_line_matches(actual_line, next_expect_line):
+ actual_line, actual = pop_line(actual)
+ if actual_line is None:
+ diff += b"---\n"
+ diff += b"< "
+ diff += next_expect_line
+ return diff
+ expect_line = next_expect_line
+ continue
+
+ if not glob_line_matches(actual_line, expect_line):
+ diff += b'---\n'
+ diff += b'< ' + expect_line
+ diff += b'> ' + actual_line
+
+ actual_line, actual = pop_line(actual)
+ expect_line, expect = pop_line(expect)
+
+
+class Options:
+ """Options configuring how to run a :TestCase:."""
+ def __init__(
+ self,
+ env: typing.Dict[str, str],
+ timeout: typing.Optional[int],
+ verbose: bool,
+ preserve: bool,
+ scratch_dir: str,
+ test_dir: str,
+ ) -> None:
+ self.env = env
+ self.timeout = timeout
+ self.verbose = verbose
+ self.preserve = preserve
+ self.scratch_dir = scratch_dir
+ self.test_dir = test_dir
+
+
+class TestCase:
+ """
+ Logic and state related to running a single test case.
+
+ 1. Initialize the test case.
+ 2. Launch the test case with :TestCase.launch():.
+ This will start the test execution in a subprocess, but
+ not wait for completion. So you could launch multiple test
+ cases in parallel. This will now print any test output.
+ 3. Analyze the results with :TestCase.analyze():. This will
+ join the test subprocess, check the results against the
+ expectations, and print the results to stdout.
+
+ :TestCase.run(): is also provided which combines the launch & analyze
+ steps for single-threaded use-cases.
+
+ All other methods, prefixed with _, are private helper functions.
+ """
+ def __init__(self, test_filename: str, options: Options) -> None:
+ """
+ Initialize the :TestCase: for the test located in :test_filename:
+ with the given :options:.
+ """
+ self._opts = options
+ self._test_file = test_filename
+ self._test_name = os.path.normpath(
+ os.path.relpath(test_filename, start=self._opts.test_dir)
+ )
+ self._success = {}
+ self._message = {}
+ self._test_stdin = None
+ self._scratch_dir = os.path.abspath(os.path.join(self._opts.scratch_dir, self._test_name))
+
+ @property
+ def name(self) -> str:
+ """Returns the unique name for the test."""
+ return self._test_name
+
+ def launch(self) -> None:
+ """
+ Launch the test case as a subprocess, but do not block on completion.
+ This allows users to run multiple tests in parallel. Results aren't yet
+ printed out.
+ """
+ self._launch_test()
+
+ def analyze(self) -> bool:
+ """
+ Must be called after :TestCase.launch():. Joins the test subprocess and
+ checks the results against expectations. Finally prints the results to
+ stdout and returns the success.
+ """
+ self._join_test()
+ self._check_exit()
+ self._check_stderr()
+ self._check_stdout()
+ self._analyze_results()
+ return self._succeeded
+
+ def run(self) -> bool:
+ """Shorthand for combining both :TestCase.launch(): and :TestCase.analyze():."""
+ self.launch()
+ return self.analyze()
+
+ def _log(self, *args, **kwargs) -> None:
+ """Logs test output."""
+ print(file=sys.stdout, *args, **kwargs)
+
+ def _vlog(self, *args, **kwargs) -> None:
+ """Logs verbose test output."""
+ if self._opts.verbose:
+ print(file=sys.stdout, *args, **kwargs)
+
+ def _test_environment(self) -> typing.Dict[str, str]:
+ """
+ Returns the environment to be used for the
+ test subprocess.
+ """
+ env = copy.copy(os.environ)
+ for k, v in self._opts.env.items():
+ self._vlog(f"${k}='{v}'")
+ env[k] = v
+
+ def _launch_test(self) -> None:
+ """Launch the test subprocess, but do not join it."""
+ args = [os.path.abspath(self._test_file)]
+ stdin_name = f"{self._test_file}.stdin"
+ if os.path.exists(stdin_name):
+ self._test_stdin = open(stdin_name, "rb")
+ stdin = self._test_stdin
+ else:
+ stdin = subprocess.DEVNULL
+ cwd = self._scratch_dir
+ env = self._test_environment()
+ self._test_process = subprocess.Popen(
+ args=args,
+ stdin=stdin,
+ cwd=cwd,
+ env=env,
+ stderr=subprocess.PIPE,
+ stdout=subprocess.PIPE
+ )
+
+ def _join_test(self) -> None:
+ """Join the test process and save stderr, stdout, and the exit code."""
+ (stdout, stderr) = self._test_process.communicate(timeout=self._opts.timeout)
+ self._output = {}
+ self._output["stdout"] = stdout
+ self._output["stderr"] = stderr
+ self._exit_code = self._test_process.returncode
+ self._test_process = None
+ if self._test_stdin is not None:
+ self._test_stdin.close()
+ self._test_stdin = None
+
+ def _check_output_exact(self, out_name: str, expected: bytes) -> None:
+ """
+ Check the output named :out_name: for an exact match against the :expected: content.
+ Saves the success and message.
+ """
+ check_name = f"check_{out_name}"
+ actual = self._output[out_name]
+ if actual == expected:
+ self._success[check_name] = True
+ self._message[check_name] = f"{out_name} matches!"
+ else:
+ self._success[check_name] = False
+ self._message[check_name] = f"{out_name} does not match!\n> diff expected actual\n{diff(expected, actual)}"
+
+ def _check_output_glob(self, out_name: str, expected: bytes) -> None:
+ """
+ Check the output named :out_name: for a glob match against the :expected: glob.
+ Saves the success and message.
+ """
+ check_name = f"check_{out_name}"
+ actual = self._output[out_name]
+ diff = glob_diff(actual, expected)
+ if diff is None:
+ self._success[check_name] = True
+ self._message[check_name] = f"{out_name} matches!"
+ else:
+ utf8_diff = diff.decode('utf8')
+ self._success[check_name] = False
+ self._message[check_name] = f"{out_name} does not match!\n> diff expected actual\n{utf8_diff}"
+
+ def _check_output(self, out_name: str) -> None:
+ """
+ Checks the output named :out_name: for a match against the expectation.
+ We check for a .exact, .glob, and a .ignore file. If none are found we
+ expect that the output should be empty.
+
+ If :Options.preserve: was set then we save the scratch directory and
+ save the stderr, stdout, and exit code to the scratch directory for
+ debugging.
+ """
+ if self._opts.preserve:
+ # Save the output to the scratch directory
+ actual_name = os.path.join(self._scratch_dir, f"{out_name}")
+ with open(actual_name, "wb") as f:
+ f.write(self._output[out_name])
+
+ exact_name = f"{self._test_file}.{out_name}.exact"
+ glob_name = f"{self._test_file}.{out_name}.glob"
+ ignore_name = f"{self._test_file}.{out_name}.ignore"
+
+ if os.path.exists(exact_name):
+ return self._check_output_exact(out_name, read_file(exact_name))
+ elif os.path.exists(glob_name):
+ return self._check_output_glob(out_name, read_file(glob_name))
+ elif os.path.exists(ignore_name):
+ check_name = f"check_{out_name}"
+ self._success[check_name] = True
+ self._message[check_name] = f"{out_name} ignored!"
+ else:
+ return self._check_output_exact(out_name, bytes())
+
+ def _check_stderr(self) -> None:
+ """Checks the stderr output against the expectation."""
+ self._check_output("stderr")
+
+ def _check_stdout(self) -> None:
+ """Checks the stdout output against the expectation."""
+ self._check_output("stdout")
+
+ def _check_exit(self) -> None:
+ """
+ Checks the exit code against expectations. If a .exit file
+ exists, we expect that the exit code matches the contents.
+ Otherwise we expect the exit code to be zero.
+
+ If :Options.preserve: is set we save the exit code to the
+ scratch directory under the filename "exit".
+ """
+ if self._opts.preserve:
+ exit_name = os.path.join(self._scratch_dir, "exit")
+ with open(exit_name, "w") as f:
+ f.write(str(self._exit_code) + "\n")
+ exit_name = f"{self._test_file}.exit"
+ if os.path.exists(exit_name):
+ exit_code: int = int(read_file(exit_name))
+ else:
+ exit_code: int = 0
+ if exit_code == self._exit_code:
+ self._success["check_exit"] = True
+ self._message["check_exit"] = "Exit code matches!"
+ else:
+ self._success["check_exit"] = False
+ self._message["check_exit"] = f"Exit code mismatch! Expected {exit_code} but got {self._exit_code}"
+
+ def _analyze_results(self) -> None:
+ """
+ After all tests have been checked, collect all the successes
+ and messages, and print the results to stdout.
+ """
+ STATUS = {True: "PASS", False: "FAIL"}
+ checks = sorted(self._success.keys())
+ self._succeeded = all(self._success.values())
+ self._log(f"{STATUS[self._succeeded]}: {self._test_name}")
+
+ if not self._succeeded or self._opts.verbose:
+ for check in checks:
+ if self._opts.verbose or not self._success[check]:
+ self._log(f"{STATUS[self._success[check]]}: {self._test_name}.{check}")
+ self._log(self._message[check])
+
+ self._log("----------------------------------------")
+
+
+class TestSuite:
+ """
+ Setup & teardown test suite & cases.
+ This class is intended to be used as a context manager.
+
+ TODO: Make setup/teardown failure emit messages, not throw exceptions.
+ """
+ def __init__(self, test_directory: str, options: Options) -> None:
+ self._opts = options
+ self._test_dir = os.path.abspath(test_directory)
+ rel_test_dir = os.path.relpath(test_directory, start=self._opts.test_dir)
+ assert not rel_test_dir.startswith(os.path.sep)
+ self._scratch_dir = os.path.normpath(os.path.join(self._opts.scratch_dir, rel_test_dir))
+
+ def __enter__(self) -> 'TestSuite':
+ self._setup_once()
+ return self
+
+ def __exit__(self, _exc_type, _exc_value, _traceback) -> None:
+ self._teardown_once()
+
+ @contextlib.contextmanager
+ def test_case(self, test_basename: str) -> TestCase:
+ """
+ Context manager for a test case in the test suite.
+ Pass the basename of the test relative to the :test_directory:.
+ """
+ assert os.path.dirname(test_basename) == ""
+ try:
+ self._setup(test_basename)
+ test_filename = os.path.join(self._test_dir, test_basename)
+ yield TestCase(test_filename, self._opts)
+ finally:
+ self._teardown(test_basename)
+
+ def _remove_scratch_dir(self, dir: str) -> None:
+ """Helper to remove a scratch directory with sanity checks"""
+ assert "scratch" in dir
+ assert dir.startswith(self._scratch_dir)
+ assert os.path.exists(dir)
+ shutil.rmtree(dir)
+
+ def _setup_once(self) -> None:
+ if os.path.exists(self._scratch_dir):
+ self._remove_scratch_dir(self._scratch_dir)
+ os.makedirs(self._scratch_dir)
+ setup_script = os.path.join(self._test_dir, "setup_once")
+ if os.path.exists(setup_script):
+ self._run_script(setup_script, cwd=self._scratch_dir)
+
+ def _teardown_once(self) -> None:
+ assert os.path.exists(self._scratch_dir)
+ teardown_script = os.path.join(self._test_dir, "teardown_once")
+ if os.path.exists(teardown_script):
+ self._run_script(teardown_script, cwd=self._scratch_dir)
+ if not self._opts.preserve:
+ self._remove_scratch_dir(self._scratch_dir)
+
+ def _setup(self, test_basename: str) -> None:
+ test_scratch_dir = os.path.join(self._scratch_dir, test_basename)
+ assert not os.path.exists(test_scratch_dir)
+ os.makedirs(test_scratch_dir)
+ setup_script = os.path.join(self._test_dir, "setup")
+ if os.path.exists(setup_script):
+ self._run_script(setup_script, cwd=test_scratch_dir)
+
+ def _teardown(self, test_basename: str) -> None:
+ test_scratch_dir = os.path.join(self._scratch_dir, test_basename)
+ assert os.path.exists(test_scratch_dir)
+ teardown_script = os.path.join(self._test_dir, "teardown")
+ if os.path.exists(teardown_script):
+ self._run_script(teardown_script, cwd=test_scratch_dir)
+ if not self._opts.preserve:
+ self._remove_scratch_dir(test_scratch_dir)
+
+ def _run_script(self, script: str, cwd: str) -> None:
+ env = copy.copy(os.environ)
+ for k, v in self._opts.env.items():
+ env[k] = v
+ try:
+ subprocess.run(
+ args=[script],
+ stdin=subprocess.DEVNULL,
+ capture_output=True,
+ cwd=cwd,
+ env=env,
+ check=True,
+ )
+ except subprocess.CalledProcessError as e:
+ print(f"{script} failed with exit code {e.returncode}!")
+ print(f"stderr:\n{e.stderr}")
+ print(f"stdout:\n{e.stdout}")
+ raise
+
+TestSuites = typing.Dict[str, typing.List[str]]
+
+def get_all_tests(options: Options) -> TestSuites:
+ """
+ Find all the test in the test directory and return the test suites.
+ """
+ test_suites = {}
+ for root, dirs, files in os.walk(options.test_dir, topdown=True):
+ dirs[:] = [d for d in dirs if not exclude_dir(d)]
+ test_cases = []
+ for file in files:
+ if not exclude_file(file):
+ test_cases.append(file)
+ assert root == os.path.normpath(root)
+ test_suites[root] = test_cases
+ return test_suites
+
+
+def resolve_listed_tests(
+ tests: typing.List[str], options: Options
+) -> TestSuites:
+ """
+ Resolve the list of tests passed on the command line into their
+ respective test suites. Tests can either be paths, or test names
+ relative to the test directory.
+ """
+ test_suites = {}
+ for test in tests:
+ if not os.path.exists(test):
+ test = os.path.join(options.test_dir, test)
+ if not os.path.exists(test):
+ raise RuntimeError(f"Test {test} does not exist!")
+
+ test = os.path.normpath(os.path.abspath(test))
+ assert test.startswith(options.test_dir)
+ test_suite = os.path.dirname(test)
+ test_case = os.path.basename(test)
+ test_suites.setdefault(test_suite, []).append(test_case)
+
+ return test_suites
+
+def run_tests(test_suites: TestSuites, options: Options) -> bool:
+ """
+ Runs all the test in the :test_suites: with the given :options:.
+ Prints the results to stdout.
+ """
+ tests = {}
+ for test_dir, test_files in test_suites.items():
+ with TestSuite(test_dir, options) as test_suite:
+ test_files = sorted(set(test_files))
+ for test_file in test_files:
+ with test_suite.test_case(test_file) as test_case:
+ tests[test_case.name] = test_case.run()
+
+ successes = 0
+ for test, status in tests.items():
+ if status:
+ successes += 1
+ else:
+ print(f"FAIL: {test}")
+ if successes == len(tests):
+ print(f"PASSED all {len(tests)} tests!")
+ return True
+ else:
+ print(f"FAILED {len(tests) - successes} / {len(tests)} tests!")
+ return False
+
+
+if __name__ == "__main__":
+ CLI_TEST_DIR = os.path.dirname(sys.argv[0])
+ REPO_DIR = os.path.join(CLI_TEST_DIR, "..", "..")
+ PROGRAMS_DIR = os.path.join(REPO_DIR, "programs")
+ TESTS_DIR = os.path.join(REPO_DIR, "tests")
+ ZSTD_PATH = os.path.join(PROGRAMS_DIR, "zstd")
+ ZSTDGREP_PATH = os.path.join(PROGRAMS_DIR, "zstdgrep")
+ DATAGEN_PATH = os.path.join(TESTS_DIR, "datagen")
+
+ parser = argparse.ArgumentParser(
+ (
+ "Runs the zstd CLI tests. Exits nonzero on failure. Default arguments are\n"
+ "generally correct. Pass --preserve to preserve test output for debugging,\n"
+ "and --verbose to get verbose test output.\n"
+ )
+ )
+ parser.add_argument(
+ "--preserve",
+ action="store_true",
+ help="Preserve the scratch directory TEST_DIR/scratch/ for debugging purposes."
+ )
+ parser.add_argument("--verbose", action="store_true", help="Verbose test output.")
+ parser.add_argument("--timeout", default=60, type=int, help="Test case timeout in seconds. Set to 0 to disable timeouts.")
+ parser.add_argument(
+ "--exec-prefix",
+ default=None,
+ help="Sets the EXEC_PREFIX environment variable. Prefix to invocations of the zstd CLI."
+ )
+ parser.add_argument(
+ "--zstd",
+ default=ZSTD_PATH,
+ help="Sets the ZSTD_BIN environment variable. Path of the zstd CLI."
+ )
+ parser.add_argument(
+ "--zstdgrep",
+ default=ZSTDGREP_PATH,
+ help="Sets the ZSTDGREP_BIN environment variable. Path of the zstdgrep CLI."
+ )
+ parser.add_argument(
+ "--datagen",
+ default=DATAGEN_PATH,
+ help="Sets the DATAGEN_BIN environment variable. Path to the datagen CLI."
+ )
+ parser.add_argument(
+ "--test-dir",
+ default=CLI_TEST_DIR,
+ help=(
+ "Runs the tests under this directory. "
+ "Adds TEST_DIR/bin/ to path. "
+ "Scratch directory located in TEST_DIR/scratch/."
+ )
+ )
+ parser.add_argument(
+ "tests",
+ nargs="*",
+ help="Run only these test cases. Can either be paths or test names relative to TEST_DIR/"
+ )
+ args = parser.parse_args()
+
+ if args.timeout <= 0:
+ args.timeout = None
+
+ args.test_dir = os.path.normpath(os.path.abspath(args.test_dir))
+ bin_dir = os.path.join(args.test_dir, "bin")
+ scratch_dir = os.path.join(args.test_dir, "scratch")
+
+ env = {}
+ if args.exec_prefix is not None:
+ env["EXEC_PREFIX"] = args.exec_prefix
+ env["ZSTD_BIN"] = os.path.abspath(args.zstd)
+ env["DATAGEN_BIN"] = os.path.abspath(args.datagen)
+ env["ZSTDGREP_BIN"] = os.path.abspath(args.zstdgrep)
+ env["COMMON"] = os.path.abspath(os.path.join(args.test_dir, "common"))
+ env["PATH"] = os.path.abspath(bin_dir) + ":" + os.getenv("PATH", "")
+
+ opts = Options(
+ env=env,
+ timeout=args.timeout,
+ verbose=args.verbose,
+ preserve=args.preserve,
+ test_dir=args.test_dir,
+ scratch_dir=scratch_dir,
+ )
+
+ if len(args.tests) == 0:
+ tests = get_all_tests(opts)
+ else:
+ tests = resolve_listed_tests(args.tests, opts)
+
+ success = run_tests(tests, opts)
+ if success:
+ sys.exit(0)
+ else:
+ sys.exit(1)
+
From 1fc42de86a53320c056c9a3ca9847eae7ce1262b Mon Sep 17 00:00:00 2001
From: Nick Terrell
Date: Mon, 24 Jan 2022 13:52:08 -0800
Subject: [PATCH 065/472] [CI] Hook cli-tests up to CI
Add cli-tests to `make test`. This adds a `python3` dependency to `make
test`, but not `make check`. We could make this dependency optional by
skipping the tests if `python3` is not present.
---
tests/Makefile | 8 +-
tests/cli-tests/basic/help.sh | 5 +-
tests/cli-tests/basic/version.sh | 5 +-
tests/cli-tests/bin/cmp_size | 2 -
tests/cli-tests/bin/println | 2 +-
tests/cli-tests/common/format.sh | 2 +-
tests/cli-tests/common/mtime.sh | 2 +-
tests/cli-tests/common/permissions.sh | 2 +-
tests/cli-tests/compression/basic.sh | 8 +-
tests/cli-tests/compression/format.sh | 2 +-
tests/cli-tests/compression/levels.sh | 16 +--
.../compression/levels.sh.stderr.exact | 124 +++++++++---------
.../dict-builder/{no-inputs => no-inputs.sh} | 2 +-
.../{no-inputs.exit => no-inputs.sh.exit} | 0
...stderr.exact => no-inputs.sh.stderr.exact} | 2 +-
.../dictionaries/dictionary-mismatch.sh | 4 +-
.../dictionary-mismatch.sh.stderr.exact | 10 +-
tests/cli-tests/dictionaries/setup_once | 2 +-
18 files changed, 102 insertions(+), 96 deletions(-)
rename tests/cli-tests/dict-builder/{no-inputs => no-inputs.sh} (76%)
rename tests/cli-tests/dict-builder/{no-inputs.exit => no-inputs.sh.exit} (100%)
rename tests/cli-tests/dict-builder/{no-inputs.stderr.exact => no-inputs.sh.stderr.exact} (93%)
diff --git a/tests/Makefile b/tests/Makefile
index 132fa7a0818..cb77b0160a8 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -297,7 +297,7 @@ check: shortest
fuzztest: test-fuzzer test-zstream test-decodecorpus
.PHONY: test
-test: test-zstd test-fullbench test-fuzzer test-zstream test-invalidDictionaries test-legacy test-decodecorpus
+test: test-zstd test-fullbench test-fuzzer test-zstream test-invalidDictionaries test-legacy test-decodecorpus test-cli-tests
ifeq ($(QEMU_SYS),)
test: test-pool
endif
@@ -322,6 +322,12 @@ test-zstd test-zstd32 test-zstd-nolegacy: datagen
file $(ZSTD)
EXE_PREFIX="$(QEMU_SYS)" ZSTD_BIN="$(ZSTD)" DATAGEN_BIN=./datagen ./playTests.sh $(ZSTDRTTEST)
+test-cli-tests: ZSTD = $(PRGDIR)/zstd
+test-cli-tests: zstd datagen
+ file $(ZSTD)
+ ./cli-tests/run.py --exec-prefix="$(QEMU_SYS)" --zstd="$(ZSTD)" --datagen=./datagen
+
+
test-fullbench: fullbench datagen
$(QEMU_SYS) ./fullbench -i1
$(QEMU_SYS) ./fullbench -i1 -P0
diff --git a/tests/cli-tests/basic/help.sh b/tests/cli-tests/basic/help.sh
index c683b6a6a72..927c3ffbcc5 100755
--- a/tests/cli-tests/basic/help.sh
+++ b/tests/cli-tests/basic/help.sh
@@ -1,4 +1,7 @@
-#!/bin/sh -e
+#!/bin/sh
+
+set -e
+
println "+ zstd -h"
zstd -h
println "+ zstd -H"
diff --git a/tests/cli-tests/basic/version.sh b/tests/cli-tests/basic/version.sh
index d50de0f3cac..f75eaa84fb4 100755
--- a/tests/cli-tests/basic/version.sh
+++ b/tests/cli-tests/basic/version.sh
@@ -1,3 +1,6 @@
-#!/bin/sh -e
+#!/bin/sh
+
+set -e
+
zstd -V
zstd --version
diff --git a/tests/cli-tests/bin/cmp_size b/tests/cli-tests/bin/cmp_size
index 5afa1c590ad..8e4bef88eab 100755
--- a/tests/cli-tests/bin/cmp_size
+++ b/tests/cli-tests/bin/cmp_size
@@ -1,7 +1,5 @@
#!/bin/sh
-# Small utility to
-
set -e
usage()
diff --git a/tests/cli-tests/bin/println b/tests/cli-tests/bin/println
index 1da2460471b..494eb18c850 100755
--- a/tests/cli-tests/bin/println
+++ b/tests/cli-tests/bin/println
@@ -1,2 +1,2 @@
-#!/bin/env sh
+#!/bin/sh
printf '%b\n' "${*}"
diff --git a/tests/cli-tests/common/format.sh b/tests/cli-tests/common/format.sh
index 20ff0f05672..e574e973075 100644
--- a/tests/cli-tests/common/format.sh
+++ b/tests/cli-tests/common/format.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-source "$COMMON/platform.sh"
+. "$COMMON/platform.sh"
zstd_supports_format()
{
diff --git a/tests/cli-tests/common/mtime.sh b/tests/cli-tests/common/mtime.sh
index 7ce931a96c0..344074d398c 100644
--- a/tests/cli-tests/common/mtime.sh
+++ b/tests/cli-tests/common/mtime.sh
@@ -1,4 +1,4 @@
-source "$COMMON/platform.sh"
+. "$COMMON/platform.sh"
MTIME="stat -c %Y"
case "$UNAME" in
diff --git a/tests/cli-tests/common/permissions.sh b/tests/cli-tests/common/permissions.sh
index b1f6ea3ba70..6bce1f0b387 100644
--- a/tests/cli-tests/common/permissions.sh
+++ b/tests/cli-tests/common/permissions.sh
@@ -1,4 +1,4 @@
-source "$COMMON/platform.sh"
+. "$COMMON/platform.sh"
GET_PERMS="stat -c %a"
case "$UNAME" in
diff --git a/tests/cli-tests/compression/basic.sh b/tests/cli-tests/compression/basic.sh
index 6f0f87932fb..8b63e40760c 100755
--- a/tests/cli-tests/compression/basic.sh
+++ b/tests/cli-tests/compression/basic.sh
@@ -1,7 +1,9 @@
-#!/bin/sh -e
+#!/bin/sh
-# Uncomment the set -x line for debugging
-# set -x
+set -e
+
+# Uncomment the set -v line for debugging
+# set -v
# Test compression flags and check that they work
zstd file ; zstd -t file.zst
diff --git a/tests/cli-tests/compression/format.sh b/tests/cli-tests/compression/format.sh
index 86fb400809c..192fa2cf29f 100755
--- a/tests/cli-tests/compression/format.sh
+++ b/tests/cli-tests/compression/format.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-source "$COMMON/format.sh"
+. "$COMMON/format.sh"
set -e
diff --git a/tests/cli-tests/compression/levels.sh b/tests/cli-tests/compression/levels.sh
index 6bd0aca0078..4837790ce5d 100755
--- a/tests/cli-tests/compression/levels.sh
+++ b/tests/cli-tests/compression/levels.sh
@@ -1,7 +1,7 @@
#!/bin/sh
set -e
-set -x
+set -v
datagen > file
@@ -12,7 +12,7 @@ zstd -1 file -o file-1.zst
zstd -19 file -o file-19.zst
zstd -22 --ultra file -o file-22.zst
-zstd -t file-{f10,f1,1,19,22}.zst
+zstd -t file-f10.zst file-f1.zst file-1.zst file-19.zst file-22.zst
cmp_size -ne file-19.zst file-22.zst
cmp_size -lt file-19.zst file-1.zst
@@ -41,10 +41,10 @@ ZSTD_CLEVEL=1 zstd file -o file-1-env.zst
ZSTD_CLEVEL=+19 zstd file -o file-19-env.zst
ZSTD_CLEVEL=+99 zstd file -o file-99-env.zst
-cmp file-f10{,-env}.zst || die "Environment variable failed to set level"
-cmp file-1{,-env}.zst || die "Environment variable failed to set level"
-cmp file-19{,-env}.zst || die "Environment variable failed to set level"
-cmp file-99{,-env}.zst || die "Environment variable failed to set level"
+cmp file-f10.zst file-f10-env.zst || die "Environment variable failed to set level"
+cmp file-1.zst file-1-env.zst || die "Environment variable failed to set level"
+cmp file-19.zst file-19-env.zst || die "Environment variable failed to set level"
+cmp file-99.zst file-99-env.zst || die "Environment variable failed to set level"
# Test invalid environment clevel is the default level
zstd -f file
@@ -60,5 +60,5 @@ ZSTD_CLEVEL=5000000000 zstd -f file -o file-env.zst; cmp file.zst file-env.zst
ZSTD_CLEVEL=10 zstd -f file -1 -o file-1-env.zst
ZSTD_CLEVEL=10 zstd -f file --fast=1 -o file-f1-env.zst
-cmp file-1{,-env}.zst || die "Environment variable not overridden"
-cmp file-f1{,-env}.zst || die "Environment variable not overridden"
+cmp file-1.zst file-1-env.zst || die "Environment variable not overridden"
+cmp file-f1.zst file-f1-env.zst || die "Environment variable not overridden"
diff --git a/tests/cli-tests/compression/levels.sh.stderr.exact b/tests/cli-tests/compression/levels.sh.stderr.exact
index c0b7066fcba..cb00433e6e1 100644
--- a/tests/cli-tests/compression/levels.sh.stderr.exact
+++ b/tests/cli-tests/compression/levels.sh.stderr.exact
@@ -1,75 +1,71 @@
-+ datagen
-+ zstd --fast=10 file -o file-f10.zst
-+ zstd --fast=1 file -o file-f1.zst
-+ zstd -1 file -o file-1.zst
-+ zstd -19 file -o file-19.zst
-+ zstd -22 --ultra file -o file-22.zst
-+ zstd -t file-f10.zst file-f1.zst file-1.zst file-19.zst file-22.zst
-+ cmp_size -ne file-19.zst file-22.zst
-+ cmp_size -lt file-19.zst file-1.zst
-+ cmp_size -lt file-1.zst file-f1.zst
-+ cmp_size -lt file-f1.zst file-f10.zst
-+ zstd --fast file -f
-+ cmp file.zst file-f1.zst
-+ zstd -0 file -o file-0.zst
-+ zstd -f file
-+ cmp file.zst file-0.zst
-+ zstd -99 file -o file-99.zst
+
+datagen > file
+
+# Compress with various levels and ensure that their sizes are ordered
+zstd --fast=10 file -o file-f10.zst
+zstd --fast=1 file -o file-f1.zst
+zstd -1 file -o file-1.zst
+zstd -19 file -o file-19.zst
+zstd -22 --ultra file -o file-22.zst
+
+zstd -t file-f10.zst file-f1.zst file-1.zst file-19.zst file-22.zst
+
+cmp_size -ne file-19.zst file-22.zst
+cmp_size -lt file-19.zst file-1.zst
+cmp_size -lt file-1.zst file-f1.zst
+cmp_size -lt file-f1.zst file-f10.zst
+
+# Test default levels
+zstd --fast file -f
+cmp file.zst file-f1.zst || die "--fast is not level -1"
+
+zstd -0 file -o file-0.zst
+zstd -f file
+cmp file.zst file-0.zst || die "Level 0 is not the default level"
+
+# Test level clamping
+zstd -99 file -o file-99.zst
Warning : compression level higher than max, reduced to 19
-+ cmp file-19.zst file-99.zst
-+ zstd --fast=200000 file -c
-+ zstd -t
-+ zstd -5000000000 -f file
+cmp file-19.zst file-99.zst || die "Level 99 is clamped to 19"
+zstd --fast=200000 file -c | zstd -t
+
+zstd -5000000000 -f file && die "Level too large, must fail" ||:
error: numeric value overflows 32-bit unsigned int
-+ :
-+ zstd --fast=5000000000 -f file
+zstd --fast=5000000000 -f file && die "Level too large, must fail" ||:
error: numeric value overflows 32-bit unsigned int
-+ :
-+ ZSTD_CLEVEL=-10
-+ zstd file -o file-f10-env.zst
-+ ZSTD_CLEVEL=1
-+ zstd file -o file-1-env.zst
-+ ZSTD_CLEVEL=+19
-+ zstd file -o file-19-env.zst
-+ ZSTD_CLEVEL=+99
-+ zstd file -o file-99-env.zst
+
+# Test setting a level through the environment variable
+ZSTD_CLEVEL=-10 zstd file -o file-f10-env.zst
+ZSTD_CLEVEL=1 zstd file -o file-1-env.zst
+ZSTD_CLEVEL=+19 zstd file -o file-19-env.zst
+ZSTD_CLEVEL=+99 zstd file -o file-99-env.zst
Warning : compression level higher than max, reduced to 19
-+ cmp file-f10.zst file-f10-env.zst
-+ cmp file-1.zst file-1-env.zst
-+ cmp file-19.zst file-19-env.zst
-+ cmp file-99.zst file-99-env.zst
-+ zstd -f file
-+ ZSTD_CLEVEL=-
-+ zstd -f file -o file-env.zst
+
+cmp file-f10.zst file-f10-env.zst || die "Environment variable failed to set level"
+cmp file-1.zst file-1-env.zst || die "Environment variable failed to set level"
+cmp file-19.zst file-19-env.zst || die "Environment variable failed to set level"
+cmp file-99.zst file-99-env.zst || die "Environment variable failed to set level"
+
+# Test invalid environment clevel is the default level
+zstd -f file
+ZSTD_CLEVEL=- zstd -f file -o file-env.zst ; cmp file.zst file-env.zst
Ignore environment variable setting ZSTD_CLEVEL=-: not a valid integer value
-+ cmp file.zst file-env.zst
-+ ZSTD_CLEVEL=+
-+ zstd -f file -o file-env.zst
+ZSTD_CLEVEL=+ zstd -f file -o file-env.zst ; cmp file.zst file-env.zst
Ignore environment variable setting ZSTD_CLEVEL=+: not a valid integer value
-+ cmp file.zst file-env.zst
-+ ZSTD_CLEVEL=a
-+ zstd -f file -o file-env.zst
+ZSTD_CLEVEL=a zstd -f file -o file-env.zst ; cmp file.zst file-env.zst
Ignore environment variable setting ZSTD_CLEVEL=a: not a valid integer value
-+ cmp file.zst file-env.zst
-+ ZSTD_CLEVEL=-a
-+ zstd -f file -o file-env.zst
+ZSTD_CLEVEL=-a zstd -f file -o file-env.zst ; cmp file.zst file-env.zst
Ignore environment variable setting ZSTD_CLEVEL=-a: not a valid integer value
-+ cmp file.zst file-env.zst
-+ ZSTD_CLEVEL=+a
-+ zstd -f file -o file-env.zst
+ZSTD_CLEVEL=+a zstd -f file -o file-env.zst ; cmp file.zst file-env.zst
Ignore environment variable setting ZSTD_CLEVEL=+a: not a valid integer value
-+ cmp file.zst file-env.zst
-+ ZSTD_CLEVEL=3a7
-+ zstd -f file -o file-env.zst
+ZSTD_CLEVEL=3a7 zstd -f file -o file-env.zst ; cmp file.zst file-env.zst
Ignore environment variable setting ZSTD_CLEVEL=3a7: not a valid integer value
-+ cmp file.zst file-env.zst
-+ ZSTD_CLEVEL=5000000000
-+ zstd -f file -o file-env.zst
+ZSTD_CLEVEL=5000000000 zstd -f file -o file-env.zst; cmp file.zst file-env.zst
Ignore environment variable setting ZSTD_CLEVEL=5000000000: numeric value too large
-+ cmp file.zst file-env.zst
-+ ZSTD_CLEVEL=10
-+ zstd -f file -1 -o file-1-env.zst
-+ ZSTD_CLEVEL=10
-+ zstd -f file --fast=1 -o file-f1-env.zst
-+ cmp file-1.zst file-1-env.zst
-+ cmp file-f1.zst file-f1-env.zst
+
+# Test environment clevel is overridden by command line
+ZSTD_CLEVEL=10 zstd -f file -1 -o file-1-env.zst
+ZSTD_CLEVEL=10 zstd -f file --fast=1 -o file-f1-env.zst
+
+cmp file-1.zst file-1-env.zst || die "Environment variable not overridden"
+cmp file-f1.zst file-f1-env.zst || die "Environment variable not overridden"
diff --git a/tests/cli-tests/dict-builder/no-inputs b/tests/cli-tests/dict-builder/no-inputs.sh
similarity index 76%
rename from tests/cli-tests/dict-builder/no-inputs
rename to tests/cli-tests/dict-builder/no-inputs.sh
index d37fdce5a20..416b83742f2 100755
--- a/tests/cli-tests/dict-builder/no-inputs
+++ b/tests/cli-tests/dict-builder/no-inputs.sh
@@ -1,3 +1,3 @@
#!/bin/sh
-set -x
+set -v
zstd --train
diff --git a/tests/cli-tests/dict-builder/no-inputs.exit b/tests/cli-tests/dict-builder/no-inputs.sh.exit
similarity index 100%
rename from tests/cli-tests/dict-builder/no-inputs.exit
rename to tests/cli-tests/dict-builder/no-inputs.sh.exit
diff --git a/tests/cli-tests/dict-builder/no-inputs.stderr.exact b/tests/cli-tests/dict-builder/no-inputs.sh.stderr.exact
similarity index 93%
rename from tests/cli-tests/dict-builder/no-inputs.stderr.exact
rename to tests/cli-tests/dict-builder/no-inputs.sh.stderr.exact
index b3b5599d5ae..d7b3ea020f7 100644
--- a/tests/cli-tests/dict-builder/no-inputs.stderr.exact
+++ b/tests/cli-tests/dict-builder/no-inputs.sh.stderr.exact
@@ -1,4 +1,4 @@
-+ zstd --train
+zstd --train
! Warning : nb of samples too low for proper processing !
! Please provide _one file per sample_.
! Alternatively, split files into fixed-size blocks representative of samples, with -B#
diff --git a/tests/cli-tests/dictionaries/dictionary-mismatch.sh b/tests/cli-tests/dictionaries/dictionary-mismatch.sh
index 2b5d5a0d227..8264ccca5a2 100755
--- a/tests/cli-tests/dictionaries/dictionary-mismatch.sh
+++ b/tests/cli-tests/dictionaries/dictionary-mismatch.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-source "$COMMON/platform.sh"
+. "$COMMON/platform.sh"
set -e
@@ -22,7 +22,7 @@ if [ false ]; then
datagen -g1000 -s0 > file0
fi
-set -x
+set -v
zstd files/0 -D dicts/0
zstd -t files/0.zst -D dicts/0
zstd -t files/0.zst -D dicts/1 && die "Must fail" ||:
diff --git a/tests/cli-tests/dictionaries/dictionary-mismatch.sh.stderr.exact b/tests/cli-tests/dictionaries/dictionary-mismatch.sh.stderr.exact
index 399a3207d26..0afea722e98 100644
--- a/tests/cli-tests/dictionaries/dictionary-mismatch.sh.stderr.exact
+++ b/tests/cli-tests/dictionaries/dictionary-mismatch.sh.stderr.exact
@@ -1,8 +1,6 @@
-+ zstd files/0 -D dicts/0
-+ zstd -t files/0.zst -D dicts/0
-+ zstd -t files/0.zst -D dicts/1
+zstd files/0 -D dicts/0
+zstd -t files/0.zst -D dicts/0
+zstd -t files/0.zst -D dicts/1 && die "Must fail" ||:
files/0.zst : Decoding error (36) : Dictionary mismatch
-+ :
-+ zstd -t files/0.zst
+zstd -t files/0.zst && die "Must fail" ||:
files/0.zst : Decoding error (36) : Dictionary mismatch
-+ :
diff --git a/tests/cli-tests/dictionaries/setup_once b/tests/cli-tests/dictionaries/setup_once
index 6316df165fa..1241c578214 100755
--- a/tests/cli-tests/dictionaries/setup_once
+++ b/tests/cli-tests/dictionaries/setup_once
@@ -2,7 +2,7 @@
set -e
-source "$COMMON/platform.sh"
+. "$COMMON/platform.sh"
mkdir files/ dicts/
From d64d5ddc57ac58f3bcff2bd531cbe1e71bc4e356 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Thu, 27 Jan 2022 14:54:18 -0800
Subject: [PATCH 066/472] fix 44122 test error
It's a bug in the test itself, in exceptional circumstances (no more space for additional sequence).
There should be enough room for all cases to work fine from now on,
and if not, we have an additional `assert()` to catch that situation.
---
tests/fuzz/sequence_compression_api.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/tests/fuzz/sequence_compression_api.c b/tests/fuzz/sequence_compression_api.c
index b35f5a79563..443e0a18148 100644
--- a/tests/fuzz/sequence_compression_api.c
+++ b/tests/fuzz/sequence_compression_api.c
@@ -153,7 +153,7 @@ static size_t generateRandomSequences(FUZZ_dataProducer_t* producer,
matchLengthMax = MIN(matchLengthMax, blockSizeMax/2);
}
- while ( nbSeqGenerated < ZSTD_FUZZ_MAX_NBSEQ-1
+ while ( nbSeqGenerated < ZSTD_FUZZ_MAX_NBSEQ-2 /* extra room for explicit delimiters */
&& bytesGenerated < ZSTD_FUZZ_GENERATED_SRC_MAXSIZE
&& !FUZZ_dataProducer_empty(producer)) {
uint32_t matchLength;
@@ -214,6 +214,7 @@ static size_t generateRandomSequences(FUZZ_dataProducer_t* producer,
if (mode == ZSTD_sf_explicitBlockDelimiters) {
/* always end sequences with a block delimiter */
const ZSTD_Sequence endBlock = {0, 0, 0, 0};
+ assert(nbSeqGenerated < ZSTD_FUZZ_MAX_NBSEQ);
generatedSequences[nbSeqGenerated++] = endBlock;
}
From 9a68840176ec9334060b2379f204ada59408bfd1 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Thu, 27 Jan 2022 11:06:16 -0800
Subject: [PATCH 067/472] minor refactor to blocksplit
notably simplication of ZSTD_deriveSeqStoreChunk()
---
lib/common/zstd_internal.h | 10 +++++-----
lib/compress/zstd_compress.c | 38 +++++++++++++++++-------------------
2 files changed, 23 insertions(+), 25 deletions(-)
diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h
index ff2c2cd4493..4e19cb3c2fb 100644
--- a/lib/common/zstd_internal.h
+++ b/lib/common/zstd_internal.h
@@ -299,11 +299,11 @@ typedef enum {
typedef struct {
seqDef* sequencesStart;
seqDef* sequences; /* ptr to end of sequences */
- BYTE* litStart;
- BYTE* lit; /* ptr to end of literals */
- BYTE* llCode;
- BYTE* mlCode;
- BYTE* ofCode;
+ BYTE* litStart;
+ BYTE* lit; /* ptr to end of literals */
+ BYTE* llCode;
+ BYTE* mlCode;
+ BYTE* ofCode;
size_t maxNbSeq;
size_t maxNbLit;
diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
index 3a248189242..c8290157022 100644
--- a/lib/compress/zstd_compress.c
+++ b/lib/compress/zstd_compress.c
@@ -3394,15 +3394,12 @@ static size_t ZSTD_countSeqStoreMatchBytes(const seqStore_t* const seqStore) {
*/
static void ZSTD_deriveSeqStoreChunk(seqStore_t* resultSeqStore,
const seqStore_t* originalSeqStore,
- size_t startIdx, size_t endIdx) {
- BYTE* const litEnd = originalSeqStore->lit;
- size_t literalsBytes;
- size_t literalsBytesPreceding = 0;
-
+ size_t startIdx, size_t endIdx)
+{
*resultSeqStore = *originalSeqStore;
if (startIdx > 0) {
resultSeqStore->sequences = originalSeqStore->sequencesStart + startIdx;
- literalsBytesPreceding = ZSTD_countSeqStoreLiteralsBytes(resultSeqStore);
+ resultSeqStore->litStart += ZSTD_countSeqStoreLiteralsBytes(resultSeqStore);
}
/* Move longLengthPos into the correct position if necessary */
@@ -3415,13 +3412,12 @@ static void ZSTD_deriveSeqStoreChunk(seqStore_t* resultSeqStore,
}
resultSeqStore->sequencesStart = originalSeqStore->sequencesStart + startIdx;
resultSeqStore->sequences = originalSeqStore->sequencesStart + endIdx;
- literalsBytes = ZSTD_countSeqStoreLiteralsBytes(resultSeqStore);
- resultSeqStore->litStart += literalsBytesPreceding;
if (endIdx == (size_t)(originalSeqStore->sequences - originalSeqStore->sequencesStart)) {
/* This accounts for possible last literals if the derived chunk reaches the end of the block */
- resultSeqStore->lit = litEnd;
+ assert(resultSeqStore->lit == originalSeqStore->lit);
} else {
- resultSeqStore->lit = resultSeqStore->litStart+literalsBytes;
+ size_t const literalsBytes = ZSTD_countSeqStoreLiteralsBytes(resultSeqStore);
+ resultSeqStore->lit = resultSeqStore->litStart + literalsBytes;
}
resultSeqStore->llCode += startIdx;
resultSeqStore->mlCode += startIdx;
@@ -3580,7 +3576,8 @@ typedef struct {
* In theory, this means the absolute largest recursion depth is 10 == log2(maxNbSeqInBlock/MIN_SEQUENCES_BLOCK_SPLITTING).
* In practice, recursion depth usually doesn't go beyond 4.
*
- * Furthermore, the number of splits is capped by ZSTD_MAX_NB_BLOCK_SPLITS. At ZSTD_MAX_NB_BLOCK_SPLITS == 196 with the current existing blockSize
+ * Furthermore, the number of splits is capped by ZSTD_MAX_NB_BLOCK_SPLITS.
+ * At ZSTD_MAX_NB_BLOCK_SPLITS == 196 with the current existing blockSize
* maximum of 128 KB, this value is actually impossible to reach.
*/
static void
@@ -3599,19 +3596,20 @@ ZSTD_deriveBlockSplitsHelper(seqStoreSplits* splits, size_t startIdx, size_t end
DEBUGLOG(6, "ZSTD_deriveBlockSplitsHelper: Too few sequences");
return;
}
- DEBUGLOG(4, "ZSTD_deriveBlockSplitsHelper: startIdx=%zu endIdx=%zu", startIdx, endIdx);
+ DEBUGLOG(5, "ZSTD_deriveBlockSplitsHelper: startIdx=%zu endIdx=%zu", startIdx, endIdx);
ZSTD_deriveSeqStoreChunk(fullSeqStoreChunk, origSeqStore, startIdx, endIdx);
ZSTD_deriveSeqStoreChunk(firstHalfSeqStore, origSeqStore, startIdx, midIdx);
ZSTD_deriveSeqStoreChunk(secondHalfSeqStore, origSeqStore, midIdx, endIdx);
estimatedOriginalSize = ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(fullSeqStoreChunk, zc);
estimatedFirstHalfSize = ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(firstHalfSeqStore, zc);
estimatedSecondHalfSize = ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(secondHalfSeqStore, zc);
- DEBUGLOG(4, "Estimated original block size: %zu -- First half split: %zu -- Second half split: %zu",
+ DEBUGLOG(5, "Estimated original block size: %zu -- First half split: %zu -- Second half split: %zu",
estimatedOriginalSize, estimatedFirstHalfSize, estimatedSecondHalfSize);
if (ZSTD_isError(estimatedOriginalSize) || ZSTD_isError(estimatedFirstHalfSize) || ZSTD_isError(estimatedSecondHalfSize)) {
return;
}
if (estimatedFirstHalfSize + estimatedSecondHalfSize < estimatedOriginalSize) {
+ DEBUGLOG(5, "split decided at seqNb:%zu", midIdx);
ZSTD_deriveBlockSplitsHelper(splits, startIdx, midIdx, zc, origSeqStore);
splits->splitLocations[splits->idx] = (U32)midIdx;
splits->idx++;
@@ -3623,10 +3621,11 @@ ZSTD_deriveBlockSplitsHelper(seqStoreSplits* splits, size_t startIdx, size_t end
*
* Returns the number of splits made (which equals the size of the partition table - 1).
*/
-static size_t ZSTD_deriveBlockSplits(ZSTD_CCtx* zc, U32 partitions[], U32 nbSeq) {
+static size_t ZSTD_deriveBlockSplits(ZSTD_CCtx* zc, U32 partitions[], U32 nbSeq)
+{
seqStoreSplits splits = {partitions, 0};
if (nbSeq <= 4) {
- DEBUGLOG(4, "ZSTD_deriveBlockSplits: Too few sequences to split");
+ DEBUGLOG(5, "ZSTD_deriveBlockSplits: Too few sequences to split");
/* Refuse to try and split anything with less than 4 sequences */
return 0;
}
@@ -3693,12 +3692,11 @@ ZSTD_compressBlock_splitBlock_internal(ZSTD_CCtx* zc, void* dst, size_t dstCapac
ZSTD_deriveSeqStoreChunk(currSeqStore, &zc->seqStore, 0, partitions[0]);
for (i = 0; i <= numSplits; ++i) {
- size_t srcBytes;
size_t cSizeChunk;
U32 const lastPartition = (i == numSplits);
U32 lastBlockEntireSrc = 0;
- srcBytes = ZSTD_countSeqStoreLiteralsBytes(currSeqStore) + ZSTD_countSeqStoreMatchBytes(currSeqStore);
+ size_t srcBytes = ZSTD_countSeqStoreLiteralsBytes(currSeqStore) + ZSTD_countSeqStoreMatchBytes(currSeqStore);
srcBytesTotal += srcBytes;
if (lastPartition) {
/* This is the final partition, need to account for possible last literals */
@@ -3765,9 +3763,9 @@ ZSTD_compressBlock_internal(ZSTD_CCtx* zc,
void* dst, size_t dstCapacity,
const void* src, size_t srcSize, U32 frame)
{
- /* This the upper bound for the length of an rle block.
- * This isn't the actual upper bound. Finding the real threshold
- * needs further investigation.
+ /* This is an estimated upper bound for the length of an rle block.
+ * This isn't the actual upper bound.
+ * Finding the real threshold needs further investigation.
*/
const U32 rleMaxLength = 25;
size_t cSize;
From 637b2d7a24faf32b3cd465b6d46d890ed1e8ff6d Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Sat, 29 Jan 2022 16:23:21 -0800
Subject: [PATCH 068/472] fixed bug 44168
discovered by oss-fuzz
It's a bug in the test itself :
ZSTD_compressBound() as an upper bound of the compress size
only works for data compressed "normally".
But in situations where many flushes are forcefully introduced,
this creates many more blocks,
each of which has a potential to increase the size by 3 bytes.
In extreme cases (lots of small incompressible blocks), the expansion can go beyond ZSTD_compressBound().
This situation is similar when using the CompressSequences() API
with Explicit Block Delimiters.
In which case, each explicit block acts like a deliberate flush.
When employed by a fuzzer, it's possible to generate scenarios like the one described above,
with tons of incompressible blocks of small sizes,
thus going beyond ZSTD_compressBound().
fix : when using Explicit Block Delimiters, use a larger bound, to account for this scenario.
---
lib/common/zstd_internal.h | 4 ++--
lib/compress/zstd_compress.c | 2 +-
tests/fuzz/sequence_compression_api.c | 6 +++++-
3 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h
index 4e19cb3c2fb..485b23a690a 100644
--- a/lib/common/zstd_internal.h
+++ b/lib/common/zstd_internal.h
@@ -311,8 +311,8 @@ typedef struct {
* in the seqStore that has a value larger than U16 (if it exists). To do so, we increment
* the existing value of the litLength or matchLength by 0x10000.
*/
- ZSTD_longLengthType_e longLengthType;
- U32 longLengthPos; /* Index of the sequence to apply long length modification to */
+ ZSTD_longLengthType_e longLengthType;
+ U32 longLengthPos; /* Index of the sequence to apply long length modification to */
} seqStore_t;
typedef struct {
diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
index c8290157022..b9358fa0c81 100644
--- a/lib/compress/zstd_compress.c
+++ b/lib/compress/zstd_compress.c
@@ -6140,7 +6140,7 @@ ZSTD_compressSequences_internal(ZSTD_CCtx* cctx,
FORWARD_IF_ERROR(blockSize, "Error while trying to determine block size");
assert(blockSize <= remaining);
ZSTD_resetSeqStore(&cctx->seqStore);
- DEBUGLOG(5, "Working on new block. Blocksize: %zu", blockSize);
+ DEBUGLOG(5, "Working on new block. Blocksize: %zu (total:%zu)", blockSize, (ip - (const BYTE*)src) + blockSize);
additionalByteAdjustment = sequenceCopier(cctx, &seqPos, inSeqs, inSeqsSize, ip, blockSize);
FORWARD_IF_ERROR(additionalByteAdjustment, "Bad sequence copy");
diff --git a/tests/fuzz/sequence_compression_api.c b/tests/fuzz/sequence_compression_api.c
index 443e0a18148..68923e11e60 100644
--- a/tests/fuzz/sequence_compression_api.c
+++ b/tests/fuzz/sequence_compression_api.c
@@ -297,7 +297,11 @@ int LLVMFuzzerTestOneInput(const uint8_t* src, size_t size)
}
nbSequences = generateRandomSequences(producer, ZSTD_FUZZ_GENERATED_LITERALS_SIZE, dictSize, wLog, mode);
generatedSrcSize = decodeSequences(generatedSrc, nbSequences, ZSTD_FUZZ_GENERATED_LITERALS_SIZE, dictBuffer, dictSize, mode);
- cBufSize = ZSTD_compressBound(generatedSrcSize);
+ /* Note : in explicit block delimiters mode,
+ * the fuzzer might generate a lot of small incompressible blocks.
+ * In which case, the final compressed size might be > ZSTD_compressBound().
+ * Solution : provide a much more generous cBufSize to cover these scenarios */
+ cBufSize = (mode == ZSTD_sf_noBlockDelimiters) ? ZSTD_compressBound(generatedSrcSize) : 256 + (generatedSrcSize * 2);
cBuf = FUZZ_malloc(cBufSize);
rBufSize = generatedSrcSize;
From 8d65f87416740444da4a713d2778b78c11c6b38b Mon Sep 17 00:00:00 2001
From: Nick Terrell
Date: Sun, 30 Jan 2022 12:16:16 -0800
Subject: [PATCH 069/472] Fix static analysis false-positives
* It couldn't detect that the `fastCoverParams` can't be non-null, since it was just an assertion.
* It thought we were accesing `wksp->dtable` beyond the bounds because we were using it to set the `workSpace` value. Instead, compute the workspace size used in a different way.
---
lib/common/fse.h | 2 +-
lib/common/fse_decompress.c | 3 ++-
programs/dibio.c | 8 +++++---
3 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/lib/common/fse.h b/lib/common/fse.h
index 714bfd3e7f2..bd29e9ac55c 100644
--- a/lib/common/fse.h
+++ b/lib/common/fse.h
@@ -353,7 +353,7 @@ size_t FSE_buildDTable_raw (FSE_DTable* dt, unsigned nbBits);
size_t FSE_buildDTable_rle (FSE_DTable* dt, unsigned char symbolValue);
/**< build a fake FSE_DTable, designed to always generate the same symbolValue */
-#define FSE_DECOMPRESS_WKSP_SIZE_U32(maxTableLog, maxSymbolValue) (FSE_DTABLE_SIZE_U32(maxTableLog) + FSE_BUILD_DTABLE_WKSP_SIZE_U32(maxTableLog, maxSymbolValue) + (FSE_MAX_SYMBOL_VALUE + 1) / 2 + 1)
+#define FSE_DECOMPRESS_WKSP_SIZE_U32(maxTableLog, maxSymbolValue) (FSE_DTABLE_SIZE_U32(maxTableLog) + 1 + FSE_BUILD_DTABLE_WKSP_SIZE_U32(maxTableLog, maxSymbolValue) + (FSE_MAX_SYMBOL_VALUE + 1) / 2 + 1)
#define FSE_DECOMPRESS_WKSP_SIZE(maxTableLog, maxSymbolValue) (FSE_DECOMPRESS_WKSP_SIZE_U32(maxTableLog, maxSymbolValue) * sizeof(unsigned))
size_t FSE_decompress_wksp(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, unsigned maxLog, void* workSpace, size_t wkspSize);
/**< same as FSE_decompress(), using an externally allocated `workSpace` produced with `FSE_DECOMPRESS_WKSP_SIZE_U32(maxLog, maxSymbolValue)` */
diff --git a/lib/common/fse_decompress.c b/lib/common/fse_decompress.c
index a5a358015fc..bc0c1be2f68 100644
--- a/lib/common/fse_decompress.c
+++ b/lib/common/fse_decompress.c
@@ -342,7 +342,8 @@ FORCE_INLINE_TEMPLATE size_t FSE_decompress_wksp_body(
}
if (FSE_DECOMPRESS_WKSP_SIZE(tableLog, maxSymbolValue) > wkspSize) return ERROR(tableLog_tooLarge);
- workSpace = wksp->dtable + FSE_DTABLE_SIZE_U32(tableLog);
+ assert(sizeof(*wksp) + FSE_DTABLE_SIZE(tableLog) <= wkspSize);
+ workSpace = (BYTE*)workSpace + sizeof(*wksp) + FSE_DTABLE_SIZE(tableLog);
wkspSize -= sizeof(*wksp) + FSE_DTABLE_SIZE(tableLog);
CHECK_F( FSE_buildDTable_internal(wksp->dtable, wksp->ncount, maxSymbolValue, tableLog, workSpace, wkspSize) );
diff --git a/programs/dibio.c b/programs/dibio.c
index 147d1e7bfdf..fddbc9e5769 100644
--- a/programs/dibio.c
+++ b/programs/dibio.c
@@ -31,6 +31,7 @@
#include "timefn.h" /* UTIL_time_t, UTIL_clockSpanMicro, UTIL_getTime */
#include "../lib/common/debug.h" /* assert */
#include "../lib/common/mem.h" /* read */
+#include "../lib/zstd_errors.h"
#include "dibio.h"
@@ -380,7 +381,7 @@ int DiB_trainFromFiles(const char* dictFileName, size_t maxDictSize,
srcBuffer, &loadedSize, sampleSizes, fs.nbSamples, fileNamesTable,
nbFiles, chunkSize, displayLevel);
- { size_t dictSize;
+ { size_t dictSize = ZSTD_error_GENERIC;
if (params) {
DiB_fillNoise((char*)srcBuffer + loadedSize, NOISELENGTH); /* guard band, for end of buffer condition */
dictSize = ZDICT_trainFromBuffer_legacy(dictBuffer, maxDictSize,
@@ -400,8 +401,7 @@ int DiB_trainFromFiles(const char* dictFileName, size_t maxDictSize,
dictSize = ZDICT_trainFromBuffer_cover(dictBuffer, maxDictSize, srcBuffer,
sampleSizes, nbSamplesLoaded, *coverParams);
}
- } else {
- assert(fastCoverParams != NULL);
+ } else if (fastCoverParams != NULL) {
if (optimize) {
dictSize = ZDICT_optimizeTrainFromBuffer_fastCover(dictBuffer, maxDictSize,
srcBuffer, sampleSizes, nbSamplesLoaded,
@@ -416,6 +416,8 @@ int DiB_trainFromFiles(const char* dictFileName, size_t maxDictSize,
dictSize = ZDICT_trainFromBuffer_fastCover(dictBuffer, maxDictSize, srcBuffer,
sampleSizes, nbSamplesLoaded, *fastCoverParams);
}
+ } else {
+ assert(0 /* Impossible */);
}
if (ZDICT_isError(dictSize)) {
DISPLAYLEVEL(1, "dictionary training failed : %s \n", ZDICT_getErrorName(dictSize)); /* should not happen */
From 5b2c6c776acadcd86247fdaa3e48c11c6848263e Mon Sep 17 00:00:00 2001
From: Eli Schwartz
Date: Sun, 30 Jan 2022 20:42:35 -0500
Subject: [PATCH 070/472] meson: fix resource file compilation on Windows
It needs to know about the correct include directories on its own.
---
build/meson/lib/meson.build | 3 ++-
build/meson/programs/meson.build | 3 ++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/build/meson/lib/meson.build b/build/meson/lib/meson.build
index 6b093378101..bb761a4fc2f 100644
--- a/build/meson/lib/meson.build
+++ b/build/meson/lib/meson.build
@@ -83,7 +83,8 @@ libzstd_c_args = []
if cc_id == compiler_msvc
if default_library_type != 'static'
libzstd_sources += [windows_mod.compile_resources(
- join_paths(zstd_rootdir, 'build/VS2010/libzstd-dll/libzstd-dll.rc'))]
+ join_paths(zstd_rootdir, 'build/VS2010/libzstd-dll/libzstd-dll.rc'),
+ include_directories: libzstd_includes)]
libzstd_c_args += ['-DZSTD_DLL_EXPORT=1',
'-DZSTD_HEAPMODE=0',
'-D_CONSOLE',
diff --git a/build/meson/programs/meson.build b/build/meson/programs/meson.build
index 5ccd679a167..0704155d845 100644
--- a/build/meson/programs/meson.build
+++ b/build/meson/programs/meson.build
@@ -66,7 +66,8 @@ endif
if cc_id == compiler_msvc
if default_library_type != 'static'
zstd_programs_sources += [windows_mod.compile_resources(
- join_paths(zstd_rootdir, 'build/VS2010/zstd/zstd.rc'))]
+ join_paths(zstd_rootdir, 'build/VS2010/zstd/zstd.rc'),
+ include_directories: libzstd_includes)]
endif
endif
From 84c05453db61a5c518bda486ea36b0c00fc645a1 Mon Sep 17 00:00:00 2001
From: Eli Schwartz
Date: Sun, 30 Jan 2022 21:02:44 -0500
Subject: [PATCH 071/472] meson: never require a libm
libm is not guaranteed to exist. POSIX requires the math functions to
exist, but doesn't require to have it be a standalone library.
On platforms where libm exists as a standalone library, it will always
be found by meson -- it is shipped with libc.
If it is not found, then we can safely assume the linker will make the
math functions available by default.
See https://mesonbuild.com/howtox.html#add-math-library-lm-portably
Fixes building with bin_tests=true on Windows.
---
build/meson/meson.build | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build/meson/meson.build b/build/meson/meson.build
index 0c29a7621b9..e0ea3dff9ce 100644
--- a/build/meson/meson.build
+++ b/build/meson/meson.build
@@ -92,7 +92,7 @@ feature_lz4 = get_option('lz4')
# Dependencies
# =============================================================================
-libm_dep = cc.find_library('m', required: bin_tests)
+libm_dep = cc.find_library('m', required: false)
thread_dep = dependency('threads', required: feature_multi_thread)
use_multi_thread = thread_dep.found()
# Arguments in dependency should be equivalent to those passed to pkg-config
From ef78b9af30422f8affe1ab35cd59b7170cde707a Mon Sep 17 00:00:00 2001
From: Eli Schwartz
Date: Sun, 30 Jan 2022 21:45:19 -0500
Subject: [PATCH 072/472] meson: valgrind wrapper should return correct errors
While trying to raise an exception on failures, it instead raised an
exception for misusing the exception. CalledProcessError is only
supposed to be used when given a return code and a command, and it
prints:
Command '{cmd}' returned non-zero exit status {ret}
Passing an error message string instead, just errored out with:
TypeError: __init__() missing 1 required positional argument
Instead use the subprocess module's base error which does accept string
messages. Everything that used to error out, still errors out, but now
they do so with a slightly prettier console message.
---
build/meson/tests/valgrindTest.py | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/build/meson/tests/valgrindTest.py b/build/meson/tests/valgrindTest.py
index 218f7458bbf..05d84878b9d 100644
--- a/build/meson/tests/valgrindTest.py
+++ b/build/meson/tests/valgrindTest.py
@@ -21,7 +21,7 @@ def valgrindTest(valgrind, datagen, fuzzer, zstd, fullbench):
if subprocess.call([*VALGRIND_ARGS, zstd],
stdout=subprocess.DEVNULL) == 0:
- raise subprocess.CalledProcessError('zstd without argument should have failed')
+ raise subprocess.SubprocessError('zstd without argument should have failed')
with subprocess.Popen([datagen, '-g80'], stdout=subprocess.PIPE) as p1, \
subprocess.Popen([*VALGRIND_ARGS, zstd, '-', '-c'],
@@ -30,7 +30,7 @@ def valgrindTest(valgrind, datagen, fuzzer, zstd, fullbench):
p1.stdout.close() # Allow p1 to receive a SIGPIPE if p2 exits.
p2.communicate()
if p2.returncode != 0:
- raise subprocess.CalledProcessError()
+ raise subprocess.SubprocessError()
with subprocess.Popen([datagen, '-g16KB'], stdout=subprocess.PIPE) as p1, \
subprocess.Popen([*VALGRIND_ARGS, zstd, '-vf', '-', '-c'],
@@ -39,7 +39,7 @@ def valgrindTest(valgrind, datagen, fuzzer, zstd, fullbench):
p1.stdout.close()
p2.communicate()
if p2.returncode != 0:
- raise subprocess.CalledProcessError()
+ raise subprocess.SubprocessError()
with tempfile.NamedTemporaryFile() as tmp_fd:
with subprocess.Popen([datagen, '-g2930KB'], stdout=subprocess.PIPE) as p1, \
@@ -48,7 +48,7 @@ def valgrindTest(valgrind, datagen, fuzzer, zstd, fullbench):
p1.stdout.close()
p2.communicate()
if p2.returncode != 0:
- raise subprocess.CalledProcessError()
+ raise subprocess.SubprocessError()
subprocess.check_call([*VALGRIND_ARGS, zstd, '-vdf', tmp_fd.name, '-c'],
stdout=subprocess.DEVNULL)
@@ -60,7 +60,7 @@ def valgrindTest(valgrind, datagen, fuzzer, zstd, fullbench):
p1.stdout.close()
p2.communicate()
if p2.returncode != 0:
- raise subprocess.CalledProcessError()
+ raise subprocess.SubprocessError()
subprocess.check_call([*VALGRIND_ARGS, fuzzer, '-T1mn', '-t1'])
subprocess.check_call([*VALGRIND_ARGS, fullbench, '-i1'])
From c01582dc8aee541171089fa2979c890752365104 Mon Sep 17 00:00:00 2001
From: Eli Schwartz
Date: Sun, 30 Jan 2022 22:38:04 -0500
Subject: [PATCH 073/472] travis CI: update meson image to one with a python
that isn't EOL
Currently the build errors out with:
```
ERROR: This script does not work on Python 3.6 The minimum supported Python version is 3.7. Please use https://bootstrap.pypa.io/pip/3.6/get-pip.py instead.
```
While in theory this advice could be followed to get a better pip on
xenial, Meson has now deprecated python 3.6 support too, and the next
(unreleased) version requires python 3.7
There are a couple solutions to this:
- hold the version of pip, allow pip to only install 3.6-compatible
versions of meson (effectively freezing meson going forward)
- install python 3.7 on xenial
- update to a 2-year-old image instead of a 4-year-old one
Option 3 is the simplest.
---
.travis.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 6a1295b4cee..34cf0e1ae9a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -92,8 +92,8 @@ matrix:
- make -C tests versionsTest
# meson dedicated test
- - name: Xenial (Meson + clang) # ~15mn
- dist: bionic
+ - name: Focal (Meson + clang) # ~15mn
+ dist: focal
language: cpp
compiler: clang
install:
From cc0657f27d81da8a7db3aa199d24a566b95c4dfe Mon Sep 17 00:00:00 2001
From: Yonatan Komornik <11005061+yoniko@users.noreply.github.com>
Date: Mon, 31 Jan 2022 15:43:41 -0800
Subject: [PATCH 074/472] AsyncIO compression part 2 - added async read and
asyncio to compression code (#3022)
* Compression asyncio:
- Added asyncio functionality for compression flow
- Added ReadPool for async reads, implemented in both comp and decomp flows
---
programs/README.md | 1 +
programs/fileio.c | 425 ++++++++++++++++++--------------------
programs/fileio_asyncio.c | 353 ++++++++++++++++++++++++++-----
programs/fileio_asyncio.h | 85 ++++++--
programs/fileio_common.h | 2 +-
programs/fileio_types.h | 4 +-
programs/zstdcli.c | 8 +-
tests/playTests.sh | 15 +-
8 files changed, 591 insertions(+), 302 deletions(-)
diff --git a/programs/README.md b/programs/README.md
index 5570f90c3b4..b88cf78d71a 100644
--- a/programs/README.md
+++ b/programs/README.md
@@ -164,6 +164,7 @@ Advanced arguments :
--filelist FILE : read list of files to operate upon from FILE
--output-dir-flat DIR : processed files are stored into DIR
--output-dir-mirror DIR : processed files are stored into DIR respecting original directory structure
+--[no-]asyncio : use asynchronous IO (default: enabled)
--[no-]check : during compression, add XXH64 integrity checksum to frame (default: enabled). If specified with -d, decompressor will ignore/validate checksums in compressed frame (default: validate).
-- : All arguments after "--" are treated as files
diff --git a/programs/fileio.c b/programs/fileio.c
index 64909b96401..502f69c1576 100644
--- a/programs/fileio.c
+++ b/programs/fileio.c
@@ -289,7 +289,7 @@ FIO_prefs_t* FIO_createPreferences(void)
ret->literalCompressionMode = ZSTD_ps_auto;
ret->excludeCompressedFiles = 0;
ret->allowBlockDevices = 0;
- ret->asyncIO = 0;
+ ret->asyncIO = AIO_supported();
return ret;
}
@@ -848,16 +848,12 @@ static int FIO_removeMultiFilesWarning(FIO_ctx_t* const fCtx, const FIO_prefs_t*
* Compression
************************************************************************/
typedef struct {
- FILE* srcFile;
- FILE* dstFile;
- void* srcBuffer;
- size_t srcBufferSize;
- void* dstBuffer;
- size_t dstBufferSize;
void* dictBuffer;
size_t dictBufferSize;
const char* dictFileName;
ZSTD_CStream* cctx;
+ WritePoolCtx_t *writeCtx;
+ ReadPoolCtx_t *readCtx;
} cRess_t;
/** ZSTD_cycleLog() :
@@ -906,9 +902,6 @@ static cRess_t FIO_createCResources(FIO_prefs_t* const prefs,
if (ress.cctx == NULL)
EXM_THROW(30, "allocation error (%s): can't create ZSTD_CCtx",
strerror(errno));
- ress.srcBufferSize = ZSTD_CStreamInSize();
- ress.srcBuffer = malloc(ress.srcBufferSize);
- ress.dstBufferSize = ZSTD_CStreamOutSize();
/* need to update memLimit before calling createDictBuffer
* because of memLimit check inside it */
@@ -916,10 +909,10 @@ static cRess_t FIO_createCResources(FIO_prefs_t* const prefs,
unsigned long long const ssSize = (unsigned long long)prefs->streamSrcSize;
FIO_adjustParamsForPatchFromMode(prefs, &comprParams, UTIL_getFileSize(dictFileName), ssSize > 0 ? ssSize : maxSrcFileSize, cLevel);
}
- ress.dstBuffer = malloc(ress.dstBufferSize);
ress.dictBufferSize = FIO_createDictBuffer(&ress.dictBuffer, dictFileName, prefs); /* works with dictFileName==NULL */
- if (!ress.srcBuffer || !ress.dstBuffer)
- EXM_THROW(31, "allocation error : not enough memory");
+
+ ress.writeCtx = AIO_WritePool_create(prefs, ZSTD_CStreamOutSize());
+ ress.readCtx = AIO_ReadPool_create(prefs, ZSTD_CStreamInSize());
/* Advanced parameters, including dictionary */
if (dictFileName && (ress.dictBuffer==NULL))
@@ -982,9 +975,9 @@ static cRess_t FIO_createCResources(FIO_prefs_t* const prefs,
static void FIO_freeCResources(const cRess_t* const ress)
{
- free(ress->srcBuffer);
- free(ress->dstBuffer);
free(ress->dictBuffer);
+ AIO_WritePool_free(ress->writeCtx);
+ AIO_ReadPool_free(ress->readCtx);
ZSTD_freeCStream(ress->cctx); /* never fails */
}
@@ -997,6 +990,7 @@ FIO_compressGzFrame(const cRess_t* ress, /* buffers & handlers are used, but no
{
unsigned long long inFileSize = 0, outFileSize = 0;
z_stream strm;
+ IOJob_t *writeJob = NULL;
if (compressionLevel > Z_BEST_COMPRESSION)
compressionLevel = Z_BEST_COMPRESSION;
@@ -1012,51 +1006,58 @@ FIO_compressGzFrame(const cRess_t* ress, /* buffers & handlers are used, but no
EXM_THROW(71, "zstd: %s: deflateInit2 error %d \n", srcFileName, ret);
} }
+ writeJob = AIO_WritePool_acquireJob(ress->writeCtx);
strm.next_in = 0;
strm.avail_in = 0;
- strm.next_out = (Bytef*)ress->dstBuffer;
- strm.avail_out = (uInt)ress->dstBufferSize;
+ strm.next_out = (Bytef*)writeJob->buffer;
+ strm.avail_out = (uInt)writeJob->bufferSize;
while (1) {
int ret;
if (strm.avail_in == 0) {
- size_t const inSize = fread(ress->srcBuffer, 1, ress->srcBufferSize, ress->srcFile);
- if (inSize == 0) break;
- inFileSize += inSize;
- strm.next_in = (z_const unsigned char*)ress->srcBuffer;
- strm.avail_in = (uInt)inSize;
+ AIO_ReadPool_fillBuffer(ress->readCtx, ZSTD_CStreamInSize());
+ if (ress->readCtx->srcBufferLoaded == 0) break;
+ inFileSize += ress->readCtx->srcBufferLoaded;
+ strm.next_in = (z_const unsigned char*)ress->readCtx->srcBuffer;
+ strm.avail_in = (uInt)ress->readCtx->srcBufferLoaded;
+ }
+
+ {
+ size_t const availBefore = strm.avail_in;
+ ret = deflate(&strm, Z_NO_FLUSH);
+ AIO_ReadPool_consumeBytes(ress->readCtx, availBefore - strm.avail_in);
}
- ret = deflate(&strm, Z_NO_FLUSH);
+
if (ret != Z_OK)
EXM_THROW(72, "zstd: %s: deflate error %d \n", srcFileName, ret);
- { size_t const cSize = ress->dstBufferSize - strm.avail_out;
+ { size_t const cSize = writeJob->bufferSize - strm.avail_out;
if (cSize) {
- if (fwrite(ress->dstBuffer, 1, cSize, ress->dstFile) != cSize)
- EXM_THROW(73, "Write error : cannot write to output file : %s ", strerror(errno));
+ writeJob->usedBufferSize = cSize;
+ AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
outFileSize += cSize;
- strm.next_out = (Bytef*)ress->dstBuffer;
- strm.avail_out = (uInt)ress->dstBufferSize;
- } }
+ strm.next_out = (Bytef*)writeJob->buffer;
+ strm.avail_out = (uInt)writeJob->bufferSize;
+ } }
if (srcFileSize == UTIL_FILESIZE_UNKNOWN) {
DISPLAYUPDATE(2, "\rRead : %u MB ==> %.2f%% ",
- (unsigned)(inFileSize>>20),
- (double)outFileSize/inFileSize*100)
+ (unsigned)(inFileSize>>20),
+ (double)outFileSize/inFileSize*100)
} else {
DISPLAYUPDATE(2, "\rRead : %u / %u MB ==> %.2f%% ",
- (unsigned)(inFileSize>>20), (unsigned)(srcFileSize>>20),
- (double)outFileSize/inFileSize*100);
- } }
+ (unsigned)(inFileSize>>20), (unsigned)(srcFileSize>>20),
+ (double)outFileSize/inFileSize*100);
+ } }
while (1) {
int const ret = deflate(&strm, Z_FINISH);
- { size_t const cSize = ress->dstBufferSize - strm.avail_out;
+ { size_t const cSize = writeJob->bufferSize - strm.avail_out;
if (cSize) {
- if (fwrite(ress->dstBuffer, 1, cSize, ress->dstFile) != cSize)
- EXM_THROW(75, "Write error : %s ", strerror(errno));
+ writeJob->usedBufferSize = cSize;
+ AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
outFileSize += cSize;
- strm.next_out = (Bytef*)ress->dstBuffer;
- strm.avail_out = (uInt)ress->dstBufferSize;
- } }
+ strm.next_out = (Bytef*)writeJob->buffer;
+ strm.avail_out = (uInt)writeJob->bufferSize;
+ } }
if (ret == Z_STREAM_END) break;
if (ret != Z_BUF_ERROR)
EXM_THROW(77, "zstd: %s: deflate error %d \n", srcFileName, ret);
@@ -1067,6 +1068,8 @@ FIO_compressGzFrame(const cRess_t* ress, /* buffers & handlers are used, but no
EXM_THROW(79, "zstd: %s: deflateEnd error %d \n", srcFileName, ret);
} }
*readsize = inFileSize;
+ AIO_WritePool_releaseIoJob(writeJob);
+ AIO_WritePool_sparseWriteEnd(ress->writeCtx);
return outFileSize;
}
#endif
@@ -1082,6 +1085,7 @@ FIO_compressLzmaFrame(cRess_t* ress,
lzma_stream strm = LZMA_STREAM_INIT;
lzma_action action = LZMA_RUN;
lzma_ret ret;
+ IOJob_t *writeJob = NULL;
if (compressionLevel < 0) compressionLevel = 0;
if (compressionLevel > 9) compressionLevel = 9;
@@ -1099,31 +1103,37 @@ FIO_compressLzmaFrame(cRess_t* ress,
EXM_THROW(83, "zstd: %s: lzma_easy_encoder error %d", srcFileName, ret);
}
+ writeJob =AIO_WritePool_acquireJob(ress->writeCtx);
+ strm.next_out = (Bytef*)writeJob->buffer;
+ strm.avail_out = (uInt)writeJob->bufferSize;
strm.next_in = 0;
strm.avail_in = 0;
- strm.next_out = (BYTE*)ress->dstBuffer;
- strm.avail_out = ress->dstBufferSize;
while (1) {
if (strm.avail_in == 0) {
- size_t const inSize = fread(ress->srcBuffer, 1, ress->srcBufferSize, ress->srcFile);
- if (inSize == 0) action = LZMA_FINISH;
+ size_t const inSize = AIO_ReadPool_fillBuffer(ress->readCtx, ZSTD_CStreamInSize());
+ if (ress->readCtx->srcBufferLoaded == 0) action = LZMA_FINISH;
inFileSize += inSize;
- strm.next_in = (BYTE const*)ress->srcBuffer;
- strm.avail_in = inSize;
+ strm.next_in = (BYTE const*)ress->readCtx->srcBuffer;
+ strm.avail_in = ress->readCtx->srcBufferLoaded;
+ }
+
+ {
+ size_t const availBefore = strm.avail_in;
+ ret = lzma_code(&strm, action);
+ AIO_ReadPool_consumeBytes(ress->readCtx, availBefore - strm.avail_in);
}
- ret = lzma_code(&strm, action);
if (ret != LZMA_OK && ret != LZMA_STREAM_END)
EXM_THROW(84, "zstd: %s: lzma_code encoding error %d", srcFileName, ret);
- { size_t const compBytes = ress->dstBufferSize - strm.avail_out;
+ { size_t const compBytes = writeJob->bufferSize - strm.avail_out;
if (compBytes) {
- if (fwrite(ress->dstBuffer, 1, compBytes, ress->dstFile) != compBytes)
- EXM_THROW(85, "Write error : %s", strerror(errno));
+ writeJob->usedBufferSize = compBytes;
+ AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
outFileSize += compBytes;
- strm.next_out = (BYTE*)ress->dstBuffer;
- strm.avail_out = ress->dstBufferSize;
+ strm.next_out = (Bytef*)writeJob->buffer;
+ strm.avail_out = writeJob->bufferSize;
} }
if (srcFileSize == UTIL_FILESIZE_UNKNOWN)
DISPLAYUPDATE(2, "\rRead : %u MB ==> %.2f%%",
@@ -1139,6 +1149,9 @@ FIO_compressLzmaFrame(cRess_t* ress,
lzma_end(&strm);
*readsize = inFileSize;
+ AIO_WritePool_releaseIoJob(writeJob);
+ AIO_WritePool_sparseWriteEnd(ress->writeCtx);
+
return outFileSize;
}
#endif
@@ -1164,15 +1177,18 @@ FIO_compressLz4Frame(cRess_t* ress,
LZ4F_preferences_t prefs;
LZ4F_compressionContext_t ctx;
+ IOJob_t* writeJob = AIO_WritePool_acquireJob(ress->writeCtx);
+
LZ4F_errorCode_t const errorCode = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
if (LZ4F_isError(errorCode))
EXM_THROW(31, "zstd: failed to create lz4 compression context");
memset(&prefs, 0, sizeof(prefs));
- assert(blockSize <= ress->srcBufferSize);
+ assert(blockSize <= ress->readCtx->base.jobBufferSize);
- prefs.autoFlush = 1;
+ /* autoflush off to mitigate a bug in lz4<=1.9.3 for compression level 12 */
+ prefs.autoFlush = 0;
prefs.compressionLevel = compressionLevel;
prefs.frameInfo.blockMode = LZ4F_blockLinked;
prefs.frameInfo.blockSizeID = LZ4F_max64KB;
@@ -1180,27 +1196,25 @@ 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);
+ assert(LZ4F_compressBound(blockSize, &prefs) <= writeJob->bufferSize);
{
- size_t readSize;
- size_t headerSize = LZ4F_compressBegin(ctx, ress->dstBuffer, ress->dstBufferSize, &prefs);
+ size_t headerSize = LZ4F_compressBegin(ctx, writeJob->buffer, writeJob->bufferSize, &prefs);
if (LZ4F_isError(headerSize))
EXM_THROW(33, "File header generation failed : %s",
LZ4F_getErrorName(headerSize));
- if (fwrite(ress->dstBuffer, 1, headerSize, ress->dstFile) != headerSize)
- EXM_THROW(34, "Write error : %s (cannot write header)", strerror(errno));
+ writeJob->usedBufferSize = headerSize;
+ AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
outFileSize += headerSize;
/* Read first block */
- readSize = fread(ress->srcBuffer, (size_t)1, (size_t)blockSize, ress->srcFile);
- inFileSize += readSize;
+ inFileSize += AIO_ReadPool_fillBuffer(ress->readCtx, blockSize);
/* Main Loop */
- while (readSize>0) {
- size_t const outSize = LZ4F_compressUpdate(ctx,
- ress->dstBuffer, ress->dstBufferSize,
- ress->srcBuffer, readSize, NULL);
+ while (ress->readCtx->srcBufferLoaded) {
+ size_t inSize = MIN(blockSize, ress->readCtx->srcBufferLoaded);
+ size_t const outSize = LZ4F_compressUpdate(ctx, writeJob->buffer, writeJob->bufferSize,
+ ress->readCtx->srcBuffer, inSize, NULL);
if (LZ4F_isError(outSize))
EXM_THROW(35, "zstd: %s: lz4 compression failed : %s",
srcFileName, LZ4F_getErrorName(outSize));
@@ -1216,33 +1230,29 @@ FIO_compressLz4Frame(cRess_t* ress,
}
/* Write Block */
- { size_t const sizeCheck = fwrite(ress->dstBuffer, 1, outSize, ress->dstFile);
- if (sizeCheck != outSize)
- EXM_THROW(36, "Write error : %s", strerror(errno));
- }
+ writeJob->usedBufferSize = outSize;
+ AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
/* Read next block */
- readSize = fread(ress->srcBuffer, (size_t)1, (size_t)blockSize, ress->srcFile);
- inFileSize += readSize;
+ AIO_ReadPool_consumeBytes(ress->readCtx, inSize);
+ inFileSize += AIO_ReadPool_fillBuffer(ress->readCtx, blockSize);
}
- if (ferror(ress->srcFile)) EXM_THROW(37, "Error reading %s ", srcFileName);
/* End of Stream mark */
- headerSize = LZ4F_compressEnd(ctx, ress->dstBuffer, ress->dstBufferSize, NULL);
+ headerSize = LZ4F_compressEnd(ctx, writeJob->buffer, writeJob->bufferSize, NULL);
if (LZ4F_isError(headerSize))
EXM_THROW(38, "zstd: %s: lz4 end of file generation failed : %s",
srcFileName, LZ4F_getErrorName(headerSize));
- { size_t const sizeCheck = fwrite(ress->dstBuffer, 1, headerSize, ress->dstFile);
- if (sizeCheck != headerSize)
- EXM_THROW(39, "Write error : %s (cannot write end of stream)",
- strerror(errno));
- }
+ writeJob->usedBufferSize = headerSize;
+ AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
outFileSize += headerSize;
}
*readsize = inFileSize;
LZ4F_freeCompressionContext(ctx);
+ AIO_WritePool_releaseIoJob(writeJob);
+ AIO_WritePool_sparseWriteEnd(ress->writeCtx);
return outFileSize;
}
@@ -1257,8 +1267,8 @@ FIO_compressZstdFrame(FIO_ctx_t* const fCtx,
int compressionLevel, U64* readsize)
{
cRess_t const ress = *ressPtr;
- FILE* const srcFile = ress.srcFile;
- FILE* const dstFile = ress.dstFile;
+ IOJob_t *writeJob = AIO_WritePool_acquireJob(ressPtr->writeCtx);
+
U64 compressedfilesize = 0;
ZSTD_EndDirective directive = ZSTD_e_continue;
U64 pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN;
@@ -1303,12 +1313,12 @@ FIO_compressZstdFrame(FIO_ctx_t* const fCtx,
do {
size_t stillToFlush;
/* Fill input Buffer */
- size_t const inSize = fread(ress.srcBuffer, (size_t)1, ress.srcBufferSize, srcFile);
- ZSTD_inBuffer inBuff = { ress.srcBuffer, inSize, 0 };
+ size_t const inSize = AIO_ReadPool_fillBuffer(ress.readCtx, ZSTD_CStreamInSize());
+ ZSTD_inBuffer inBuff = { ress.readCtx->srcBuffer, ress.readCtx->srcBufferLoaded, 0 };
DISPLAYLEVEL(6, "fread %u bytes from source \n", (unsigned)inSize);
*readsize += inSize;
- if ((inSize == 0) || (*readsize == fileSize))
+ if ((ress.readCtx->srcBufferLoaded == 0) || (*readsize == fileSize))
directive = ZSTD_e_end;
stillToFlush = 1;
@@ -1316,9 +1326,10 @@ FIO_compressZstdFrame(FIO_ctx_t* const fCtx,
|| (directive == ZSTD_e_end && stillToFlush != 0) ) {
size_t const oldIPos = inBuff.pos;
- ZSTD_outBuffer outBuff = { ress.dstBuffer, ress.dstBufferSize, 0 };
+ ZSTD_outBuffer outBuff= { writeJob->buffer, writeJob->bufferSize, 0 };
size_t const toFlushNow = ZSTD_toFlushNow(ress.cctx);
CHECK_V(stillToFlush, ZSTD_compressStream2(ress.cctx, &outBuff, &inBuff, directive));
+ AIO_ReadPool_consumeBytes(ress.readCtx, inBuff.pos - oldIPos);
/* count stats */
inputPresented++;
@@ -1327,12 +1338,10 @@ FIO_compressZstdFrame(FIO_ctx_t* const fCtx,
/* Write compressed stream */
DISPLAYLEVEL(6, "ZSTD_compress_generic(end:%u) => input pos(%u)<=(%u)size ; output generated %u bytes \n",
- (unsigned)directive, (unsigned)inBuff.pos, (unsigned)inBuff.size, (unsigned)outBuff.pos);
+ (unsigned)directive, (unsigned)inBuff.pos, (unsigned)inBuff.size, (unsigned)outBuff.pos);
if (outBuff.pos) {
- size_t const sizeCheck = fwrite(ress.dstBuffer, 1, outBuff.pos, dstFile);
- if (sizeCheck != outBuff.pos)
- EXM_THROW(25, "Write error : %s (cannot write compressed block)",
- strerror(errno));
+ writeJob->usedBufferSize = outBuff.pos;
+ AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
compressedfilesize += outBuff.pos;
}
@@ -1464,14 +1473,14 @@ FIO_compressZstdFrame(FIO_ctx_t* const fCtx,
} /* while ((inBuff.pos != inBuff.size) */
} while (directive != ZSTD_e_end);
- if (ferror(srcFile)) {
- EXM_THROW(26, "Read error : I/O error");
- }
if (fileSize != UTIL_FILESIZE_UNKNOWN && *readsize != fileSize) {
EXM_THROW(27, "Read error : Incomplete read : %llu / %llu B",
(unsigned long long)*readsize, (unsigned long long)fileSize);
}
+ AIO_WritePool_releaseIoJob(writeJob);
+ AIO_WritePool_sparseWriteEnd(ressPtr->writeCtx);
+
return compressedfilesize;
}
@@ -1572,7 +1581,7 @@ FIO_compressFilename_internal(FIO_ctx_t* const fCtx,
/*! FIO_compressFilename_dstFile() :
- * open dstFileName, or pass-through if ress.dstFile != NULL,
+ * open dstFileName, or pass-through if ress.file != NULL,
* then start compression with FIO_compressFilename_internal().
* Manages source removal (--rm) and file permissions transfer.
* note : ress.srcFile must be != NULL,
@@ -1591,8 +1600,9 @@ static int FIO_compressFilename_dstFile(FIO_ctx_t* const fCtx,
int result;
stat_t statbuf;
int transferMTime = 0;
- assert(ress.srcFile != NULL);
- if (ress.dstFile == NULL) {
+ FILE *dstFile;
+ assert(AIO_ReadPool_getFile(ress.readCtx) != NULL);
+ if (AIO_WritePool_getFile(ress.writeCtx) == NULL) {
int dstFilePermissions = DEFAULT_FILE_PERMISSIONS;
if ( strcmp (srcFileName, stdinmark)
&& strcmp (dstFileName, stdoutmark)
@@ -1604,8 +1614,9 @@ static int FIO_compressFilename_dstFile(FIO_ctx_t* const fCtx,
closeDstFile = 1;
DISPLAYLEVEL(6, "FIO_compressFilename_dstFile: opening dst: %s \n", dstFileName);
- ress.dstFile = FIO_openDstFile(fCtx, prefs, srcFileName, dstFileName, dstFilePermissions);
- if (ress.dstFile==NULL) return 1; /* could not open dstFileName */
+ dstFile = FIO_openDstFile(fCtx, prefs, srcFileName, dstFileName, dstFilePermissions);
+ if (dstFile==NULL) return 1; /* could not open dstFileName */
+ AIO_WritePool_setFile(ress.writeCtx, dstFile);
/* Must only be added after FIO_openDstFile() succeeds.
* Otherwise we may delete the destination file if it already exists,
* and the user presses Ctrl-C when asked if they wish to overwrite.
@@ -1616,13 +1627,10 @@ static int FIO_compressFilename_dstFile(FIO_ctx_t* const fCtx,
result = FIO_compressFilename_internal(fCtx, prefs, ress, dstFileName, srcFileName, compressionLevel);
if (closeDstFile) {
- FILE* const dstFile = ress.dstFile;
- ress.dstFile = NULL;
-
clearHandler();
DISPLAYLEVEL(6, "FIO_compressFilename_dstFile: closing dst: %s \n", dstFileName);
- if (fclose(dstFile)) { /* error closing dstFile */
+ if (AIO_WritePool_closeFile(ress.writeCtx)) { /* error closing file */
DISPLAYLEVEL(1, "zstd: %s: %s \n", dstFileName, strerror(errno));
result=1;
}
@@ -1668,6 +1676,7 @@ FIO_compressFilename_srcFile(FIO_ctx_t* const fCtx,
int compressionLevel)
{
int result;
+ FILE* srcFile;
DISPLAYLEVEL(6, "FIO_compressFilename_srcFile: %s \n", srcFileName);
/* ensure src is not a directory */
@@ -1691,13 +1700,13 @@ FIO_compressFilename_srcFile(FIO_ctx_t* const fCtx,
return 0;
}
- ress.srcFile = FIO_openSrcFile(prefs, srcFileName);
- if (ress.srcFile == NULL) return 1; /* srcFile could not be opened */
+ srcFile = FIO_openSrcFile(prefs, srcFileName);
+ if (srcFile == NULL) return 1; /* srcFile could not be opened */
+ AIO_ReadPool_setFile(ress.readCtx, srcFile);
result = FIO_compressFilename_dstFile(fCtx, prefs, ress, dstFileName, srcFileName, compressionLevel);
+ AIO_ReadPool_closeFile(ress.readCtx);
- fclose(ress.srcFile);
- ress.srcFile = NULL;
if ( prefs->removeSrcFile /* --rm */
&& result == 0 /* success */
&& strcmp(srcFileName, stdinmark) /* exception : don't erase stdin */
@@ -1844,23 +1853,24 @@ int FIO_compressMultipleFilenames(FIO_ctx_t* const fCtx,
/* init */
assert(outFileName != NULL || suffix != NULL);
if (outFileName != NULL) { /* output into a single destination (stdout typically) */
+ FILE *dstFile;
if (FIO_removeMultiFilesWarning(fCtx, prefs, outFileName, 1 /* displayLevelCutoff */)) {
FIO_freeCResources(&ress);
return 1;
}
- ress.dstFile = FIO_openDstFile(fCtx, prefs, NULL, outFileName, DEFAULT_FILE_PERMISSIONS);
- if (ress.dstFile == NULL) { /* could not open outFileName */
+ dstFile = FIO_openDstFile(fCtx, prefs, NULL, outFileName, DEFAULT_FILE_PERMISSIONS);
+ if (dstFile == NULL) { /* could not open outFileName */
error = 1;
} else {
+ AIO_WritePool_setFile(ress.writeCtx, dstFile);
for (; fCtx->currFileIdx < fCtx->nbFilesTotal; ++fCtx->currFileIdx) {
status = FIO_compressFilename_srcFile(fCtx, prefs, ress, outFileName, inFileNamesTable[fCtx->currFileIdx], compressionLevel);
if (!status) fCtx->nbFilesProcessed++;
error |= status;
}
- if (fclose(ress.dstFile))
+ if (AIO_WritePool_closeFile(ress.writeCtx))
EXM_THROW(29, "Write error (%s) : cannot properly close %s",
strerror(errno), outFileName);
- ress.dstFile = NULL;
}
} else {
if (outMirroredRootDirName)
@@ -1916,13 +1926,10 @@ int FIO_compressMultipleFilenames(FIO_ctx_t* const fCtx,
/* **************************************************************************
* Decompression
***************************************************************************/
-
typedef struct {
- void* srcBuffer;
- size_t srcBufferSize;
- size_t srcBufferLoaded;
ZSTD_DStream* dctx;
WritePoolCtx_t *writeCtx;
+ ReadPoolCtx_t *readCtx;
} dRess_t;
static dRess_t FIO_createDResources(FIO_prefs_t* const prefs, const char* dictFileName)
@@ -1940,11 +1947,6 @@ static dRess_t FIO_createDResources(FIO_prefs_t* const prefs, const char* dictFi
CHECK( ZSTD_DCtx_setMaxWindowSize(ress.dctx, prefs->memLimit) );
CHECK( ZSTD_DCtx_setParameter(ress.dctx, ZSTD_d_forceIgnoreChecksum, !prefs->checksumFlag));
- ress.srcBufferSize = ZSTD_DStreamInSize();
- ress.srcBuffer = malloc(ress.srcBufferSize);
- if (!ress.srcBuffer)
- EXM_THROW(61, "Allocation error : not enough memory");
-
/* dictionary */
{ void* dictBuffer;
size_t const dictBufferSize = FIO_createDictBuffer(&dictBuffer, dictFileName, prefs);
@@ -1953,6 +1955,7 @@ static dRess_t FIO_createDResources(FIO_prefs_t* const prefs, const char* dictFi
}
ress.writeCtx = AIO_WritePool_create(prefs, ZSTD_DStreamOutSize());
+ ress.readCtx = AIO_ReadPool_create(prefs, ZSTD_DStreamInSize());
return ress;
}
@@ -1960,47 +1963,31 @@ static dRess_t FIO_createDResources(FIO_prefs_t* const prefs, const char* dictFi
static void FIO_freeDResources(dRess_t ress)
{
CHECK( ZSTD_freeDStream(ress.dctx) );
- free(ress.srcBuffer);
AIO_WritePool_free(ress.writeCtx);
-}
-
-/* FIO_consumeDSrcBuffer:
- * Consumes len bytes from srcBuffer's start and moves the remaining data and srcBufferLoaded accordingly. */
-static void FIO_consumeDSrcBuffer(dRess_t *ress, size_t len) {
- assert(ress->srcBufferLoaded >= len);
- ress->srcBufferLoaded -= len;
- memmove(ress->srcBuffer, (char *)ress->srcBuffer + len, ress->srcBufferLoaded);
+ AIO_ReadPool_free(ress.readCtx);
}
/** FIO_passThrough() : just copy input into output, for compatibility with gzip -df mode
@return : 0 (no error) */
-static int FIO_passThrough(const FIO_prefs_t* const prefs,
- FILE* foutput, FILE* finput,
- void* buffer, size_t bufferSize,
- size_t alreadyLoaded)
+static int FIO_passThrough(dRess_t *ress)
{
- size_t const blockSize = MIN(64 KB, bufferSize);
- size_t readFromInput;
- unsigned storedSkips = 0;
-
- /* assumption : ress->srcBufferLoaded bytes already loaded and stored within buffer */
- { size_t const sizeCheck = fwrite(buffer, 1, alreadyLoaded, foutput);
- if (sizeCheck != alreadyLoaded) {
- DISPLAYLEVEL(1, "Pass-through write error : %s\n", strerror(errno));
- return 1;
- } }
-
- do {
- readFromInput = fread(buffer, 1, blockSize, finput);
- storedSkips = AIO_fwriteSparse(foutput, buffer, readFromInput, prefs, storedSkips);
- } while (readFromInput == blockSize);
- if (ferror(finput)) {
- DISPLAYLEVEL(1, "Pass-through read error : %s\n", strerror(errno));
- return 1;
+ size_t const blockSize = MIN(MIN(64 KB, ZSTD_DStreamInSize()), ZSTD_DStreamOutSize());
+ IOJob_t *writeJob = AIO_WritePool_acquireJob(ress->writeCtx);
+ AIO_ReadPool_fillBuffer(ress->readCtx, blockSize);
+
+ while(ress->readCtx->srcBufferLoaded) {
+ size_t writeSize;
+ writeSize = MIN(blockSize, ress->readCtx->srcBufferLoaded);
+ assert(writeSize <= writeJob->bufferSize);
+ memcpy(writeJob->buffer, ress->readCtx->srcBuffer, writeSize);
+ writeJob->usedBufferSize = writeSize;
+ AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
+ AIO_ReadPool_consumeBytes(ress->readCtx, writeSize);
+ AIO_ReadPool_fillBuffer(ress->readCtx, blockSize);
}
- assert(feof(finput));
-
- AIO_fwriteSparseEnd(prefs, foutput, storedSkips);
+ assert(ress->readCtx->reachedEof);
+ AIO_WritePool_releaseIoJob(writeJob);
+ AIO_WritePool_sparseWriteEnd(ress->writeCtx);
return 0;
}
@@ -2018,7 +2005,7 @@ FIO_zstdErrorHelp(const FIO_prefs_t* const prefs,
return;
/* Try to decode the frame header */
- err = ZSTD_getFrameHeader(&header, ress->srcBuffer, ress->srcBufferLoaded);
+ err = ZSTD_getFrameHeader(&header, ress->readCtx->srcBuffer, ress->readCtx->srcBufferLoaded);
if (err == 0) {
unsigned long long const windowSize = header.windowSize;
unsigned const windowLog = FIO_highbit64(windowSize) + ((windowSize & (windowSize - 1)) != 0);
@@ -2041,7 +2028,7 @@ FIO_zstdErrorHelp(const FIO_prefs_t* const prefs,
*/
#define FIO_ERROR_FRAME_DECODING ((unsigned long long)(-2))
static unsigned long long
-FIO_decompressZstdFrame(FIO_ctx_t* const fCtx, dRess_t* ress, FILE* finput,
+FIO_decompressZstdFrame(FIO_ctx_t* const fCtx, dRess_t* ress,
const FIO_prefs_t* const prefs,
const char* srcFileName,
U64 alreadyDecoded) /* for multi-frames streams */
@@ -2057,16 +2044,11 @@ FIO_decompressZstdFrame(FIO_ctx_t* const fCtx, dRess_t* ress, FILE* finput,
ZSTD_DCtx_reset(ress->dctx, ZSTD_reset_session_only);
/* Header loading : ensures ZSTD_getFrameHeader() will succeed */
- { size_t const toDecode = ZSTD_FRAMEHEADERSIZE_MAX;
- if (ress->srcBufferLoaded < toDecode) {
- size_t const toRead = toDecode - ress->srcBufferLoaded;
- void* const startPosition = (char*)ress->srcBuffer + ress->srcBufferLoaded;
- ress->srcBufferLoaded += fread(startPosition, 1, toRead, finput);
- } }
+ AIO_ReadPool_fillBuffer(ress->readCtx, ZSTD_FRAMEHEADERSIZE_MAX);
/* Main decompression Loop */
while (1) {
- ZSTD_inBuffer inBuff = { ress->srcBuffer, ress->srcBufferLoaded, 0 };
+ ZSTD_inBuffer inBuff = { ress->readCtx->srcBuffer, ress->readCtx->srcBufferLoaded, 0 };
ZSTD_outBuffer outBuff= { writeJob->buffer, writeJob->bufferSize, 0 };
size_t const readSizeHint = ZSTD_decompressStream(ress->dctx, &outBuff, &inBuff);
const int displayLevel = (g_display_prefs.progressSetting == FIO_ps_always) ? 1 : 2;
@@ -2088,7 +2070,7 @@ FIO_decompressZstdFrame(FIO_ctx_t* const fCtx, dRess_t* ress, FILE* finput,
if (srcFileNameSize > 18) {
const char* truncatedSrcFileName = srcFileName + srcFileNameSize - 15;
DISPLAYUPDATE(displayLevel, "\rDecompress: %2u/%2u files. Current: ...%s : %.*f%s... ",
- fCtx->currFileIdx+1, fCtx->nbFilesTotal, truncatedSrcFileName, hrs.precision, hrs.value, hrs.suffix);
+ fCtx->currFileIdx+1, fCtx->nbFilesTotal, truncatedSrcFileName, hrs.precision, hrs.value, hrs.suffix);
} else {
DISPLAYUPDATE(displayLevel, "\rDecompress: %2u/%2u files. Current: %s : %.*f%s... ",
fCtx->currFileIdx+1, fCtx->nbFilesTotal, srcFileName, hrs.precision, hrs.value, hrs.suffix);
@@ -2098,23 +2080,21 @@ FIO_decompressZstdFrame(FIO_ctx_t* const fCtx, dRess_t* ress, FILE* finput,
srcFileName, hrs.precision, hrs.value, hrs.suffix);
}
- FIO_consumeDSrcBuffer(ress, inBuff.pos);
+ AIO_ReadPool_consumeBytes(ress->readCtx, inBuff.pos);
if (readSizeHint == 0) break; /* end of frame */
/* Fill input buffer */
- { size_t const toDecode = MIN(readSizeHint, ress->srcBufferSize); /* support large skippable frames */
- if (ress->srcBufferLoaded < toDecode) {
- size_t const toRead = toDecode - ress->srcBufferLoaded; /* > 0 */
- void* const startPosition = (char*)ress->srcBuffer + ress->srcBufferLoaded;
- size_t const readSize = fread(startPosition, 1, toRead, finput);
+ { size_t const toDecode = MIN(readSizeHint, ZSTD_DStreamInSize()); /* support large skippable frames */
+ if (ress->readCtx->srcBufferLoaded < toDecode) {
+ size_t const readSize = AIO_ReadPool_fillBuffer(ress->readCtx, toDecode);
if (readSize==0) {
DISPLAYLEVEL(1, "%s : Read error (39) : premature end \n",
- srcFileName);
+ srcFileName);
+ AIO_WritePool_releaseIoJob(writeJob);
return FIO_ERROR_FRAME_DECODING;
}
- ress->srcBufferLoaded += readSize;
- } } }
+ } } }
AIO_WritePool_releaseIoJob(writeJob);
AIO_WritePool_sparseWriteEnd(ress->writeCtx);
@@ -2125,7 +2105,7 @@ FIO_decompressZstdFrame(FIO_ctx_t* const fCtx, dRess_t* ress, FILE* finput,
#ifdef ZSTD_GZDECOMPRESS
static unsigned long long
-FIO_decompressGzFrame(dRess_t* ress, FILE* srcFile, const char* srcFileName)
+FIO_decompressGzFrame(dRess_t* ress, const char* srcFileName)
{
unsigned long long outFileSize = 0;
z_stream strm;
@@ -2145,16 +2125,16 @@ FIO_decompressGzFrame(dRess_t* ress, FILE* srcFile, const char* srcFileName)
writeJob = AIO_WritePool_acquireJob(ress->writeCtx);
strm.next_out = (Bytef*)writeJob->buffer;
strm.avail_out = (uInt)writeJob->bufferSize;
- strm.avail_in = (uInt)ress->srcBufferLoaded;
- strm.next_in = (z_const unsigned char*)ress->srcBuffer;
+ strm.avail_in = (uInt)ress->readCtx->srcBufferLoaded;
+ strm.next_in = (z_const unsigned char*)ress->readCtx->srcBuffer;
for ( ; ; ) {
int ret;
if (strm.avail_in == 0) {
- ress->srcBufferLoaded = fread(ress->srcBuffer, 1, ress->srcBufferSize, srcFile);
- if (ress->srcBufferLoaded == 0) flush = Z_FINISH;
- strm.next_in = (z_const unsigned char*)ress->srcBuffer;
- strm.avail_in = (uInt)ress->srcBufferLoaded;
+ AIO_ReadPool_consumeAndRefill(ress->readCtx);
+ if (ress->readCtx->srcBufferLoaded == 0) flush = Z_FINISH;
+ strm.next_in = (z_const unsigned char*)ress->readCtx->srcBuffer;
+ strm.avail_in = (uInt)ress->readCtx->srcBufferLoaded;
}
ret = inflate(&strm, flush);
if (ret == Z_BUF_ERROR) {
@@ -2177,7 +2157,7 @@ FIO_decompressGzFrame(dRess_t* ress, FILE* srcFile, const char* srcFileName)
if (ret == Z_STREAM_END) break;
}
- FIO_consumeDSrcBuffer(ress, ress->srcBufferLoaded - strm.avail_in);
+ AIO_ReadPool_consumeBytes(ress->readCtx, ress->readCtx->srcBufferLoaded - strm.avail_in);
if ( (inflateEnd(&strm) != Z_OK) /* release resources ; error detected */
&& (decodingError==0) ) {
@@ -2192,7 +2172,7 @@ FIO_decompressGzFrame(dRess_t* ress, FILE* srcFile, const char* srcFileName)
#ifdef ZSTD_LZMADECOMPRESS
static unsigned long long
-FIO_decompressLzmaFrame(dRess_t* ress, FILE* srcFile,
+FIO_decompressLzmaFrame(dRess_t* ress,
const char* srcFileName, int plain_lzma)
{
unsigned long long outFileSize = 0;
@@ -2220,16 +2200,16 @@ FIO_decompressLzmaFrame(dRess_t* ress, FILE* srcFile,
writeJob = AIO_WritePool_acquireJob(ress->writeCtx);
strm.next_out = (Bytef*)writeJob->buffer;
strm.avail_out = (uInt)writeJob->bufferSize;
- strm.next_in = (BYTE const*)ress->srcBuffer;
- strm.avail_in = ress->srcBufferLoaded;
+ strm.next_in = (BYTE const*)ress->readCtx->srcBuffer;
+ strm.avail_in = ress->readCtx->srcBufferLoaded;
for ( ; ; ) {
lzma_ret ret;
if (strm.avail_in == 0) {
- ress->srcBufferLoaded = fread(ress->srcBuffer, 1, ress->srcBufferSize, srcFile);
- if (ress->srcBufferLoaded == 0) action = LZMA_FINISH;
- strm.next_in = (BYTE const*)ress->srcBuffer;
- strm.avail_in = ress->srcBufferLoaded;
+ AIO_ReadPool_consumeAndRefill(ress->readCtx);
+ if (ress->readCtx->srcBufferLoaded == 0) action = LZMA_FINISH;
+ strm.next_in = (BYTE const*)ress->readCtx->srcBuffer;
+ strm.avail_in = ress->readCtx->srcBufferLoaded;
}
ret = lzma_code(&strm, action);
@@ -2253,7 +2233,7 @@ FIO_decompressLzmaFrame(dRess_t* ress, FILE* srcFile,
if (ret == LZMA_STREAM_END) break;
}
- FIO_consumeDSrcBuffer(ress, ress->srcBufferLoaded - strm.avail_in);
+ AIO_ReadPool_consumeBytes(ress->readCtx, ress->readCtx->srcBufferLoaded - strm.avail_in);
lzma_end(&strm);
AIO_WritePool_releaseIoJob(writeJob);
AIO_WritePool_sparseWriteEnd(ress->writeCtx);
@@ -2263,8 +2243,7 @@ FIO_decompressLzmaFrame(dRess_t* ress, FILE* srcFile,
#ifdef ZSTD_LZ4DECOMPRESS
static unsigned long long
-FIO_decompressLz4Frame(dRess_t* ress, FILE* srcFile,
- const char* srcFileName)
+FIO_decompressLz4Frame(dRess_t* ress, const char* srcFileName)
{
unsigned long long filesize = 0;
LZ4F_errorCode_t nextToLoad = 4;
@@ -2282,34 +2261,27 @@ FIO_decompressLz4Frame(dRess_t* ress, FILE* srcFile,
/* Main Loop */
for (;nextToLoad;) {
- size_t readSize;
size_t pos = 0;
size_t decodedBytes = writeJob->bufferSize;
int fullBufferDecoded = 0;
/* Read input */
- nextToLoad = MIN(nextToLoad, ress->srcBufferSize-ress->srcBufferLoaded);
- readSize = fread((char *)ress->srcBuffer + ress->srcBufferLoaded, 1, nextToLoad, srcFile);
- if(!readSize && ferror(srcFile)) {
- DISPLAYLEVEL(1, "zstd: %s: read error \n", srcFileName);
- decodingError=1;
- break;
- }
- if(!readSize && !ress->srcBufferLoaded) break; /* reached end of file */
- ress->srcBufferLoaded += readSize;
+ AIO_ReadPool_fillBuffer(ress->readCtx, nextToLoad);
+ if(!ress->readCtx->srcBufferLoaded) break; /* reached end of file */
- while ((pos < ress->srcBufferLoaded) || fullBufferDecoded) { /* still to read, or still to flush */
+ while ((pos < ress->readCtx->srcBufferLoaded) || fullBufferDecoded) { /* still to read, or still to flush */
/* Decode Input (at least partially) */
- size_t remaining = ress->srcBufferLoaded - pos;
+ size_t remaining = ress->readCtx->srcBufferLoaded - pos;
decodedBytes = writeJob->bufferSize;
- nextToLoad = LZ4F_decompress(dCtx, writeJob->buffer, &decodedBytes, (char*)(ress->srcBuffer)+pos, &remaining, NULL);
+ nextToLoad = LZ4F_decompress(dCtx, writeJob->buffer, &decodedBytes, (char*)(ress->readCtx->srcBuffer)+pos,
+ &remaining, NULL);
if (LZ4F_isError(nextToLoad)) {
DISPLAYLEVEL(1, "zstd: %s: lz4 decompression error : %s \n",
srcFileName, LZ4F_getErrorName(nextToLoad));
decodingError = 1; nextToLoad = 0; break;
}
pos += remaining;
- assert(pos <= ress->srcBufferLoaded);
+ assert(pos <= ress->readCtx->srcBufferLoaded);
fullBufferDecoded = decodedBytes == writeJob->bufferSize;
/* Write Block */
@@ -2324,7 +2296,7 @@ FIO_decompressLz4Frame(dRess_t* ress, FILE* srcFile,
if (!nextToLoad) break;
}
- FIO_consumeDSrcBuffer(ress, pos);
+ AIO_ReadPool_consumeBytes(ress->readCtx, pos);
}
if (nextToLoad!=0) {
DISPLAYLEVEL(1, "zstd: %s: unfinished lz4 stream \n", srcFileName);
@@ -2348,23 +2320,20 @@ FIO_decompressLz4Frame(dRess_t* ress, FILE* srcFile,
* 1 : error
*/
static int FIO_decompressFrames(FIO_ctx_t* const fCtx,
- dRess_t ress, FILE* srcFile,
- const FIO_prefs_t* const prefs,
- const char* dstFileName, const char* srcFileName)
+ dRess_t ress, const FIO_prefs_t* const prefs,
+ const char* dstFileName, const char* srcFileName)
{
unsigned readSomething = 0;
unsigned long long filesize = 0;
- assert(srcFile != NULL);
/* for each frame */
for ( ; ; ) {
/* check magic number -> version */
size_t const toRead = 4;
- const BYTE* const buf = (const BYTE*)ress.srcBuffer;
- if (ress.srcBufferLoaded < toRead) /* load up to 4 bytes for header */
- ress.srcBufferLoaded += fread((char*)ress.srcBuffer + ress.srcBufferLoaded,
- (size_t)1, toRead - ress.srcBufferLoaded, srcFile);
- if (ress.srcBufferLoaded==0) {
+ const BYTE* buf;
+ AIO_ReadPool_fillBuffer(ress.readCtx, toRead);
+ buf = (const BYTE*)ress.readCtx->srcBuffer;
+ if (ress.readCtx->srcBufferLoaded==0) {
if (readSomething==0) { /* srcFile is empty (which is invalid) */
DISPLAYLEVEL(1, "zstd: %s: unexpected end of file \n", srcFileName);
return 1;
@@ -2372,17 +2341,17 @@ static int FIO_decompressFrames(FIO_ctx_t* const fCtx,
break; /* no more input */
}
readSomething = 1; /* there is at least 1 byte in srcFile */
- if (ress.srcBufferLoaded < toRead) {
+ if (ress.readCtx->srcBufferLoaded < toRead) {
DISPLAYLEVEL(1, "zstd: %s: unknown header \n", srcFileName);
return 1;
}
- if (ZSTD_isFrame(buf, ress.srcBufferLoaded)) {
- unsigned long long const frameSize = FIO_decompressZstdFrame(fCtx, &ress, srcFile, prefs, srcFileName, filesize);
+ if (ZSTD_isFrame(buf, ress.readCtx->srcBufferLoaded)) {
+ unsigned long long const frameSize = FIO_decompressZstdFrame(fCtx, &ress, prefs, srcFileName, filesize);
if (frameSize == FIO_ERROR_FRAME_DECODING) return 1;
filesize += frameSize;
} else if (buf[0] == 31 && buf[1] == 139) { /* gz magic number */
#ifdef ZSTD_GZDECOMPRESS
- unsigned long long const frameSize = FIO_decompressGzFrame(&ress, srcFile, srcFileName);
+ unsigned long long const frameSize = FIO_decompressGzFrame(&ress, srcFileName);
if (frameSize == FIO_ERROR_FRAME_DECODING) return 1;
filesize += frameSize;
#else
@@ -2392,7 +2361,7 @@ static int FIO_decompressFrames(FIO_ctx_t* const fCtx,
} else if ((buf[0] == 0xFD && buf[1] == 0x37) /* xz magic number */
|| (buf[0] == 0x5D && buf[1] == 0x00)) { /* lzma header (no magic number) */
#ifdef ZSTD_LZMADECOMPRESS
- unsigned long long const frameSize = FIO_decompressLzmaFrame(&ress, srcFile, srcFileName, buf[0] != 0xFD);
+ unsigned long long const frameSize = FIO_decompressLzmaFrame(&ress, srcFileName, buf[0] != 0xFD);
if (frameSize == FIO_ERROR_FRAME_DECODING) return 1;
filesize += frameSize;
#else
@@ -2401,7 +2370,7 @@ static int FIO_decompressFrames(FIO_ctx_t* const fCtx,
#endif
} else if (MEM_readLE32(buf) == LZ4_MAGICNUMBER) {
#ifdef ZSTD_LZ4DECOMPRESS
- unsigned long long const frameSize = FIO_decompressLz4Frame(&ress, srcFile, srcFileName);
+ unsigned long long const frameSize = FIO_decompressLz4Frame(&ress, srcFileName);
if (frameSize == FIO_ERROR_FRAME_DECODING) return 1;
filesize += frameSize;
#else
@@ -2409,10 +2378,7 @@ static int FIO_decompressFrames(FIO_ctx_t* const fCtx,
return 1;
#endif
} else if ((prefs->overwrite) && !strcmp (dstFileName, stdoutmark)) { /* pass-through mode */
- return FIO_passThrough(prefs,
- AIO_WritePool_getFile(ress.writeCtx), srcFile,
- ress.srcBuffer, ress.srcBufferSize,
- ress.srcBufferLoaded);
+ return FIO_passThrough(&ress);
} else {
DISPLAYLEVEL(1, "zstd: %s: unsupported format \n", srcFileName);
return 1;
@@ -2432,15 +2398,14 @@ static int FIO_decompressFrames(FIO_ctx_t* const fCtx,
}
/** FIO_decompressDstFile() :
- open `dstFileName`,
- or path-through if ress.dstFile is already != 0,
+ open `dstFileName`, or pass-through if writeCtx's file is already != 0,
then start decompression process (FIO_decompressFrames()).
@return : 0 : OK
1 : operation aborted
*/
static int FIO_decompressDstFile(FIO_ctx_t* const fCtx,
FIO_prefs_t* const prefs,
- dRess_t ress, FILE* srcFile,
+ dRess_t ress,
const char* dstFileName, const char* srcFileName)
{
int result;
@@ -2472,7 +2437,7 @@ static int FIO_decompressDstFile(FIO_ctx_t* const fCtx,
addHandler(dstFileName);
}
- result = FIO_decompressFrames(fCtx, ress, srcFile, prefs, dstFileName, srcFileName);
+ result = FIO_decompressFrames(fCtx, ress, prefs, dstFileName, srcFileName);
if (releaseDstFile) {
clearHandler();
@@ -2513,9 +2478,11 @@ static int FIO_decompressSrcFile(FIO_ctx_t* const fCtx, FIO_prefs_t* const prefs
srcFile = FIO_openSrcFile(prefs, srcFileName);
if (srcFile==NULL) return 1;
- ress.srcBufferLoaded = 0;
+ AIO_ReadPool_setFile(ress.readCtx, srcFile);
+
+ result = FIO_decompressDstFile(fCtx, prefs, ress, dstFileName, srcFileName);
- result = FIO_decompressDstFile(fCtx, prefs, ress, srcFile, dstFileName, srcFileName);
+ AIO_ReadPool_setFile(ress.readCtx, NULL);
/* Close file */
if (fclose(srcFile)) {
diff --git a/programs/fileio_asyncio.c b/programs/fileio_asyncio.c
index 868720a1da2..332292bbc1b 100644
--- a/programs/fileio_asyncio.c
+++ b/programs/fileio_asyncio.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under both the BSD-style license (found in the
@@ -29,7 +29,8 @@
/** AIO_fwriteSparse() :
* @return : storedSkips,
* argument for next call to AIO_fwriteSparse() or AIO_fwriteSparseEnd() */
-unsigned AIO_fwriteSparse(FILE* file,
+static unsigned
+AIO_fwriteSparse(FILE* file,
const void* buffer, size_t bufferSize,
const FIO_prefs_t* const prefs,
unsigned storedSkips)
@@ -45,7 +46,7 @@ unsigned AIO_fwriteSparse(FILE* file,
if (!prefs->sparseFileSupport) { /* normal write */
size_t const sizeCheck = fwrite(buffer, 1, bufferSize, file);
if (sizeCheck != bufferSize)
- EXM_THROW(70, "Write error : cannot write decoded block : %s",
+ EXM_THROW(70, "Write error : cannot write block : %s",
strerror(errno));
return 0;
}
@@ -77,7 +78,7 @@ unsigned AIO_fwriteSparse(FILE* file,
storedSkips = 0;
/* write the rest */
if (fwrite(ptrT + nb0T, sizeof(size_t), nbNon0ST, file) != nbNon0ST)
- EXM_THROW(93, "Write error : cannot write decoded block : %s",
+ EXM_THROW(93, "Write error : cannot write block : %s",
strerror(errno));
}
ptrT += seg0SizeT;
@@ -106,7 +107,8 @@ unsigned AIO_fwriteSparse(FILE* file,
return storedSkips;
}
-void AIO_fwriteSparseEnd(const FIO_prefs_t* const prefs, FILE* file, unsigned storedSkips)
+static void
+AIO_fwriteSparseEnd(const FIO_prefs_t* const prefs, FILE* file, unsigned storedSkips)
{
if (prefs->testMode) assert(storedSkips == 0);
if (storedSkips>0) {
@@ -127,17 +129,25 @@ void AIO_fwriteSparseEnd(const FIO_prefs_t* const prefs, FILE* file, unsigned st
* AsyncIO functionality
************************************************************************/
+/* AIO_supported:
+ * Returns 1 if AsyncIO is supported on the system, 0 otherwise. */
+int AIO_supported(void) {
+#ifdef ZSTD_MULTITHREAD
+ return 1;
+#else
+ return 0;
+#endif
+}
+
/* ***********************************
* General IoPool implementation
*************************************/
static IOJob_t *AIO_IOPool_createIoJob(IOPoolCtx_t *ctx, size_t bufferSize) {
- void *buffer;
- IOJob_t *job;
- job = (IOJob_t*) malloc(sizeof(IOJob_t));
- buffer = malloc(bufferSize);
+ IOJob_t* const job = (IOJob_t*) malloc(sizeof(IOJob_t));
+ void* const buffer = malloc(bufferSize);
if(!job || !buffer)
- EXM_THROW(101, "Allocation error : not enough memory");
+ EXM_THROW(101, "Allocation error : not enough memory");
job->buffer = buffer;
job->bufferSize = bufferSize;
job->usedBufferSize = 0;
@@ -151,49 +161,47 @@ static IOJob_t *AIO_IOPool_createIoJob(IOPoolCtx_t *ctx, size_t bufferSize) {
/* AIO_IOPool_createThreadPool:
* Creates a thread pool and a mutex for threaded IO pool.
* Displays warning if asyncio is requested but MT isn't available. */
-static void AIO_IOPool_createThreadPool(IOPoolCtx_t *ctx, const FIO_prefs_t *prefs) {
+static void AIO_IOPool_createThreadPool(IOPoolCtx_t* ctx, const FIO_prefs_t* prefs) {
ctx->threadPool = NULL;
if(prefs->asyncIO) {
if (ZSTD_pthread_mutex_init(&ctx->ioJobsMutex, NULL))
- EXM_THROW(102,"Failed creating write availableJobs mutex");
+ EXM_THROW(102,"Failed creating write availableJobs mutex");
/* We want MAX_IO_JOBS-2 queue items because we need to always have 1 free buffer to
* decompress into and 1 buffer that's actively written to disk and owned by the writing thread. */
assert(MAX_IO_JOBS >= 2);
ctx->threadPool = POOL_create(1, MAX_IO_JOBS - 2);
if (!ctx->threadPool)
- EXM_THROW(104, "Failed creating writer thread pool");
+ EXM_THROW(104, "Failed creating writer thread pool");
}
}
/* AIO_IOPool_init:
* Allocates and sets and a new write pool including its included availableJobs. */
-static void AIO_IOPool_init(IOPoolCtx_t *ctx, FIO_prefs_t* const prefs, POOL_function poolFunction, size_t bufferSize) {
+static void AIO_IOPool_init(IOPoolCtx_t* ctx, const FIO_prefs_t* prefs, POOL_function poolFunction, size_t bufferSize) {
int i;
AIO_IOPool_createThreadPool(ctx, prefs);
ctx->prefs = prefs;
ctx->poolFunction = poolFunction;
- ctx->totalIoJobs = ctx->threadPool ? MAX_IO_JOBS : 1;
+ ctx->totalIoJobs = ctx->threadPool ? MAX_IO_JOBS : 2;
ctx->availableJobsCount = ctx->totalIoJobs;
for(i=0; i < ctx->availableJobsCount; i++) {
ctx->availableJobs[i] = AIO_IOPool_createIoJob(ctx, bufferSize);
}
+ ctx->jobBufferSize = bufferSize;
ctx->file = NULL;
}
/* AIO_IOPool_releaseIoJob:
* Releases an acquired job back to the pool. Doesn't execute the job. */
-static void AIO_IOPool_releaseIoJob(IOJob_t *job) {
- IOPoolCtx_t *ctx = (IOPoolCtx_t *) job->ctx;
- if(ctx->threadPool) {
+static void AIO_IOPool_releaseIoJob(IOJob_t* job) {
+ IOPoolCtx_t* const ctx = (IOPoolCtx_t *) job->ctx;
+ if(ctx->threadPool)
ZSTD_pthread_mutex_lock(&ctx->ioJobsMutex);
- assert(ctx->availableJobsCount < MAX_IO_JOBS);
- ctx->availableJobs[ctx->availableJobsCount++] = job;
+ assert(ctx->availableJobsCount < ctx->totalIoJobs);
+ ctx->availableJobs[ctx->availableJobsCount++] = job;
+ if(ctx->threadPool)
ZSTD_pthread_mutex_unlock(&ctx->ioJobsMutex);
- } else {
- assert(ctx->availableJobsCount == 0);
- ctx->availableJobsCount++;
- }
}
/* AIO_IOPool_join:
@@ -225,19 +233,15 @@ static void AIO_IOPool_destroy(IOPoolCtx_t* ctx) {
/* AIO_IOPool_acquireJob:
* Returns an available io job to be used for a future io. */
-static IOJob_t* AIO_IOPool_acquireJob(IOPoolCtx_t *ctx) {
+static IOJob_t* AIO_IOPool_acquireJob(IOPoolCtx_t* ctx) {
IOJob_t *job;
assert(ctx->file != NULL || ctx->prefs->testMode);
- if(ctx->threadPool) {
+ if(ctx->threadPool)
ZSTD_pthread_mutex_lock(&ctx->ioJobsMutex);
- assert(ctx->availableJobsCount > 0);
- job = (IOJob_t*) ctx->availableJobs[--ctx->availableJobsCount];
+ assert(ctx->availableJobsCount > 0);
+ job = (IOJob_t*) ctx->availableJobs[--ctx->availableJobsCount];
+ if(ctx->threadPool)
ZSTD_pthread_mutex_unlock(&ctx->ioJobsMutex);
- } else {
- assert(ctx->availableJobsCount == 1);
- ctx->availableJobsCount--;
- job = (IOJob_t*)ctx->availableJobs[0];
- }
job->usedBufferSize = 0;
job->file = ctx->file;
job->offset = 0;
@@ -249,22 +253,22 @@ static IOJob_t* AIO_IOPool_acquireJob(IOPoolCtx_t *ctx) {
* Sets the destination file for future files in the pool.
* Requires completion of all queues write jobs and release of all otherwise acquired jobs.
* Also requires ending of sparse write if a previous file was used in sparse mode. */
-static void AIO_IOPool_setFile(IOPoolCtx_t *ctx, FILE* file) {
+static void AIO_IOPool_setFile(IOPoolCtx_t* ctx, FILE* file) {
assert(ctx!=NULL);
AIO_IOPool_join(ctx);
assert(ctx->availableJobsCount == ctx->totalIoJobs);
ctx->file = file;
}
-static FILE* AIO_IOPool_getFile(IOPoolCtx_t *ctx) {
+static FILE* AIO_IOPool_getFile(const IOPoolCtx_t* ctx) {
return ctx->file;
}
/* AIO_IOPool_enqueueJob:
* Enqueues an io job for execution.
* The queued job shouldn't be used directly after queueing it. */
-static void AIO_IOPool_enqueueJob(IOJob_t *job) {
- IOPoolCtx_t* ctx = (IOPoolCtx_t *)job->ctx;
+static void AIO_IOPool_enqueueJob(IOJob_t* job) {
+ IOPoolCtx_t* const ctx = (IOPoolCtx_t *)job->ctx;
if(ctx->threadPool)
POOL_add(ctx->threadPool, ctx->poolFunction, job);
else
@@ -277,7 +281,7 @@ static void AIO_IOPool_enqueueJob(IOJob_t *job) {
/* AIO_WritePool_acquireJob:
* Returns an available write job to be used for a future write. */
-IOJob_t* AIO_WritePool_acquireJob(WritePoolCtx_t *ctx) {
+IOJob_t* AIO_WritePool_acquireJob(WritePoolCtx_t* ctx) {
return AIO_IOPool_acquireJob(&ctx->base);
}
@@ -294,7 +298,7 @@ void AIO_WritePool_enqueueAndReacquireWriteJob(IOJob_t **job) {
/* AIO_WritePool_sparseWriteEnd:
* Ends sparse writes to the current file.
* Blocks on completion of all current write jobs before executing. */
-void AIO_WritePool_sparseWriteEnd(WritePoolCtx_t *ctx) {
+void AIO_WritePool_sparseWriteEnd(WritePoolCtx_t* ctx) {
assert(ctx != NULL);
if(ctx->base.threadPool)
POOL_joinJobs(ctx->base.threadPool);
@@ -306,28 +310,28 @@ void AIO_WritePool_sparseWriteEnd(WritePoolCtx_t *ctx) {
* Sets the destination file for future writes in the pool.
* Requires completion of all queues write jobs and release of all otherwise acquired jobs.
* Also requires ending of sparse write if a previous file was used in sparse mode. */
-void AIO_WritePool_setFile(WritePoolCtx_t *ctx, FILE* file) {
+void AIO_WritePool_setFile(WritePoolCtx_t* ctx, FILE* file) {
AIO_IOPool_setFile(&ctx->base, file);
assert(ctx->storedSkips == 0);
}
/* AIO_WritePool_getFile:
* Returns the file the writePool is currently set to write to. */
-FILE* AIO_WritePool_getFile(WritePoolCtx_t *ctx) {
+FILE* AIO_WritePool_getFile(const WritePoolCtx_t* ctx) {
return AIO_IOPool_getFile(&ctx->base);
}
/* AIO_WritePool_releaseIoJob:
* Releases an acquired job back to the pool. Doesn't execute the job. */
-void AIO_WritePool_releaseIoJob(IOJob_t *job) {
+void AIO_WritePool_releaseIoJob(IOJob_t* job) {
AIO_IOPool_releaseIoJob(job);
}
/* AIO_WritePool_closeFile:
* Ends sparse write and closes the writePool's current file and sets the file to NULL.
* Requires completion of all queues write jobs and release of all otherwise acquired jobs. */
-int AIO_WritePool_closeFile(WritePoolCtx_t *ctx) {
- FILE *dstFile = ctx->base.file;
+int AIO_WritePool_closeFile(WritePoolCtx_t* ctx) {
+ FILE* const dstFile = ctx->base.file;
assert(dstFile!=NULL || ctx->base.prefs->testMode!=0);
AIO_WritePool_sparseWriteEnd(ctx);
AIO_IOPool_setFile(&ctx->base, NULL);
@@ -337,16 +341,16 @@ int AIO_WritePool_closeFile(WritePoolCtx_t *ctx) {
/* AIO_WritePool_executeWriteJob:
* Executes a write job synchronously. Can be used as a function for a thread pool. */
static void AIO_WritePool_executeWriteJob(void* opaque){
- IOJob_t* job = (IOJob_t*) opaque;
- WritePoolCtx_t* ctx = (WritePoolCtx_t*) job->ctx;
+ IOJob_t* const job = (IOJob_t*) opaque;
+ WritePoolCtx_t* const ctx = (WritePoolCtx_t*) job->ctx;
ctx->storedSkips = AIO_fwriteSparse(job->file, job->buffer, job->usedBufferSize, ctx->base.prefs, ctx->storedSkips);
AIO_IOPool_releaseIoJob(job);
}
/* AIO_WritePool_create:
* Allocates and sets and a new write pool including its included jobs. */
-WritePoolCtx_t* AIO_WritePool_create(FIO_prefs_t* const prefs, size_t bufferSize) {
- WritePoolCtx_t* ctx = (WritePoolCtx_t*) malloc(sizeof(WritePoolCtx_t));
+WritePoolCtx_t* AIO_WritePool_create(const FIO_prefs_t* prefs, size_t bufferSize) {
+ WritePoolCtx_t* const ctx = (WritePoolCtx_t*) malloc(sizeof(WritePoolCtx_t));
if(!ctx) EXM_THROW(100, "Allocation error : not enough memory");
AIO_IOPool_init(&ctx->base, prefs, AIO_WritePool_executeWriteJob, bufferSize);
ctx->storedSkips = 0;
@@ -363,3 +367,256 @@ void AIO_WritePool_free(WritePoolCtx_t* ctx) {
assert(ctx->storedSkips==0);
free(ctx);
}
+
+
+/* ***********************************
+ * ReadPool implementation
+ *************************************/
+static void AIO_ReadPool_releaseAllCompletedJobs(ReadPoolCtx_t* ctx) {
+ int i;
+ for(i=0; icompletedJobsCount; i++) {
+ IOJob_t* job = (IOJob_t*) ctx->completedJobs[i];
+ AIO_IOPool_releaseIoJob(job);
+ }
+ ctx->completedJobsCount = 0;
+}
+
+static void AIO_ReadPool_addJobToCompleted(IOJob_t* job) {
+ ReadPoolCtx_t* const ctx = (ReadPoolCtx_t *)job->ctx;
+ if(ctx->base.threadPool)
+ ZSTD_pthread_mutex_lock(&ctx->base.ioJobsMutex);
+ assert(ctx->completedJobsCount < MAX_IO_JOBS);
+ ctx->completedJobs[ctx->completedJobsCount++] = job;
+ if(ctx->base.threadPool) {
+ ZSTD_pthread_cond_signal(&ctx->jobCompletedCond);
+ ZSTD_pthread_mutex_unlock(&ctx->base.ioJobsMutex);
+ }
+}
+
+/* AIO_ReadPool_findNextWaitingOffsetCompletedJob_locked:
+ * Looks through the completed jobs for a job matching the waitingOnOffset and returns it,
+ * if job wasn't found returns NULL.
+ * IMPORTANT: assumes ioJobsMutex is locked. */
+static IOJob_t* AIO_ReadPool_findNextWaitingOffsetCompletedJob_locked(ReadPoolCtx_t* ctx) {
+ IOJob_t *job = NULL;
+ int i;
+ /* This implementation goes through all completed jobs and looks for the one matching the next offset.
+ * While not strictly needed for a single threaded reader implementation (as in such a case we could expect
+ * reads to be completed in order) this implementation was chosen as it better fits other asyncio
+ * interfaces (such as io_uring) that do not provide promises regarding order of completion. */
+ for (i=0; icompletedJobsCount; i++) {
+ job = (IOJob_t *) ctx->completedJobs[i];
+ if (job->offset == ctx->waitingOnOffset) {
+ ctx->completedJobs[i] = ctx->completedJobs[--ctx->completedJobsCount];
+ return job;
+ }
+ }
+ return NULL;
+}
+
+/* AIO_ReadPool_numReadsInFlight:
+ * Returns the number of IO read jobs currrently in flight. */
+static size_t AIO_ReadPool_numReadsInFlight(ReadPoolCtx_t* ctx) {
+ const size_t jobsHeld = (ctx->currentJobHeld==NULL ? 0 : 1);
+ return ctx->base.totalIoJobs - (ctx->base.availableJobsCount + ctx->completedJobsCount + jobsHeld);
+}
+
+/* AIO_ReadPool_getNextCompletedJob:
+ * Returns a completed IOJob_t for the next read in line based on waitingOnOffset and advances waitingOnOffset.
+ * Would block. */
+static IOJob_t* AIO_ReadPool_getNextCompletedJob(ReadPoolCtx_t* ctx) {
+ IOJob_t *job = NULL;
+ if (ctx->base.threadPool)
+ ZSTD_pthread_mutex_lock(&ctx->base.ioJobsMutex);
+
+ job = AIO_ReadPool_findNextWaitingOffsetCompletedJob_locked(ctx);
+
+ /* As long as we didn't find the job matching the next read, and we have some reads in flight continue waiting */
+ while (!job && (AIO_ReadPool_numReadsInFlight(ctx) > 0)) {
+ assert(ctx->base.threadPool != NULL); /* we shouldn't be here if we work in sync mode */
+ ZSTD_pthread_cond_wait(&ctx->jobCompletedCond, &ctx->base.ioJobsMutex);
+ job = AIO_ReadPool_findNextWaitingOffsetCompletedJob_locked(ctx);
+ }
+
+ if(job) {
+ assert(job->offset == ctx->waitingOnOffset);
+ ctx->waitingOnOffset += job->usedBufferSize;
+ }
+
+ if (ctx->base.threadPool)
+ ZSTD_pthread_mutex_unlock(&ctx->base.ioJobsMutex);
+ return job;
+}
+
+
+/* AIO_ReadPool_executeReadJob:
+ * Executes a read job synchronously. Can be used as a function for a thread pool. */
+static void AIO_ReadPool_executeReadJob(void* opaque){
+ IOJob_t* const job = (IOJob_t*) opaque;
+ ReadPoolCtx_t* const ctx = (ReadPoolCtx_t *)job->ctx;
+ if(ctx->reachedEof) {
+ job->usedBufferSize = 0;
+ AIO_ReadPool_addJobToCompleted(job);
+ return;
+ }
+ job->usedBufferSize = fread(job->buffer, 1, job->bufferSize, job->file);
+ if(job->usedBufferSize < job->bufferSize) {
+ if(ferror(job->file)) {
+ EXM_THROW(37, "Read error");
+ } else if(feof(job->file)) {
+ ctx->reachedEof = 1;
+ } else {
+ EXM_THROW(37, "Unexpected short read");
+ }
+ }
+ AIO_ReadPool_addJobToCompleted(job);
+}
+
+static void AIO_ReadPool_enqueueRead(ReadPoolCtx_t* ctx) {
+ IOJob_t* const job = AIO_IOPool_acquireJob(&ctx->base);
+ job->offset = ctx->nextReadOffset;
+ ctx->nextReadOffset += job->bufferSize;
+ AIO_IOPool_enqueueJob(job);
+}
+
+static void AIO_ReadPool_startReading(ReadPoolCtx_t* ctx) {
+ int i;
+ for (i = 0; i < ctx->base.availableJobsCount; i++) {
+ AIO_ReadPool_enqueueRead(ctx);
+ }
+}
+
+/* AIO_ReadPool_setFile:
+ * Sets the source file for future read in the pool. Initiates reading immediately if file is not NULL.
+ * Waits for all current enqueued tasks to complete if a previous file was set. */
+void AIO_ReadPool_setFile(ReadPoolCtx_t* ctx, FILE* file) {
+ assert(ctx!=NULL);
+ AIO_IOPool_join(&ctx->base);
+ AIO_ReadPool_releaseAllCompletedJobs(ctx);
+ if (ctx->currentJobHeld) {
+ AIO_IOPool_releaseIoJob((IOJob_t *)ctx->currentJobHeld);
+ ctx->currentJobHeld = NULL;
+ }
+ AIO_IOPool_setFile(&ctx->base, file);
+ ctx->nextReadOffset = 0;
+ ctx->waitingOnOffset = 0;
+ ctx->srcBuffer = ctx->coalesceBuffer;
+ ctx->srcBufferLoaded = 0;
+ ctx->reachedEof = 0;
+ if(file != NULL)
+ AIO_ReadPool_startReading(ctx);
+}
+
+/* AIO_ReadPool_create:
+ * Allocates and sets and a new readPool including its included jobs.
+ * bufferSize should be set to the maximal buffer we want to read at a time, will also be used
+ * as our basic read size. */
+ReadPoolCtx_t* AIO_ReadPool_create(const FIO_prefs_t* prefs, size_t bufferSize) {
+ ReadPoolCtx_t* const ctx = (ReadPoolCtx_t*) malloc(sizeof(ReadPoolCtx_t));
+ if(!ctx) EXM_THROW(100, "Allocation error : not enough memory");
+ AIO_IOPool_init(&ctx->base, prefs, AIO_ReadPool_executeReadJob, bufferSize);
+
+ ctx->coalesceBuffer = (U8*) malloc(bufferSize * 2);
+ ctx->srcBuffer = ctx->coalesceBuffer;
+ ctx->srcBufferLoaded = 0;
+ ctx->completedJobsCount = 0;
+ ctx->currentJobHeld = NULL;
+
+ if(ctx->base.threadPool)
+ if (ZSTD_pthread_cond_init(&ctx->jobCompletedCond, NULL))
+ EXM_THROW(103,"Failed creating write jobCompletedCond mutex");
+
+ return ctx;
+}
+
+/* AIO_ReadPool_free:
+ * Frees and releases a readPool and its resources. Closes source file. */
+void AIO_ReadPool_free(ReadPoolCtx_t* ctx) {
+ if(AIO_ReadPool_getFile(ctx))
+ AIO_ReadPool_closeFile(ctx);
+ if(ctx->base.threadPool)
+ ZSTD_pthread_cond_destroy(&ctx->jobCompletedCond);
+ AIO_IOPool_destroy(&ctx->base);
+ free(ctx->coalesceBuffer);
+ free(ctx);
+}
+
+/* AIO_ReadPool_consumeBytes:
+ * Consumes byes from srcBuffer's beginning and updates srcBufferLoaded accordingly. */
+void AIO_ReadPool_consumeBytes(ReadPoolCtx_t* ctx, size_t n) {
+ assert(n <= ctx->srcBufferLoaded);
+ ctx->srcBufferLoaded -= n;
+ ctx->srcBuffer += n;
+}
+
+/* AIO_ReadPool_releaseCurrentlyHeldAndGetNext:
+ * Release the current held job and get the next one, returns NULL if no next job available. */
+static IOJob_t* AIO_ReadPool_releaseCurrentHeldAndGetNext(ReadPoolCtx_t* ctx) {
+ if (ctx->currentJobHeld) {
+ AIO_IOPool_releaseIoJob((IOJob_t *)ctx->currentJobHeld);
+ ctx->currentJobHeld = NULL;
+ AIO_ReadPool_enqueueRead(ctx);
+ }
+ ctx->currentJobHeld = AIO_ReadPool_getNextCompletedJob(ctx);
+ return (IOJob_t*) ctx->currentJobHeld;
+}
+
+/* AIO_ReadPool_fillBuffer:
+ * Tries to fill the buffer with at least n or jobBufferSize bytes (whichever is smaller).
+ * Returns if srcBuffer has at least the expected number of bytes loaded or if we've reached the end of the file.
+ * Return value is the number of bytes added to the buffer.
+ * Note that srcBuffer might have up to 2 times jobBufferSize bytes. */
+size_t AIO_ReadPool_fillBuffer(ReadPoolCtx_t* ctx, size_t n) {
+ IOJob_t *job;
+ int useCoalesce = 0;
+ if(n > ctx->base.jobBufferSize)
+ n = ctx->base.jobBufferSize;
+
+ /* We are good, don't read anything */
+ if (ctx->srcBufferLoaded >= n)
+ return 0;
+
+ /* We still have bytes loaded, but not enough to satisfy caller. We need to get the next job
+ * and coalesce the remaining bytes with the next job's buffer */
+ if (ctx->srcBufferLoaded > 0) {
+ useCoalesce = 1;
+ memcpy(ctx->coalesceBuffer, ctx->srcBuffer, ctx->srcBufferLoaded);
+ ctx->srcBuffer = ctx->coalesceBuffer;
+ }
+
+ /* Read the next chunk */
+ job = AIO_ReadPool_releaseCurrentHeldAndGetNext(ctx);
+ if(!job)
+ return 0;
+ if(useCoalesce) {
+ assert(ctx->srcBufferLoaded + job->usedBufferSize <= 2*ctx->base.jobBufferSize);
+ memcpy(ctx->coalesceBuffer + ctx->srcBufferLoaded, job->buffer, job->usedBufferSize);
+ ctx->srcBufferLoaded += job->usedBufferSize;
+ }
+ else {
+ ctx->srcBuffer = (U8 *) job->buffer;
+ ctx->srcBufferLoaded = job->usedBufferSize;
+ }
+ return job->usedBufferSize;
+}
+
+/* AIO_ReadPool_consumeAndRefill:
+ * Consumes the current buffer and refills it with bufferSize bytes. */
+size_t AIO_ReadPool_consumeAndRefill(ReadPoolCtx_t* ctx) {
+ AIO_ReadPool_consumeBytes(ctx, ctx->srcBufferLoaded);
+ return AIO_ReadPool_fillBuffer(ctx, ctx->base.jobBufferSize);
+}
+
+/* AIO_ReadPool_getFile:
+ * Returns the current file set for the read pool. */
+FILE* AIO_ReadPool_getFile(const ReadPoolCtx_t* ctx) {
+ return AIO_IOPool_getFile(&ctx->base);
+}
+
+/* AIO_ReadPool_closeFile:
+ * Closes the current set file. Waits for all current enqueued tasks to complete and resets state. */
+int AIO_ReadPool_closeFile(ReadPoolCtx_t* ctx) {
+ FILE* const file = AIO_ReadPool_getFile(ctx);
+ AIO_ReadPool_setFile(ctx, NULL);
+ return fclose(file);
+}
diff --git a/programs/fileio_asyncio.h b/programs/fileio_asyncio.h
index 3e91164c558..bf07f85947b 100644
--- a/programs/fileio_asyncio.h
+++ b/programs/fileio_asyncio.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under both the BSD-style license (found in the
@@ -28,7 +28,7 @@ typedef struct {
/* These struct fields should be set only on creation and not changed afterwards */
POOL_ctx* threadPool;
int totalIoJobs;
- FIO_prefs_t* prefs;
+ const FIO_prefs_t* prefs;
POOL_function poolFunction;
/* Controls the file we currently write to, make changes only by using provided utility functions */
@@ -39,8 +39,36 @@ typedef struct {
ZSTD_pthread_mutex_t ioJobsMutex;
void* availableJobs[MAX_IO_JOBS];
int availableJobsCount;
+ size_t jobBufferSize;
} IOPoolCtx_t;
+typedef struct {
+ IOPoolCtx_t base;
+
+ /* State regarding the currently read file */
+ int reachedEof;
+ U64 nextReadOffset;
+ U64 waitingOnOffset;
+
+ /* We may hold an IOJob object as needed if we actively expose its buffer. */
+ void *currentJobHeld;
+
+ /* Coalesce buffer is used to join two buffers in case where we need to read more bytes than left in
+ * the first of them. Shouldn't be accessed from outside ot utility functions. */
+ U8 *coalesceBuffer;
+
+ /* Read buffer can be used by consumer code, take care when copying this pointer aside as it might
+ * change when consuming / refilling buffer. */
+ U8 *srcBuffer;
+ size_t srcBufferLoaded;
+
+ /* We need to know what tasks completed so we can use their buffers when their time comes.
+ * Should only be accessed after locking base.ioJobsMutex . */
+ void* completedJobs[MAX_IO_JOBS];
+ int completedJobsCount;
+ ZSTD_pthread_cond_t jobCompletedCond;
+} ReadPoolCtx_t;
+
typedef struct {
IOPoolCtx_t base;
unsigned storedSkips;
@@ -59,15 +87,10 @@ typedef struct {
U64 offset;
} IOJob_t;
-/** AIO_fwriteSparse() :
-* @return : storedSkips,
-* argument for next call to AIO_fwriteSparse() or AIO_fwriteSparseEnd() */
-unsigned AIO_fwriteSparse(FILE* file,
- const void* buffer, size_t bufferSize,
- const FIO_prefs_t* const prefs,
- unsigned storedSkips);
+/* AIO_supported:
+ * Returns 1 if AsyncIO is supported on the system, 0 otherwise. */
+int AIO_supported(void);
-void AIO_fwriteSparseEnd(const FIO_prefs_t* const prefs, FILE* file, unsigned storedSkips);
/* AIO_WritePool_releaseIoJob:
* Releases an acquired job back to the pool. Doesn't execute the job. */
@@ -97,7 +120,7 @@ void AIO_WritePool_setFile(WritePoolCtx_t *ctx, FILE* file);
/* AIO_WritePool_getFile:
* Returns the file the writePool is currently set to write to. */
-FILE* AIO_WritePool_getFile(WritePoolCtx_t *ctx);
+FILE* AIO_WritePool_getFile(const WritePoolCtx_t* ctx);
/* AIO_WritePool_closeFile:
* Ends sparse write and closes the writePool's current file and sets the file to NULL.
@@ -107,12 +130,50 @@ int AIO_WritePool_closeFile(WritePoolCtx_t *ctx);
/* AIO_WritePool_create:
* Allocates and sets and a new write pool including its included jobs.
* bufferSize should be set to the maximal buffer we want to write to at a time. */
-WritePoolCtx_t* AIO_WritePool_create(FIO_prefs_t* const prefs, size_t bufferSize);
+WritePoolCtx_t* AIO_WritePool_create(const FIO_prefs_t* prefs, size_t bufferSize);
/* AIO_WritePool_free:
* Frees and releases a writePool and its resources. Closes destination file. */
void AIO_WritePool_free(WritePoolCtx_t* ctx);
+/* AIO_ReadPool_create:
+ * Allocates and sets and a new readPool including its included jobs.
+ * bufferSize should be set to the maximal buffer we want to read at a time, will also be used
+ * as our basic read size. */
+ReadPoolCtx_t* AIO_ReadPool_create(const FIO_prefs_t* prefs, size_t bufferSize);
+
+/* AIO_ReadPool_free:
+ * Frees and releases a readPool and its resources. Closes source file. */
+void AIO_ReadPool_free(ReadPoolCtx_t* ctx);
+
+/* AIO_ReadPool_consumeBytes:
+ * Consumes byes from srcBuffer's beginning and updates srcBufferLoaded accordingly. */
+void AIO_ReadPool_consumeBytes(ReadPoolCtx_t *ctx, size_t n);
+
+/* AIO_ReadPool_fillBuffer:
+ * Makes sure buffer has at least n bytes loaded (as long as n is not bigger than the initalized bufferSize).
+ * Returns if srcBuffer has at least n bytes loaded or if we've reached the end of the file.
+ * Return value is the number of bytes added to the buffer.
+ * Note that srcBuffer might have up to 2 times bufferSize bytes. */
+size_t AIO_ReadPool_fillBuffer(ReadPoolCtx_t *ctx, size_t n);
+
+/* AIO_ReadPool_consumeAndRefill:
+ * Consumes the current buffer and refills it with bufferSize bytes. */
+size_t AIO_ReadPool_consumeAndRefill(ReadPoolCtx_t *ctx);
+
+/* AIO_ReadPool_setFile:
+ * Sets the source file for future read in the pool. Initiates reading immediately if file is not NULL.
+ * Waits for all current enqueued tasks to complete if a previous file was set. */
+void AIO_ReadPool_setFile(ReadPoolCtx_t *ctx, FILE* file);
+
+/* AIO_ReadPool_getFile:
+ * Returns the current file set for the read pool. */
+FILE* AIO_ReadPool_getFile(const ReadPoolCtx_t *ctx);
+
+/* AIO_ReadPool_closeFile:
+ * Closes the current set file. Waits for all current enqueued tasks to complete and resets state. */
+int AIO_ReadPool_closeFile(ReadPoolCtx_t *ctx);
+
#if defined (__cplusplus)
}
#endif
diff --git a/programs/fileio_common.h b/programs/fileio_common.h
index d33c19d7bd1..282c2f13b4d 100644
--- a/programs/fileio_common.h
+++ b/programs/fileio_common.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under both the BSD-style license (found in the
diff --git a/programs/fileio_types.h b/programs/fileio_types.h
index 1909ab1ab5a..cf566aa2063 100644
--- a/programs/fileio_types.h
+++ b/programs/fileio_types.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) Yann Collet, Facebook, Inc.
+ * Copyright (c) Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under both the BSD-style license (found in the
@@ -70,4 +70,4 @@ typedef struct FIO_prefs_s {
int allowBlockDevices;
} FIO_prefs_t;
-#endif /* FILEIO_TYPES_HEADER */
\ No newline at end of file
+#endif /* FILEIO_TYPES_HEADER */
diff --git a/programs/zstdcli.c b/programs/zstdcli.c
index 2e3f4ddb7b0..29da261dfbd 100644
--- a/programs/zstdcli.c
+++ b/programs/zstdcli.c
@@ -46,6 +46,7 @@
# include "zstdcli_trace.h"
#endif
#include "../lib/zstd.h" /* ZSTD_VERSION_STRING, ZSTD_minCLevel, ZSTD_maxCLevel */
+#include "fileio_asyncio.h"
/*-************************************
@@ -179,7 +180,8 @@ static void usage_advanced(const char* programName)
#ifdef UTIL_HAS_MIRRORFILELIST
DISPLAYOUT( "--output-dir-mirror DIR : processed files are stored into DIR respecting original directory structure \n");
#endif
-
+ if (AIO_supported())
+ DISPLAYOUT( "--[no-]asyncio : use asynchronous IO (default: enabled) \n");
#ifndef ZSTD_NOCOMPRESS
DISPLAYOUT( "--[no-]check : during compression, add XXH64 integrity checksum to frame (default: enabled)");
@@ -242,9 +244,6 @@ static void usage_advanced(const char* programName)
DISPLAYOUT( " -l : print information about zstd compressed files \n");
DISPLAYOUT( "--test : test compressed file integrity \n");
DISPLAYOUT( " -M# : Set a memory usage limit for decompression \n");
-#ifdef ZSTD_MULTITHREAD
- DISPLAYOUT( "--[no-]asyncio : use threaded asynchronous IO for output (default: disabled) \n");
-#endif
# if ZSTD_SPARSE_DEFAULT
DISPLAYOUT( "--[no-]sparse : sparse mode (default: enabled on file, disabled on stdout) \n");
# else
@@ -1459,6 +1458,7 @@ int main(int argCount, const char* argv[])
FIO_setTargetCBlockSize(prefs, targetCBlockSize);
FIO_setSrcSizeHint(prefs, srcSizeHint);
FIO_setLiteralCompressionMode(prefs, literalCompressionMode);
+ FIO_setSparseWrite(prefs, 0);
if (adaptMin > cLevel) cLevel = adaptMin;
if (adaptMax < cLevel) cLevel = adaptMax;
diff --git a/tests/playTests.sh b/tests/playTests.sh
index f04f1da7252..f97e96e374a 100755
--- a/tests/playTests.sh
+++ b/tests/playTests.sh
@@ -260,10 +260,13 @@ zstd -dc - < tmp.zst > $INTOVOID
zstd -d < tmp.zst > $INTOVOID # implicit stdout when stdin is used
zstd -d - < tmp.zst > $INTOVOID
println "test : impose memory limitation (must fail)"
-zstd -d -f tmp.zst -M2K -c > $INTOVOID && die "decompression needs more memory than allowed"
-zstd -d -f tmp.zst --memlimit=2K -c > $INTOVOID && die "decompression needs more memory than allowed" # long command
-zstd -d -f tmp.zst --memory=2K -c > $INTOVOID && die "decompression needs more memory than allowed" # long command
-zstd -d -f tmp.zst --memlimit-decompress=2K -c > $INTOVOID && die "decompression needs more memory than allowed" # long command
+datagen -g500K > tmplimit
+zstd -f tmplimit
+zstd -d -f tmplimit.zst -M2K -c > $INTOVOID && die "decompression needs more memory than allowed"
+zstd -d -f tmplimit.zst --memlimit=2K -c > $INTOVOID && die "decompression needs more memory than allowed" # long command
+zstd -d -f tmplimit.zst --memory=2K -c > $INTOVOID && die "decompression needs more memory than allowed" # long command
+zstd -d -f tmplimit.zst --memlimit-decompress=2K -c > $INTOVOID && die "decompression needs more memory than allowed" # long command
+rm -f tmplimit tmplimit.zst
println "test : overwrite protection"
zstd -q tmp && die "overwrite check failed!"
println "test : force overwrite"
@@ -1596,11 +1599,11 @@ elif [ "$longCSize19wlog23" -gt "$optCSize19wlog23" ]; then
exit 1
fi
-println "\n===> zstd asyncio decompression tests "
+println "\n===> zstd asyncio tests "
addFrame() {
datagen -g2M -s$2 >> tmp_uncompressed
- datagen -g2M -s$2 | zstd --format=$1 >> tmp_compressed.zst
+ datagen -g2M -s$2 | zstd -1 --format=$1 >> tmp_compressed.zst
}
addTwoFrames() {
From 9a758ce52068b2daffd36a6ae9af16adf5791f14 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Mon, 31 Jan 2022 16:17:11 -0800
Subject: [PATCH 075/472] update sequence_compression_api fuzzer test
to check for under-sized dstCapacity.
---
tests/fuzz/sequence_compression_api.c | 41 ++++++++++++++++-----------
tests/fuzz/zstd_helpers.h | 1 +
2 files changed, 26 insertions(+), 16 deletions(-)
diff --git a/tests/fuzz/sequence_compression_api.c b/tests/fuzz/sequence_compression_api.c
index 68923e11e60..e01daacaa89 100644
--- a/tests/fuzz/sequence_compression_api.c
+++ b/tests/fuzz/sequence_compression_api.c
@@ -221,11 +221,11 @@ static size_t generateRandomSequences(FUZZ_dataProducer_t* producer,
return nbSeqGenerated;
}
-static size_t roundTripTest(void *result, size_t resultCapacity,
- void *compressed, size_t compressedCapacity,
- size_t srcSize,
- const void *dict, size_t dictSize,
- size_t generatedSequencesSize,
+static size_t roundTripTest(void* result, size_t resultCapacity,
+ void* compressed, size_t compressedCapacity,
+ const void* src, size_t srcSize,
+ const void* dict, size_t dictSize,
+ const ZSTD_Sequence* seqs, size_t seqSize,
int wLog, int cLevel, unsigned hasDict,
ZSTD_sequenceFormat_e mode)
{
@@ -245,12 +245,22 @@ static size_t roundTripTest(void *result, size_t resultCapacity,
}
cSize = ZSTD_compressSequences(cctx, compressed, compressedCapacity,
- generatedSequences, generatedSequencesSize,
- generatedSrc, srcSize);
+ seqs, seqSize,
+ src, srcSize);
+ if ( (ZSTD_getErrorCode(cSize) == ZSTD_error_dstSize_tooSmall)
+ && (mode == ZSTD_sf_explicitBlockDelimiters) ) {
+ /* Valid scenario : in explicit delimiter mode,
+ * it might be possible for the compressed size to outgrow dstCapacity.
+ * In which case, it's still a valid fuzzer scenario,
+ * but no roundtrip shall be possible */
+ return 0;
+ }
+ /* round-trip */
FUZZ_ZASSERT(cSize);
dSize = ZSTD_decompressDCtx(dctx, result, resultCapacity, compressed, cSize);
FUZZ_ZASSERT(dSize);
-
+ FUZZ_ASSERT_MSG(dSize == srcSize, "Incorrect regenerated size");
+ FUZZ_ASSERT_MSG(!FUZZ_memcmp(src, result, srcSize), "Corruption!");
return dSize;
}
@@ -298,10 +308,11 @@ int LLVMFuzzerTestOneInput(const uint8_t* src, size_t size)
nbSequences = generateRandomSequences(producer, ZSTD_FUZZ_GENERATED_LITERALS_SIZE, dictSize, wLog, mode);
generatedSrcSize = decodeSequences(generatedSrc, nbSequences, ZSTD_FUZZ_GENERATED_LITERALS_SIZE, dictBuffer, dictSize, mode);
/* Note : in explicit block delimiters mode,
- * the fuzzer might generate a lot of small incompressible blocks.
+ * the fuzzer might generate a lot of small blocks.
* In which case, the final compressed size might be > ZSTD_compressBound().
- * Solution : provide a much more generous cBufSize to cover these scenarios */
- cBufSize = (mode == ZSTD_sf_noBlockDelimiters) ? ZSTD_compressBound(generatedSrcSize) : 256 + (generatedSrcSize * 2);
+ * This is still a valid scenario fuzzer though, which makes it possible to check under-sized dstCapacity.
+ * The test just doesn't roundtrip. */
+ cBufSize = ZSTD_compressBound(generatedSrcSize);
cBuf = FUZZ_malloc(cBufSize);
rBufSize = generatedSrcSize;
@@ -318,14 +329,12 @@ int LLVMFuzzerTestOneInput(const uint8_t* src, size_t size)
{ const size_t result = roundTripTest(rBuf, rBufSize,
cBuf, cBufSize,
- generatedSrcSize,
+ generatedSrc, generatedSrcSize,
dictBuffer, dictSize,
- nbSequences,
+ generatedSequences, nbSequences,
(int)wLog, cLevel, hasDict, mode);
- FUZZ_ZASSERT(result);
- FUZZ_ASSERT_MSG(result == generatedSrcSize, "Incorrect regenerated size");
+ FUZZ_ASSERT(result <= generatedSrcSize); /* can be 0 when no round-trip */
}
- FUZZ_ASSERT_MSG(!FUZZ_memcmp(generatedSrc, rBuf, generatedSrcSize), "Corruption!");
free(rBuf);
free(cBuf);
diff --git a/tests/fuzz/zstd_helpers.h b/tests/fuzz/zstd_helpers.h
index 7813884d3f3..9fbefdc72a9 100644
--- a/tests/fuzz/zstd_helpers.h
+++ b/tests/fuzz/zstd_helpers.h
@@ -17,6 +17,7 @@
#define ZSTD_STATIC_LINKING_ONLY
#include "zstd.h"
+#include "zstd_errors.h"
#include "fuzz_data_producer.h"
#include
From 4b24ebdcf33cc5c2819272278e2d742b9d0f72fe Mon Sep 17 00:00:00 2001
From: Yonatan Komornik <11005061+yoniko@users.noreply.github.com>
Date: Mon, 31 Jan 2022 16:49:49 -0800
Subject: [PATCH 076/472] Travis CI: fix by installing pip compatible with
python 3.6 (#3041)
Pip install script no longer supports python3.6 by default, switched to a script that does.
---
.travis.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.travis.yml b/.travis.yml
index 6a1295b4cee..c49f3ee3d6f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -102,7 +102,7 @@ matrix:
travis_retry curl -o ~/ninja.zip -L 'https://github.com/ninja-build/ninja/releases/download/v1.9.0/ninja-linux.zip' &&
unzip ~/ninja.zip -d ~/.local/bin
- |
- travis_retry curl -o ~/get-pip.py -L 'https://bootstrap.pypa.io/get-pip.py' &&
+ travis_retry curl -o ~/get-pip.py -L 'https://bootstrap.pypa.io/pip/3.6/get-pip.py' &&
python3 ~/get-pip.py --user &&
pip3 install --user meson
script:
From cad9f8d5f9c451b1cc8ce00a16c125e3d2ffc418 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Mon, 31 Jan 2022 09:34:04 -0800
Subject: [PATCH 077/472] fix 44239
credit to oss-fuzz
This issue could happen when using the new Sequence Compression API in Explicit Delimiter Mode
with a too small dstCapacity.
In which case, there was one place where the buffer size wasn't checked.
---
lib/compress/zstd_compress.c | 21 ++++++++++++---------
lib/compress/zstd_compress_internal.h | 7 +++++--
lib/compress/zstd_compress_literals.c | 6 ++++--
3 files changed, 21 insertions(+), 13 deletions(-)
diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
index b9358fa0c81..eda38480b5b 100644
--- a/lib/compress/zstd_compress.c
+++ b/lib/compress/zstd_compress.c
@@ -2595,7 +2595,7 @@ ZSTD_entropyCompressSeqStore_internal(seqStore_t* seqStorePtr,
entropyWorkspace = count + (MaxSeq + 1);
entropyWkspSize -= (MaxSeq + 1) * sizeof(*count);
- DEBUGLOG(5, "ZSTD_entropyCompressSeqStore_internal (nbSeq=%zu)", nbSeq);
+ DEBUGLOG(5, "ZSTD_entropyCompressSeqStore_internal (nbSeq=%zu, dstCapacity=%zu)", nbSeq, dstCapacity);
ZSTD_STATIC_ASSERT(HUF_WORKSPACE_SIZE >= (1<= HUF_WORKSPACE_SIZE);
@@ -2639,7 +2639,7 @@ ZSTD_entropyCompressSeqStore_internal(seqStore_t* seqStorePtr,
ZSTD_memcpy(&nextEntropy->fse, &prevEntropy->fse, sizeof(prevEntropy->fse));
return (size_t)(op - ostart);
}
- { BYTE* seqHead = op++;
+ { BYTE* const seqHead = op++;
/* build stats for sequences */
const ZSTD_symbolEncodingTypeStats_t stats =
ZSTD_buildSequencesStatistics(seqStorePtr, nbSeq,
@@ -2702,15 +2702,17 @@ ZSTD_entropyCompressSeqStore(seqStore_t* seqStorePtr,
/* When srcSize <= dstCapacity, there is enough space to write a raw uncompressed block.
* Since we ran out of space, block must be not compressible, so fall back to raw uncompressed block.
*/
- if ((cSize == ERROR(dstSize_tooSmall)) & (srcSize <= dstCapacity))
+ if ((cSize == ERROR(dstSize_tooSmall)) & (srcSize <= dstCapacity)) {
+ DEBUGLOG(4, "not enough dstCapacity (%zu) for ZSTD_entropyCompressSeqStore_internal()=> do not compress block", dstCapacity);
return 0; /* block not compressed */
+ }
FORWARD_IF_ERROR(cSize, "ZSTD_entropyCompressSeqStore_internal failed");
/* Check compressibility */
{ size_t const maxCSize = srcSize - ZSTD_minGain(srcSize, cctxParams->cParams.strategy);
if (cSize >= maxCSize) return 0; /* block not compressed */
}
- DEBUGLOG(4, "ZSTD_entropyCompressSeqStore() cSize: %zu", cSize);
+ DEBUGLOG(5, "ZSTD_entropyCompressSeqStore() cSize: %zu", cSize);
return cSize;
}
@@ -6159,6 +6161,7 @@ ZSTD_compressSequences_internal(ZSTD_CCtx* cctx,
continue;
}
+ RETURN_ERROR_IF(dstCapacity < ZSTD_blockHeaderSize, dstSize_tooSmall, "not enough dstCapacity to write a new compressed block");
compressedSeqsSize = ZSTD_entropyCompressSeqStore(&cctx->seqStore,
&cctx->blockState.prevCBlock->entropy, &cctx->blockState.nextCBlock->entropy,
&cctx->appliedParams,
@@ -6182,11 +6185,11 @@ ZSTD_compressSequences_internal(ZSTD_CCtx* cctx,
if (compressedSeqsSize == 0) {
/* ZSTD_noCompressBlock writes the block header as well */
cBlockSize = ZSTD_noCompressBlock(op, dstCapacity, ip, blockSize, lastBlock);
- FORWARD_IF_ERROR(cBlockSize, "Nocompress block failed");
+ FORWARD_IF_ERROR(cBlockSize, "ZSTD_noCompressBlock failed");
DEBUGLOG(5, "Writing out nocompress block, size: %zu", cBlockSize);
} else if (compressedSeqsSize == 1) {
cBlockSize = ZSTD_rleCompressBlock(op, dstCapacity, *ip, blockSize, lastBlock);
- FORWARD_IF_ERROR(cBlockSize, "RLE compress block failed");
+ FORWARD_IF_ERROR(cBlockSize, "ZSTD_rleCompressBlock failed");
DEBUGLOG(5, "Writing out RLE block, size: %zu", cBlockSize);
} else {
U32 cBlockHeader;
@@ -6203,7 +6206,6 @@ ZSTD_compressSequences_internal(ZSTD_CCtx* cctx,
}
cSize += cBlockSize;
- DEBUGLOG(5, "cSize running total: %zu", cSize);
if (lastBlock) {
break;
@@ -6214,6 +6216,7 @@ ZSTD_compressSequences_internal(ZSTD_CCtx* cctx,
dstCapacity -= cBlockSize;
cctx->isFirstBlock = 0;
}
+ DEBUGLOG(5, "cSize running total: %zu (remaining dstCapacity=%zu)", cSize, dstCapacity);
}
DEBUGLOG(4, "cSize final total: %zu", cSize);
@@ -6231,7 +6234,7 @@ size_t ZSTD_compressSequences(ZSTD_CCtx* cctx,
size_t frameHeaderSize = 0;
/* Transparent initialization stage, same as compressStream2() */
- DEBUGLOG(3, "ZSTD_compressSequences()");
+ DEBUGLOG(4, "ZSTD_compressSequences (dstCapacity=%zu)", dstCapacity);
assert(cctx != NULL);
FORWARD_IF_ERROR(ZSTD_CCtx_init_compressStream2(cctx, ZSTD_e_end, srcSize), "CCtx initialization failed");
/* Begin writing output, starting with frame header */
@@ -6259,7 +6262,7 @@ size_t ZSTD_compressSequences(ZSTD_CCtx* cctx,
cSize += 4;
}
- DEBUGLOG(3, "Final compressed size: %zu", cSize);
+ DEBUGLOG(4, "Final compressed size: %zu", cSize);
return cSize;
}
diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h
index ddd5d029688..f027f461283 100644
--- a/lib/compress/zstd_compress_internal.h
+++ b/lib/compress/zstd_compress_internal.h
@@ -513,9 +513,11 @@ MEM_STATIC int ZSTD_cParam_withinBounds(ZSTD_cParameter cParam, int value)
/* ZSTD_noCompressBlock() :
* Writes uncompressed block to dst buffer from given src.
* Returns the size of the block */
-MEM_STATIC size_t ZSTD_noCompressBlock (void* dst, size_t dstCapacity, const void* src, size_t srcSize, U32 lastBlock)
+MEM_STATIC size_t
+ZSTD_noCompressBlock(void* dst, size_t dstCapacity, const void* src, size_t srcSize, U32 lastBlock)
{
U32 const cBlockHeader24 = lastBlock + (((U32)bt_raw)<<1) + (U32)(srcSize << 3);
+ DEBUGLOG(5, "ZSTD_noCompressBlock (srcSize=%zu, dstCapacity=%zu)", srcSize, dstCapacity);
RETURN_ERROR_IF(srcSize + ZSTD_blockHeaderSize > dstCapacity,
dstSize_tooSmall, "dst buf too small for uncompressed block");
MEM_writeLE24(dst, cBlockHeader24);
@@ -523,7 +525,8 @@ MEM_STATIC size_t ZSTD_noCompressBlock (void* dst, size_t dstCapacity, const voi
return ZSTD_blockHeaderSize + srcSize;
}
-MEM_STATIC size_t ZSTD_rleCompressBlock (void* dst, size_t dstCapacity, BYTE src, size_t srcSize, U32 lastBlock)
+MEM_STATIC size_t
+ZSTD_rleCompressBlock(void* dst, size_t dstCapacity, BYTE src, size_t srcSize, U32 lastBlock)
{
BYTE* const op = (BYTE*)dst;
U32 const cBlockHeader = lastBlock + (((U32)bt_rle)<<1) + (U32)(srcSize << 3);
diff --git a/lib/compress/zstd_compress_literals.c b/lib/compress/zstd_compress_literals.c
index 83f9116487f..15bde09e625 100644
--- a/lib/compress/zstd_compress_literals.c
+++ b/lib/compress/zstd_compress_literals.c
@@ -41,6 +41,8 @@ size_t ZSTD_noCompressLiterals (void* dst, size_t dstCapacity, const void* src,
BYTE* const ostart = (BYTE*)dst;
U32 const flSize = 1 + (srcSize>31) + (srcSize>4095);
+ DEBUGLOG(5, "ZSTD_noCompressLiterals: srcSize=%zu, dstCapacity=%zu", srcSize, dstCapacity);
+
RETURN_ERROR_IF(srcSize + flSize > dstCapacity, dstSize_tooSmall, "");
switch(flSize)
@@ -106,8 +108,8 @@ size_t ZSTD_compressLiterals (ZSTD_hufCTables_t const* prevHuf,
symbolEncodingType_e hType = set_compressed;
size_t cLitSize;
- DEBUGLOG(5,"ZSTD_compressLiterals (disableLiteralCompression=%i srcSize=%u)",
- disableLiteralCompression, (U32)srcSize);
+ DEBUGLOG(5,"ZSTD_compressLiterals (disableLiteralCompression=%i, srcSize=%u, dstCapacity=%zu)",
+ disableLiteralCompression, (U32)srcSize, dstCapacity);
DEBUGLOG(6, "Completed literals listing (%zu bytes)", showHexa(src, srcSize));
From 470eb8330a9821c334df7efe66945a63d1faf017 Mon Sep 17 00:00:00 2001
From: Jonathan McDowell
Date: Tue, 1 Feb 2022 03:20:30 -0800
Subject: [PATCH 078/472] Fix required decompression memory usage reported by
-vv + --long
The use of --long alters the window size internally in the underlying
library (lib/compress/zstd_compress.c:ZSTD_getCParamsFromCCtxParams),
which changes the memory required for decompression. This means that the
reported requirement from the zstd binary when -vv is specified is
incorrect.
A full fix for this would be to add an API call to be able to retrieve
the required decompression memory from the library, but as a
lighterweight fix we can just take account of the fact we've enabled
long mode and update our verbose output appropriately.
Fixes #2968
---
programs/fileio.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/programs/fileio.c b/programs/fileio.c
index 502f69c1576..e37921adadc 100644
--- a/programs/fileio.c
+++ b/programs/fileio.c
@@ -1301,8 +1301,13 @@ FIO_compressZstdFrame(FIO_ctx_t* const fCtx,
UTIL_HumanReadableSize_t windowSize;
CHECK(ZSTD_CCtx_getParameter(ress.cctx, ZSTD_c_windowLog, &windowLog));
if (windowLog == 0) {
- const ZSTD_compressionParameters cParams = ZSTD_getCParams(compressionLevel, fileSize, 0);
- windowLog = cParams.windowLog;
+ if (prefs->ldmFlag) {
+ /* If long mode is set without a window size libzstd will set this size internally */
+ windowLog = ZSTD_WINDOWLOG_LIMIT_DEFAULT;
+ } else {
+ const ZSTD_compressionParameters cParams = ZSTD_getCParams(compressionLevel, fileSize, 0);
+ windowLog = cParams.windowLog;
+ }
}
windowSize = UTIL_makeHumanReadableSize(MAX(1ULL, MIN(1ULL << windowLog, pledgedSrcSize)));
DISPLAYLEVEL(4, "Decompression will require %.*f%s of memory\n", windowSize.precision, windowSize.value, windowSize.suffix);
From 4c4d403ecba80e372e413b219e44df2ff7339a52 Mon Sep 17 00:00:00 2001
From: Dimitris Apostolou
Date: Wed, 2 Feb 2022 19:32:31 +0200
Subject: [PATCH 079/472] Fix typos
---
programs/fileio_asyncio.c | 2 +-
programs/fileio_asyncio.h | 2 +-
tests/cli-tests/README.md | 4 ++--
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/programs/fileio_asyncio.c b/programs/fileio_asyncio.c
index 332292bbc1b..92c9a5b1d35 100644
--- a/programs/fileio_asyncio.c
+++ b/programs/fileio_asyncio.c
@@ -415,7 +415,7 @@ static IOJob_t* AIO_ReadPool_findNextWaitingOffsetCompletedJob_locked(ReadPoolCt
}
/* AIO_ReadPool_numReadsInFlight:
- * Returns the number of IO read jobs currrently in flight. */
+ * Returns the number of IO read jobs currently in flight. */
static size_t AIO_ReadPool_numReadsInFlight(ReadPoolCtx_t* ctx) {
const size_t jobsHeld = (ctx->currentJobHeld==NULL ? 0 : 1);
return ctx->base.totalIoJobs - (ctx->base.availableJobsCount + ctx->completedJobsCount + jobsHeld);
diff --git a/programs/fileio_asyncio.h b/programs/fileio_asyncio.h
index bf07f85947b..30db44b6ef0 100644
--- a/programs/fileio_asyncio.h
+++ b/programs/fileio_asyncio.h
@@ -151,7 +151,7 @@ void AIO_ReadPool_free(ReadPoolCtx_t* ctx);
void AIO_ReadPool_consumeBytes(ReadPoolCtx_t *ctx, size_t n);
/* AIO_ReadPool_fillBuffer:
- * Makes sure buffer has at least n bytes loaded (as long as n is not bigger than the initalized bufferSize).
+ * Makes sure buffer has at least n bytes loaded (as long as n is not bigger than the initialized bufferSize).
* Returns if srcBuffer has at least n bytes loaded or if we've reached the end of the file.
* Return value is the number of bytes added to the buffer.
* Note that srcBuffer might have up to 2 times bufferSize bytes. */
diff --git a/tests/cli-tests/README.md b/tests/cli-tests/README.md
index 3098f466f2c..cdf9b8e7148 100644
--- a/tests/cli-tests/README.md
+++ b/tests/cli-tests/README.md
@@ -67,9 +67,9 @@ They are generally used by the helper scripts in `bin/` to coordinate everything
### Basic test case
-When executing your `$TEST` executable, by default the exit code is expected to be `0`. However, you can provide an alterate expected exit code in a `$TEST.exit` file.
+When executing your `$TEST` executable, by default the exit code is expected to be `0`. However, you can provide an alternate expected exit code in a `$TEST.exit` file.
-When executing your `$TEST` exectuable, by default the expected stderr and stdout are empty. However, you can override the default by providing one of three files:
+When executing your `$TEST` executable, by default the expected stderr and stdout are empty. However, you can override the default by providing one of three files:
* `$TEST.{stdout,stderr}.exact`
* `$TEST.{stdout,stderr}.glob`
From b9566fc558830de2a3fd486961b674774c17b74e Mon Sep 17 00:00:00 2001
From: binhdvo
Date: Wed, 2 Feb 2022 15:12:48 -0500
Subject: [PATCH 080/472] Add rails for huffman table log calculation (#3047)
---
lib/compress/huf_compress.c | 5 ++++-
lib/compress/zstd_compress.c | 1 +
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/lib/compress/huf_compress.c b/lib/compress/huf_compress.c
index d1b98afdb42..a8677733b10 100644
--- a/lib/compress/huf_compress.c
+++ b/lib/compress/huf_compress.c
@@ -1224,7 +1224,10 @@ static size_t HUF_compressCTable_internal(
unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue)
{
- return FSE_optimalTableLog_internal(maxTableLog, srcSize, maxSymbolValue, 1);
+ unsigned tableLog = FSE_optimalTableLog_internal(maxTableLog, srcSize, maxSymbolValue, 1);
+ assert(tableLog <= HUF_TABLELOG_MAX);
+
+ return tableLog;
}
typedef struct {
diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
index eda38480b5b..37b6e2ff421 100644
--- a/lib/compress/zstd_compress.c
+++ b/lib/compress/zstd_compress.c
@@ -3112,6 +3112,7 @@ static size_t ZSTD_buildBlockEntropyStats_literals(void* const src, size_t srcSi
/* Build Huffman Tree */
ZSTD_memset(nextHuf->CTable, 0, sizeof(nextHuf->CTable));
huffLog = HUF_optimalTableLog(huffLog, srcSize, maxSymbolValue);
+ assert(huffLog <= LitHufLog);
{ size_t const maxBits = HUF_buildCTable_wksp((HUF_CElt*)nextHuf->CTable, countWksp,
maxSymbolValue, huffLog,
nodeWksp, nodeWkspSize);
From 4bba97b4cb8705447123a9447854b78662c82ba0 Mon Sep 17 00:00:00 2001
From: Yonatan Komornik <11005061+yoniko@users.noreply.github.com>
Date: Thu, 3 Feb 2022 18:42:20 -0800
Subject: [PATCH 081/472] Macos playtest envvars fix (#3035)
* playtests.sh: fix for a bug in macos' /bin/sh that persists temporary env vars when introduced before function calls
* cli-tests/run.py: Do not use existing ZSTD* envvars
---
tests/cli-tests/run.py | 4 +++-
tests/playTests.sh | 12 ++++++++++++
2 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/tests/cli-tests/run.py b/tests/cli-tests/run.py
index 6791918a5bd..39b53a09bc5 100755
--- a/tests/cli-tests/run.py
+++ b/tests/cli-tests/run.py
@@ -278,10 +278,12 @@ def _test_environment(self) -> typing.Dict[str, str]:
Returns the environment to be used for the
test subprocess.
"""
- env = copy.copy(os.environ)
+ # We want to omit ZSTD cli flags so tests will be consistent across environments
+ env = {k: v for k, v in os.environ.items() if not k.startswith("ZSTD")}
for k, v in self._opts.env.items():
self._vlog(f"${k}='{v}'")
env[k] = v
+ return env
def _launch_test(self) -> None:
"""Launch the test subprocess, but do not join it."""
diff --git a/tests/playTests.sh b/tests/playTests.sh
index f97e96e374a..9b01d4d5eaf 100755
--- a/tests/playTests.sh
+++ b/tests/playTests.sh
@@ -2,6 +2,10 @@
set -e
+unset ZSTD_CLEVEL
+unset ZSTD_NBTHREADS
+
+
die() {
println "$@" 1>&2
exit 1
@@ -210,6 +214,7 @@ zstd -c --fast=0 tmp > $INTOVOID && die "--fast must not accept value 0"
println "test : too large numeric argument"
zstd --fast=9999999999 -f tmp && die "should have refused numeric value"
println "test : set compression level with environment variable ZSTD_CLEVEL"
+
ZSTD_CLEVEL=12 zstd -f tmp # positive compression level
ZSTD_CLEVEL=-12 zstd -f tmp # negative compression level
ZSTD_CLEVEL=+12 zstd -f tmp # valid: verbose '+' sign
@@ -221,6 +226,11 @@ ZSTD_CLEVEL=3a7 zstd -f tmp # malformed env var, warn and revert to default sett
ZSTD_CLEVEL=50000000000 zstd -f tmp # numeric value too large, warn and revert to default setting
println "test : override ZSTD_CLEVEL with command line option"
ZSTD_CLEVEL=12 zstd --fast=3 -f tmp # overridden by command line option
+
+# temporary envvar chnages in the above tests would actually persist in macos /bin/sh
+unset ZSTD_CLEVEL
+
+
println "test : compress to stdout"
zstd tmp -c > tmpCompressed
zstd tmp --stdout > tmpCompressed # long command format
@@ -1465,6 +1475,8 @@ then
ZSTD_NBTHREADS=50000000000 zstd -f mt_tmp # numeric value too large, warn and revert to default setting=
ZSTD_NBTHREADS=2 zstd -f mt_tmp # correct usage
ZSTD_NBTHREADS=1 zstd -f mt_tmp # correct usage: single thread
+ # temporary envvar chnages in the above tests would actually persist in macos /bin/sh
+ unset ZSTD_NBTHREADS
rm -f mt_tmp*
println "\n===> ovLog tests "
From 317bd108fe7b75ac426ad7ff95f9ca48536f0b03 Mon Sep 17 00:00:00 2001
From: Alexander Shadchin
Date: Fri, 4 Feb 2022 14:24:58 +0300
Subject: [PATCH 082/472] Select legacy level for cmake
---
build/cmake/CMakeLists.txt | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt
index 93a167cafb6..fdf7c8637ea 100644
--- a/build/cmake/CMakeLists.txt
+++ b/build/cmake/CMakeLists.txt
@@ -86,7 +86,8 @@ option(ZSTD_LEGACY_SUPPORT "LEGACY SUPPORT" OFF)
if (ZSTD_LEGACY_SUPPORT)
message(STATUS "ZSTD_LEGACY_SUPPORT defined!")
- add_definitions(-DZSTD_LEGACY_SUPPORT=5)
+ set(ZSTD_LEGACY_LEVEL 5 CACHE STRING "")
+ add_definitions(-DZSTD_LEGACY_SUPPORT=${ZSTD_LEGACY_LEVEL})
else ()
message(STATUS "ZSTD_LEGACY_SUPPORT not defined!")
add_definitions(-DZSTD_LEGACY_SUPPORT=0)
From fede1d3abe08f52ab981eefa5c87208334933f36 Mon Sep 17 00:00:00 2001
From: Oscar Shi
Date: Mon, 7 Feb 2022 14:41:07 -0800
Subject: [PATCH 083/472] [trace] Add aarch64 to supported architectures for
zstd_trace
Arm Toolchain should support weak symbols
---
lib/common/zstd_trace.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lib/common/zstd_trace.h b/lib/common/zstd_trace.h
index f9121f7d8ed..6215f1e70cf 100644
--- a/lib/common/zstd_trace.h
+++ b/lib/common/zstd_trace.h
@@ -21,13 +21,13 @@ extern "C" {
* For now, enable conservatively:
* - Only GNUC
* - Only ELF
- * - Only x86-64 and i386
+ * - Only x86-64, i386 and aarch64
* Also, explicitly disable on platforms known not to work so they aren't
* forgotten in the future.
*/
#if !defined(ZSTD_HAVE_WEAK_SYMBOLS) && \
defined(__GNUC__) && defined(__ELF__) && \
- (defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || defined(_M_IX86)) && \
+ (defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || defined(_M_IX86) || defined(__aarch64__)) && \
!defined(__APPLE__) && !defined(_WIN32) && !defined(__MINGW32__) && \
!defined(__CYGWIN__) && !defined(_AIX)
# define ZSTD_HAVE_WEAK_SYMBOLS 1
From 169f8c11fff94c7cb2280efb1b5ae3c8fb3a7c5c Mon Sep 17 00:00:00 2001
From: Nick Terrell
Date: Mon, 7 Feb 2022 15:20:42 -0800
Subject: [PATCH 084/472] [cli-tests] Fix zstd symlinks
The zstd symlinks, notably `zstdcat`, weren't working as expected
because only the `tests/cli-tests/bin/zstd` wrapper was symlinked. We
still invoked `zstd` with the name `zstd`. The fix is to create a
directory of zstd symlinks in `tests/cli-tests/bin/symlinks` for each
name that zstd recognizes. And when `tets/cli-tests/bin/zstd` is
invoked, it selects the correct symlink to call.
See the test `zstd-cli/zstdcat.sh` for an example of how it would work.
---
tests/cli-tests/.gitignore | 4 +-
tests/cli-tests/bin/zstd | 6 ++-
tests/cli-tests/run.py | 37 +++++++++++++++++--
tests/cli-tests/zstd-symlinks/setup | 6 +++
tests/cli-tests/zstd-symlinks/zstdcat.sh | 12 ++++++
.../zstd-symlinks/zstdcat.sh.stdout.exact | 8 ++++
6 files changed, 67 insertions(+), 6 deletions(-)
create mode 100755 tests/cli-tests/zstd-symlinks/setup
create mode 100755 tests/cli-tests/zstd-symlinks/zstdcat.sh
create mode 100644 tests/cli-tests/zstd-symlinks/zstdcat.sh.stdout.exact
diff --git a/tests/cli-tests/.gitignore b/tests/cli-tests/.gitignore
index 4bb425b6139..0ad01b24eff 100644
--- a/tests/cli-tests/.gitignore
+++ b/tests/cli-tests/.gitignore
@@ -1,4 +1,6 @@
-scratch/
!bin/
!datagen
!zstdcat
+
+scratch/
+bin/symlinks
diff --git a/tests/cli-tests/bin/zstd b/tests/cli-tests/bin/zstd
index 198fc6d2d93..7a40aec90d4 100755
--- a/tests/cli-tests/bin/zstd
+++ b/tests/cli-tests/bin/zstd
@@ -1,7 +1,9 @@
#!/bin/sh
+zstdname=$(basename $0)
+
if [ -z "$EXEC_PREFIX" ]; then
- "$ZSTD_BIN" $@
+ "$ZSTD_SYMLINK_DIR/$zstdname" $@
else
- $EXEC_PREFIX "$ZSTD_BIN" $@
+ $EXEC_PREFIX "$ZSTD_SYMLINK_DIR/$zstdname" $@
fi
diff --git a/tests/cli-tests/run.py b/tests/cli-tests/run.py
index 39b53a09bc5..f2614b059f6 100755
--- a/tests/cli-tests/run.py
+++ b/tests/cli-tests/run.py
@@ -21,6 +21,24 @@
import typing
+ZSTD_SYMLINKS = [
+ "zstd",
+ "zstdmt",
+ "unzstd",
+ "zstdcat",
+ "zcat",
+ "gzip",
+ "gunzip",
+ "gzcat",
+ "lzma",
+ "unlzma",
+ "xz",
+ "unxz",
+ "lz4",
+ "unlz4",
+]
+
+
EXCLUDED_DIRS = {
"bin",
"common",
@@ -592,6 +610,16 @@ def run_tests(test_suites: TestSuites, options: Options) -> bool:
return False
+def setup_zstd_symlink_dir(zstd_symlink_dir: str, zstd: str) -> None:
+ assert os.path.join("bin", "symlinks") in zstd_symlink_dir
+ if not os.path.exists(zstd_symlink_dir):
+ os.makedirs(zstd_symlink_dir)
+ for symlink in ZSTD_SYMLINKS:
+ path = os.path.join(zstd_symlink_dir, symlink)
+ if os.path.exists(path):
+ os.remove(path)
+ os.symlink(zstd, path)
+
if __name__ == "__main__":
CLI_TEST_DIR = os.path.dirname(sys.argv[0])
REPO_DIR = os.path.join(CLI_TEST_DIR, "..", "..")
@@ -655,17 +683,20 @@ def run_tests(test_suites: TestSuites, options: Options) -> bool:
args.timeout = None
args.test_dir = os.path.normpath(os.path.abspath(args.test_dir))
- bin_dir = os.path.join(args.test_dir, "bin")
+ bin_dir = os.path.abspath(os.path.join(args.test_dir, "bin"))
+ zstd_symlink_dir = os.path.join(bin_dir, "symlinks")
scratch_dir = os.path.join(args.test_dir, "scratch")
+ setup_zstd_symlink_dir(zstd_symlink_dir, os.path.abspath(args.zstd))
+
env = {}
if args.exec_prefix is not None:
env["EXEC_PREFIX"] = args.exec_prefix
- env["ZSTD_BIN"] = os.path.abspath(args.zstd)
+ env["ZSTD_SYMLINK_DIR"] = zstd_symlink_dir
env["DATAGEN_BIN"] = os.path.abspath(args.datagen)
env["ZSTDGREP_BIN"] = os.path.abspath(args.zstdgrep)
env["COMMON"] = os.path.abspath(os.path.join(args.test_dir, "common"))
- env["PATH"] = os.path.abspath(bin_dir) + ":" + os.getenv("PATH", "")
+ env["PATH"] = bin_dir + ":" + os.getenv("PATH", "")
opts = Options(
env=env,
diff --git a/tests/cli-tests/zstd-symlinks/setup b/tests/cli-tests/zstd-symlinks/setup
new file mode 100755
index 00000000000..cf391ed2117
--- /dev/null
+++ b/tests/cli-tests/zstd-symlinks/setup
@@ -0,0 +1,6 @@
+#!/bin/sh
+set -e
+
+println "hello" > hello
+println "world" > world
+zstd hello world
diff --git a/tests/cli-tests/zstd-symlinks/zstdcat.sh b/tests/cli-tests/zstd-symlinks/zstdcat.sh
new file mode 100755
index 00000000000..74ec063d13c
--- /dev/null
+++ b/tests/cli-tests/zstd-symlinks/zstdcat.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+set -e
+
+# Test zstdcat symlink in bin/
+zstdcat hello.zst
+zstdcat hello.zst world
+zstdcat hello world.zst
+zstdcat hello.zst world.zst
+
+# Test local zstdcat symlink
+ln -s $(which zstd) ./zstdcat
+./zstdcat hello.zst
diff --git a/tests/cli-tests/zstd-symlinks/zstdcat.sh.stdout.exact b/tests/cli-tests/zstd-symlinks/zstdcat.sh.stdout.exact
new file mode 100644
index 00000000000..3205b059b34
--- /dev/null
+++ b/tests/cli-tests/zstd-symlinks/zstdcat.sh.stdout.exact
@@ -0,0 +1,8 @@
+hello
+hello
+world
+hello
+world
+hello
+world
+hello
From f17652931c4f8cebeecebbc4f4a45c47eb81ffca Mon Sep 17 00:00:00 2001
From: yhoogstrate
Date: Tue, 8 Feb 2022 14:06:00 +0100
Subject: [PATCH 085/472] seekable_format no header when compressing empty
string to stream
---
.../seekable_format/tests/seekable_tests.c | 30 +++++++++++++++++++
contrib/seekable_format/zstdseek_compress.c | 2 +-
2 files changed, 31 insertions(+), 1 deletion(-)
diff --git a/contrib/seekable_format/tests/seekable_tests.c b/contrib/seekable_format/tests/seekable_tests.c
index a482638b918..cf98e734f48 100644
--- a/contrib/seekable_format/tests/seekable_tests.c
+++ b/contrib/seekable_format/tests/seekable_tests.c
@@ -186,6 +186,36 @@ int main(int argc, const char** argv)
}
printf("Success!\n");
+
+ printf("Test %u - check ZSTD magic in compressing empty string: ", testNb++);
+ { // compressing empty string should return a zstd header
+ char *data_in = (char *) malloc(255 * sizeof(char));
+ assert(data_in != NULL);
+ data_in = "\0";
+
+ char *data_out = (char *) malloc(255 * 255 * sizeof(char));
+ assert(data_out != NULL);
+
+ ZSTD_seekable_CStream *s = ZSTD_seekable_createCStream();
+ ZSTD_seekable_initCStream(s, 1, 1, 1024 * 1024);
+
+ ZSTD_inBuffer input = { data_in, 0, 0 };
+ ZSTD_outBuffer output = { data_out, 255*255, 0 };
+
+ ZSTD_seekable_compressStream(s, &output, &input);
+ ZSTD_seekable_endStream(s, &output);
+
+ if((((char*)output.dst)[0] != '\x28') | (((char*)output.dst)[1] != '\xb5') | (((char*)output.dst)[2] != '\x2f') | (((char*)output.dst)[3] != '\xfd')) {
+ printf("%#02x %#02x %#02x %#02x\n", ((char*)output.dst)[0], ((char*)output.dst)[1] , ((char*)output.dst)[2] , ((char*)output.dst)[3] );
+
+ ZSTD_seekable_freeCStream(s);
+ goto _test_error;
+ }
+
+ ZSTD_seekable_freeCStream(s);
+ }
+ printf("Success!\n");
+
/* TODO: Add more tests */
printf("Finished tests\n");
return 0;
diff --git a/contrib/seekable_format/zstdseek_compress.c b/contrib/seekable_format/zstdseek_compress.c
index 242bd2ac3a1..7ec9bb577ce 100644
--- a/contrib/seekable_format/zstdseek_compress.c
+++ b/contrib/seekable_format/zstdseek_compress.c
@@ -350,7 +350,7 @@ size_t ZSTD_seekable_writeSeekTable(ZSTD_frameLog* fl, ZSTD_outBuffer* output)
size_t ZSTD_seekable_endStream(ZSTD_seekable_CStream* zcs, ZSTD_outBuffer* output)
{
- if (!zcs->writingSeekTable && zcs->frameDSize) {
+ if (!zcs->writingSeekTable) {
const size_t endFrame = ZSTD_seekable_endFrame(zcs, output);
if (ZSTD_isError(endFrame)) return endFrame;
/* return an accurate size hint */
From 936ae8a3a0bdb8b4cf54f29b2713ca3573e1c609 Mon Sep 17 00:00:00 2001
From: binhdvo
Date: Wed, 9 Feb 2022 11:21:33 -0500
Subject: [PATCH 086/472] Move zstdgrep and zstdless tests to cli-tests (#3057)
* Move zstdgrep and zstdless tests to cli-tests
Co-authored-by: Binh Vo
---
tests/cli-tests/bin/zstdless | 2 ++
tests/cli-tests/cltools/setup | 6 +++++
tests/cli-tests/cltools/zstdgrep.sh | 8 ++++++
tests/cli-tests/cltools/zstdgrep.sh.exit | 1 +
.../cltools/zstdgrep.sh.stderr.exact | 1 +
.../cli-tests/cltools/zstdgrep.sh.stdout.glob | 4 +++
tests/cli-tests/cltools/zstdless.sh | 10 +++++++
.../cltools/zstdless.sh.stderr.exact | 2 ++
.../cli-tests/cltools/zstdless.sh.stdout.glob | 5 ++++
tests/cli-tests/run.py | 7 +++++
tests/playTests.sh | 27 -------------------
11 files changed, 46 insertions(+), 27 deletions(-)
create mode 100755 tests/cli-tests/bin/zstdless
create mode 100755 tests/cli-tests/cltools/setup
create mode 100755 tests/cli-tests/cltools/zstdgrep.sh
create mode 100644 tests/cli-tests/cltools/zstdgrep.sh.exit
create mode 100644 tests/cli-tests/cltools/zstdgrep.sh.stderr.exact
create mode 100644 tests/cli-tests/cltools/zstdgrep.sh.stdout.glob
create mode 100755 tests/cli-tests/cltools/zstdless.sh
create mode 100644 tests/cli-tests/cltools/zstdless.sh.stderr.exact
create mode 100644 tests/cli-tests/cltools/zstdless.sh.stdout.glob
diff --git a/tests/cli-tests/bin/zstdless b/tests/cli-tests/bin/zstdless
new file mode 100755
index 00000000000..d1d6f82d20a
--- /dev/null
+++ b/tests/cli-tests/bin/zstdless
@@ -0,0 +1,2 @@
+#!/bin/sh
+"$ZSTDLESS_BIN" $@
diff --git a/tests/cli-tests/cltools/setup b/tests/cli-tests/cltools/setup
new file mode 100755
index 00000000000..3009bd5be5b
--- /dev/null
+++ b/tests/cli-tests/cltools/setup
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+set -e
+
+echo "1234" > file
+zstd file
diff --git a/tests/cli-tests/cltools/zstdgrep.sh b/tests/cli-tests/cltools/zstdgrep.sh
new file mode 100755
index 00000000000..6cd68b7ab27
--- /dev/null
+++ b/tests/cli-tests/cltools/zstdgrep.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+set -e
+
+println "+ good path"
+zstdgrep "1234" file file.zst
+println "+ bad path"
+zstdgrep "1234" bad.zst
diff --git a/tests/cli-tests/cltools/zstdgrep.sh.exit b/tests/cli-tests/cltools/zstdgrep.sh.exit
new file mode 100644
index 00000000000..56a6051ca2b
--- /dev/null
+++ b/tests/cli-tests/cltools/zstdgrep.sh.exit
@@ -0,0 +1 @@
+1
\ No newline at end of file
diff --git a/tests/cli-tests/cltools/zstdgrep.sh.stderr.exact b/tests/cli-tests/cltools/zstdgrep.sh.stderr.exact
new file mode 100644
index 00000000000..f147f28d7ea
--- /dev/null
+++ b/tests/cli-tests/cltools/zstdgrep.sh.stderr.exact
@@ -0,0 +1 @@
+zstd: can't stat bad.zst : No such file or directory -- ignored
diff --git a/tests/cli-tests/cltools/zstdgrep.sh.stdout.glob b/tests/cli-tests/cltools/zstdgrep.sh.stdout.glob
new file mode 100644
index 00000000000..96d4fa2cbcc
--- /dev/null
+++ b/tests/cli-tests/cltools/zstdgrep.sh.stdout.glob
@@ -0,0 +1,4 @@
++ good path
+file:1234
+file.zst:1234
++ bad path
diff --git a/tests/cli-tests/cltools/zstdless.sh b/tests/cli-tests/cltools/zstdless.sh
new file mode 100755
index 00000000000..61f768862c4
--- /dev/null
+++ b/tests/cli-tests/cltools/zstdless.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+set -e
+
+println "+ good path"
+zstdless file.zst
+println "+ pass parameters"
+zstdless -N file.zst # This parameter does not produce line #s when piped, but still serves to test that the flag went to less and not zstd
+println "+ bad path"
+zstdless bad.zst
diff --git a/tests/cli-tests/cltools/zstdless.sh.stderr.exact b/tests/cli-tests/cltools/zstdless.sh.stderr.exact
new file mode 100644
index 00000000000..5a726f1d149
--- /dev/null
+++ b/tests/cli-tests/cltools/zstdless.sh.stderr.exact
@@ -0,0 +1,2 @@
+zstd: can't stat bad.zst : No such file or directory -- ignored
+bad.zst: No such file or directory
diff --git a/tests/cli-tests/cltools/zstdless.sh.stdout.glob b/tests/cli-tests/cltools/zstdless.sh.stdout.glob
new file mode 100644
index 00000000000..2784dddf17c
--- /dev/null
+++ b/tests/cli-tests/cltools/zstdless.sh.stdout.glob
@@ -0,0 +1,5 @@
++ good path
+1234
++ pass parameters
+1234
++ bad path
diff --git a/tests/cli-tests/run.py b/tests/cli-tests/run.py
index f2614b059f6..9bba2ecd1ab 100755
--- a/tests/cli-tests/run.py
+++ b/tests/cli-tests/run.py
@@ -627,6 +627,7 @@ def setup_zstd_symlink_dir(zstd_symlink_dir: str, zstd: str) -> None:
TESTS_DIR = os.path.join(REPO_DIR, "tests")
ZSTD_PATH = os.path.join(PROGRAMS_DIR, "zstd")
ZSTDGREP_PATH = os.path.join(PROGRAMS_DIR, "zstdgrep")
+ ZSTDLESS_PATH = os.path.join(PROGRAMS_DIR, "zstdless")
DATAGEN_PATH = os.path.join(TESTS_DIR, "datagen")
parser = argparse.ArgumentParser(
@@ -658,6 +659,11 @@ def setup_zstd_symlink_dir(zstd_symlink_dir: str, zstd: str) -> None:
default=ZSTDGREP_PATH,
help="Sets the ZSTDGREP_BIN environment variable. Path of the zstdgrep CLI."
)
+ parser.add_argument(
+ "--zstdless",
+ default=ZSTDLESS_PATH,
+ help="Sets the ZSTDLESS_BIN environment variable. Path of the zstdless CLI."
+ )
parser.add_argument(
"--datagen",
default=DATAGEN_PATH,
@@ -695,6 +701,7 @@ def setup_zstd_symlink_dir(zstd_symlink_dir: str, zstd: str) -> None:
env["ZSTD_SYMLINK_DIR"] = zstd_symlink_dir
env["DATAGEN_BIN"] = os.path.abspath(args.datagen)
env["ZSTDGREP_BIN"] = os.path.abspath(args.zstdgrep)
+ env["ZSTDLESS_BIN"] = os.path.abspath(args.zstdless)
env["COMMON"] = os.path.abspath(os.path.join(args.test_dir, "common"))
env["PATH"] = bin_dir + ":" + os.getenv("PATH", "")
diff --git a/tests/playTests.sh b/tests/playTests.sh
index 9b01d4d5eaf..71e8dc05818 100755
--- a/tests/playTests.sh
+++ b/tests/playTests.sh
@@ -92,8 +92,6 @@ SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
PRGDIR="$SCRIPT_DIR/../programs"
TESTDIR="$SCRIPT_DIR/../tests"
UNAME=$(uname)
-ZSTDGREP="$PRGDIR/zstdgrep"
-ZSTDLESS="$PRGDIR/zstdless"
detectedTerminal=false
if [ -t 0 ] && [ -t 1 ]
@@ -326,31 +324,6 @@ if [ "$isWindows" = false ]; then
fi
fi
-println "\n===> zstdgrep tests"
-ln -sf "$ZSTD_BIN" zstdcat
-rm -f tmp_grep
-echo "1234" > tmp_grep
-zstd -f tmp_grep
-lines=$(ZCAT=./zstdcat "$ZSTDGREP" 2>&1 "1234" tmp_grep tmp_grep.zst | wc -l)
-test 2 -eq $lines
-ZCAT=./zstdcat "$ZSTDGREP" 2>&1 "1234" tmp_grep_bad.zst && die "Should have failed"
-ZCAT=./zstdcat "$ZSTDGREP" 2>&1 "1234" tmp_grep_bad.zst | grep "No such file or directory" || true
-rm -f tmp_grep*
-
-println "\n===> zstdless tests"
-if [ -n "$(which less)" ]; then
- ln -sf "$ZSTD_BIN" zstd
- rm -f tmp_less*
- echo "1234" > tmp_less
- zstd -f tmp_less
- lines=$(ZSTD=./zstd "$ZSTDLESS" 2>&1 tmp_less.zst | wc -l)
- test 1 -eq $lines
- ZSTD=./zstd "$ZSTDLESS" -f tmp_less.zst > tmp_less_regenerated
- $DIFF tmp_less tmp_less_regenerated
- ZSTD=./zstd "$ZSTDLESS" 2>&1 tmp_less_bad.zst | grep "No such file or directory" || die
- rm -f tmp_less*
-fi
-
println "\n===> --exclude-compressed flag"
rm -rf precompressedFilterTestDir
mkdir -p precompressedFilterTestDir
From 9caabc01c412ab3bd3f6a34c34efd59c0fc659ac Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Wed, 9 Feb 2022 14:44:46 -0500
Subject: [PATCH 087/472] Replace "windows-latest" with "windows-2019" in CI
workflows
---
.github/workflows/dev-long-tests.yml | 2 +-
.github/workflows/dev-short-tests.yml | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/.github/workflows/dev-long-tests.yml b/.github/workflows/dev-long-tests.yml
index 7a94fb66654..60d1d45b9a7 100644
--- a/.github/workflows/dev-long-tests.yml
+++ b/.github/workflows/dev-long-tests.yml
@@ -158,7 +158,7 @@ jobs:
make -C tests test-fuzzer-stackmode
mingw-long-test:
- runs-on: windows-latest
+ runs-on: windows-2019
strategy:
fail-fast: false
matrix:
diff --git a/.github/workflows/dev-short-tests.yml b/.github/workflows/dev-short-tests.yml
index dfff4cbed45..a5479691664 100644
--- a/.github/workflows/dev-short-tests.yml
+++ b/.github/workflows/dev-short-tests.yml
@@ -175,7 +175,7 @@ jobs:
CC=clang MOREFLAGS="-Werror -Wimplicit-fallthrough -O0" make -C lib -j libzstd.a ZSTD_LEGACY_SUPPORT=0
cmake-visual-2019:
- runs-on: windows-latest
+ runs-on: windows-2019
strategy:
matrix:
include:
@@ -198,7 +198,7 @@ jobs:
cmake.exe --build .
visual-2019:
- runs-on: windows-latest
+ runs-on: windows-2019
strategy:
matrix:
platform: [x64, Win32]
@@ -330,7 +330,7 @@ jobs:
LDFLAGS="-static" CC=$XCC QEMU_SYS=$XEMU make clean check
mingw-short-test:
- runs-on: windows-latest
+ runs-on: windows-2019
strategy:
fail-fast: false
matrix:
@@ -367,7 +367,7 @@ jobs:
visual-runtime-tests:
- runs-on: windows-latest
+ runs-on: windows-2019
strategy:
matrix:
platform: [x64, Win32]
From e653e97f77e7e53867e39ed6ce11dbbde6617337 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dirk=20M=C3=BCller?=
Date: Tue, 8 Feb 2022 21:32:42 +0100
Subject: [PATCH 088/472] Implement more gzip compatibility (#3037)
-n --no-name is the current behavior already, so we can implement
this as a noop.
--best is an alias for -9 in gzip
add basic cli tests.
---
programs/zstd.1 | 11 ++++++
programs/zstd.1.md | 12 +++++++
programs/zstdcli.c | 41 ++++++++++++++++------
tests/cli-tests/compression/gzip-compat.sh | 15 ++++++++
4 files changed, 69 insertions(+), 10 deletions(-)
create mode 100755 tests/cli-tests/compression/gzip-compat.sh
diff --git a/programs/zstd.1 b/programs/zstd.1
index c7a19dbacac..0e6e016e950 100644
--- a/programs/zstd.1
+++ b/programs/zstd.1
@@ -217,6 +217,17 @@ If input directory contains "\.\.", the files in this directory will be ignored\
.
.IP "" 0
.
+.SS "gzip Operation modifiers"
+When invoked via a \fBgzip\fR symlink, \fBzstd\fR will support further options that intend to mimic the \fBgzip\fR behavior:
+.
+.TP
+\fB\-n\fR, \fB\-\-no\-name\fR
+do not store the original filename and timestamps when compressing a file\. This is the default behavior and hence a no\-op\.
+.
+.TP
+\fB\-\-best\fR
+alias to the option \fB\-9\fR\.
+.
.SS "Restricted usage of Environment Variables"
Using environment variables to set parameters has security implications\. Therefore, this avenue is intentionally restricted\. Only \fBZSTD_CLEVEL\fR and \fBZSTD_NBTHREADS\fR are currently supported\. They set the compression level and number of threads to use during compression, respectively\.
.
diff --git a/programs/zstd.1.md b/programs/zstd.1.md
index e343ec0448b..569ca1aa0db 100644
--- a/programs/zstd.1.md
+++ b/programs/zstd.1.md
@@ -280,6 +280,18 @@ the last one takes effect.
* `--`:
All arguments after `--` are treated as files
+
+### gzip Operation modifiers
+When invoked via a `gzip` symlink, `zstd` will support further
+options that intend to mimic the `gzip` behavior:
+
+* `-n`, `--no-name`:
+ do not store the original filename and timestamps when compressing
+ a file. This is the default behavior and hence a no-op.
+* `--best`:
+ alias to the option `-9`.
+
+
### Restricted usage of Environment Variables
Using environment variables to set parameters has security implications.
diff --git a/programs/zstdcli.c b/programs/zstdcli.c
index 29da261dfbd..0b9b82a9859 100644
--- a/programs/zstdcli.c
+++ b/programs/zstdcli.c
@@ -125,6 +125,15 @@ static void checkLibVersion(void)
}
+/*! exeNameMatch() :
+ @return : a non-zero value if exeName matches test, excluding the extension
+ */
+static int exeNameMatch(const char* exeName, const char* test)
+{
+ return !strncmp(exeName, test, strlen(test)) &&
+ (exeName[strlen(test)] == '\0' || exeName[strlen(test)] == '.');
+}
+
/*-************************************
* Command Line
**************************************/
@@ -153,6 +162,11 @@ static void usage(FILE* f, const char* programName)
DISPLAY_F(f, " block devices, etc.\n");
DISPLAY_F(f, "--rm : remove source file(s) after successful de/compression \n");
DISPLAY_F(f, " -k : preserve source file(s) (default) \n");
+#ifdef ZSTD_GZCOMPRESS
+ if (exeNameMatch(programName, ZSTD_GZ)) { /* behave like gzip */
+ DISPLAY_F(f, " -n : do not store original filename when compressing \n");
+ }
+#endif
DISPLAY_F(f, " -h/-H : display help/long help and exit \n");
}
@@ -208,6 +222,12 @@ static void usage_advanced(const char* programName)
DISPLAYOUT( "--ultra : enable levels beyond %i, up to %i (requires more memory) \n", ZSTDCLI_CLEVEL_MAX, ZSTD_maxCLevel());
DISPLAYOUT( "--long[=#]: enable long distance matching with given window log (default: %u) \n", g_defaultMaxWindowLog);
DISPLAYOUT( "--fast[=#]: switch to very fast compression levels (default: %u) \n", 1);
+#ifdef ZSTD_GZCOMPRESS
+ if (exeNameMatch(programName, ZSTD_GZ)) { /* behave like gzip */
+ DISPLAYOUT( "--best : compatibility alias for -9 \n");
+ DISPLAYOUT( "--no-name : do not store original filename when compressing \n");
+ }
+#endif
DISPLAYOUT( "--adapt : dynamically adapt compression level to I/O conditions \n");
DISPLAYOUT( "--[no-]row-match-finder : force enable/disable usage of fast row-based matchfinder for greedy, lazy, and lazy2 strategies \n");
DISPLAYOUT( "--patch-from=FILE : specify the file to be used as a reference point for zstd's diff engine. \n");
@@ -298,15 +318,6 @@ static const char* lastNameFromPath(const char* path)
return name;
}
-/*! exeNameMatch() :
- @return : a non-zero value if exeName matches test, excluding the extension
- */
-static int exeNameMatch(const char* exeName, const char* test)
-{
- return !strncmp(exeName, test, strlen(test)) &&
- (exeName[strlen(test)] == '\0' || exeName[strlen(test)] == '.');
-}
-
static void errorOut(const char* msg)
{
DISPLAY("%s \n", msg); exit(1);
@@ -866,7 +877,10 @@ int main(int argCount, const char* argv[])
if (exeNameMatch(programName, ZSTD_UNZSTD)) operation=zom_decompress;
if (exeNameMatch(programName, ZSTD_CAT)) { operation=zom_decompress; FIO_overwriteMode(prefs); forceStdout=1; followLinks=1; outFileName=stdoutmark; g_displayLevel=1; } /* supports multiple formats */
if (exeNameMatch(programName, ZSTD_ZCAT)) { operation=zom_decompress; FIO_overwriteMode(prefs); forceStdout=1; followLinks=1; outFileName=stdoutmark; g_displayLevel=1; } /* behave like zcat, also supports multiple formats */
- if (exeNameMatch(programName, ZSTD_GZ)) { suffix = GZ_EXTENSION; FIO_setCompressionType(prefs, FIO_gzipCompression); FIO_setRemoveSrcFile(prefs, 1); } /* behave like gzip */
+ if (exeNameMatch(programName, ZSTD_GZ)) { /* behave like gzip */
+ suffix = GZ_EXTENSION; FIO_setCompressionType(prefs, FIO_gzipCompression); FIO_setRemoveSrcFile(prefs, 1);
+ dictCLevel = cLevel = 6; /* gzip default is -6 */
+ }
if (exeNameMatch(programName, ZSTD_GUNZIP)) { operation=zom_decompress; FIO_setRemoveSrcFile(prefs, 1); } /* behave like gunzip, also supports multiple formats */
if (exeNameMatch(programName, ZSTD_GZCAT)) { operation=zom_decompress; FIO_overwriteMode(prefs); forceStdout=1; followLinks=1; outFileName=stdoutmark; g_displayLevel=1; } /* behave like gzcat, also supports multiple formats */
if (exeNameMatch(programName, ZSTD_LZMA)) { suffix = LZMA_EXTENSION; FIO_setCompressionType(prefs, FIO_lzmaCompression); FIO_setRemoveSrcFile(prefs, 1); } /* behave like lzma */
@@ -936,6 +950,10 @@ int main(int argCount, const char* argv[])
if (!strcmp(argument, "--format=zstd")) { suffix = ZSTD_EXTENSION; FIO_setCompressionType(prefs, FIO_zstdCompression); continue; }
#ifdef ZSTD_GZCOMPRESS
if (!strcmp(argument, "--format=gzip")) { suffix = GZ_EXTENSION; FIO_setCompressionType(prefs, FIO_gzipCompression); continue; }
+ if (exeNameMatch(programName, ZSTD_GZ)) { /* behave like gzip */
+ if (!strcmp(argument, "--best")) { dictCLevel = cLevel = 9; continue; }
+ if (!strcmp(argument, "--no-name")) { /* ignore for now */; continue; }
+ }
#endif
#ifdef ZSTD_LZMACOMPRESS
if (!strcmp(argument, "--format=lzma")) { suffix = LZMA_EXTENSION; FIO_setCompressionType(prefs, FIO_lzmaCompression); continue; }
@@ -1098,6 +1116,9 @@ int main(int argCount, const char* argv[])
/* Force stdout, even if stdout==console */
case 'c': forceStdout=1; outFileName=stdoutmark; argument++; break;
+ /* do not store filename - gzip compatibility - nothing to do */
+ case 'n': argument++; break;
+
/* Use file content as dictionary */
case 'D': argument++; NEXT_FIELD(dictFileName); break;
diff --git a/tests/cli-tests/compression/gzip-compat.sh b/tests/cli-tests/compression/gzip-compat.sh
new file mode 100755
index 00000000000..bb72e05fcc0
--- /dev/null
+++ b/tests/cli-tests/compression/gzip-compat.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+set -e
+
+# Uncomment the set -v line for debugging
+# set -v
+
+# Test gzip specific compression option
+$ZSTD_SYMLINK_DIR/gzip --fast file ; $ZSTD_SYMLINK_DIR/gzip -d file.gz
+$ZSTD_SYMLINK_DIR/gzip --best file ; $ZSTD_SYMLINK_DIR/gzip -d file.gz
+
+# Test -n / --no-name: do not embed original filename in archive
+$ZSTD_SYMLINK_DIR/gzip -n file ; grep -qv file file.gz ; $ZSTD_SYMLINK_DIR/gzip -d file.gz
+$ZSTD_SYMLINK_DIR/gzip --no-name file ; grep -qv file file.gz ; $ZSTD_SYMLINK_DIR/gzip -d file.gz
+$ZSTD_SYMLINK_DIR/gzip -c --no-name file | grep -qv file
From 762898f5e4424a72bdbb3d3847abd598c8be1846 Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Tue, 8 Feb 2022 11:49:31 -0500
Subject: [PATCH 089/472] Bugfix and new features for largeNbDicts benchmark
---
contrib/largeNbDicts/README.md | 24 ++++++++-----
contrib/largeNbDicts/largeNbDicts.c | 52 ++++++++++++++---------------
2 files changed, 42 insertions(+), 34 deletions(-)
diff --git a/contrib/largeNbDicts/README.md b/contrib/largeNbDicts/README.md
index f29bcdfe8e3..010102c904f 100644
--- a/contrib/largeNbDicts/README.md
+++ b/contrib/largeNbDicts/README.md
@@ -14,12 +14,20 @@ Command line :
```
largeNbDicts [Options] filename(s)
-Options :
--r : recursively load all files in subdirectories (default: off)
--B# : split input into blocks of size # (default: no split)
--# : use compression level # (default: 3)
--D # : use # as a dictionary (default: create one)
--i# : nb benchmark rounds (default: 6)
---nbDicts=# : set nb of dictionaries to # (default: one per block)
--h : help (this text)
+Options :
+-z : benchmark compression (default)
+-d : benchmark decompression
+-r : recursively load all files in subdirectories (default: off)
+-B# : split input into blocks of size # (default: no split)
+-# : use compression level # (default: 3)
+-D # : use # as a dictionary (default: create one)
+-i# : nb benchmark rounds (default: 6)
+--nbBlocks=#: use # blocks for bench (default: one per file)
+--nbDicts=# : create # dictionaries for bench (default: one per block)
+-h : help (this text)
+
+Advanced Options (see zstd.h for documentation) :
+--dedicated-dict-search
+--dict-content-type=#
+--dict-attach-pref=#
```
diff --git a/contrib/largeNbDicts/largeNbDicts.c b/contrib/largeNbDicts/largeNbDicts.c
index ddbb3e081be..ae5383d25cb 100644
--- a/contrib/largeNbDicts/largeNbDicts.c
+++ b/contrib/largeNbDicts/largeNbDicts.c
@@ -39,6 +39,7 @@
#define BLOCKSIZE_DEFAULT 0 /* no slicing into blocks */
#define DICTSIZE (4 KB)
#define CLEVEL_DEFAULT 3
+#define DICT_LOAD_METHOD ZSTD_dlm_byCopy
#define BENCH_TIME_DEFAULT_S 6
#define RUN_TIME_DEFAULT_MS 1000
@@ -156,19 +157,6 @@ createDictionaryBuffer(const char* dictionaryName,
}
}
-static ZSTD_CDict* createCDictForDedicatedDictSearch(const void* dict, size_t dictSize, int compressionLevel)
-{
- ZSTD_CCtx_params* params = ZSTD_createCCtxParams();
- ZSTD_CCtxParams_init(params, compressionLevel);
- ZSTD_CCtxParams_setParameter(params, ZSTD_c_enableDedicatedDictSearch, 1);
- ZSTD_CCtxParams_setParameter(params, ZSTD_c_compressionLevel, compressionLevel);
-
- ZSTD_CDict* cdict = ZSTD_createCDict_advanced2(dict, dictSize, ZSTD_dlm_byCopy, ZSTD_dct_auto, params, ZSTD_defaultCMem);
-
- ZSTD_freeCCtxParams(params);
- return cdict;
-}
-
/*! BMK_loadFiles() :
* Loads `buffer`, with content from files listed within `fileNamesTable`.
* Fills `buffer` entirely.
@@ -461,14 +449,12 @@ static void freeCDictCollection(cdict_collection_t cdictc)
}
/* returns .buffers=NULL if operation fails */
-static cdict_collection_t createCDictCollection(const void* dictBuffer, size_t dictSize, size_t nbCDict, int cLevel, int dedicatedDictSearch)
+static cdict_collection_t createCDictCollection(const void* dictBuffer, size_t dictSize, size_t nbCDict, ZSTD_dictContentType_e dictContentType, ZSTD_CCtx_params* cctxParams)
{
ZSTD_CDict** const cdicts = malloc(nbCDict * sizeof(ZSTD_CDict*));
if (cdicts==NULL) return kNullCDictCollection;
for (size_t dictNb=0; dictNb < nbCDict; dictNb++) {
- cdicts[dictNb] = dedicatedDictSearch ?
- createCDictForDedicatedDictSearch(dictBuffer, dictSize, cLevel) :
- ZSTD_createCDict(dictBuffer, dictSize, cLevel);
+ cdicts[dictNb] = ZSTD_createCDict_advanced2(dictBuffer, dictSize, DICT_LOAD_METHOD, dictContentType, cctxParams, ZSTD_defaultCMem);
CONTROL(cdicts[dictNb] != NULL);
}
cdict_collection_t cdictc;
@@ -735,7 +721,7 @@ int bench(const char** fileNameTable, unsigned nbFiles,
size_t blockSize, int clevel,
unsigned nbDictMax, unsigned nbBlocks,
unsigned nbRounds, int benchCompression,
- int dedicatedDictSearch)
+ ZSTD_dictContentType_e dictContentType, ZSTD_CCtx_params* cctxParams)
{
int result = 0;
@@ -786,13 +772,11 @@ int bench(const char** fileNameTable, unsigned nbFiles,
/* dictionary determination */
buffer_t const dictBuffer = createDictionaryBuffer(dictionary,
srcs.buffer.ptr,
- srcs.slices.capacities, srcs.slices.nbSlices,
+ srcSlices.capacities, srcSlices.nbSlices,
DICTSIZE);
CONTROL(dictBuffer.ptr != NULL);
- ZSTD_CDict* const cdict = dedicatedDictSearch ?
- createCDictForDedicatedDictSearch(dictBuffer.ptr, dictBuffer.size, clevel) :
- ZSTD_createCDict(dictBuffer.ptr, dictBuffer.size, clevel);
+ ZSTD_CDict* const cdict = ZSTD_createCDict_advanced2(dictBuffer.ptr, dictBuffer.size, DICT_LOAD_METHOD, dictContentType, cctxParams, ZSTD_defaultCMem);
CONTROL(cdict != NULL);
size_t const cTotalSizeNoDict = compressBlocks(NULL, dstSlices, srcSlices, NULL, clevel);
@@ -815,14 +799,14 @@ int bench(const char** fileNameTable, unsigned nbFiles,
unsigned const nbDicts = nbDictMax ? nbDictMax : nbBlocks;
- cdict_collection_t const cdictionaries = createCDictCollection(dictBuffer.ptr, dictBuffer.size, nbDicts, clevel, dedicatedDictSearch);
+ cdict_collection_t const cdictionaries = createCDictCollection(dictBuffer.ptr, dictBuffer.size, nbDicts, dictContentType, cctxParams);
CONTROL(cdictionaries.cdicts != NULL);
ddict_collection_t const ddictionaries = createDDictCollection(dictBuffer.ptr, dictBuffer.size, nbDicts);
CONTROL(ddictionaries.ddicts != NULL);
if (benchCompression) {
- size_t const dictMem = ZSTD_estimateCDictSize(dictBuffer.size, ZSTD_dlm_byCopy);
+ size_t const dictMem = ZSTD_estimateCDictSize(dictBuffer.size, DICT_LOAD_METHOD);
size_t const allDictMem = dictMem * nbDicts;
DISPLAYLEVEL(3, "generating %u dictionaries, using %.1f MB of memory \n",
nbDicts, (double)allDictMem / (1 MB));
@@ -836,7 +820,7 @@ int bench(const char** fileNameTable, unsigned nbFiles,
freeBufferCollection(resultCollection);
} else {
- size_t const dictMem = ZSTD_estimateDDictSize(dictBuffer.size, ZSTD_dlm_byCopy);
+ size_t const dictMem = ZSTD_estimateDDictSize(dictBuffer.size, DICT_LOAD_METHOD);
size_t const allDictMem = dictMem * nbDicts;
DISPLAYLEVEL(3, "generating %u dictionaries, using %.1f MB of memory \n",
nbDicts, (double)allDictMem / (1 MB));
@@ -927,6 +911,11 @@ int usage(const char* exeName)
DISPLAY ("--nbBlocks=#: use # blocks for bench (default: one per file) \n");
DISPLAY ("--nbDicts=# : create # dictionaries for bench (default: one per block) \n");
DISPLAY ("-h : help (this text) \n");
+ DISPLAY (" \n");
+ DISPLAY ("Advanced Options (see zstd.h for documentation) : \n");
+ DISPLAY ("--dedicated-dict-search\n");
+ DISPLAY ("--dict-content-type=#\n");
+ DISPLAY ("--dict-attach-pref=#\n");
return 0;
}
@@ -956,6 +945,8 @@ int main (int argc, const char** argv)
size_t blockSize = BLOCKSIZE_DEFAULT;
unsigned nbDicts = 0; /* determine nbDicts automatically: 1 dictionary per block */
unsigned nbBlocks = 0; /* determine nbBlocks automatically, from source and blockSize */
+ ZSTD_dictContentType_e dictContentType = ZSTD_dct_auto;
+ ZSTD_dictAttachPref_e dictAttachPref = ZSTD_dictDefaultAttach;
for (int argNb = 1; argNb < argc ; argNb++) {
const char* argument = argv[argNb];
@@ -972,6 +963,8 @@ int main (int argc, const char** argv)
if (longCommandWArg(&argument, "--nbBlocks=")) { nbBlocks = readU32FromChar(&argument); continue; }
if (longCommandWArg(&argument, "--clevel=")) { cLevel = (int)readU32FromChar(&argument); continue; }
if (longCommandWArg(&argument, "--dedicated-dict-search")) { dedicatedDictSearch = 1; continue; }
+ if (longCommandWArg(&argument, "--dict-content-type=")) { dictContentType = (int)readU32FromChar(&argument); continue; }
+ if (longCommandWArg(&argument, "--dict-attach-pref=")) { dictAttachPref = (int)readU32FromChar(&argument); continue; }
if (longCommandWArg(&argument, "-")) { cLevel = (int)readU32FromChar(&argument); continue; }
/* anything that's not a command is a filename */
nameTable[nameIdx++] = argument;
@@ -989,10 +982,17 @@ int main (int argc, const char** argv)
nameTable = NULL; /* UTIL_createFileNamesTable() takes ownership of nameTable */
}
- int result = bench(filenameTable->fileNames, (unsigned)filenameTable->tableSize, dictionary, blockSize, cLevel, nbDicts, nbBlocks, nbRounds, benchCompression, dedicatedDictSearch);
+ ZSTD_CCtx_params* cctxParams = ZSTD_createCCtxParams();
+ ZSTD_CCtxParams_init(cctxParams, cLevel);
+ ZSTD_CCtxParams_setParameter(cctxParams, ZSTD_c_enableDedicatedDictSearch, dedicatedDictSearch);
+ ZSTD_CCtxParams_setParameter(cctxParams, ZSTD_c_nbWorkers, 0);
+ ZSTD_CCtxParams_setParameter(cctxParams, ZSTD_c_forceAttachDict, dictAttachPref);
+
+ int result = bench(filenameTable->fileNames, (unsigned)filenameTable->tableSize, dictionary, blockSize, cLevel, nbDicts, nbBlocks, nbRounds, benchCompression, dictContentType, cctxParams);
UTIL_freeFileNamesTable(filenameTable);
free(nameTable);
+ ZSTD_freeCCtxParams(cctxParams);
return result;
}
From db2f4a6532532b9d532d7db34212fea3b1fc02f9 Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Fri, 21 Jan 2022 11:29:14 -0700
Subject: [PATCH 090/472] Move bitwise builtins into bits.h
---
lib/common/bits.h | 212 +++++++++++++++++++++++++
lib/common/bitstream.h | 47 +-----
lib/common/entropy_common.c | 41 +----
lib/common/fse_decompress.c | 3 +-
lib/common/zstd_internal.h | 92 -----------
lib/compress/fse_compress.c | 9 +-
lib/compress/huf_compress.c | 9 +-
lib/compress/zstd_compress.c | 1 +
lib/compress/zstd_compress_internal.h | 98 +-----------
lib/compress/zstd_lazy.c | 43 +----
lib/decompress/huf_decompress.c | 5 +-
lib/decompress/zstd_decompress.c | 1 +
lib/decompress/zstd_decompress_block.c | 3 +-
lib/dictBuilder/cover.c | 1 +
lib/dictBuilder/zdict.c | 82 +---------
tests/fuzz/huf_round_trip.c | 3 +-
16 files changed, 250 insertions(+), 400 deletions(-)
create mode 100644 lib/common/bits.h
diff --git a/lib/common/bits.h b/lib/common/bits.h
new file mode 100644
index 00000000000..67db5a9e78a
--- /dev/null
+++ b/lib/common/bits.h
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 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 ZSTD_BITS_H
+#define ZSTD_BITS_H
+
+#include "mem.h"
+
+MEM_STATIC unsigned ZSTD_highbit32(U32 val) /* compress, dictBuilder, decodeCorpus */
+{
+ assert(val != 0);
+ {
+# if defined(_MSC_VER) /* Visual */
+# if STATIC_BMI2 == 1
+ return _lzcnt_u32(val)^31;
+# else
+ if (val != 0) {
+ unsigned long r;
+ _BitScanReverse(&r, val);
+ return (unsigned)r;
+ } else {
+ /* Should not reach this code path */
+ __assume(0);
+ }
+# endif
+# elif defined(__GNUC__) && (__GNUC__ >= 3) /* GCC Intrinsic */
+ return (unsigned)__builtin_clz (val) ^ 31;
+# elif defined(__ICCARM__) /* IAR Intrinsic */
+ return 31 - __CLZ(val);
+# else /* Software version */
+ 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;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ return DeBruijnClz[(v * 0x07C4ACDDU) >> 27];
+# endif
+ }
+}
+
+MEM_STATIC unsigned ZSTD_countTrailingZeros32(U32 val)
+{
+ assert(val != 0);
+# if defined(_MSC_VER)
+ if (val != 0) {
+ unsigned long r;
+ _BitScanForward(&r, val);
+ return (unsigned)r;
+ } else {
+ /* Should not reach this code path */
+ __assume(0);
+ }
+# elif defined(__GNUC__) && (__GNUC__ >= 3)
+ return (unsigned)__builtin_ctz(val);
+# elif defined(__ICCARM__) /* IAR Intrinsic */
+ return __CTZ(val);
+# else
+ static const int DeBruijnBytePos[32] = { 0, 1, 28, 2, 29, 14, 24, 3,
+ 30, 22, 20, 15, 25, 17, 4, 8,
+ 31, 27, 13, 23, 21, 19, 16, 7,
+ 26, 12, 18, 6, 11, 5, 10, 9 };
+ return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27];
+# endif
+}
+
+MEM_STATIC unsigned ZSTD_countTrailingZeros64(U64 val)
+{
+ assert(val != 0);
+# if defined(_MSC_VER) && defined(_WIN64)
+# if STATIC_BMI2
+ return _tzcnt_u64(val);
+# else
+ if (val != 0) {
+ unsigned long r;
+ _BitScanForward64(&r, val);
+ return (unsigned)r;
+ } else {
+ /* Should not reach this code path */
+ __assume(0);
+ }
+# endif
+# elif defined(__GNUC__) && (__GNUC__ >= 4)
+ if (MEM_32bits()) {
+ U32 mostSignificantWord = (U32)(val >> 32);
+ U32 leastSignificantWord = (U32)val;
+ if (leastSignificantWord == 0) {
+ return 32 + (unsigned)__builtin_ctz(mostSignificantWord);
+ } else {
+ return (unsigned)__builtin_ctz(leastSignificantWord);
+ }
+ } else {
+ return (unsigned)__builtin_ctzll(val);
+ }
+# else
+ static const int DeBruijnBytePos[64] = { 0, 1, 2, 7, 3, 13, 8, 19,
+ 4, 25, 14, 28, 9, 34, 20, 56,
+ 5, 17, 26, 54, 15, 41, 29, 43,
+ 10, 31, 38, 35, 21, 45, 49, 57,
+ 63, 6, 12, 18, 24, 27, 33, 55,
+ 16, 53, 40, 42, 30, 37, 44, 48,
+ 62, 11, 23, 32, 52, 39, 36, 47,
+ 61, 22, 51, 46, 60, 50, 59, 58 };
+ return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58];
+# endif
+}
+
+MEM_STATIC unsigned ZSTD_NbCommonBytes(size_t val)
+{
+ if (MEM_isLittleEndian()) {
+ if (MEM_64bits()) {
+# if defined(_MSC_VER) && defined(_WIN64)
+# if STATIC_BMI2
+ return _tzcnt_u64(val) >> 3;
+# else
+ if (val != 0) {
+ unsigned long r;
+ _BitScanForward64(&r, (U64)val);
+ return (unsigned)(r >> 3);
+ } else {
+ /* Should not reach this code path */
+ __assume(0);
+ }
+# endif
+# elif defined(__GNUC__) && (__GNUC__ >= 4)
+ return (unsigned)(__builtin_ctzll((U64)val) >> 3);
+# else
+ static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2,
+ 0, 3, 1, 3, 1, 4, 2, 7,
+ 0, 2, 3, 6, 1, 5, 3, 5,
+ 1, 3, 4, 4, 2, 5, 6, 7,
+ 7, 0, 1, 2, 3, 3, 4, 6,
+ 2, 6, 5, 5, 3, 4, 5, 6,
+ 7, 1, 2, 4, 6, 4, 4, 5,
+ 7, 2, 6, 5, 7, 6, 7, 7 };
+ return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58];
+# endif
+ } else { /* 32 bits */
+# if defined(_MSC_VER)
+ if (val != 0) {
+ unsigned long r;
+ _BitScanForward(&r, (U32)val);
+ return (unsigned)(r >> 3);
+ } else {
+ /* Should not reach this code path */
+ __assume(0);
+ }
+# elif defined(__GNUC__) && (__GNUC__ >= 3)
+ return (unsigned)(__builtin_ctz((U32)val) >> 3);
+# else
+ static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0,
+ 3, 2, 2, 1, 3, 2, 0, 1,
+ 3, 3, 1, 2, 2, 2, 2, 0,
+ 3, 1, 2, 0, 1, 0, 1, 1 };
+ return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27];
+# endif
+ }
+ } else { /* Big Endian CPU */
+ if (MEM_64bits()) {
+# if defined(_MSC_VER) && defined(_WIN64)
+# if STATIC_BMI2
+ return _lzcnt_u64(val) >> 3;
+# else
+ if (val != 0) {
+ unsigned long r;
+ _BitScanReverse64(&r, (U64)val);
+ return (unsigned)(r >> 3);
+ } else {
+ /* Should not reach this code path */
+ __assume(0);
+ }
+# endif
+# elif defined(__GNUC__) && (__GNUC__ >= 4)
+ return (unsigned)(__builtin_clzll(val) >> 3);
+# else
+ unsigned r;
+ const unsigned n32 = sizeof(size_t)*4; /* calculate this way due to compiler complaining in 32-bits mode */
+ if (!(val>>n32)) { r=4; } else { r=0; val>>=n32; }
+ if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; }
+ r += (!val);
+ return r;
+# endif
+ } else { /* 32 bits */
+# if defined(_MSC_VER)
+ if (val != 0) {
+ unsigned long r;
+ _BitScanReverse(&r, (unsigned long)val);
+ return (unsigned)(r >> 3);
+ } else {
+ /* Should not reach this code path */
+ __assume(0);
+ }
+# elif defined(__GNUC__) && (__GNUC__ >= 3)
+ return (unsigned)(__builtin_clz((U32)val) >> 3);
+# else
+ unsigned r;
+ if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; }
+ r += (!val);
+ return r;
+# endif
+ } }
+}
+
+#endif /* ZSTD_BITS_H */
diff --git a/lib/common/bitstream.h b/lib/common/bitstream.h
index 84b6062ff35..103b378329b 100644
--- a/lib/common/bitstream.h
+++ b/lib/common/bitstream.h
@@ -30,6 +30,7 @@ extern "C" {
#include "compiler.h" /* UNLIKELY() */
#include "debug.h" /* assert(), DEBUGLOG(), RAWLOG() */
#include "error_private.h" /* error codes and messages */
+#include "bits.h" /* ZSTD_highbit32 */
/*=========================================
@@ -132,48 +133,6 @@ MEM_STATIC void BIT_flushBitsFast(BIT_CStream_t* bitC);
MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, unsigned nbBits);
/* faster, but works only if nbBits >= 1 */
-
-
-/*-**************************************************************
-* Internal functions
-****************************************************************/
-MEM_STATIC unsigned BIT_highbit32 (U32 val)
-{
- assert(val != 0);
- {
-# if defined(_MSC_VER) /* Visual */
-# if STATIC_BMI2 == 1
- return _lzcnt_u32(val) ^ 31;
-# else
- if (val != 0) {
- unsigned long r;
- _BitScanReverse(&r, val);
- return (unsigned)r;
- } else {
- /* Should not reach this code path */
- __assume(0);
- }
-# endif
-# elif defined(__GNUC__) && (__GNUC__ >= 3) /* Use GCC Intrinsic */
- return __builtin_clz (val) ^ 31;
-# elif defined(__ICCARM__) /* IAR Intrinsic */
- return 31 - __CLZ(val);
-# else /* Software version */
- static const unsigned 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;
- v |= v >> 1;
- v |= v >> 2;
- v |= v >> 4;
- v |= v >> 8;
- v |= v >> 16;
- return DeBruijnClz[ (U32) (v * 0x07C4ACDDU) >> 27];
-# endif
- }
-}
-
/*===== Local Constants =====*/
static const unsigned BIT_mask[] = {
0, 1, 3, 7, 0xF, 0x1F,
@@ -291,7 +250,7 @@ MEM_STATIC size_t BIT_initDStream(BIT_DStream_t* bitD, const void* srcBuffer, si
bitD->ptr = (const char*)srcBuffer + srcSize - sizeof(bitD->bitContainer);
bitD->bitContainer = MEM_readLEST(bitD->ptr);
{ BYTE const lastByte = ((const BYTE*)srcBuffer)[srcSize-1];
- bitD->bitsConsumed = lastByte ? 8 - BIT_highbit32(lastByte) : 0; /* ensures bitsConsumed is always set */
+ bitD->bitsConsumed = lastByte ? 8 - ZSTD_highbit32(lastByte) : 0; /* ensures bitsConsumed is always set */
if (lastByte == 0) return ERROR(GENERIC); /* endMark not present */ }
} else {
bitD->ptr = bitD->start;
@@ -319,7 +278,7 @@ MEM_STATIC size_t BIT_initDStream(BIT_DStream_t* bitD, const void* srcBuffer, si
default: break;
}
{ BYTE const lastByte = ((const BYTE*)srcBuffer)[srcSize-1];
- bitD->bitsConsumed = lastByte ? 8 - BIT_highbit32(lastByte) : 0;
+ bitD->bitsConsumed = lastByte ? 8 - ZSTD_highbit32(lastByte) : 0;
if (lastByte == 0) return ERROR(corruption_detected); /* endMark not present */
}
bitD->bitsConsumed += (U32)(sizeof(bitD->bitContainer) - srcSize)*8;
diff --git a/lib/common/entropy_common.c b/lib/common/entropy_common.c
index 4229b40c5ee..98bd4238d62 100644
--- a/lib/common/entropy_common.c
+++ b/lib/common/entropy_common.c
@@ -21,6 +21,7 @@
#include "fse.h"
#define HUF_STATIC_LINKING_ONLY /* HUF_TABLELOG_ABSOLUTEMAX */
#include "huf.h"
+#include "bits.h" /* ZSDT_highbit32, ZSTD_countTrailingZeros32 */
/*=== Version ===*/
@@ -38,34 +39,6 @@ const char* HUF_getErrorName(size_t code) { return ERR_getErrorName(code); }
/*-**************************************************************
* FSE NCount encoding-decoding
****************************************************************/
-static U32 FSE_ctz(U32 val)
-{
- assert(val != 0);
- {
-# if defined(_MSC_VER) /* Visual */
- if (val != 0) {
- unsigned long r;
- _BitScanForward(&r, val);
- return (unsigned)r;
- } else {
- /* Should not reach this code path */
- __assume(0);
- }
-# elif defined(__GNUC__) && (__GNUC__ >= 3) /* GCC Intrinsic */
- return __builtin_ctz(val);
-# elif defined(__ICCARM__) /* IAR Intrinsic */
- return __CTZ(val);
-# else /* Software version */
- U32 count = 0;
- while ((val & 1) == 0) {
- val >>= 1;
- ++count;
- }
- return count;
-# endif
- }
-}
-
FORCE_INLINE_TEMPLATE
size_t FSE_readNCount_body(short* normalizedCounter, unsigned* maxSVPtr, unsigned* tableLogPtr,
const void* headerBuffer, size_t hbSize)
@@ -113,7 +86,7 @@ size_t FSE_readNCount_body(short* normalizedCounter, unsigned* maxSVPtr, unsigne
* repeat.
* Avoid UB by setting the high bit to 1.
*/
- int repeats = FSE_ctz(~bitStream | 0x80000000) >> 1;
+ int repeats = ZSTD_countTrailingZeros32(~bitStream | 0x80000000) >> 1;
while (repeats >= 12) {
charnum += 3 * 12;
if (LIKELY(ip <= iend-7)) {
@@ -124,7 +97,7 @@ size_t FSE_readNCount_body(short* normalizedCounter, unsigned* maxSVPtr, unsigne
ip = iend - 4;
}
bitStream = MEM_readLE32(ip) >> bitCount;
- repeats = FSE_ctz(~bitStream | 0x80000000) >> 1;
+ repeats = ZSTD_countTrailingZeros32(~bitStream | 0x80000000) >> 1;
}
charnum += 3 * repeats;
bitStream >>= 2 * repeats;
@@ -189,7 +162,7 @@ size_t FSE_readNCount_body(short* normalizedCounter, unsigned* maxSVPtr, unsigne
* know that threshold > 1.
*/
if (remaining <= 1) break;
- nbBits = BIT_highbit32(remaining) + 1;
+ nbBits = ZSTD_highbit32(remaining) + 1;
threshold = 1 << (nbBits - 1);
}
if (charnum >= maxSV1) break;
@@ -312,14 +285,14 @@ HUF_readStats_body(BYTE* huffWeight, size_t hwSize, U32* rankStats,
if (weightTotal == 0) return ERROR(corruption_detected);
/* get last non-null symbol weight (implied, total must be 2^n) */
- { U32 const tableLog = BIT_highbit32(weightTotal) + 1;
+ { U32 const tableLog = ZSTD_highbit32(weightTotal) + 1;
if (tableLog > HUF_TABLELOG_MAX) return ERROR(corruption_detected);
*tableLogPtr = tableLog;
/* determine last weight */
{ U32 const total = 1 << tableLog;
U32 const rest = total - weightTotal;
- U32 const verif = 1 << BIT_highbit32(rest);
- U32 const lastWeight = BIT_highbit32(rest) + 1;
+ U32 const verif = 1 << ZSTD_highbit32(rest);
+ U32 const lastWeight = ZSTD_highbit32(rest) + 1;
if (verif != rest) return ERROR(corruption_detected); /* last value must be a clean power of 2 */
huffWeight[oSize] = (BYTE)lastWeight;
rankStats[lastWeight]++;
diff --git a/lib/common/fse_decompress.c b/lib/common/fse_decompress.c
index bc0c1be2f68..184b35dd17a 100644
--- a/lib/common/fse_decompress.c
+++ b/lib/common/fse_decompress.c
@@ -24,6 +24,7 @@
#include "error_private.h"
#define ZSTD_DEPS_NEED_MALLOC
#include "zstd_deps.h"
+#include "bits.h" /* ZSTD_highbit32 */
/* **************************************************************
@@ -166,7 +167,7 @@ static size_t FSE_buildDTable_internal(FSE_DTable* dt, const short* normalizedCo
for (u=0; u= 3) /* GCC Intrinsic */
- return (U32)__builtin_clz (val) ^ 31;
-# elif defined(__ICCARM__) /* IAR Intrinsic */
- return 31 - __CLZ(val);
-# else /* Software version */
- 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;
- v |= v >> 1;
- v |= v >> 2;
- v |= v >> 4;
- v |= v >> 8;
- v |= v >> 16;
- return DeBruijnClz[(v * 0x07C4ACDDU) >> 27];
-# endif
- }
-}
-
-/**
- * Counts the number of trailing zeros of a `size_t`.
- * Most compilers should support CTZ as a builtin. A backup
- * implementation is provided if the builtin isn't supported, but
- * it may not be terribly efficient.
- */
-MEM_STATIC unsigned ZSTD_countTrailingZeros(size_t val)
-{
- if (MEM_64bits()) {
-# if defined(_MSC_VER) && defined(_WIN64)
-# if STATIC_BMI2
- return _tzcnt_u64(val);
-# else
- if (val != 0) {
- unsigned long r;
- _BitScanForward64(&r, (U64)val);
- return (unsigned)r;
- } else {
- /* Should not reach this code path */
- __assume(0);
- }
-# endif
-# elif defined(__GNUC__) && (__GNUC__ >= 4)
- return (unsigned)__builtin_ctzll((U64)val);
-# else
- static const int DeBruijnBytePos[64] = { 0, 1, 2, 7, 3, 13, 8, 19,
- 4, 25, 14, 28, 9, 34, 20, 56,
- 5, 17, 26, 54, 15, 41, 29, 43,
- 10, 31, 38, 35, 21, 45, 49, 57,
- 63, 6, 12, 18, 24, 27, 33, 55,
- 16, 53, 40, 42, 30, 37, 44, 48,
- 62, 11, 23, 32, 52, 39, 36, 47,
- 61, 22, 51, 46, 60, 50, 59, 58 };
- return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58];
-# endif
- } else { /* 32 bits */
-# if defined(_MSC_VER)
- if (val != 0) {
- unsigned long r;
- _BitScanForward(&r, (U32)val);
- return (unsigned)r;
- } else {
- /* Should not reach this code path */
- __assume(0);
- }
-# elif defined(__GNUC__) && (__GNUC__ >= 3)
- return (unsigned)__builtin_ctz((U32)val);
-# else
- static const int DeBruijnBytePos[32] = { 0, 1, 28, 2, 29, 14, 24, 3,
- 30, 22, 20, 15, 25, 17, 4, 8,
- 31, 27, 13, 23, 21, 19, 16, 7,
- 26, 12, 18, 6, 11, 5, 10, 9 };
- return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27];
-# endif
- }
-}
-
-
/* ZSTD_invalidateRepCodes() :
* ensures next compression will not use repcodes from previous block.
* Note : only works with regular variant;
diff --git a/lib/compress/fse_compress.c b/lib/compress/fse_compress.c
index 5547b4ac099..21be8c54fef 100644
--- a/lib/compress/fse_compress.c
+++ b/lib/compress/fse_compress.c
@@ -26,6 +26,7 @@
#define ZSTD_DEPS_NEED_MALLOC
#define ZSTD_DEPS_NEED_MATH64
#include "../common/zstd_deps.h" /* ZSTD_malloc, ZSTD_free, ZSTD_memcpy, ZSTD_memset */
+#include "../common/bits.h" /* ZSTD_highbit32 */
/* **************************************************************
@@ -191,7 +192,7 @@ size_t FSE_buildCTable_wksp(FSE_CTable* ct,
break;
default :
assert(normalizedCounter[s] > 1);
- { U32 const maxBitsOut = tableLog - BIT_highbit32 ((U32)normalizedCounter[s]-1);
+ { U32 const maxBitsOut = tableLog - ZSTD_highbit32 ((U32)normalizedCounter[s]-1);
U32 const minStatePlus = (U32)normalizedCounter[s] << maxBitsOut;
symbolTT[s].deltaNbBits = (maxBitsOut << 16) - minStatePlus;
symbolTT[s].deltaFindState = (int)(total - (unsigned)normalizedCounter[s]);
@@ -355,8 +356,8 @@ void FSE_freeCTable (FSE_CTable* ct) { ZSTD_free(ct); }
/* provides the minimum logSize to safely represent a distribution */
static unsigned FSE_minTableLog(size_t srcSize, unsigned maxSymbolValue)
{
- U32 minBitsSrc = BIT_highbit32((U32)(srcSize)) + 1;
- U32 minBitsSymbols = BIT_highbit32(maxSymbolValue) + 2;
+ U32 minBitsSrc = ZSTD_highbit32((U32)(srcSize)) + 1;
+ U32 minBitsSymbols = ZSTD_highbit32(maxSymbolValue) + 2;
U32 minBits = minBitsSrc < minBitsSymbols ? minBitsSrc : minBitsSymbols;
assert(srcSize > 1); /* Not supported, RLE should be used instead */
return minBits;
@@ -364,7 +365,7 @@ static unsigned FSE_minTableLog(size_t srcSize, unsigned maxSymbolValue)
unsigned FSE_optimalTableLog_internal(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue, unsigned minus)
{
- U32 maxBitsSrc = BIT_highbit32((U32)(srcSize - 1)) - minus;
+ U32 maxBitsSrc = ZSTD_highbit32((U32)(srcSize - 1)) - minus;
U32 tableLog = maxTableLog;
U32 minBits = FSE_minTableLog(srcSize, maxSymbolValue);
assert(srcSize > 1); /* Not supported, RLE should be used instead */
diff --git a/lib/compress/huf_compress.c b/lib/compress/huf_compress.c
index a8677733b10..5d90c162e73 100644
--- a/lib/compress/huf_compress.c
+++ b/lib/compress/huf_compress.c
@@ -32,6 +32,7 @@
#define HUF_STATIC_LINKING_ONLY
#include "../common/huf.h"
#include "../common/error_private.h"
+#include "../common/bits.h" /* ZSTD_highbit32 */
/* **************************************************************
@@ -407,7 +408,7 @@ static U32 HUF_setMaxHeight(nodeElt* huffNode, U32 lastNonNull, U32 targetNbBits
/* Try to reduce the next power of 2 above totalCost because we
* gain back half the rank.
*/
- U32 nBitsToDecrease = BIT_highbit32((U32)totalCost) + 1;
+ U32 nBitsToDecrease = ZSTD_highbit32((U32)totalCost) + 1;
for ( ; nBitsToDecrease > 1; nBitsToDecrease--) {
U32 const highPos = rankLast[nBitsToDecrease];
U32 const lowPos = rankLast[nBitsToDecrease-1];
@@ -505,7 +506,7 @@ typedef struct {
*/
#define RANK_POSITION_MAX_COUNT_LOG 32
#define RANK_POSITION_LOG_BUCKETS_BEGIN (RANK_POSITION_TABLE_SIZE - 1) - RANK_POSITION_MAX_COUNT_LOG - 1 /* == 158 */
-#define RANK_POSITION_DISTINCT_COUNT_CUTOFF RANK_POSITION_LOG_BUCKETS_BEGIN + BIT_highbit32(RANK_POSITION_LOG_BUCKETS_BEGIN) /* == 166 */
+#define RANK_POSITION_DISTINCT_COUNT_CUTOFF RANK_POSITION_LOG_BUCKETS_BEGIN + ZSTD_highbit32(RANK_POSITION_LOG_BUCKETS_BEGIN) /* == 166 */
/* Return the appropriate bucket index for a given count. See definition of
* RANK_POSITION_DISTINCT_COUNT_CUTOFF for explanation of bucketing strategy.
@@ -513,7 +514,7 @@ typedef struct {
static U32 HUF_getIndex(U32 const count) {
return (count < RANK_POSITION_DISTINCT_COUNT_CUTOFF)
? count
- : BIT_highbit32(count) + RANK_POSITION_LOG_BUCKETS_BEGIN;
+ : ZSTD_highbit32(count) + RANK_POSITION_LOG_BUCKETS_BEGIN;
}
/* Helper swap function for HUF_quickSortPartition() */
@@ -870,7 +871,7 @@ FORCE_INLINE_TEMPLATE void HUF_addBits(HUF_CStream_t* bitC, HUF_CElt elt, int id
#if DEBUGLEVEL >= 1
{
size_t const nbBits = HUF_getNbBits(elt);
- size_t const dirtyBits = nbBits == 0 ? 0 : BIT_highbit32((U32)nbBits) + 1;
+ size_t const dirtyBits = nbBits == 0 ? 0 : ZSTD_highbit32((U32)nbBits) + 1;
(void)dirtyBits;
/* Middle bits are 0. */
assert(((elt >> dirtyBits) << (dirtyBits + nbBits)) == 0);
diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
index 37b6e2ff421..aa1a9f35b9d 100644
--- a/lib/compress/zstd_compress.c
+++ b/lib/compress/zstd_compress.c
@@ -27,6 +27,7 @@
#include "zstd_opt.h"
#include "zstd_ldm.h"
#include "zstd_compress_superblock.h"
+#include "../common/bits.h" /* ZSTD_highbit32 */
/* ***************************************************************
* Tuning parameters
diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h
index f027f461283..bbae53303e6 100644
--- a/lib/compress/zstd_compress_internal.h
+++ b/lib/compress/zstd_compress_internal.h
@@ -23,6 +23,7 @@
#ifdef ZSTD_MULTITHREAD
# include "zstdmt_compress.h"
#endif
+#include "../common/bits.h" /* ZSTD_highbit32, ZSTD_NbCommonBytes */
#if defined (__cplusplus)
extern "C" {
@@ -699,103 +700,6 @@ ZSTD_newRep(U32 const rep[ZSTD_REP_NUM], U32 const offBase, U32 const ll0)
/*-*************************************
* Match length counter
***************************************/
-static unsigned ZSTD_NbCommonBytes (size_t val)
-{
- if (MEM_isLittleEndian()) {
- if (MEM_64bits()) {
-# if defined(_MSC_VER) && defined(_WIN64)
-# if STATIC_BMI2
- return _tzcnt_u64(val) >> 3;
-# else
- if (val != 0) {
- unsigned long r;
- _BitScanForward64(&r, (U64)val);
- return (unsigned)(r >> 3);
- } else {
- /* Should not reach this code path */
- __assume(0);
- }
-# endif
-# elif defined(__GNUC__) && (__GNUC__ >= 4)
- return (unsigned)(__builtin_ctzll((U64)val) >> 3);
-# else
- static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2,
- 0, 3, 1, 3, 1, 4, 2, 7,
- 0, 2, 3, 6, 1, 5, 3, 5,
- 1, 3, 4, 4, 2, 5, 6, 7,
- 7, 0, 1, 2, 3, 3, 4, 6,
- 2, 6, 5, 5, 3, 4, 5, 6,
- 7, 1, 2, 4, 6, 4, 4, 5,
- 7, 2, 6, 5, 7, 6, 7, 7 };
- return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58];
-# endif
- } else { /* 32 bits */
-# if defined(_MSC_VER)
- if (val != 0) {
- unsigned long r;
- _BitScanForward(&r, (U32)val);
- return (unsigned)(r >> 3);
- } else {
- /* Should not reach this code path */
- __assume(0);
- }
-# elif defined(__GNUC__) && (__GNUC__ >= 3)
- return (unsigned)(__builtin_ctz((U32)val) >> 3);
-# else
- static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0,
- 3, 2, 2, 1, 3, 2, 0, 1,
- 3, 3, 1, 2, 2, 2, 2, 0,
- 3, 1, 2, 0, 1, 0, 1, 1 };
- return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27];
-# endif
- }
- } else { /* Big Endian CPU */
- if (MEM_64bits()) {
-# if defined(_MSC_VER) && defined(_WIN64)
-# if STATIC_BMI2
- return _lzcnt_u64(val) >> 3;
-# else
- if (val != 0) {
- unsigned long r;
- _BitScanReverse64(&r, (U64)val);
- return (unsigned)(r >> 3);
- } else {
- /* Should not reach this code path */
- __assume(0);
- }
-# endif
-# elif defined(__GNUC__) && (__GNUC__ >= 4)
- return (unsigned)(__builtin_clzll(val) >> 3);
-# else
- unsigned r;
- const unsigned n32 = sizeof(size_t)*4; /* calculate this way due to compiler complaining in 32-bits mode */
- if (!(val>>n32)) { r=4; } else { r=0; val>>=n32; }
- if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; }
- r += (!val);
- return r;
-# endif
- } else { /* 32 bits */
-# if defined(_MSC_VER)
- if (val != 0) {
- unsigned long r;
- _BitScanReverse(&r, (unsigned long)val);
- return (unsigned)(r >> 3);
- } else {
- /* Should not reach this code path */
- __assume(0);
- }
-# elif defined(__GNUC__) && (__GNUC__ >= 3)
- return (unsigned)(__builtin_clz((U32)val) >> 3);
-# else
- unsigned r;
- if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; }
- r += (!val);
- return r;
-# endif
- } }
-}
-
-
MEM_STATIC size_t ZSTD_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* const pInLimit)
{
const BYTE* const pStart = pIn;
diff --git a/lib/compress/zstd_lazy.c b/lib/compress/zstd_lazy.c
index 47c0687c95d..c619aea3a9b 100644
--- a/lib/compress/zstd_lazy.c
+++ b/lib/compress/zstd_lazy.c
@@ -10,6 +10,7 @@
#include "zstd_compress_internal.h"
#include "zstd_lazy.h"
+#include "../common/bits.h" /* ZSTD_countTrailingZeros64 */
/*-*************************************
@@ -765,44 +766,6 @@ size_t ZSTD_HcFindBestMatch(
typedef U64 ZSTD_VecMask; /* Clarifies when we are interacting with a U64 representing a mask of matches */
-/* ZSTD_VecMask_next():
- * Starting from the LSB, returns the idx of the next non-zero bit.
- * Basically counting the nb of trailing zeroes.
- */
-static U32 ZSTD_VecMask_next(ZSTD_VecMask val) {
- assert(val != 0);
-# if defined(_MSC_VER) && defined(_WIN64)
- if (val != 0) {
- unsigned long r;
- _BitScanForward64(&r, val);
- return (U32)(r);
- } else {
- /* Should not reach this code path */
- __assume(0);
- }
-# elif (defined(__GNUC__) && ((__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))
- if (sizeof(size_t) == 4) {
- U32 mostSignificantWord = (U32)(val >> 32);
- U32 leastSignificantWord = (U32)val;
- if (leastSignificantWord == 0) {
- return 32 + (U32)__builtin_ctz(mostSignificantWord);
- } else {
- return (U32)__builtin_ctz(leastSignificantWord);
- }
- } else {
- return (U32)__builtin_ctzll(val);
- }
-# else
- /* Software ctz version: http://aggregate.org/MAGIC/#Trailing%20Zero%20Count
- * and: https://stackoverflow.com/questions/2709430/count-number-of-bits-in-a-64-bit-long-big-integer
- */
- val = ~val & (val - 1ULL); /* Lowest set bit mask */
- val = val - ((val >> 1) & 0x5555555555555555);
- val = (val & 0x3333333333333333ULL) + ((val >> 2) & 0x3333333333333333ULL);
- return (U32)((((val + (val >> 4)) & 0xF0F0F0F0F0F0F0FULL) * 0x101010101010101ULL) >> 56);
-# endif
-}
-
/* ZSTD_rotateRight_*():
* Rotates a bitfield to the right by "count" bits.
* https://en.wikipedia.org/w/index.php?title=Circular_shift&oldid=991635599#Implementing_circular_shifts
@@ -1202,7 +1165,7 @@ size_t ZSTD_RowFindBestMatch(
/* Cycle through the matches and prefetch */
for (; (matches > 0) && (nbAttempts > 0); --nbAttempts, matches &= (matches - 1)) {
- U32 const matchPos = (head + ZSTD_VecMask_next(matches)) & rowMask;
+ U32 const matchPos = (head + ZSTD_countTrailingZeros64(matches)) & rowMask;
U32 const matchIndex = row[matchPos];
assert(numMatches < rowEntries);
if (matchIndex < lowLimit)
@@ -1270,7 +1233,7 @@ size_t ZSTD_RowFindBestMatch(
ZSTD_VecMask matches = ZSTD_row_getMatchMask(dmsTagRow, (BYTE)dmsTag, head, rowEntries);
for (; (matches > 0) && (nbAttempts > 0); --nbAttempts, matches &= (matches - 1)) {
- U32 const matchPos = (head + ZSTD_VecMask_next(matches)) & rowMask;
+ U32 const matchPos = (head + ZSTD_countTrailingZeros64(matches)) & rowMask;
U32 const matchIndex = dmsRow[matchPos];
if (matchIndex < dmsLowestIndex)
break;
diff --git a/lib/decompress/huf_decompress.c b/lib/decompress/huf_decompress.c
index 2027188255e..4541ca98c3e 100644
--- a/lib/decompress/huf_decompress.c
+++ b/lib/decompress/huf_decompress.c
@@ -23,6 +23,7 @@
#include "../common/huf.h"
#include "../common/error_private.h"
#include "../common/zstd_internal.h"
+#include "../common/bits.h" /* ZSTD_highbit32, ZSTD_countTrailingZeros64 */
/* **************************************************************
* Constants
@@ -142,7 +143,7 @@ static DTableDesc HUF_getDTableDesc(const HUF_DTable* table)
static size_t HUF_initDStream(BYTE const* ip) {
BYTE const lastByte = ip[7];
- size_t const bitsConsumed = lastByte ? 8 - BIT_highbit32(lastByte) : 0;
+ size_t const bitsConsumed = lastByte ? 8 - ZSTD_highbit32(lastByte) : 0;
size_t const value = MEM_readLEST(ip) | 1;
assert(bitsConsumed <= 8);
return value << bitsConsumed;
@@ -263,7 +264,7 @@ static size_t HUF_initRemainingDStream(BIT_DStream_t* bit, HUF_DecompressAsmArgs
/* Construct the BIT_DStream_t. */
bit->bitContainer = MEM_readLE64(args->ip[stream]);
- bit->bitsConsumed = ZSTD_countTrailingZeros((size_t)args->bits[stream]);
+ bit->bitsConsumed = ZSTD_countTrailingZeros64(args->bits[stream]);
bit->start = (const char*)args->iend[0];
bit->limitPtr = bit->start + sizeof(size_t);
bit->ptr = (const char*)args->ip[stream];
diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c
index 44649dd3718..8950453f446 100644
--- a/lib/decompress/zstd_decompress.c
+++ b/lib/decompress/zstd_decompress.c
@@ -66,6 +66,7 @@
#include "zstd_decompress_internal.h" /* ZSTD_DCtx */
#include "zstd_ddict.h" /* ZSTD_DDictDictContent */
#include "zstd_decompress_block.h" /* ZSTD_decompressBlock_internal */
+#include "../common/bits.h" /* ZSTD_highbit32 */
#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1)
# include "../legacy/zstd_legacy.h"
diff --git a/lib/decompress/zstd_decompress_block.c b/lib/decompress/zstd_decompress_block.c
index 758c7ad99ae..6f8a75f538f 100644
--- a/lib/decompress/zstd_decompress_block.c
+++ b/lib/decompress/zstd_decompress_block.c
@@ -26,6 +26,7 @@
#include "zstd_decompress_internal.h" /* ZSTD_DCtx */
#include "zstd_ddict.h" /* ZSTD_DDictDictContent */
#include "zstd_decompress_block.h"
+#include "../common/bits.h" /* ZSTD_highbit32 */
/*_*******************************************************
* Macros
@@ -551,7 +552,7 @@ void ZSTD_buildFSETable_body(ZSTD_seqSymbol* dt,
for (u=0; u> 3);
- } else {
- /* Should not reach this code path */
- __assume(0);
- }
-# elif defined(__GNUC__) && (__GNUC__ >= 3)
- return (unsigned)(__builtin_ctzll((U64)val) >> 3);
-# else
- static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2, 0, 3, 1, 3, 1, 4, 2, 7, 0, 2, 3, 6, 1, 5, 3, 5, 1, 3, 4, 4, 2, 5, 6, 7, 7, 0, 1, 2, 3, 3, 4, 6, 2, 6, 5, 5, 3, 4, 5, 6, 7, 1, 2, 4, 6, 4, 4, 5, 7, 2, 6, 5, 7, 6, 7, 7 };
- return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58];
-# endif
- } else { /* 32 bits */
-# if defined(_MSC_VER)
- if (val != 0) {
- unsigned long r;
- _BitScanForward(&r, (U32)val);
- return (unsigned)(r >> 3);
- } else {
- /* Should not reach this code path */
- __assume(0);
- }
-# elif defined(__GNUC__) && (__GNUC__ >= 3)
- return (unsigned)(__builtin_ctz((U32)val) >> 3);
-# else
- static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 1, 3, 2, 0, 1, 3, 3, 1, 2, 2, 2, 2, 0, 3, 1, 2, 0, 1, 0, 1, 1 };
- return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27];
-# endif
- }
- } else { /* Big Endian CPU */
- if (MEM_64bits()) {
-# if defined(_MSC_VER) && defined(_WIN64)
- if (val != 0) {
- unsigned long r;
- _BitScanReverse64(&r, val);
- return (unsigned)(r >> 3);
- } else {
- /* Should not reach this code path */
- __assume(0);
- }
-# elif defined(__GNUC__) && (__GNUC__ >= 3)
- return (unsigned)(__builtin_clzll(val) >> 3);
-# else
- unsigned r;
- const unsigned n32 = sizeof(size_t)*4; /* calculate this way due to compiler complaining in 32-bits mode */
- if (!(val>>n32)) { r=4; } else { r=0; val>>=n32; }
- if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; }
- r += (!val);
- return r;
-# endif
- } else { /* 32 bits */
-# if defined(_MSC_VER)
- if (val != 0) {
- unsigned long r;
- _BitScanReverse(&r, (unsigned long)val);
- return (unsigned)(r >> 3);
- } else {
- /* Should not reach this code path */
- __assume(0);
- }
-# elif defined(__GNUC__) && (__GNUC__ >= 3)
- return (unsigned)(__builtin_clz((U32)val) >> 3);
-# else
- unsigned r;
- if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; }
- r += (!val);
- return r;
-# endif
- } }
-}
-
-
/*! ZDICT_count() :
Count the nb of common bytes between 2 pointers.
Note : this function presumes end of buffer followed by noisy guard band.
@@ -223,7 +145,7 @@ static size_t ZDICT_count(const void* pIn, const void* pMatch)
pMatch = (const char*)pMatch+sizeof(size_t);
continue;
}
- pIn = (const char*)pIn+ZDICT_NbCommonBytes(diff);
+ pIn = (const char*)pIn+ZSTD_NbCommonBytes(diff);
return (size_t)((const char*)pIn - pStart);
}
}
diff --git a/tests/fuzz/huf_round_trip.c b/tests/fuzz/huf_round_trip.c
index 0e26ca9b51c..bda9a9842ce 100644
--- a/tests/fuzz/huf_round_trip.c
+++ b/tests/fuzz/huf_round_trip.c
@@ -24,11 +24,12 @@
#include "common/huf.h"
#include "fuzz_helpers.h"
#include "fuzz_data_producer.h"
+#include "common/bits.h"
static size_t adjustTableLog(size_t tableLog, size_t maxSymbol)
{
size_t const alphabetSize = maxSymbol + 1;
- size_t minTableLog = BIT_highbit32(alphabetSize) + 1;
+ size_t minTableLog = ZSTD_highbit32(alphabetSize) + 1;
if ((alphabetSize & (alphabetSize - 1)) != 0) {
++minTableLog;
}
From 796182652d652d57fca9ece6c63715db2d0e0858 Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Mon, 31 Jan 2022 14:59:51 -0500
Subject: [PATCH 091/472] Pull out software fallbacks
---
lib/common/bits.h | 124 +++++++++++++++++++++++++++++-----------------
lib/common/mem.h | 34 ++++++++-----
2 files changed, 100 insertions(+), 58 deletions(-)
diff --git a/lib/common/bits.h b/lib/common/bits.h
index 67db5a9e78a..65fb456db84 100644
--- a/lib/common/bits.h
+++ b/lib/common/bits.h
@@ -13,6 +13,19 @@
#include "mem.h"
+MEM_STATIC unsigned ZSTD_highbit32_fallback(U32 val) {
+ 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 };
+ val |= val >> 1;
+ val |= val >> 2;
+ val |= val >> 4;
+ val |= val >> 8;
+ val |= val >> 16;
+ return DeBruijnClz[(val * 0x07C4ACDDU) >> 27];
+}
+
MEM_STATIC unsigned ZSTD_highbit32(U32 val) /* compress, dictBuilder, decodeCorpus */
{
assert(val != 0);
@@ -35,18 +48,20 @@ MEM_STATIC unsigned ZSTD_highbit32(U32 val) /* compress, dictBuilder, decodeCo
# elif defined(__ICCARM__) /* IAR Intrinsic */
return 31 - __CLZ(val);
# else /* Software version */
- 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;
- v |= v >> 1;
- v |= v >> 2;
- v |= v >> 4;
- v |= v >> 8;
- v |= v >> 16;
- return DeBruijnClz[(v * 0x07C4ACDDU) >> 27];
+ return ZSTD_highbit32_fallback(val);
# endif
}
}
+MEM_STATIC unsigned ZSTD_countTrailingZeros32_fallback(U32 val)
+{
+ static const int DeBruijnBytePos[32] = { 0, 1, 28, 2, 29, 14, 24, 3,
+ 30, 22, 20, 15, 25, 17, 4, 8,
+ 31, 27, 13, 23, 21, 19, 16, 7,
+ 26, 12, 18, 6, 11, 5, 10, 9 };
+ return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27];
+}
+
MEM_STATIC unsigned ZSTD_countTrailingZeros32(U32 val)
{
assert(val != 0);
@@ -64,14 +79,23 @@ MEM_STATIC unsigned ZSTD_countTrailingZeros32(U32 val)
# elif defined(__ICCARM__) /* IAR Intrinsic */
return __CTZ(val);
# else
- static const int DeBruijnBytePos[32] = { 0, 1, 28, 2, 29, 14, 24, 3,
- 30, 22, 20, 15, 25, 17, 4, 8,
- 31, 27, 13, 23, 21, 19, 16, 7,
- 26, 12, 18, 6, 11, 5, 10, 9 };
- return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27];
+ return ZSTD_countTrailingZeros32_fallback(val);
# endif
}
+MEM_STATIC unsigned ZSTD_countTrailingZeros64_fallback(U64 val)
+{
+ static const int DeBruijnBytePos[64] = { 0, 1, 2, 7, 3, 13, 8, 19,
+ 4, 25, 14, 28, 9, 34, 20, 56,
+ 5, 17, 26, 54, 15, 41, 29, 43,
+ 10, 31, 38, 35, 21, 45, 49, 57,
+ 63, 6, 12, 18, 24, 27, 33, 55,
+ 16, 53, 40, 42, 30, 37, 44, 48,
+ 62, 11, 23, 32, 52, 39, 36, 47,
+ 61, 22, 51, 46, 60, 50, 59, 58 };
+ return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58];
+}
+
MEM_STATIC unsigned ZSTD_countTrailingZeros64(U64 val)
{
assert(val != 0);
@@ -101,18 +125,46 @@ MEM_STATIC unsigned ZSTD_countTrailingZeros64(U64 val)
return (unsigned)__builtin_ctzll(val);
}
# else
- static const int DeBruijnBytePos[64] = { 0, 1, 2, 7, 3, 13, 8, 19,
- 4, 25, 14, 28, 9, 34, 20, 56,
- 5, 17, 26, 54, 15, 41, 29, 43,
- 10, 31, 38, 35, 21, 45, 49, 57,
- 63, 6, 12, 18, 24, 27, 33, 55,
- 16, 53, 40, 42, 30, 37, 44, 48,
- 62, 11, 23, 32, 52, 39, 36, 47,
- 61, 22, 51, 46, 60, 50, 59, 58 };
- return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58];
+ return ZSTD_countTrailingZeros64_fallback(val);
# endif
}
+MEM_STATIC unsigned ZSTD_NbCommonBytes_fallback(size_t val)
+{
+ if (MEM_isLittleEndian()) {
+ if (MEM_64bits()) {
+ static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2,
+ 0, 3, 1, 3, 1, 4, 2, 7,
+ 0, 2, 3, 6, 1, 5, 3, 5,
+ 1, 3, 4, 4, 2, 5, 6, 7,
+ 7, 0, 1, 2, 3, 3, 4, 6,
+ 2, 6, 5, 5, 3, 4, 5, 6,
+ 7, 1, 2, 4, 6, 4, 4, 5,
+ 7, 2, 6, 5, 7, 6, 7, 7 };
+ return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58];
+ } else { /* 32 bits */
+ static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0,
+ 3, 2, 2, 1, 3, 2, 0, 1,
+ 3, 3, 1, 2, 2, 2, 2, 0,
+ 3, 1, 2, 0, 1, 0, 1, 1 };
+ return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27];
+ }
+ } else { /* Big Endian CPU */
+ unsigned r;
+ if (MEM_64bits()) {
+ const unsigned n32 = sizeof(size_t)*4; /* calculate this way due to compiler complaining in 32-bits mode */
+ if (!(val>>n32)) { r=4; } else { r=0; val>>=n32; }
+ if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; }
+ r += (!val);
+ return r;
+ } else { /* 32 bits */
+ if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; }
+ r += (!val);
+ return r;
+ }
+ }
+}
+
MEM_STATIC unsigned ZSTD_NbCommonBytes(size_t val)
{
if (MEM_isLittleEndian()) {
@@ -133,15 +185,7 @@ MEM_STATIC unsigned ZSTD_NbCommonBytes(size_t val)
# elif defined(__GNUC__) && (__GNUC__ >= 4)
return (unsigned)(__builtin_ctzll((U64)val) >> 3);
# else
- static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2,
- 0, 3, 1, 3, 1, 4, 2, 7,
- 0, 2, 3, 6, 1, 5, 3, 5,
- 1, 3, 4, 4, 2, 5, 6, 7,
- 7, 0, 1, 2, 3, 3, 4, 6,
- 2, 6, 5, 5, 3, 4, 5, 6,
- 7, 1, 2, 4, 6, 4, 4, 5,
- 7, 2, 6, 5, 7, 6, 7, 7 };
- return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58];
+ return ZSTD_NbCommonBytes_fallback(val);
# endif
} else { /* 32 bits */
# if defined(_MSC_VER)
@@ -156,11 +200,7 @@ MEM_STATIC unsigned ZSTD_NbCommonBytes(size_t val)
# elif defined(__GNUC__) && (__GNUC__ >= 3)
return (unsigned)(__builtin_ctz((U32)val) >> 3);
# else
- static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0,
- 3, 2, 2, 1, 3, 2, 0, 1,
- 3, 3, 1, 2, 2, 2, 2, 0,
- 3, 1, 2, 0, 1, 0, 1, 1 };
- return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27];
+ return ZSTD_NbCommonBytes_fallback(val);
# endif
}
} else { /* Big Endian CPU */
@@ -181,12 +221,7 @@ MEM_STATIC unsigned ZSTD_NbCommonBytes(size_t val)
# elif defined(__GNUC__) && (__GNUC__ >= 4)
return (unsigned)(__builtin_clzll(val) >> 3);
# else
- unsigned r;
- const unsigned n32 = sizeof(size_t)*4; /* calculate this way due to compiler complaining in 32-bits mode */
- if (!(val>>n32)) { r=4; } else { r=0; val>>=n32; }
- if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; }
- r += (!val);
- return r;
+ return ZSTD_NbCommonBytes_fallback(val);
# endif
} else { /* 32 bits */
# if defined(_MSC_VER)
@@ -201,10 +236,7 @@ MEM_STATIC unsigned ZSTD_NbCommonBytes(size_t val)
# elif defined(__GNUC__) && (__GNUC__ >= 3)
return (unsigned)(__builtin_clz((U32)val) >> 3);
# else
- unsigned r;
- if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; }
- r += (!val);
- return r;
+ return ZSTD_NbCommonBytes_fallback(val);
# endif
} }
}
diff --git a/lib/common/mem.h b/lib/common/mem.h
index 85581c38478..4b10f7c1f06 100644
--- a/lib/common/mem.h
+++ b/lib/common/mem.h
@@ -257,6 +257,14 @@ MEM_STATIC void MEM_write64(void* memPtr, U64 value)
#endif /* MEM_FORCE_MEMORY_ACCESS */
+MEM_STATIC U32 MEM_swap32_fallback(U32 in)
+{
+ return ((in << 24) & 0xff000000 ) |
+ ((in << 8) & 0x00ff0000 ) |
+ ((in >> 8) & 0x0000ff00 ) |
+ ((in >> 24) & 0x000000ff );
+}
+
MEM_STATIC U32 MEM_swap32(U32 in)
{
#if defined(_MSC_VER) /* Visual Studio */
@@ -265,22 +273,13 @@ MEM_STATIC U32 MEM_swap32(U32 in)
|| (defined(__clang__) && __has_builtin(__builtin_bswap32))
return __builtin_bswap32(in);
#else
- return ((in << 24) & 0xff000000 ) |
- ((in << 8) & 0x00ff0000 ) |
- ((in >> 8) & 0x0000ff00 ) |
- ((in >> 24) & 0x000000ff );
+ return MEM_swap32_fallback(in);
#endif
}
-MEM_STATIC U64 MEM_swap64(U64 in)
+MEM_STATIC U64 MEM_swap64_fallback(U64 in)
{
-#if defined(_MSC_VER) /* Visual Studio */
- return _byteswap_uint64(in);
-#elif (defined (__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 403)) \
- || (defined(__clang__) && __has_builtin(__builtin_bswap64))
- return __builtin_bswap64(in);
-#else
- return ((in << 56) & 0xff00000000000000ULL) |
+ return ((in << 56) & 0xff00000000000000ULL) |
((in << 40) & 0x00ff000000000000ULL) |
((in << 24) & 0x0000ff0000000000ULL) |
((in << 8) & 0x000000ff00000000ULL) |
@@ -288,6 +287,17 @@ MEM_STATIC U64 MEM_swap64(U64 in)
((in >> 24) & 0x0000000000ff0000ULL) |
((in >> 40) & 0x000000000000ff00ULL) |
((in >> 56) & 0x00000000000000ffULL);
+}
+
+MEM_STATIC U64 MEM_swap64(U64 in)
+{
+#if defined(_MSC_VER) /* Visual Studio */
+ return _byteswap_uint64(in);
+#elif (defined (__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 403)) \
+ || (defined(__clang__) && __has_builtin(__builtin_bswap64))
+ return __builtin_bswap64(in);
+#else
+ return MEM_swap64_fallback(in);
#endif
}
From 529cd7b82174cde8a6ad62a8d6ec666b530cb3ac Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Mon, 7 Feb 2022 12:22:04 -0500
Subject: [PATCH 092/472] Fix nits
---
lib/common/bits.h | 251 ++++++++++++--------------------
lib/compress/zstd_lazy.c | 12 +-
lib/decompress/huf_decompress.c | 1 +
3 files changed, 102 insertions(+), 162 deletions(-)
diff --git a/lib/common/bits.h b/lib/common/bits.h
index 65fb456db84..40d86a750f7 100644
--- a/lib/common/bits.h
+++ b/lib/common/bits.h
@@ -14,16 +14,19 @@
#include "mem.h"
MEM_STATIC unsigned ZSTD_highbit32_fallback(U32 val) {
- 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 };
- val |= val >> 1;
- val |= val >> 2;
- val |= val >> 4;
- val |= val >> 8;
- val |= val >> 16;
- return DeBruijnClz[(val * 0x07C4ACDDU) >> 27];
+ assert(val != 0);
+ {
+ 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};
+ val |= val >> 1;
+ val |= val >> 2;
+ val |= val >> 4;
+ val |= val >> 8;
+ val |= val >> 16;
+ return DeBruijnClz[(val * 0x07C4ACDDU) >> 27];
+ }
}
MEM_STATIC unsigned ZSTD_highbit32(U32 val) /* compress, dictBuilder, decodeCorpus */
@@ -34,16 +37,11 @@ MEM_STATIC unsigned ZSTD_highbit32(U32 val) /* compress, dictBuilder, decodeCo
# if STATIC_BMI2 == 1
return _lzcnt_u32(val)^31;
# else
- if (val != 0) {
- unsigned long r;
- _BitScanReverse(&r, val);
- return (unsigned)r;
- } else {
- /* Should not reach this code path */
- __assume(0);
- }
+ unsigned long r;
+ _BitScanReverse(&r, val);
+ return (unsigned)r;
# endif
-# elif defined(__GNUC__) && (__GNUC__ >= 3) /* GCC Intrinsic */
+# elif defined(__GNUC__) && (__GNUC__ >= 4) /* GCC Intrinsic */
return (unsigned)__builtin_clz (val) ^ 31;
# elif defined(__ICCARM__) /* IAR Intrinsic */
return 31 - __CLZ(val);
@@ -55,45 +53,54 @@ MEM_STATIC unsigned ZSTD_highbit32(U32 val) /* compress, dictBuilder, decodeCo
MEM_STATIC unsigned ZSTD_countTrailingZeros32_fallback(U32 val)
{
- static const int DeBruijnBytePos[32] = { 0, 1, 28, 2, 29, 14, 24, 3,
- 30, 22, 20, 15, 25, 17, 4, 8,
- 31, 27, 13, 23, 21, 19, 16, 7,
- 26, 12, 18, 6, 11, 5, 10, 9 };
- return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27];
+ assert(val != 0);
+ {
+ static const int DeBruijnBytePos[32] = {0, 1, 28, 2, 29, 14, 24, 3,
+ 30, 22, 20, 15, 25, 17, 4, 8,
+ 31, 27, 13, 23, 21, 19, 16, 7,
+ 26, 12, 18, 6, 11, 5, 10, 9};
+ return DeBruijnBytePos[((U32) ((val & -(S32) val) * 0x077CB531U)) >> 27];
+ }
}
MEM_STATIC unsigned ZSTD_countTrailingZeros32(U32 val)
{
assert(val != 0);
# if defined(_MSC_VER)
- if (val != 0) {
- unsigned long r;
- _BitScanForward(&r, val);
- return (unsigned)r;
- } else {
- /* Should not reach this code path */
- __assume(0);
- }
-# elif defined(__GNUC__) && (__GNUC__ >= 3)
+ unsigned long r;
+ _BitScanForward(&r, val);
+ return (unsigned)r;
+# elif defined(__GNUC__) && (__GNUC__ >= 4)
return (unsigned)__builtin_ctz(val);
-# elif defined(__ICCARM__) /* IAR Intrinsic */
- return __CTZ(val);
# else
return ZSTD_countTrailingZeros32_fallback(val);
# endif
}
-MEM_STATIC unsigned ZSTD_countTrailingZeros64_fallback(U64 val)
+MEM_STATIC unsigned ZSTD_countLeadingZeros32_fallback(U32 val) {
+ assert(val != 0);
+ {
+ unsigned result = 0;
+ while ((val & 0x80000000) == 0) {
+ result++;
+ val <<= 1;
+ }
+ return result;
+ }
+}
+
+MEM_STATIC unsigned ZSTD_countLeadingZeros32(U32 val)
{
- static const int DeBruijnBytePos[64] = { 0, 1, 2, 7, 3, 13, 8, 19,
- 4, 25, 14, 28, 9, 34, 20, 56,
- 5, 17, 26, 54, 15, 41, 29, 43,
- 10, 31, 38, 35, 21, 45, 49, 57,
- 63, 6, 12, 18, 24, 27, 33, 55,
- 16, 53, 40, 42, 30, 37, 44, 48,
- 62, 11, 23, 32, 52, 39, 36, 47,
- 61, 22, 51, 46, 60, 50, 59, 58 };
- return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58];
+ assert(val != 0);
+# if defined(_MSC_VER)
+ unsigned long r;
+ _BitScanReverse(&r, val);
+ return (unsigned)r;
+# elif defined(__GNUC__) && (__GNUC__ >= 4)
+ return (unsigned)__builtin_clz(val);
+# else
+ return ZSTD_countLeadingZeros32_fallback(val);
+# endif
}
MEM_STATIC unsigned ZSTD_countTrailingZeros64(U64 val)
@@ -103,142 +110,66 @@ MEM_STATIC unsigned ZSTD_countTrailingZeros64(U64 val)
# if STATIC_BMI2
return _tzcnt_u64(val);
# else
- if (val != 0) {
- unsigned long r;
- _BitScanForward64(&r, val);
- return (unsigned)r;
- } else {
- /* Should not reach this code path */
- __assume(0);
- }
+ unsigned long r;
+ _BitScanForward64(&r, val);
+ return (unsigned)r;
# endif
-# elif defined(__GNUC__) && (__GNUC__ >= 4)
- if (MEM_32bits()) {
+# elif defined(__GNUC__) && (__GNUC__ >= 4) && defined(__LP64__)
+ return (unsigned)__builtin_ctzll(val);
+# else
+ {
U32 mostSignificantWord = (U32)(val >> 32);
U32 leastSignificantWord = (U32)val;
if (leastSignificantWord == 0) {
- return 32 + (unsigned)__builtin_ctz(mostSignificantWord);
+ return 32 + ZSTD_countTrailingZeros32(mostSignificantWord);
} else {
- return (unsigned)__builtin_ctz(leastSignificantWord);
+ return ZSTD_countTrailingZeros32(leastSignificantWord);
}
- } else {
- return (unsigned)__builtin_ctzll(val);
}
-# else
- return ZSTD_countTrailingZeros64_fallback(val);
# endif
}
-MEM_STATIC unsigned ZSTD_NbCommonBytes_fallback(size_t val)
+MEM_STATIC unsigned ZSTD_countLeadingZeros64(U64 val)
{
- if (MEM_isLittleEndian()) {
- if (MEM_64bits()) {
- static const int DeBruijnBytePos[64] = { 0, 0, 0, 0, 0, 1, 1, 2,
- 0, 3, 1, 3, 1, 4, 2, 7,
- 0, 2, 3, 6, 1, 5, 3, 5,
- 1, 3, 4, 4, 2, 5, 6, 7,
- 7, 0, 1, 2, 3, 3, 4, 6,
- 2, 6, 5, 5, 3, 4, 5, 6,
- 7, 1, 2, 4, 6, 4, 4, 5,
- 7, 2, 6, 5, 7, 6, 7, 7 };
- return DeBruijnBytePos[((U64)((val & -(long long)val) * 0x0218A392CDABBD3FULL)) >> 58];
- } else { /* 32 bits */
- static const int DeBruijnBytePos[32] = { 0, 0, 3, 0, 3, 1, 3, 0,
- 3, 2, 2, 1, 3, 2, 0, 1,
- 3, 3, 1, 2, 2, 2, 2, 0,
- 3, 1, 2, 0, 1, 0, 1, 1 };
- return DeBruijnBytePos[((U32)((val & -(S32)val) * 0x077CB531U)) >> 27];
- }
- } else { /* Big Endian CPU */
- unsigned r;
- if (MEM_64bits()) {
- const unsigned n32 = sizeof(size_t)*4; /* calculate this way due to compiler complaining in 32-bits mode */
- if (!(val>>n32)) { r=4; } else { r=0; val>>=n32; }
- if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; }
- r += (!val);
- return r;
- } else { /* 32 bits */
- if (!(val>>16)) { r=2; val>>=8; } else { r=0; val>>=24; }
- r += (!val);
- return r;
+ assert(val != 0);
+# if defined(_MSC_VER) && defined(_WIN64)
+# if STATIC_BMI2
+ return _lzcnt_u64(val);
+# else
+ unsigned long r;
+ _BitScanReverse64(&r, val);
+ return (unsigned)r;
+# endif
+# elif defined(__GNUC__) && (__GNUC__ >= 4)
+ return (unsigned)(__builtin_clzll(val));
+# else
+ {
+ U32 mostSignificantWord = (U32)(val >> 32);
+ U32 leastSignificantWord = (U32)val;
+ if (mostSignificantWord == 0) {
+ return 32 + ZSTD_countLeadingZeros32(leastSignificantWord);
+ } else {
+ return ZSTD_countLeadingZeros32(mostSignificantWord);
+ }
}
- }
+# endif
}
MEM_STATIC unsigned ZSTD_NbCommonBytes(size_t val)
{
if (MEM_isLittleEndian()) {
if (MEM_64bits()) {
-# if defined(_MSC_VER) && defined(_WIN64)
-# if STATIC_BMI2
- return _tzcnt_u64(val) >> 3;
-# else
- if (val != 0) {
- unsigned long r;
- _BitScanForward64(&r, (U64)val);
- return (unsigned)(r >> 3);
- } else {
- /* Should not reach this code path */
- __assume(0);
- }
-# endif
-# elif defined(__GNUC__) && (__GNUC__ >= 4)
- return (unsigned)(__builtin_ctzll((U64)val) >> 3);
-# else
- return ZSTD_NbCommonBytes_fallback(val);
-# endif
- } else { /* 32 bits */
-# if defined(_MSC_VER)
- if (val != 0) {
- unsigned long r;
- _BitScanForward(&r, (U32)val);
- return (unsigned)(r >> 3);
- } else {
- /* Should not reach this code path */
- __assume(0);
- }
-# elif defined(__GNUC__) && (__GNUC__ >= 3)
- return (unsigned)(__builtin_ctz((U32)val) >> 3);
-# else
- return ZSTD_NbCommonBytes_fallback(val);
-# endif
+ return ZSTD_countTrailingZeros64((U64)val) >> 3;
+ } else {
+ return ZSTD_countTrailingZeros32((U32)val) >> 3;
}
} else { /* Big Endian CPU */
if (MEM_64bits()) {
-# if defined(_MSC_VER) && defined(_WIN64)
-# if STATIC_BMI2
- return _lzcnt_u64(val) >> 3;
-# else
- if (val != 0) {
- unsigned long r;
- _BitScanReverse64(&r, (U64)val);
- return (unsigned)(r >> 3);
- } else {
- /* Should not reach this code path */
- __assume(0);
- }
-# endif
-# elif defined(__GNUC__) && (__GNUC__ >= 4)
- return (unsigned)(__builtin_clzll(val) >> 3);
-# else
- return ZSTD_NbCommonBytes_fallback(val);
-# endif
- } else { /* 32 bits */
-# if defined(_MSC_VER)
- if (val != 0) {
- unsigned long r;
- _BitScanReverse(&r, (unsigned long)val);
- return (unsigned)(r >> 3);
- } else {
- /* Should not reach this code path */
- __assume(0);
- }
-# elif defined(__GNUC__) && (__GNUC__ >= 3)
- return (unsigned)(__builtin_clz((U32)val) >> 3);
-# else
- return ZSTD_NbCommonBytes_fallback(val);
-# endif
- } }
+ return ZSTD_countLeadingZeros64((U64)val) >> 3;
+ } else {
+ return ZSTD_countLeadingZeros32((U32)val) >> 3;
+ }
+ }
}
#endif /* ZSTD_BITS_H */
diff --git a/lib/compress/zstd_lazy.c b/lib/compress/zstd_lazy.c
index c619aea3a9b..278995491ec 100644
--- a/lib/compress/zstd_lazy.c
+++ b/lib/compress/zstd_lazy.c
@@ -766,6 +766,14 @@ size_t ZSTD_HcFindBestMatch(
typedef U64 ZSTD_VecMask; /* Clarifies when we are interacting with a U64 representing a mask of matches */
+/* ZSTD_VecMask_next():
+ * Starting from the LSB, returns the idx of the next non-zero bit.
+ * Basically counting the nb of trailing zeroes.
+ */
+MEM_STATIC U32 ZSTD_VecMask_next(ZSTD_VecMask val) {
+ return ZSTD_countTrailingZeros64(val);
+}
+
/* ZSTD_rotateRight_*():
* Rotates a bitfield to the right by "count" bits.
* https://en.wikipedia.org/w/index.php?title=Circular_shift&oldid=991635599#Implementing_circular_shifts
@@ -1165,7 +1173,7 @@ size_t ZSTD_RowFindBestMatch(
/* Cycle through the matches and prefetch */
for (; (matches > 0) && (nbAttempts > 0); --nbAttempts, matches &= (matches - 1)) {
- U32 const matchPos = (head + ZSTD_countTrailingZeros64(matches)) & rowMask;
+ U32 const matchPos = (head + ZSTD_VecMask_next(matches)) & rowMask;
U32 const matchIndex = row[matchPos];
assert(numMatches < rowEntries);
if (matchIndex < lowLimit)
@@ -1233,7 +1241,7 @@ size_t ZSTD_RowFindBestMatch(
ZSTD_VecMask matches = ZSTD_row_getMatchMask(dmsTagRow, (BYTE)dmsTag, head, rowEntries);
for (; (matches > 0) && (nbAttempts > 0); --nbAttempts, matches &= (matches - 1)) {
- U32 const matchPos = (head + ZSTD_countTrailingZeros64(matches)) & rowMask;
+ U32 const matchPos = (head + ZSTD_VecMask_next(matches)) & rowMask;
U32 const matchIndex = dmsRow[matchPos];
if (matchIndex < dmsLowestIndex)
break;
diff --git a/lib/decompress/huf_decompress.c b/lib/decompress/huf_decompress.c
index 4541ca98c3e..c6fd9286006 100644
--- a/lib/decompress/huf_decompress.c
+++ b/lib/decompress/huf_decompress.c
@@ -263,6 +263,7 @@ static size_t HUF_initRemainingDStream(BIT_DStream_t* bit, HUF_DecompressAsmArgs
return ERROR(corruption_detected);
/* Construct the BIT_DStream_t. */
+ assert(sizeof(size_t) == 8);
bit->bitContainer = MEM_readLE64(args->ip[stream]);
bit->bitsConsumed = ZSTD_countTrailingZeros64(args->bits[stream]);
bit->start = (const char*)args->iend[0];
From 6994a9f99c97f817e42f638840b65c53cad2479b Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Mon, 14 Feb 2022 16:08:00 -0500
Subject: [PATCH 093/472] bits.h refactor and bugfix
---
lib/common/bits.h | 70 ++++++++++++++++-------------------------------
1 file changed, 23 insertions(+), 47 deletions(-)
diff --git a/lib/common/bits.h b/lib/common/bits.h
index 40d86a750f7..07b4e4dca48 100644
--- a/lib/common/bits.h
+++ b/lib/common/bits.h
@@ -13,44 +13,6 @@
#include "mem.h"
-MEM_STATIC unsigned ZSTD_highbit32_fallback(U32 val) {
- assert(val != 0);
- {
- 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};
- val |= val >> 1;
- val |= val >> 2;
- val |= val >> 4;
- val |= val >> 8;
- val |= val >> 16;
- return DeBruijnClz[(val * 0x07C4ACDDU) >> 27];
- }
-}
-
-MEM_STATIC unsigned ZSTD_highbit32(U32 val) /* compress, dictBuilder, decodeCorpus */
-{
- assert(val != 0);
- {
-# if defined(_MSC_VER) /* Visual */
-# if STATIC_BMI2 == 1
- return _lzcnt_u32(val)^31;
-# else
- unsigned long r;
- _BitScanReverse(&r, val);
- return (unsigned)r;
-# endif
-# elif defined(__GNUC__) && (__GNUC__ >= 4) /* GCC Intrinsic */
- return (unsigned)__builtin_clz (val) ^ 31;
-# elif defined(__ICCARM__) /* IAR Intrinsic */
- return 31 - __CLZ(val);
-# else /* Software version */
- return ZSTD_highbit32_fallback(val);
-# endif
- }
-}
-
MEM_STATIC unsigned ZSTD_countTrailingZeros32_fallback(U32 val)
{
assert(val != 0);
@@ -67,9 +29,13 @@ MEM_STATIC unsigned ZSTD_countTrailingZeros32(U32 val)
{
assert(val != 0);
# if defined(_MSC_VER)
- unsigned long r;
- _BitScanForward(&r, val);
- return (unsigned)r;
+# if STATIC_BMI2 == 1
+ return _tzcnt_u32(val);
+# else
+ unsigned long r;
+ _BitScanForward(&r, val);
+ return (unsigned)r;
+# endif
# elif defined(__GNUC__) && (__GNUC__ >= 4)
return (unsigned)__builtin_ctz(val);
# else
@@ -93,9 +59,13 @@ MEM_STATIC unsigned ZSTD_countLeadingZeros32(U32 val)
{
assert(val != 0);
# if defined(_MSC_VER)
- unsigned long r;
- _BitScanReverse(&r, val);
- return (unsigned)r;
+# if STATIC_BMI2 == 1
+ return _lzcnt_u32(val);
+# else
+ unsigned long r;
+ _BitScanReverse(&r, val);
+ return (unsigned)(r ^ 31);
+# endif
# elif defined(__GNUC__) && (__GNUC__ >= 4)
return (unsigned)__builtin_clz(val);
# else
@@ -107,7 +77,7 @@ MEM_STATIC unsigned ZSTD_countTrailingZeros64(U64 val)
{
assert(val != 0);
# if defined(_MSC_VER) && defined(_WIN64)
-# if STATIC_BMI2
+# if STATIC_BMI2 == 1
return _tzcnt_u64(val);
# else
unsigned long r;
@@ -133,12 +103,12 @@ MEM_STATIC unsigned ZSTD_countLeadingZeros64(U64 val)
{
assert(val != 0);
# if defined(_MSC_VER) && defined(_WIN64)
-# if STATIC_BMI2
+# if STATIC_BMI2 == 1
return _lzcnt_u64(val);
# else
unsigned long r;
_BitScanReverse64(&r, val);
- return (unsigned)r;
+ return (unsigned)(r ^ 63);
# endif
# elif defined(__GNUC__) && (__GNUC__ >= 4)
return (unsigned)(__builtin_clzll(val));
@@ -172,4 +142,10 @@ MEM_STATIC unsigned ZSTD_NbCommonBytes(size_t val)
}
}
+MEM_STATIC unsigned ZSTD_highbit32(U32 val) /* compress, dictBuilder, decodeCorpus */
+{
+ assert(val != 0);
+ return ZSTD_countLeadingZeros32(val) ^ 31;
+}
+
#endif /* ZSTD_BITS_H */
From 7c674a09199f1c47b88116e67f0d749ce8373a1f Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Mon, 14 Feb 2022 17:15:30 -0500
Subject: [PATCH 094/472] Add tests for bitwise intrinsics
---
tests/fuzzer.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 56 insertions(+)
diff --git a/tests/fuzzer.c b/tests/fuzzer.c
index 2060a1d318d..9b31469e383 100644
--- a/tests/fuzzer.c
+++ b/tests/fuzzer.c
@@ -3375,6 +3375,62 @@ static int basicUnitTests(U32 const seed, double compressibility)
}
DISPLAYLEVEL(3, "OK \n");
+ DISPLAYLEVEL(3, "test%3i : testing bitwise instrinsics PR#3045: ", testNb++);
+ {
+ U32 seed_32 = seed == 0 ? seed + 1 : seed; // these intrinsics are undefined on 0
+ U64 seed_64 = (U64)seed * 0x87654321; // decent 64-bit distribution
+ U32 lowbit_only_32 = 1;
+ U64 lowbit_only_64 = 1;
+ U32 highbit_only_32 = (U32)1 << 31;
+ U64 highbit_only_64 = (U64)1 << 63;
+
+ /* Test ZSTD_countTrailingZeros32 */
+ CHECK_EQ(ZSTD_countTrailingZeros32(lowbit_only_32), 0u);
+ CHECK_EQ(ZSTD_countTrailingZeros32(highbit_only_32), 31u);
+ CHECK_EQ(ZSTD_countTrailingZeros32(seed_32), ZSTD_countTrailingZeros32_fallback(seed_32));
+
+ /* Test ZSTD_countLeadingZeros32 */
+ CHECK_EQ(ZSTD_countLeadingZeros32(lowbit_only_32), 31u);
+ CHECK_EQ(ZSTD_countLeadingZeros32(highbit_only_32), 0u);
+ CHECK_EQ(ZSTD_countLeadingZeros32(seed_32), ZSTD_countLeadingZeros32_fallback(seed_32));
+
+ /* Test ZSTD_countTrailingZeros64 */
+ CHECK_EQ(ZSTD_countTrailingZeros64(lowbit_only_64), 0u);
+ CHECK_EQ(ZSTD_countTrailingZeros64(highbit_only_64), 63u);
+
+ /* Test ZSTD_countLeadingZeros64 */
+ CHECK_EQ(ZSTD_countLeadingZeros64(lowbit_only_64), 63u);
+ CHECK_EQ(ZSTD_countLeadingZeros64(highbit_only_64), 0u);
+
+ /* Test ZSTD_highbit32 */
+ CHECK_EQ(ZSTD_highbit32(lowbit_only_32), 0u);
+ CHECK_EQ(ZSTD_highbit32(highbit_only_32), 31u);
+
+ /* Test ZSTD_NbCommonBytes */
+ if (MEM_isLittleEndian()) {
+ if (MEM_64bits()) {
+ CHECK_EQ(ZSTD_NbCommonBytes(lowbit_only_32), 0u);
+ CHECK_EQ(ZSTD_NbCommonBytes(highbit_only_32), 3u);
+ } else {
+ CHECK_EQ(ZSTD_NbCommonBytes(lowbit_only_32), 0u);
+ CHECK_EQ(ZSTD_NbCommonBytes(highbit_only_32), 3u);
+ }
+ } else {
+ if (MEM_64bits()) {
+ CHECK_EQ(ZSTD_NbCommonBytes(lowbit_only_32), 7u);
+ CHECK_EQ(ZSTD_NbCommonBytes(highbit_only_32), 4u);
+ } else {
+ CHECK_EQ(ZSTD_NbCommonBytes(lowbit_only_32), 3u);
+ CHECK_EQ(ZSTD_NbCommonBytes(highbit_only_32), 0u);
+ }
+ }
+
+ /* Test MEM_ intrinsics */
+ CHECK_EQ(MEM_swap32(seed_32), MEM_swap32_fallback(seed_32));
+ CHECK_EQ(MEM_swap64(seed_64), MEM_swap64_fallback(seed_64));
+ }
+ DISPLAYLEVEL(3, "OK \n");
+
#ifdef ZSTD_MULTITHREAD
DISPLAYLEVEL(3, "test%3i : passing wrong full dict should fail on compressStream2 refPrefix ", testNb++);
{ ZSTD_CCtx* cctx = ZSTD_createCCtx();
From 00f2acba36bfe882e8e64a0115d6da31922bab91 Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Tue, 15 Feb 2022 11:41:09 -0500
Subject: [PATCH 095/472] Add back check to prevent Win32 static analysis
issues
---
lib/common/bits.h | 44 ++++++++++++++++++++++++++++++++------------
1 file changed, 32 insertions(+), 12 deletions(-)
diff --git a/lib/common/bits.h b/lib/common/bits.h
index 07b4e4dca48..191e68a78b9 100644
--- a/lib/common/bits.h
+++ b/lib/common/bits.h
@@ -32,9 +32,14 @@ MEM_STATIC unsigned ZSTD_countTrailingZeros32(U32 val)
# if STATIC_BMI2 == 1
return _tzcnt_u32(val);
# else
- unsigned long r;
- _BitScanForward(&r, val);
- return (unsigned)r;
+ if (val != 0) {
+ unsigned long r;
+ _BitScanForward(&r, val);
+ return (unsigned)r;
+ } else {
+ /* Should not reach this code path */
+ __assume(0);
+ }
# endif
# elif defined(__GNUC__) && (__GNUC__ >= 4)
return (unsigned)__builtin_ctz(val);
@@ -62,9 +67,14 @@ MEM_STATIC unsigned ZSTD_countLeadingZeros32(U32 val)
# if STATIC_BMI2 == 1
return _lzcnt_u32(val);
# else
- unsigned long r;
- _BitScanReverse(&r, val);
- return (unsigned)(r ^ 31);
+ if (val != 0) {
+ unsigned long r;
+ _BitScanReverse(&r, val);
+ return (unsigned)(r ^ 31);
+ } else {
+ /* Should not reach this code path */
+ __assume(0);
+ }
# endif
# elif defined(__GNUC__) && (__GNUC__ >= 4)
return (unsigned)__builtin_clz(val);
@@ -80,9 +90,14 @@ MEM_STATIC unsigned ZSTD_countTrailingZeros64(U64 val)
# if STATIC_BMI2 == 1
return _tzcnt_u64(val);
# else
- unsigned long r;
- _BitScanForward64(&r, val);
- return (unsigned)r;
+ if (val != 0) {
+ unsigned long r;
+ _BitScanForward64(&r, val);
+ return (unsigned)r;
+ } else {
+ /* Should not reach this code path */
+ __assume(0);
+ }
# endif
# elif defined(__GNUC__) && (__GNUC__ >= 4) && defined(__LP64__)
return (unsigned)__builtin_ctzll(val);
@@ -106,9 +121,14 @@ MEM_STATIC unsigned ZSTD_countLeadingZeros64(U64 val)
# if STATIC_BMI2 == 1
return _lzcnt_u64(val);
# else
- unsigned long r;
- _BitScanReverse64(&r, val);
- return (unsigned)(r ^ 63);
+ if (val != 0) {
+ unsigned long r;
+ _BitScanReverse64(&r, val);
+ return (unsigned)(r ^ 63);
+ } else {
+ /* Should not reach this code path */
+ __assume(0);
+ }
# endif
# elif defined(__GNUC__) && (__GNUC__ >= 4)
return (unsigned)(__builtin_clzll(val));
From 856c7dc51dcccc0c6f31a48e42a7a29475351828 Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Wed, 16 Feb 2022 11:16:55 -0500
Subject: [PATCH 096/472] Fix fuzzer.c nits and replace CLZ fallback
---
lib/common/bits.h | 16 ++++++++++------
tests/fuzzer.c | 24 ++++++++++++++++++------
2 files changed, 28 insertions(+), 12 deletions(-)
diff --git a/lib/common/bits.h b/lib/common/bits.h
index 191e68a78b9..2c4ade865ac 100644
--- a/lib/common/bits.h
+++ b/lib/common/bits.h
@@ -51,12 +51,16 @@ MEM_STATIC unsigned ZSTD_countTrailingZeros32(U32 val)
MEM_STATIC unsigned ZSTD_countLeadingZeros32_fallback(U32 val) {
assert(val != 0);
{
- unsigned result = 0;
- while ((val & 0x80000000) == 0) {
- result++;
- val <<= 1;
- }
- return result;
+ 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};
+ val |= val >> 1;
+ val |= val >> 2;
+ val |= val >> 4;
+ val |= val >> 8;
+ val |= val >> 16;
+ return DeBruijnClz[(val * 0x07C4ACDDU) >> 27] ^ 31;
}
}
diff --git a/tests/fuzzer.c b/tests/fuzzer.c
index 9b31469e383..047afc1c82b 100644
--- a/tests/fuzzer.c
+++ b/tests/fuzzer.c
@@ -3377,22 +3377,26 @@ static int basicUnitTests(U32 const seed, double compressibility)
DISPLAYLEVEL(3, "test%3i : testing bitwise instrinsics PR#3045: ", testNb++);
{
- U32 seed_32 = seed == 0 ? seed + 1 : seed; // these intrinsics are undefined on 0
- U64 seed_64 = (U64)seed * 0x87654321; // decent 64-bit distribution
+ U32 seed_copy = seed; // need non-const seed to avoid compiler warning for FUZ_rand(&seed)
+ U32 rand32 = FUZ_rand(&seed_copy);
+ U64 rand64 = ((U64)FUZ_rand(&seed_copy) << 32) | FUZ_rand(&seed_copy);
U32 lowbit_only_32 = 1;
U64 lowbit_only_64 = 1;
U32 highbit_only_32 = (U32)1 << 31;
U64 highbit_only_64 = (U64)1 << 63;
+ U32 i;
+ if (rand32 == 0) rand32 = 1; // CLZ and CTZ are undefined on 0
+ if (rand64 == 0) rand64 = 1; // CLZ and CTZ are undefined on 0
/* Test ZSTD_countTrailingZeros32 */
CHECK_EQ(ZSTD_countTrailingZeros32(lowbit_only_32), 0u);
CHECK_EQ(ZSTD_countTrailingZeros32(highbit_only_32), 31u);
- CHECK_EQ(ZSTD_countTrailingZeros32(seed_32), ZSTD_countTrailingZeros32_fallback(seed_32));
+ CHECK_EQ(ZSTD_countTrailingZeros32(rand32), ZSTD_countTrailingZeros32_fallback(rand32));
/* Test ZSTD_countLeadingZeros32 */
CHECK_EQ(ZSTD_countLeadingZeros32(lowbit_only_32), 31u);
CHECK_EQ(ZSTD_countLeadingZeros32(highbit_only_32), 0u);
- CHECK_EQ(ZSTD_countLeadingZeros32(seed_32), ZSTD_countLeadingZeros32_fallback(seed_32));
+ CHECK_EQ(ZSTD_countLeadingZeros32(rand32), ZSTD_countLeadingZeros32_fallback(rand32));
/* Test ZSTD_countTrailingZeros64 */
CHECK_EQ(ZSTD_countTrailingZeros64(lowbit_only_64), 0u);
@@ -3426,8 +3430,16 @@ static int basicUnitTests(U32 const seed, double compressibility)
}
/* Test MEM_ intrinsics */
- CHECK_EQ(MEM_swap32(seed_32), MEM_swap32_fallback(seed_32));
- CHECK_EQ(MEM_swap64(seed_64), MEM_swap64_fallback(seed_64));
+ CHECK_EQ(MEM_swap32(rand32), MEM_swap32_fallback(rand32));
+ CHECK_EQ(MEM_swap64(rand64), MEM_swap64_fallback(rand64));
+
+ /* Test fallbacks vs intrinsics on a range of small integers */
+ for (i=1; i <= 1000; i++) {
+ CHECK_EQ(MEM_swap32(i), MEM_swap32_fallback(i));
+ CHECK_EQ(MEM_swap64((U64)i), MEM_swap64_fallback((U64)i));
+ CHECK_EQ(ZSTD_countTrailingZeros32(i), ZSTD_countTrailingZeros32_fallback(i));
+ CHECK_EQ(ZSTD_countLeadingZeros32(i), ZSTD_countLeadingZeros32_fallback(i));
+ }
}
DISPLAYLEVEL(3, "OK \n");
From 71d9dab76f6c496a39356f4d5e984525f2050373 Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Wed, 16 Feb 2022 16:49:42 -0500
Subject: [PATCH 097/472] Replace XOR with subtraction for readability
---
lib/common/bits.h | 8 ++++----
tests/fuzzer.c | 2 +-
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/lib/common/bits.h b/lib/common/bits.h
index 2c4ade865ac..c0e9177507f 100644
--- a/lib/common/bits.h
+++ b/lib/common/bits.h
@@ -60,7 +60,7 @@ MEM_STATIC unsigned ZSTD_countLeadingZeros32_fallback(U32 val) {
val |= val >> 4;
val |= val >> 8;
val |= val >> 16;
- return DeBruijnClz[(val * 0x07C4ACDDU) >> 27] ^ 31;
+ return 31 - DeBruijnClz[(val * 0x07C4ACDDU) >> 27];
}
}
@@ -74,7 +74,7 @@ MEM_STATIC unsigned ZSTD_countLeadingZeros32(U32 val)
if (val != 0) {
unsigned long r;
_BitScanReverse(&r, val);
- return (unsigned)(r ^ 31);
+ return (unsigned)(31 - r);
} else {
/* Should not reach this code path */
__assume(0);
@@ -128,7 +128,7 @@ MEM_STATIC unsigned ZSTD_countLeadingZeros64(U64 val)
if (val != 0) {
unsigned long r;
_BitScanReverse64(&r, val);
- return (unsigned)(r ^ 63);
+ return (unsigned)(63 - r);
} else {
/* Should not reach this code path */
__assume(0);
@@ -169,7 +169,7 @@ MEM_STATIC unsigned ZSTD_NbCommonBytes(size_t val)
MEM_STATIC unsigned ZSTD_highbit32(U32 val) /* compress, dictBuilder, decodeCorpus */
{
assert(val != 0);
- return ZSTD_countLeadingZeros32(val) ^ 31;
+ return 31 - ZSTD_countLeadingZeros32(val);
}
#endif /* ZSTD_BITS_H */
diff --git a/tests/fuzzer.c b/tests/fuzzer.c
index 047afc1c82b..018da622cea 100644
--- a/tests/fuzzer.c
+++ b/tests/fuzzer.c
@@ -3375,7 +3375,7 @@ static int basicUnitTests(U32 const seed, double compressibility)
}
DISPLAYLEVEL(3, "OK \n");
- DISPLAYLEVEL(3, "test%3i : testing bitwise instrinsics PR#3045: ", testNb++);
+ DISPLAYLEVEL(3, "test%3i : testing bitwise intrinsics PR#3045: ", testNb++);
{
U32 seed_copy = seed; // need non-const seed to avoid compiler warning for FUZ_rand(&seed)
U32 rand32 = FUZ_rand(&seed_copy);
From 0178c12dd98ad5e63968b12eaaf32e3162a0ffee Mon Sep 17 00:00:00 2001
From: Ilya Tokar
Date: Wed, 23 Feb 2022 17:59:56 -0500
Subject: [PATCH 098/472] Use helper function for bit manipulations.
We already have BIT_getLowerBits, so use it. Benefits are 2fold:
1) Somewhat cleaner code
2) We are now using bzhi instructions, when available. Performance
delta is too small for microbenchmarks, but avoiding load still helps
larger applications, by reducing data cache pressure.
---
lib/common/bitstream.h | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/lib/common/bitstream.h b/lib/common/bitstream.h
index 103b378329b..43a22167805 100644
--- a/lib/common/bitstream.h
+++ b/lib/common/bitstream.h
@@ -162,6 +162,16 @@ MEM_STATIC size_t BIT_initCStream(BIT_CStream_t* bitC,
return 0;
}
+MEM_STATIC FORCE_INLINE_ATTR size_t BIT_getLowerBits(size_t bitContainer, U32 const nbBits)
+{
+#if defined(STATIC_BMI2) && STATIC_BMI2 == 1
+ return _bzhi_u64(bitContainer, nbBits);
+#else
+ assert(nbBits < BIT_MASK_SIZE);
+ return bitContainer & BIT_mask[nbBits];
+#endif
+}
+
/*! BIT_addBits() :
* can add up to 31 bits into `bitC`.
* Note : does not check for register overflow ! */
@@ -171,7 +181,7 @@ MEM_STATIC void BIT_addBits(BIT_CStream_t* bitC,
DEBUG_STATIC_ASSERT(BIT_MASK_SIZE == 32);
assert(nbBits < BIT_MASK_SIZE);
assert(nbBits + bitC->bitPos < sizeof(bitC->bitContainer) * 8);
- bitC->bitContainer |= (value & BIT_mask[nbBits]) << bitC->bitPos;
+ bitC->bitContainer |= BIT_getLowerBits(value, nbBits) << bitC->bitPos;
bitC->bitPos += nbBits;
}
@@ -309,16 +319,6 @@ MEM_STATIC FORCE_INLINE_ATTR size_t BIT_getMiddleBits(size_t bitContainer, U32 c
#endif
}
-MEM_STATIC FORCE_INLINE_ATTR size_t BIT_getLowerBits(size_t bitContainer, U32 const nbBits)
-{
-#if defined(STATIC_BMI2) && STATIC_BMI2 == 1
- return _bzhi_u64(bitContainer, nbBits);
-#else
- assert(nbBits < BIT_MASK_SIZE);
- return bitContainer & BIT_mask[nbBits];
-#endif
-}
-
/*! BIT_lookBits() :
* Provides next n bits from local register.
* local register is not modified.
From 03bba1b0bfe3d877be354d9ba6b29532a1751e9e Mon Sep 17 00:00:00 2001
From: Dmytro Milinevskyi
Date: Tue, 1 Mar 2022 18:29:47 +0100
Subject: [PATCH 099/472] build:cmake: enable ZSTD legacy support by default
---
build/cmake/CMakeLists.txt | 2 +-
build/cmake/README.md | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/build/cmake/CMakeLists.txt b/build/cmake/CMakeLists.txt
index fdf7c8637ea..5e2cf3d5634 100644
--- a/build/cmake/CMakeLists.txt
+++ b/build/cmake/CMakeLists.txt
@@ -82,7 +82,7 @@ message(STATUS "CMAKE_INSTALL_LIBDIR: ${CMAKE_INSTALL_LIBDIR}")
#-----------------------------------------------------------------------------
# Legacy support
-option(ZSTD_LEGACY_SUPPORT "LEGACY SUPPORT" OFF)
+option(ZSTD_LEGACY_SUPPORT "LEGACY SUPPORT" ON)
if (ZSTD_LEGACY_SUPPORT)
message(STATUS "ZSTD_LEGACY_SUPPORT defined!")
diff --git a/build/cmake/README.md b/build/cmake/README.md
index 73b30dc773b..f6854f4c05a 100644
--- a/build/cmake/README.md
+++ b/build/cmake/README.md
@@ -37,7 +37,7 @@ cmake -LH ..
Bool options can be set to `ON/OFF` with `-D[option]=[ON/OFF]`. You can configure cmake options like this:
```sh
cd build/cmake/builddir
-cmake -DZSTD_BUILD_TESTS=ON -DZSTD_LEGACY_SUPPORT=ON ..
+cmake -DZSTD_BUILD_TESTS=ON -DZSTD_LEGACY_SUPPORT=OFF ..
make
```
From da737c7ab89f61f1ea7c392299137f6ffe6f9733 Mon Sep 17 00:00:00 2001
From: Nick Terrell
Date: Wed, 2 Mar 2022 11:04:04 -0800
Subject: [PATCH 100/472] [programs] Fix infinite loop when empty input is
passed to trainer
When an empty input file was passed to the dictionary trainer, it would infinite loop.
The added test case exposes the bug, and is fixed with this PR.
---
programs/dibio.c | 5 ++++-
tests/cli-tests/dict-builder/empty-input.sh | 9 +++++++++
tests/cli-tests/dict-builder/empty-input.sh.stderr.exact | 1 +
3 files changed, 14 insertions(+), 1 deletion(-)
create mode 100755 tests/cli-tests/dict-builder/empty-input.sh
create mode 100644 tests/cli-tests/dict-builder/empty-input.sh.stderr.exact
diff --git a/programs/dibio.c b/programs/dibio.c
index fddbc9e5769..f6757dd3488 100644
--- a/programs/dibio.c
+++ b/programs/dibio.c
@@ -128,8 +128,11 @@ static int DiB_loadFiles(
while ( nbSamplesLoaded < sstSize && fileIndex < nbFiles ) {
size_t fileDataLoaded;
S64 const fileSize = DiB_getFileSize(fileNamesTable[fileIndex]);
- if (fileSize <= 0) /* skip if zero-size or file error */
+ if (fileSize <= 0) {
+ /* skip if zero-size or file error */
+ ++fileIndex;
continue;
+ }
f = fopen( fileNamesTable[fileIndex], "rb");
if (f == NULL)
diff --git a/tests/cli-tests/dict-builder/empty-input.sh b/tests/cli-tests/dict-builder/empty-input.sh
new file mode 100755
index 00000000000..b500bfebda0
--- /dev/null
+++ b/tests/cli-tests/dict-builder/empty-input.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+set -e
+for i in $(seq 50); do
+ datagen -s$i > file$i
+done
+touch empty
+
+set -v
+zstd -q --train empty file*
diff --git a/tests/cli-tests/dict-builder/empty-input.sh.stderr.exact b/tests/cli-tests/dict-builder/empty-input.sh.stderr.exact
new file mode 100644
index 00000000000..2747e766cdf
--- /dev/null
+++ b/tests/cli-tests/dict-builder/empty-input.sh.stderr.exact
@@ -0,0 +1 @@
+zstd -q --train empty file*
From 0c386afbfd07b3f914a00ab5a2a0cbf3f7af0d66 Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Thu, 3 Mar 2022 14:52:06 -0500
Subject: [PATCH 101/472] Fix CI failures by adding apt-get update to Github
Actions (#3082)
* Fix CI failures by adding apt-get update to Makefile
* Fix travis failure caused by apt-get update
* Move apt-get update from Makefile to Github Actions .yml
* Revert .travis.yml change
* Fix typo
---
.github/workflows/dev-long-tests.yml | 4 ++++
Makefile | 2 ++
2 files changed, 6 insertions(+)
diff --git a/.github/workflows/dev-long-tests.yml b/.github/workflows/dev-long-tests.yml
index 60d1d45b9a7..779ba1c7c19 100644
--- a/.github/workflows/dev-long-tests.yml
+++ b/.github/workflows/dev-long-tests.yml
@@ -75,6 +75,7 @@ jobs:
- uses: actions/checkout@v2
- name: ASan + UBSan + Test Zstd, 32bit mode
run: |
+ sudo apt-get -qqq update
make libc6install
make -j uasan-test-zstd32 V=1
@@ -88,6 +89,7 @@ jobs:
- uses: actions/checkout@v2
- name: gcc-8 + ASan + UBSan + Fuzz Test
run: |
+ sudo apt-get -qqq update
make gcc8install
CC=gcc-8 FUZZER_FLAGS="--long-tests" make clean uasan-fuzztest
@@ -97,6 +99,7 @@ jobs:
- uses: actions/checkout@v2
- name: ASan + UBSan + Fuzz Test 32bit
run: |
+ sudo apt-get -qqq update
make libc6install
CFLAGS="-O3 -m32" FUZZER_FLAGS="--long-tests" make uasan-fuzztest
@@ -152,6 +155,7 @@ jobs:
- name: valgrind + fuzz test stack mode # ~ 7mn
shell: 'script -q -e -c "bash {0}"'
run: |
+ sudo apt-get -qqq update
make valgrindinstall
make -C tests valgrindTest
make clean
diff --git a/Makefile b/Makefile
index 9b5451d3d74..67ef928cce5 100644
--- a/Makefile
+++ b/Makefile
@@ -334,6 +334,8 @@ tsan-%: clean
.PHONY: apt-install
apt-install:
+ # TODO: uncomment once issue 3011 is resolved and remove hack from Github Actions .yml
+ # sudo apt-get update
sudo apt-get -yq --no-install-suggests --no-install-recommends --force-yes install $(APT_PACKAGES)
.PHONY: apt-add-repo
From 7c3d1cb3ab9b43745c65bb1796960dbbaa237a02 Mon Sep 17 00:00:00 2001
From: Ilya Tokar
Date: Tue, 1 Mar 2022 18:49:10 -0500
Subject: [PATCH 102/472] Enable STATIC_BMI2 for gcc/clang
Some usage (e.g. BIT_getLowerBit) uses it without checking for MSVC,
so enabling for clang gives a small performance boost.
---
lib/common/bitstream.h | 8 ++++----
lib/common/compiler.h | 2 ++
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/lib/common/bitstream.h b/lib/common/bitstream.h
index 43a22167805..731630ea44b 100644
--- a/lib/common/bitstream.h
+++ b/lib/common/bitstream.h
@@ -37,8 +37,8 @@ extern "C" {
* Target specific
=========================================*/
#ifndef ZSTD_NO_INTRINSICS
-# if defined(__BMI__) && defined(__GNUC__)
-# include /* support for bextr (experimental) */
+# if (defined(__BMI__) || defined(__BMI2__)) && defined(__GNUC__)
+# include /* support for bextr (experimental)/bzhi */
# elif defined(__ICCARM__)
# include
# endif
@@ -164,8 +164,8 @@ MEM_STATIC size_t BIT_initCStream(BIT_CStream_t* bitC,
MEM_STATIC FORCE_INLINE_ATTR size_t BIT_getLowerBits(size_t bitContainer, U32 const nbBits)
{
-#if defined(STATIC_BMI2) && STATIC_BMI2 == 1
- return _bzhi_u64(bitContainer, nbBits);
+#if defined(STATIC_BMI2) && STATIC_BMI2 == 1 && !defined(ZSTD_NO_INTRINSICS)
+ return _bzhi_u64(bitContainer, nbBits);
#else
assert(nbBits < BIT_MASK_SIZE);
return bitContainer & BIT_mask[nbBits];
diff --git a/lib/common/compiler.h b/lib/common/compiler.h
index 516930c01ec..6c7100e835a 100644
--- a/lib/common/compiler.h
+++ b/lib/common/compiler.h
@@ -181,6 +181,8 @@
# ifdef __AVX2__ //MSVC does not have a BMI2 specific flag, but every CPU that supports AVX2 also supports BMI2
# define STATIC_BMI2 1
# endif
+# elif defined(__BMI2__) && defined(__x86_64__) && defined(__GNUC__)
+# define STATIC_BMI2 1
# endif
#endif
From d109cef2012b1e0ca7a6f47278a2838f68bbc196 Mon Sep 17 00:00:00 2001
From: Xi Ruoyao
Date: Sat, 5 Mar 2022 03:56:44 +0800
Subject: [PATCH 103/472] fix the assertion in readLinesFromFile (#3084)
* fix the assertion in readLinesFromFile
When the file is not terminated by endline, readLineFromFile will append
a '\0' for the last line. In this case pos + lineLength == dstCapacity.
* test: don't print very long text garbage
---
programs/util.c | 2 +-
tests/playTests.sh | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/programs/util.c b/programs/util.c
index d69b72a37ca..55bcff25afa 100644
--- a/programs/util.c
+++ b/programs/util.c
@@ -418,7 +418,7 @@ readLinesFromFile(void* dst, size_t dstCapacity,
while ( !feof(inputFile) ) {
size_t const lineLength = readLineFromFile(buf+pos, dstCapacity-pos, inputFile);
if (lineLength == 0) break;
- assert(pos + lineLength < dstCapacity);
+ assert(pos + lineLength <= dstCapacity); /* '=' for inputFile not terminated with '\n' */
pos += lineLength;
++nbFiles;
}
diff --git a/tests/playTests.sh b/tests/playTests.sh
index 71e8dc05818..d4271b2f072 100755
--- a/tests/playTests.sh
+++ b/tests/playTests.sh
@@ -735,11 +735,11 @@ test -f tmp4
println "test : survive the list of files with too long filenames (--filelist=FILE)"
datagen -g5M > tmp_badList
-zstd -f --filelist=tmp_badList && die "should have failed : file name length is too long"
+zstd -qq -f --filelist=tmp_badList && die "should have failed : file name length is too long" # printing very long text garbage on console will cause CI failure
println "test : survive a list of files which is text garbage (--filelist=FILE)"
datagen > tmp_badList
-zstd -f --filelist=tmp_badList && die "should have failed : list is text garbage"
+zstd -qq -f --filelist=tmp_badList && die "should have failed : list is text garbage" # printing very long text garbage on console will cause CI failure
println "test : survive a list of files which is binary garbage (--filelist=FILE)"
datagen -P0 -g1M > tmp_badList
From cf1894b3243f5510c34b871f6d6e6b8321de01f8 Mon Sep 17 00:00:00 2001
From: Dimitris Apostolou
Date: Wed, 9 Feb 2022 08:58:23 +0200
Subject: [PATCH 104/472] Fix typos
---
build/meson/lib/meson.build | 2 +-
build/single_file_libs/combine.py | 4 ++--
lib/common/portability_macros.h | 4 ++--
tests/playTests.sh | 4 ++--
4 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/build/meson/lib/meson.build b/build/meson/lib/meson.build
index 6b093378101..cb7dc6957c7 100644
--- a/build/meson/lib/meson.build
+++ b/build/meson/lib/meson.build
@@ -47,7 +47,7 @@ libzstd_sources = [join_paths(zstd_rootdir, 'lib/common/entropy_common.c'),
# really we need anything that defines __GNUC__ as that is what ZSTD_ASM_SUPPORTED is gated on
# but these are the two compilers that are supported in tree and actually handle this correctly
-# Otherwise, explicitly disable assmebly.
+# Otherwise, explicitly disable assembly.
if [compiler_gcc, compiler_clang].contains(cc_id)
libzstd_sources += join_paths(zstd_rootdir, 'lib/decompress/huf_decompress_amd64.S')
else
diff --git a/build/single_file_libs/combine.py b/build/single_file_libs/combine.py
index badd68a91af..3d91dc3eec2 100755
--- a/build/single_file_libs/combine.py
+++ b/build/single_file_libs/combine.py
@@ -41,7 +41,7 @@
# Set of file Path objects previously inlined (and to ignore if reencountering).
found: Set[Path] = set()
-# Compiled regex Patern to handle "#pragma once" in various formats:
+# Compiled regex Pattern to handle "#pragma once" in various formats:
#
# #pragma once
# #pragma once
@@ -53,7 +53,7 @@
#
pragma_regex: Pattern = re.compile(r'^\s*#\s*pragma\s*once\s*')
-# Compiled regex Patern to handle the following type of file includes:
+# Compiled regex Pattern to handle the following type of file includes:
#
# #include "file"
# #include "file"
diff --git a/lib/common/portability_macros.h b/lib/common/portability_macros.h
index 23a6508852b..1650fa3d8cf 100644
--- a/lib/common/portability_macros.h
+++ b/lib/common/portability_macros.h
@@ -12,7 +12,7 @@
#define ZSTD_PORTABILITY_MACROS_H
/**
- * This header file contains macro defintions to support portability.
+ * This header file contains macro definitions to support portability.
* This header is shared between C and ASM code, so it MUST only
* contain macro definitions. It MUST not contain any C code.
*
@@ -88,7 +88,7 @@
#endif
/**
- * Only enable assembly for GNUC comptabile compilers,
+ * Only enable assembly for GNUC compatible compilers,
* because other platforms may not support GAS assembly syntax.
*
* Only enable assembly for Linux / MacOS, other platforms may
diff --git a/tests/playTests.sh b/tests/playTests.sh
index d4271b2f072..873c3c672cd 100755
--- a/tests/playTests.sh
+++ b/tests/playTests.sh
@@ -225,7 +225,7 @@ ZSTD_CLEVEL=50000000000 zstd -f tmp # numeric value too large, warn and revert t
println "test : override ZSTD_CLEVEL with command line option"
ZSTD_CLEVEL=12 zstd --fast=3 -f tmp # overridden by command line option
-# temporary envvar chnages in the above tests would actually persist in macos /bin/sh
+# temporary envvar changes in the above tests would actually persist in macos /bin/sh
unset ZSTD_CLEVEL
@@ -1448,7 +1448,7 @@ then
ZSTD_NBTHREADS=50000000000 zstd -f mt_tmp # numeric value too large, warn and revert to default setting=
ZSTD_NBTHREADS=2 zstd -f mt_tmp # correct usage
ZSTD_NBTHREADS=1 zstd -f mt_tmp # correct usage: single thread
- # temporary envvar chnages in the above tests would actually persist in macos /bin/sh
+ # temporary envvar changes in the above tests would actually persist in macos /bin/sh
unset ZSTD_NBTHREADS
rm -f mt_tmp*
From e470c940f6cb6cfff4b27d595ec651a3c1c2cc15 Mon Sep 17 00:00:00 2001
From: Cyber Knight
Date: Mon, 7 Mar 2022 11:55:33 +0800
Subject: [PATCH 105/472] [contrib][linux] Fix a warning in
zstd_reset_cstream()
- This fixes the below warning:
../lib/zstd/zstd_compress_module.c: In function 'zstd_reset_cstream':
../lib/zstd/zstd_compress_module.c:136:9: warning: 'ZSTD_resetCStream' is deprecated [-Wdeprecated-declarations]
136 | return ZSTD_resetCStream(cstream, pledged_src_size);
| ^~~~~~
In file included from ../include/linux/zstd.h:26,
from ../lib/zstd/zstd_compress_module.c:15:
../include/linux/zstd_lib.h:2277:8: note: declared here
2277 | size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pledgedSrcSize);
| ^~~~~~~~~~~~~~~~~
ZSTD_resetCstream is deprecated and zstd_CCtx_reset is suggested to use hence let's switch to it.
Signed-off-by: Cyber Knight
---
contrib/linux-kernel/zstd_compress_module.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/contrib/linux-kernel/zstd_compress_module.c b/contrib/linux-kernel/zstd_compress_module.c
index 65548a4bb93..7b25be6e3c3 100644
--- a/contrib/linux-kernel/zstd_compress_module.c
+++ b/contrib/linux-kernel/zstd_compress_module.c
@@ -133,7 +133,7 @@ EXPORT_SYMBOL(zstd_init_cstream);
size_t zstd_reset_cstream(zstd_cstream *cstream,
unsigned long long pledged_src_size)
{
- return ZSTD_resetCStream(cstream, pledged_src_size);
+ return ZSTD_CCtx_reset(cstream, pledged_src_size);
}
EXPORT_SYMBOL(zstd_reset_cstream);
From 8ff20c25f38f62cd1e898cf81173e280f42327f5 Mon Sep 17 00:00:00 2001
From: Cyber Knight
Date: Tue, 8 Mar 2022 13:38:06 +0800
Subject: [PATCH 106/472] [contrib][linux] Use ZSTD_CCtx_setPledgedSrcSize()
instead of ZSTD_CCtx_reset()
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- The previous patch throws the following warning:
../linux/lib/zstd/zstd_compress_module.c: In function ‘zstd_reset_cstream’:
../linux/lib/zstd/zstd_compress_module.c:136:34: error: enum conversion when passing argument 2 of ‘ZSTD_CCtx_reset’ is invalid in C++ [-Werror=c++-compat]
136 | return ZSTD_CCtx_reset(cstream, pledged_src_size);
| ^~~~~~~~~~~~~~~~
In file included from ../linux/include/linux/zstd.h:26,
from ../linux/lib/zstd/zstd_compress_module.c:15:
../linux/include/linux/zstd_lib.h:501:20: note: expected ‘ZSTD_ResetDirective’ {aka ‘enum ’} but argument is of type ‘long long unsigned int’
501 | ZSTDLIB_API size_t ZSTD_CCtx_reset(ZSTD_CCtx* cctx, ZSTD_ResetDirective reset);
| ^~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
Since we have a choice to either use ZSTD_CCtx_reset or ZSTD_CCtx_setPledgedSrcSize instead of ZSTD_resetCStream, let's switch to ZSTD_CCtx_setPledgedSrcSize to not have any unnecessary warns alongside the kernel build and CI test build.
Signed-off-by: Cyber Knight
---
contrib/linux-kernel/zstd_compress_module.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/contrib/linux-kernel/zstd_compress_module.c b/contrib/linux-kernel/zstd_compress_module.c
index 7b25be6e3c3..8b4c764d426 100644
--- a/contrib/linux-kernel/zstd_compress_module.c
+++ b/contrib/linux-kernel/zstd_compress_module.c
@@ -131,9 +131,9 @@ zstd_cstream *zstd_init_cstream(const zstd_parameters *parameters,
EXPORT_SYMBOL(zstd_init_cstream);
size_t zstd_reset_cstream(zstd_cstream *cstream,
- unsigned long long pledged_src_size)
+ unsigned long long pledgedSrcSize)
{
- return ZSTD_CCtx_reset(cstream, pledged_src_size);
+ return ZSTD_CCtx_setPledgedSrcSize(cstream, pledgedSrcSize);
}
EXPORT_SYMBOL(zstd_reset_cstream);
From 3f4f8b04ed2d318536c9f54e0c575603ed75d92f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dirk=20M=C3=BCller?=
Date: Mon, 7 Feb 2022 20:39:15 +0100
Subject: [PATCH 107/472] Keep original file if -c or --stdout is given
Set removeSrcFile back to false when -c or --stdout is used to improve
compatibility with gzip(1) behavior.
gzip(1) is removing the original file on compression unless --stdout or
/-c is used. zstd is defaulting to keep the file unless --rm is used or
when it is called via a gzip symlink, in which it is removing by
default. Specifying -c/--stdout turns this behavior off.
---
programs/zstd.1 | 4 ++--
programs/zstd.1.md | 2 +-
programs/zstdcli.c | 10 +++++-----
tests/cli-tests/compression/basic.sh | 4 ++++
4 files changed, 12 insertions(+), 8 deletions(-)
diff --git a/programs/zstd.1 b/programs/zstd.1
index 0e6e016e950..346b08b181b 100644
--- a/programs/zstd.1
+++ b/programs/zstd.1
@@ -1,5 +1,5 @@
.
-.TH "ZSTD" "1" "January 2022" "zstd 1.5.2" "User Commands"
+.TH "ZSTD" "1" "February 2022" "zstd 1.5.2" "User Commands"
.
.SH "NAME"
\fBzstd\fR \- zstd, zstdmt, unzstd, zstdcat \- Compress or decompress \.zst files
@@ -165,7 +165,7 @@ Additionally, this can be used to limit memory for dictionary training\. This pa
\fB\-f\fR, \fB\-\-force\fR: disable input and output checks\. Allows overwriting existing files, input from console, output to stdout, operating on links, block devices, etc\.
.
.IP "\(bu" 4
-\fB\-c\fR, \fB\-\-stdout\fR: write to standard output (even if it is the console)
+\fB\-c\fR, \fB\-\-stdout\fR: write to standard output (even if it is the console); keep original files unchanged\.
.
.IP "\(bu" 4
\fB\-\-[no\-]sparse\fR: enable / disable sparse FS support, to make files with many zeroes smaller on disk\. Creating sparse files may save disk space and speed up decompression by reducing the amount of disk I/O\. default: enabled when output is into a file, and disabled when output is stdout\. This setting overrides default and can force sparse mode over stdout\.
diff --git a/programs/zstd.1.md b/programs/zstd.1.md
index 569ca1aa0db..c78fd4abf06 100644
--- a/programs/zstd.1.md
+++ b/programs/zstd.1.md
@@ -212,7 +212,7 @@ the last one takes effect.
disable input and output checks. Allows overwriting existing files, input
from console, output to stdout, operating on links, block devices, etc.
* `-c`, `--stdout`:
- write to standard output (even if it is the console)
+ write to standard output (even if it is the console); keep original files unchanged.
* `--[no-]sparse`:
enable / disable sparse FS support,
to make files with many zeroes smaller on disk.
diff --git a/programs/zstdcli.c b/programs/zstdcli.c
index 0b9b82a9859..6c200bc98f6 100644
--- a/programs/zstdcli.c
+++ b/programs/zstdcli.c
@@ -178,7 +178,7 @@ static void usage_advanced(const char* programName)
DISPLAYOUT( "Advanced arguments : \n");
DISPLAYOUT( " -V : display Version number and exit \n");
- DISPLAYOUT( " -c : write to standard output (even if it is the console) \n");
+ DISPLAYOUT( " -c : write to standard output (even if it is the console), keep original file \n");
DISPLAYOUT( " -v : verbose mode; specify multiple times to increase verbosity \n");
DISPLAYOUT( " -q : suppress warnings; specify twice to suppress errors too \n");
@@ -925,7 +925,7 @@ int main(int argCount, const char* argv[])
if (!strcmp(argument, "--help")) { usage_advanced(programName); CLEAN_RETURN(0); }
if (!strcmp(argument, "--verbose")) { g_displayLevel++; continue; }
if (!strcmp(argument, "--quiet")) { g_displayLevel--; continue; }
- if (!strcmp(argument, "--stdout")) { forceStdout=1; outFileName=stdoutmark; g_displayLevel-=(g_displayLevel==2); continue; }
+ if (!strcmp(argument, "--stdout")) { forceStdout=1; outFileName=stdoutmark; FIO_setRemoveSrcFile(prefs, 0); g_displayLevel-=(g_displayLevel==2); continue; }
if (!strcmp(argument, "--ultra")) { ultra=1; continue; }
if (!strcmp(argument, "--check")) { FIO_setChecksumFlag(prefs, 2); continue; }
if (!strcmp(argument, "--no-check")) { FIO_setChecksumFlag(prefs, 0); continue; }
@@ -1114,7 +1114,7 @@ int main(int argCount, const char* argv[])
operation=zom_decompress; argument++; break;
/* Force stdout, even if stdout==console */
- case 'c': forceStdout=1; outFileName=stdoutmark; argument++; break;
+ case 'c': forceStdout=1; outFileName=stdoutmark; FIO_setRemoveSrcFile(prefs, 0); argument++; break;
/* do not store filename - gzip compatibility - nothing to do */
case 'n': argument++; break;
@@ -1279,7 +1279,7 @@ int main(int argCount, const char* argv[])
}
nbInputFileNames = filenames->tableSize; /* saving number of input files */
-
+
if (recursive) { /* at this stage, filenameTable is a list of paths, which can contain both files and directories */
UTIL_expandFNT(&filenames, followLinks);
}
@@ -1392,7 +1392,7 @@ int main(int argCount, const char* argv[])
}
UTIL_refFilename(filenames, stdinmark);
}
-
+
if (!strcmp(filenames->fileNames[0], stdinmark) && !outFileName)
outFileName = stdoutmark; /* when input is stdin, default output is stdout */
diff --git a/tests/cli-tests/compression/basic.sh b/tests/cli-tests/compression/basic.sh
index 8b63e40760c..b6e2aa0456a 100755
--- a/tests/cli-tests/compression/basic.sh
+++ b/tests/cli-tests/compression/basic.sh
@@ -24,6 +24,10 @@ zstd -c file | zstd -t
zstd --stdout file | zstd -t
println bob | zstd | zstd -t
+# Test keeping input file when compressing to stdout in gzip mode
+$ZSTD_SYMLINK_DIR/gzip -c file | zstd -t ; test -f file
+$ZSTD_SYMLINK_DIR/gzip --stdout file | zstd -t ; test -f file
+
# Test --rm
cp file file-rm
zstd --rm file-rm; zstd -t file-rm.zst
From 696fa2524a584d4e77c55d3a639d2e0283bd919f Mon Sep 17 00:00:00 2001
From: Nick Terrell
Date: Wed, 9 Mar 2022 14:39:13 -0800
Subject: [PATCH 108/472] [doc] Add decompressor errata document
Add a document that lists the known bugs in the decoder where valid
frames are rejected, along with the version that they were fixed.
---
doc/decompressor_errata.md | 84 ++++++++++++++++++++++++++++++++++++++
1 file changed, 84 insertions(+)
create mode 100644 doc/decompressor_errata.md
diff --git a/doc/decompressor_errata.md b/doc/decompressor_errata.md
new file mode 100644
index 00000000000..9c11264018d
--- /dev/null
+++ b/doc/decompressor_errata.md
@@ -0,0 +1,84 @@
+Decompressor Errata
+===================
+
+This document captures known decompressor bugs, where the decompressor rejects a valid zstd frame.
+Each entry will contain:
+1. The last affected decompressor versions.
+2. The decompressor components affected.
+2. Whether the compressed frame could ever be produced by the reference compressor.
+3. An example frame.
+4. A description of the bug.
+
+The document is in reverse chronological order, with the bugs that affect the most recent zstd decompressor versions listed first.
+
+
+Compressed block with 0 literals and 0 sequences
+------------------------------------------------
+
+**Last affected version**: v1.5.2
+
+**Affected decompressor component(s)**: Library & CLI
+
+**Produced by the reference compressor**: No
+
+**Example Frame**: `28b5 2ffd 2000 1500 0000 00`
+
+The zstd decoder incorrectly rejected blocks of type `Compressed_Block` that encodes literals as `Raw_Literals_Block` with no literals, and has no sequences.
+
+This type of block was never generated by the reference compressor.
+
+Additionally, these blocks were disallowed by the spec up until spec version 0.3.2 when the restriciton was lifted by [PR#1689](https://github.com/facebook/zstd/pull/1689).
+
+> A Compressed_Block has the extra restriction that Block_Size is always strictly less than the decompressed size. If this condition cannot be respected, the block must be sent uncompressed instead (Raw_Block).
+
+First block is RLE block
+------------------------
+
+**Last affected version**: v1.4.3
+
+**Affected decompressor component(s)**: CLI only
+
+**Produced by the reference compressor**: No
+
+**Example Frame**: `28b5 2ffd a001 0002 0002 0010 000b 0000 00`
+
+The zstd CLI decompressor rejected cases where the first block was an RLE block whose `Block_Size` is 131072, and the frame contains more than one block.
+This only affected the zstd CLI, and not the library.
+
+The example is an RLE block with 131072 bytes, followed by a second RLE block with 1 byte.
+
+The compressor currently works around this limitation by explicitly avoiding producing RLE blocks as the first
+block.
+
+https://github.com/facebook/zstd/blob/8814aa5bfa74f05a86e55e9d508da177a893ceeb/lib/compress/zstd_compress.c#L3527-L3535
+
+Tiny FSE Table & Block
+----------------------
+
+**Last affected version**: v1.3.4
+
+**Affected decompressor component(s)**: Library & CLI
+
+**Produced by the reference compressor**: Possibly until version v1.3.4, but probably never
+
+**Example Frame**: `28b5 2ffd 2027 c500 0080 f3f1 f0ec ebc6 c5c7 f09d 4300 0000 e0e0 0658 0100 603e 52`
+
+The zstd library rejected blocks of type `Compressed_Block` whose offset of the last table with type `FSE_Compressed_Mode` was less than 4 bytes from the end of the block.
+
+In more depth, let `Last_Table_Offset` be the offset in the compressed block (excluding the header) that
+the last table with type `FSE_Compressed_Mode` started. If `Block_Content - Last_Table_Offset < 4` then
+the buggy zstd decompressor would reject the block. This occurs when the last serialized table is 2 bytes
+and the bitstream size is 1 byte.
+
+For example:
+* There is 1 sequence in the block
+* `Literals_Lengths_Mode` is `FSE_Compressed_Mode` & the serialized table size is 2 bytes
+* `Offsets_Mode` is `Predefined_Mode`
+* `Match_Lengths_Mode` is `Predefined_Mode`
+* The bitstream is 1 byte. E.g. there is only one sequence and it fits in 1 byte.
+
+The total `Block_Content` is `5` bytes, and `Last_Table_Offset` is `2`.
+
+See the compressor workaround code:
+
+https://github.com/facebook/zstd/blob/8814aa5bfa74f05a86e55e9d508da177a893ceeb/lib/compress/zstd_compress.c#L2667-L2682
From 498ac8238d98cc5f7153fa58a5e0a5222ffd88be Mon Sep 17 00:00:00 2001
From: Cyber Knight
Date: Thu, 10 Mar 2022 15:32:13 +0800
Subject: [PATCH 109/472] [contrib][linux] Make zstd_reset_cstream()
functionally identical to ZSTD_resetCStream()
- As referenced by Nick Terrelln ~ the ZSTD maintainer in the linux kernel, making zstd_reset_cstream() functionally identical to ZSTD_resetCStream() would be the perfect way to fix the warning without touching any core functions or breaking other parts of the code.
Suggested-by: Nick Terrell
Signed-off-by: Cyber Knight
---
contrib/linux-kernel/zstd_compress_module.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/contrib/linux-kernel/zstd_compress_module.c b/contrib/linux-kernel/zstd_compress_module.c
index 8b4c764d426..04e1b5c01d9 100644
--- a/contrib/linux-kernel/zstd_compress_module.c
+++ b/contrib/linux-kernel/zstd_compress_module.c
@@ -131,9 +131,13 @@ zstd_cstream *zstd_init_cstream(const zstd_parameters *parameters,
EXPORT_SYMBOL(zstd_init_cstream);
size_t zstd_reset_cstream(zstd_cstream *cstream,
- unsigned long long pledgedSrcSize)
+ unsigned long long pledged_src_size)
{
- return ZSTD_CCtx_setPledgedSrcSize(cstream, pledgedSrcSize);
+ if (pledged_src_size == 0)
+ pledged_src_size = ZSTD_CONTENTSIZE_UNKNOWN;
+ ZSTD_FORWARD_IF_ERR( ZSTD_CCtx_reset(cstream, ZSTD_reset_session_only) );
+ ZSTD_FORWARD_IF_ERR( ZSTD_CCtx_setPledgedSrcSize(cstream, pledged_src_size) );
+ return 0;
}
EXPORT_SYMBOL(zstd_reset_cstream);
From 7a3997c21a6e7620eaf6562b69326e23ffdde23d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dirk=20M=C3=BCller?=
Date: Thu, 10 Mar 2022 00:25:05 +0100
Subject: [PATCH 110/472] Handle newer less versions in zstdless testing
Newer less versions appear to have changed how stderr
and stdout are showing error messages. hardcode the
expected behavior to make the tests pass with any less version.
Also set locale to C so that the strings are matching.
---
tests/cli-tests/cltools/zstdless.sh | 2 +-
tests/cli-tests/run.py | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/tests/cli-tests/cltools/zstdless.sh b/tests/cli-tests/cltools/zstdless.sh
index 61f768862c4..a0697bde644 100755
--- a/tests/cli-tests/cltools/zstdless.sh
+++ b/tests/cli-tests/cltools/zstdless.sh
@@ -7,4 +7,4 @@ zstdless file.zst
println "+ pass parameters"
zstdless -N file.zst # This parameter does not produce line #s when piped, but still serves to test that the flag went to less and not zstd
println "+ bad path"
-zstdless bad.zst
+zstdless bad.zst >&2
diff --git a/tests/cli-tests/run.py b/tests/cli-tests/run.py
index 9bba2ecd1ab..b91aa984145 100755
--- a/tests/cli-tests/run.py
+++ b/tests/cli-tests/run.py
@@ -704,6 +704,7 @@ def setup_zstd_symlink_dir(zstd_symlink_dir: str, zstd: str) -> None:
env["ZSTDLESS_BIN"] = os.path.abspath(args.zstdless)
env["COMMON"] = os.path.abspath(os.path.join(args.test_dir, "common"))
env["PATH"] = bin_dir + ":" + os.getenv("PATH", "")
+ env["LC_ALL"] = "C"
opts = Options(
env=env,
From b772f53952fa167e3c8d5630b26397e8d4fb4c5b Mon Sep 17 00:00:00 2001
From: Dominique Pelle
Date: Sat, 12 Mar 2022 08:52:40 +0100
Subject: [PATCH 111/472] Typo and grammar fixes
---
.github/ISSUE_TEMPLATE/bug_report.md | 4 ++--
CONTRIBUTING.md | 20 ++++++++++----------
build/cmake/README.md | 4 ++--
build/single_file_libs/build_decoder_test.sh | 2 +-
build/single_file_libs/build_library_test.sh | 2 +-
build/single_file_libs/combine.py | 2 +-
build/single_file_libs/zstd-in.c | 2 +-
build/single_file_libs/zstddeclib-in.c | 2 +-
contrib/match_finders/README.md | 8 ++++----
contrib/match_finders/zstd_edist.c | 14 +++++++-------
contrib/match_finders/zstd_edist.h | 8 ++++----
contrib/pzstd/utils/ScopeGuard.h | 2 +-
doc/zstd_compression_format.md | 2 +-
doc/zstd_manual.html | 6 +++---
lib/README.md | 2 +-
lib/common/bitstream.h | 2 +-
lib/common/fse.h | 2 +-
lib/common/fse_decompress.c | 4 ++--
lib/common/xxhash.h | 4 ++--
lib/compress/zstd_compress.c | 4 ++--
lib/compress/zstd_compress_sequences.c | 4 ++--
lib/compress/zstd_compress_superblock.c | 2 +-
lib/compress/zstd_lazy.c | 2 +-
lib/compress/zstd_ldm.c | 2 +-
lib/compress/zstdmt_compress.c | 2 +-
lib/decompress/zstd_decompress.c | 10 +++++-----
lib/decompress/zstd_decompress_block.c | 8 ++++----
lib/dictBuilder/cover.c | 2 +-
lib/dictBuilder/fastcover.c | 2 +-
lib/dll/example/README.md | 2 +-
lib/legacy/zstd_v02.c | 4 ++--
lib/legacy/zstd_v03.c | 4 ++--
lib/legacy/zstd_v04.c | 4 ++--
lib/legacy/zstd_v05.c | 4 ++--
lib/legacy/zstd_v06.c | 4 ++--
lib/legacy/zstd_v07.c | 4 ++--
lib/zdict.h | 4 ++--
lib/zstd.h | 8 ++++----
programs/benchfn.h | 2 +-
programs/fileio.c | 4 ++--
programs/util.c | 2 +-
programs/zstd.1 | 4 ++--
programs/zstd.1.md | 12 ++++++------
programs/zstdcli.c | 2 +-
tests/README.md | 6 +++---
tests/automated_benchmarking.py | 2 +-
tests/fuzz/fuzz.py | 2 +-
tests/fuzz/sequence_compression_api.c | 2 +-
tests/fuzz/simple_compress.c | 2 +-
tests/fuzzer.c | 14 +++++++-------
tests/paramgrill.c | 2 +-
zlibWrapper/README.md | 2 +-
52 files changed, 113 insertions(+), 113 deletions(-)
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index bacdac27b06..755a46af145 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -26,8 +26,8 @@ If applicable, add screenshots and charts to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. Mac]
- Version [e.g. 22]
- - Compiler [e.g gcc]
- - Flags [e.g O2]
+ - Compiler [e.g. gcc]
+ - Flags [e.g. O2]
- Other relevant hardware specs [e.g. Dual-core]
- Build system [e.g. Makefile]
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index e7e545129e5..f5e747ae1b9 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -7,7 +7,7 @@ New versions are being developed in the "dev" branch,
or in their own feature branch.
When they are deemed ready for a release, they are merged into "release".
-As a consequences, all contributions must stage first through "dev"
+As a consequence, all contributions must stage first through "dev"
or their own feature branch.
## Pull Requests
@@ -134,11 +134,11 @@ It can be useful to look at additional static analyzers once in a while (and we
- Static analyzers are full of false positive. The signal to noise ratio is actually pretty low.
- A good CI policy is "zero-warning tolerance". That means that all issues must be solved, including false positives. This quickly becomes a tedious workload.
- Multiple static analyzers will feature multiple kind of false positives, sometimes applying to the same code but in different ways leading to :
- + torteous code, trying to please multiple constraints, hurting readability and therefore maintenance. Sometimes, such complexity introduce other more subtle bugs, that are just out of scope of the analyzers.
+ + tortuous code, trying to please multiple constraints, hurting readability and therefore maintenance. Sometimes, such complexity introduce other more subtle bugs, that are just out of scope of the analyzers.
+ sometimes, these constraints are mutually exclusive : if one try to solve one, the other static analyzer will complain, they can't be both happy at the same time.
- As if that was not enough, the list of false positives change with each version. It's hard enough to follow one static analyzer, but multiple ones with their own update agenda, this quickly becomes a massive velocity reducer.
-This is different from running a static analyzer once in a while, looking at the output, and __cherry picking__ a few warnings that seem helpful, either because they detected a genuine risk of bug, or because it helps expressing the code in a way which is more readable or more difficult to misuse. These kind of reports can be useful, and are accepted.
+This is different from running a static analyzer once in a while, looking at the output, and __cherry picking__ a few warnings that seem helpful, either because they detected a genuine risk of bug, or because it helps expressing the code in a way which is more readable or more difficult to misuse. These kinds of reports can be useful, and are accepted.
## Continuous Integration
CI tests run every time a pull request (PR) is created or updated. The exact tests
@@ -197,7 +197,7 @@ something subtle merged is extensive benchmarking. You will be doing us a great
take the time to run extensive, long-duration, and potentially cross-(os, platform, process, etc)
benchmarks on your end before submitting a PR. Of course, you will not be able to benchmark
your changes on every single processor and os out there (and neither will we) but do that best
-you can:) We've adding some things to think about when benchmarking below in the Benchmarking
+you can:) We've added some things to think about when benchmarking below in the Benchmarking
Performance section which might be helpful for you.
3. Optimizing performance for a certain OS, processor vendor, compiler, or network system is a perfectly
legitimate thing to do as long as it does not harm the overall performance health of Zstd.
@@ -273,7 +273,7 @@ for that options you have just provided. If you want to look at the internals of
benchmarking script works, you can check out programs/benchzstd.c
For example: say you have made a change that you believe improves the speed of zstd level 1. The
-very first thing you should use to asses whether you actually achieved any sort of improvement
+very first thing you should use to assess whether you actually achieved any sort of improvement
is `zstd -b`. You might try to do something like this. Note: you can use the `-i` option to
specify a running time for your benchmark in seconds (default is 3 seconds).
Usually, the longer the running time, the more stable your results will be.
@@ -299,7 +299,7 @@ this method of evaluation will not be sufficient.
### Profiling
There are a number of great profilers out there. We're going to briefly mention how you can
profile your code using `instruments` on mac, `perf` on linux and `visual studio profiler`
-on windows.
+on Windows.
Say you have an idea for a change that you think will provide some good performance gains
for level 1 compression on Zstd. Typically this means, you have identified a section of
@@ -315,8 +315,8 @@ might be).
Most profilers (including the profilers discussed below) will generate a call graph of
functions for you. Your goal will be to find your function of interest in this call graph
-and then inspect the time spent inside of it. You might also want to to look at the
-annotated assembly which most profilers will provide you with.
+and then inspect the time spent inside of it. You might also want to look at the annotated
+assembly which most profilers will provide you with.
#### Instruments
We will once again consider the scenario where you think you've identified a piece of code
@@ -330,7 +330,7 @@ Instruments.
* You will want a benchmark that runs for at least a few seconds (5 seconds will
usually be long enough). This way the profiler will have something to work with
and you will have ample time to attach your profiler to this process:)
- * I will just use benchzstd as my bencharmking script for this example:
+ * I will just use benchzstd as my benchmarmking script for this example:
```
$ zstd -b1 -i5 # this will run for 5 seconds
```
@@ -455,7 +455,7 @@ This design requirement is fundamental to preserve the portability of the code b
Any variable that can be `const` (aka. read-only) **must** be `const`.
Any pointer which content will not be modified must be `const`.
This property is then controlled at compiler level.
- `const` variables are an important signal to readers that this variable isn’t modified.
+ `const` variables are an important signal to readers that this variable isn't modified.
Conversely, non-const variables are a signal to readers to watch out for modifications later on in the function.
* If a function must be inlined, mention it explicitly,
using project's own portable macros, such as `FORCE_INLINE_ATTR`,
diff --git a/build/cmake/README.md b/build/cmake/README.md
index f6854f4c05a..a460dd16187 100644
--- a/build/cmake/README.md
+++ b/build/cmake/README.md
@@ -1,13 +1,13 @@
# Cmake contributions
Contributions to the cmake build configurations are welcome. Please
-use case sensitivity that matches modern (ie. cmake version 2.6 and above)
+use case sensitivity that matches modern (i.e. cmake version 2.6 and above)
conventions of using lower-case for commands, and upper-case for
variables.
## How to build
-As cmake doesn't support command like `cmake clean`, it's recommended to perform a "out of source build".
+As cmake doesn't support command like `cmake clean`, it's recommended to perform an "out of source build".
To do this, you can create a new directory and build in it:
```sh
cd build/cmake
diff --git a/build/single_file_libs/build_decoder_test.sh b/build/single_file_libs/build_decoder_test.sh
index 48d017fcffa..c4ca55fa5dd 100755
--- a/build/single_file_libs/build_decoder_test.sh
+++ b/build/single_file_libs/build_decoder_test.sh
@@ -11,7 +11,7 @@ IN_FILES="examples/emscripten.c"
# Emscripten build using emcc.
emscripten_emcc_build() {
- # Compile the the same example as above
+ # Compile the same example as above
CC_FLAGS="-Wall -Wextra -Wshadow -Werror -Os -g0 -flto"
emcc $CC_FLAGS -s WASM=1 -I. -o $OUT_WASM $IN_FILES
# Did compilation work?
diff --git a/build/single_file_libs/build_library_test.sh b/build/single_file_libs/build_library_test.sh
index 31545fc3fe3..f4ba109ab38 100755
--- a/build/single_file_libs/build_library_test.sh
+++ b/build/single_file_libs/build_library_test.sh
@@ -14,7 +14,7 @@ IN_FILES="zstd.c examples/roundtrip.c"
# Emscripten build using emcc.
emscripten_emcc_build() {
- # Compile the the same example as above
+ # Compile the same example as above
CC_FLAGS="-Wall -Wextra -Wshadow -Werror -Os -g0 -flto"
emcc $CC_FLAGS -s WASM=1 -I. -o $OUT_WASM $IN_FILES
# Did compilation work?
diff --git a/build/single_file_libs/combine.py b/build/single_file_libs/combine.py
index 3d91dc3eec2..771dd20bfb1 100755
--- a/build/single_file_libs/combine.py
+++ b/build/single_file_libs/combine.py
@@ -107,7 +107,7 @@ def test_match_pragma() -> bool:
return False
# Finds 'file'. First the list of 'root' paths are searched, followed by the
-# the currently processing file's 'parent' path, returning a valid Path in
+# currently processing file's 'parent' path, returning a valid Path in
# canonical form. If no match is found None is returned.
#
def resolve_include(file: str, parent: Optional[Path] = None) -> Optional[Path]:
diff --git a/build/single_file_libs/zstd-in.c b/build/single_file_libs/zstd-in.c
index eecd9a688ee..59c6b5f19b6 100644
--- a/build/single_file_libs/zstd-in.c
+++ b/build/single_file_libs/zstd-in.c
@@ -25,7 +25,7 @@
* Note: MEM_MODULE stops xxhash redefining BYTE, U16, etc., which are also
* defined in mem.h (breaking C99 compatibility).
*
- * Note: the undefs for xxHash allow Zstd's implementation to coincide with with
+ * Note: the undefs for xxHash allow Zstd's implementation to coincide with
* standalone xxHash usage (with global defines).
*
* Note: if you enable ZSTD_LEGACY_SUPPORT the combine.py script will need
diff --git a/build/single_file_libs/zstddeclib-in.c b/build/single_file_libs/zstddeclib-in.c
index d0343c54a46..5a58589c970 100644
--- a/build/single_file_libs/zstddeclib-in.c
+++ b/build/single_file_libs/zstddeclib-in.c
@@ -25,7 +25,7 @@
* Note: MEM_MODULE stops xxhash redefining BYTE, U16, etc., which are also
* defined in mem.h (breaking C99 compatibility).
*
- * Note: the undefs for xxHash allow Zstd's implementation to coincide with with
+ * Note: the undefs for xxHash allow Zstd's implementation to coincide with
* standalone xxHash usage (with global defines).
*
* Note: if you enable ZSTD_LEGACY_SUPPORT the combine.py script will need
diff --git a/contrib/match_finders/README.md b/contrib/match_finders/README.md
index 0f4a3b1ce82..54055c37086 100644
--- a/contrib/match_finders/README.md
+++ b/contrib/match_finders/README.md
@@ -14,7 +14,7 @@
* files would be small relative to the size of the file.
*
* Various 'diffing' algorithms utilize this notion of edit distance and
- * the corrensponding concept of a minimal edit script between two
+ * the corresponding concept of a minimal edit script between two
* sequences to identify the regions within two files where they differ.
* The core algorithm used in this match finder is described in:
*
@@ -28,12 +28,12 @@
*
* Note: after some experimentation, this approach proved to not provide enough
* utility to justify the additional CPU used in finding matches. The one area
- * where this approach consistenly outperforms Zstandard even on level 19 is
- * when compressing small files (<10 KB) using a equally small dictionary that
+ * where this approach consistently outperforms Zstandard even on level 19 is
+ * when compressing small files (<10 KB) using an equally small dictionary that
* is very similar to the source file. For the use case that this was intended,
* (large similar files) this approach by itself took 5-10X longer than zstd-19 and
* generally resulted in 2-3X larger files. The core advantage that zstd-19 has
- * over this appraoch for match finding is the overlapping matches. This approach
+ * over this approach for match finding is the overlapping matches. This approach
* cannot find any.
*
* I'm leaving this in the contrib section in case this ever becomes interesting
diff --git a/contrib/match_finders/zstd_edist.c b/contrib/match_finders/zstd_edist.c
index aab545fd3ba..b5523f4e40b 100644
--- a/contrib/match_finders/zstd_edist.c
+++ b/contrib/match_finders/zstd_edist.c
@@ -12,7 +12,7 @@
* Dependencies
***************************************/
-/* Currently relies on qsort when combining contiguous matches. This can probabily
+/* Currently relies on qsort when combining contiguous matches. This can probably
* be avoided but would require changes to the algorithm. The qsort is far from
* the bottleneck in this algorithm even for medium sized files so it's probably
* not worth trying to address */
@@ -26,7 +26,7 @@
* Constants
***************************************/
-/* Just a sential for the entires of the diagnomal matrix */
+/* Just a sential for the entires of the diagonal matrix */
#define ZSTD_EDIST_DIAG_MAX (S32)(1 << 30)
/* How large should a snake be to be considered a 'big' snake.
@@ -39,7 +39,7 @@
#define ZSTD_EDIST_SNAKE_ITER_THRESH 200
/* After how many iterations should be just give up and take
- * the best availabe edit script for this round */
+ * the best available edit script for this round */
#define ZSTD_EDIST_EXPENSIVE_THRESH 1024
/*-*************************************
@@ -192,7 +192,7 @@ static void ZSTD_eDist_diag(ZSTD_eDist_state* state,
if (!useHeuristics)
continue;
- /* Everything under this point is a heuritic. Using these will
+ /* Everything under this point is a heuristic. Using these will
* substantially speed up the match finding. In some cases, taking
* the total match finding time from several minutes to seconds.
* Of course, the caveat is that the edit script found may no longer
@@ -366,8 +366,8 @@ static int ZSTD_eDist_compare(ZSTD_eDist_state* state,
}
} else if (srcLow == srcHigh) {
while (dictLow < dictHigh) {
- /* Reaching this point means deleteing dict[dictLow] from
- * the current positino of dict */
+ /* Reaching this point means deleting dict[dictLow] from
+ * the current position of dict */
dictLow++;
}
} else {
@@ -395,7 +395,7 @@ static int ZSTD_eDist_matchComp(const void* p, const void* q)
}
/* The matches from the approach above will all be of the form
- * (dictIdx, srcIdx, 1). this method combines contiguous matches
+ * (dictIdx, srcIdx, 1). This method combines contiguous matches
* of length MINMATCH or greater. Matches less than MINMATCH
* are discarded */
static void ZSTD_eDist_combineMatches(ZSTD_eDist_state* state)
diff --git a/contrib/match_finders/zstd_edist.h b/contrib/match_finders/zstd_edist.h
index c775a498503..a947649b2c8 100644
--- a/contrib/match_finders/zstd_edist.h
+++ b/contrib/match_finders/zstd_edist.h
@@ -21,7 +21,7 @@
* files would be small relative to the size of the file.
*
* Various 'diffing' algorithms utilize this notion of edit distance and
- * the corrensponding concept of a minimal edit script between two
+ * the corresponding concept of a minimal edit script between two
* sequences to identify the regions within two files where they differ.
* The core algorithm used in this match finder is described in:
*
@@ -35,12 +35,12 @@
*
* Note: after some experimentation, this approach proved to not provide enough
* utility to justify the additional CPU used in finding matches. The one area
- * where this approach consistenly outperforms Zstandard even on level 19 is
- * when compressing small files (<10 KB) using a equally small dictionary that
+ * where this approach consistently outperforms Zstandard even on level 19 is
+ * when compressing small files (<10 KB) using an equally small dictionary that
* is very similar to the source file. For the use case that this was intended,
* (large similar files) this approach by itself took 5-10X longer than zstd-19 and
* generally resulted in 2-3X larger files. The core advantage that zstd-19 has
- * over this appraoch for match finding is the overlapping matches. This approach
+ * over this approach for match finding is the overlapping matches. This approach
* cannot find any.
*
* I'm leaving this in the contrib section in case this ever becomes interesting
diff --git a/contrib/pzstd/utils/ScopeGuard.h b/contrib/pzstd/utils/ScopeGuard.h
index 31768f43d22..c26f911bf3c 100644
--- a/contrib/pzstd/utils/ScopeGuard.h
+++ b/contrib/pzstd/utils/ScopeGuard.h
@@ -15,7 +15,7 @@ namespace pzstd {
/**
* Dismissable scope guard.
* `Function` must be callable and take no parameters.
- * Unless `dissmiss()` is called, the callable is executed upon destruction of
+ * Unless `dismiss()` is called, the callable is executed upon destruction of
* `ScopeGuard`.
*
* Example:
diff --git a/doc/zstd_compression_format.md b/doc/zstd_compression_format.md
index fc09bd5538c..cd53b7f9421 100644
--- a/doc/zstd_compression_format.md
+++ b/doc/zstd_compression_format.md
@@ -974,7 +974,7 @@ and their content ignored, resuming decoding after the skippable frame.
It can be noted that a skippable frame
can be used to watermark a stream of concatenated frames
-embedding any kind of tracking information (even just an UUID).
+embedding any kind of tracking information (even just a UUID).
Users wary of such possibility should scan the stream of concatenated frames
in an attempt to detect such frame for analysis or removal.
diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html
index 6b45e0a99a8..4bbb513bbe5 100644
--- a/doc/zstd_manual.html
+++ b/doc/zstd_manual.html
@@ -790,11 +790,11 @@ Streaming decompression functions
unsigned ZSTD_getDictID_fromFrame(const void* src, size_t srcSize);
- Provides the dictID required to decompressed the frame stored within `src`.
+
Provides the dictID required to decompress the frame stored within `src`.
If @return == 0, the dictID could not be decoded.
This could for one of the following reasons :
- The frame does not require a dictionary to be decoded (most common case).
- - The frame was built with dictID intentionally removed. Whatever dictionary is necessary is a hidden information.
+ - The frame was built with dictID intentionally removed. Whatever dictionary is necessary is a hidden piece of information.
Note : this use case also happens when using a non-conformant dictionary.
- `srcSize` is too small, and as a result, the frame header could not be decoded (only possible if `srcSize < ZSTD_FRAMEHEADERSIZE_MAX`).
- This is not a Zstandard frame.
@@ -1164,7 +1164,7 @@
Streaming decompression functions
ZSTDLIB_STATIC_API size_t ZSTD_compressSequences(ZSTD_CCtx* const cctx, void* dst, size_t dstSize,
const ZSTD_Sequence* inSeqs, size_t inSeqsSize,
const void* src, size_t srcSize);
- Compress an array of ZSTD_Sequence, associted with @src buffer, into dst.
+
Compress an array of ZSTD_Sequence, associated with @src buffer, into dst.
@src contains the entire input (not just the literals).
If @srcSize > sum(sequence.length), the remaining bytes are considered all literals
If a dictionary is included, then the cctx should reference the dict. (see: ZSTD_CCtx_refCDict(), ZSTD_CCtx_loadDictionary(), etc.)
diff --git a/lib/README.md b/lib/README.md
index 4c9d8f05912..015cde567e5 100644
--- a/lib/README.md
+++ b/lib/README.md
@@ -91,7 +91,7 @@ The file structure is designed to make this selection manually achievable for an
`ZSTD_LIB_COMPRESSION, ZSTD_LIB_DECOMPRESSION`, `ZSTD_LIB_DICTBUILDER`,
and `ZSTD_LIB_DEPRECATED` as `0` to forgo compilation of the
corresponding features. This will also disable compilation of all
- dependencies (eg. `ZSTD_LIB_COMPRESSION=0` will also disable
+ dependencies (e.g. `ZSTD_LIB_COMPRESSION=0` will also disable
dictBuilder).
- There are a number of options that can help minimize the binary size of
diff --git a/lib/common/bitstream.h b/lib/common/bitstream.h
index 731630ea44b..84177862645 100644
--- a/lib/common/bitstream.h
+++ b/lib/common/bitstream.h
@@ -365,7 +365,7 @@ MEM_STATIC FORCE_INLINE_ATTR size_t BIT_readBits(BIT_DStream_t* bitD, unsigned n
}
/*! BIT_readBitsFast() :
- * unsafe version; only works only if nbBits >= 1 */
+ * unsafe version; only works if nbBits >= 1 */
MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, unsigned nbBits)
{
size_t const value = BIT_lookBitsFast(bitD, nbBits);
diff --git a/lib/common/fse.h b/lib/common/fse.h
index bd29e9ac55c..466a072818a 100644
--- a/lib/common/fse.h
+++ b/lib/common/fse.h
@@ -555,7 +555,7 @@ MEM_STATIC void FSE_flushCState(BIT_CStream_t* bitC, const FSE_CState_t* statePt
/* FSE_getMaxNbBits() :
* Approximate maximum cost of a symbol, in bits.
- * Fractional get rounded up (i.e : a symbol with a normalized frequency of 3 gives the same result as a frequency of 2)
+ * Fractional get rounded up (i.e. a symbol with a normalized frequency of 3 gives the same result as a frequency of 2)
* note 1 : assume symbolValue is valid (<= maxSymbolValue)
* note 2 : if freq[symbolValue]==0, @return a fake cost of tableLog+1 bits */
MEM_STATIC U32 FSE_getMaxNbBits(const void* symbolTTPtr, U32 symbolValue)
diff --git a/lib/common/fse_decompress.c b/lib/common/fse_decompress.c
index 184b35dd17a..7034fd97b47 100644
--- a/lib/common/fse_decompress.c
+++ b/lib/common/fse_decompress.c
@@ -128,10 +128,10 @@ static size_t FSE_buildDTable_internal(FSE_DTable* dt, const short* normalizedCo
}
}
/* Now we spread those positions across the table.
- * The benefit of doing it in two stages is that we avoid the the
+ * The benefit of doing it in two stages is that we avoid the
* variable size inner loop, which caused lots of branch misses.
* Now we can run through all the positions without any branch misses.
- * We unroll the loop twice, since that is what emperically worked best.
+ * We unroll the loop twice, since that is what empirically worked best.
*/
{
size_t position = 0;
diff --git a/lib/common/xxhash.h b/lib/common/xxhash.h
index 8ebbfdd6261..acac7ad62ed 100644
--- a/lib/common/xxhash.h
+++ b/lib/common/xxhash.h
@@ -1534,7 +1534,7 @@ static void* XXH_memcpy(void* dest, const void* src, size_t size) { return ZSTD_
* @brief Used to prevent unwanted optimizations for @p var.
*
* It uses an empty GCC inline assembly statement with a register constraint
- * which forces @p var into a general purpose register (eg eax, ebx, ecx
+ * which forces @p var into a general purpose register (e.g. eax, ebx, ecx
* on x86) and marks it as modified.
*
* This is used in a few places to avoid unwanted autovectorization (e.g.
@@ -2809,7 +2809,7 @@ enum XXH_VECTOR_TYPE /* fake enum */ {
* @ingroup tuning
* @brief Selects the minimum alignment for XXH3's accumulators.
*
- * When using SIMD, this should match the alignment reqired for said vector
+ * When using SIMD, this should match the alignment required for said vector
* type, so, for example, 32 for AVX2.
*
* Default: Auto detected.
diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
index aa1a9f35b9d..e43bbec44fc 100644
--- a/lib/compress/zstd_compress.c
+++ b/lib/compress/zstd_compress.c
@@ -4211,7 +4211,7 @@ static size_t ZSTD_loadDictionaryContent(ZSTD_matchState_t* ms,
const BYTE* const iend = ip + srcSize;
int const loadLdmDict = params->ldmParams.enableLdm == ZSTD_ps_enable && ls != NULL;
- /* Assert that we the ms params match the params we're being given */
+ /* Assert that the ms params match the params we're being given */
ZSTD_assertEqualCParams(params->cParams, ms->cParams);
if (srcSize > ZSTD_CHUNKSIZE_MAX) {
@@ -5820,7 +5820,7 @@ ZSTD_validateSequence(U32 offCode, U32 matchLength,
size_t posInSrc, U32 windowLog, size_t dictSize)
{
U32 const windowSize = 1 << windowLog;
- /* posInSrc represents the amount of data the the decoder would decode up to this point.
+ /* posInSrc represents the amount of data the decoder would decode up to this point.
* As long as the amount of data decoded is less than or equal to window size, offsets may be
* larger than the total length of output decoded in order to reference the dict, even larger than
* window size. After output surpasses windowSize, we're limited to windowSize offsets again.
diff --git a/lib/compress/zstd_compress_sequences.c b/lib/compress/zstd_compress_sequences.c
index f1e40af2ea0..2c1eee5676a 100644
--- a/lib/compress/zstd_compress_sequences.c
+++ b/lib/compress/zstd_compress_sequences.c
@@ -58,7 +58,7 @@ static unsigned ZSTD_useLowProbCount(size_t const nbSeq)
{
/* Heuristic: This should cover most blocks <= 16K and
* start to fade out after 16K to about 32K depending on
- * comprssibility.
+ * compressibility.
*/
return nbSeq >= 2048;
}
@@ -166,7 +166,7 @@ ZSTD_selectEncodingType(
if (mostFrequent == nbSeq) {
*repeatMode = FSE_repeat_none;
if (isDefaultAllowed && nbSeq <= 2) {
- /* Prefer set_basic over set_rle when there are 2 or less symbols,
+ /* Prefer set_basic over set_rle when there are 2 or fewer symbols,
* since RLE uses 1 byte, but set_basic uses 5-6 bits per symbol.
* If basic encoding isn't possible, always choose RLE.
*/
diff --git a/lib/compress/zstd_compress_superblock.c b/lib/compress/zstd_compress_superblock.c
index 5e89a706c0e..eed58e7cfca 100644
--- a/lib/compress/zstd_compress_superblock.c
+++ b/lib/compress/zstd_compress_superblock.c
@@ -36,7 +36,7 @@
* If it is set_compressed, first sub-block's literals section will be Treeless_Literals_Block
* and the following sub-blocks' literals sections will be Treeless_Literals_Block.
* @return : compressed size of literals section of a sub-block
- * Or 0 if it unable to compress.
+ * Or 0 if unable to compress.
* Or error code */
static size_t
ZSTD_compressSubBlock_literal(const HUF_CElt* hufTable,
diff --git a/lib/compress/zstd_lazy.c b/lib/compress/zstd_lazy.c
index 278995491ec..7f97e12a5b2 100644
--- a/lib/compress/zstd_lazy.c
+++ b/lib/compress/zstd_lazy.c
@@ -951,7 +951,7 @@ void ZSTD_row_update(ZSTD_matchState_t* const ms, const BYTE* ip) {
const U32 mls = MIN(ms->cParams.minMatch, 6 /* mls caps out at 6 */);
DEBUGLOG(5, "ZSTD_row_update(), rowLog=%u", rowLog);
- ZSTD_row_update_internal(ms, ip, mls, rowLog, rowMask, 0 /* dont use cache */);
+ ZSTD_row_update_internal(ms, ip, mls, rowLog, rowMask, 0 /* don't use cache */);
}
#if defined(ZSTD_ARCH_X86_SSE2)
diff --git a/lib/compress/zstd_ldm.c b/lib/compress/zstd_ldm.c
index 1c04a36f9c7..e1d2f741014 100644
--- a/lib/compress/zstd_ldm.c
+++ b/lib/compress/zstd_ldm.c
@@ -549,7 +549,7 @@ size_t ZSTD_ldm_generateSequences(
* the window through early invalidation.
* TODO: * Test the chunk size.
* * Try invalidation after the sequence generation and test the
- * the offset against maxDist directly.
+ * offset against maxDist directly.
*
* NOTE: Because of dictionaries + sequence splitting we MUST make sure
* that any offset used is valid at the END of the sequence, since it may
diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c
index b0b337e4299..4ac2249bd3e 100644
--- a/lib/compress/zstdmt_compress.c
+++ b/lib/compress/zstdmt_compress.c
@@ -1734,7 +1734,7 @@ findSynchronizationPoint(ZSTDMT_CCtx const* mtctx, ZSTD_inBuffer const input)
}
} else {
/* We have enough bytes buffered to initialize the hash,
- * and are have processed enough bytes to find a sync point.
+ * and have processed enough bytes to find a sync point.
* Start scanning at the beginning of the input.
*/
assert(mtctx->inBuff.filled >= RSYNC_MIN_BLOCK_SIZE);
diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c
index 8950453f446..85f4d2202e9 100644
--- a/lib/decompress/zstd_decompress.c
+++ b/lib/decompress/zstd_decompress.c
@@ -1109,8 +1109,8 @@ size_t ZSTD_decompress(void* dst, size_t dstCapacity, const void* src, size_t sr
size_t ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx* dctx) { return dctx->expected; }
/**
- * Similar to ZSTD_nextSrcSizeToDecompress(), but when when a block input can be streamed,
- * we allow taking a partial block as the input. Currently only raw uncompressed blocks can
+ * Similar to ZSTD_nextSrcSizeToDecompress(), but when a block input can be streamed, we
+ * allow taking a partial block as the input. Currently only raw uncompressed blocks can
* be streamed.
*
* For blocks that can be streamed, this allows us to reduce the latency until we produce
@@ -1310,7 +1310,7 @@ size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, c
default:
assert(0); /* impossible */
- RETURN_ERROR(GENERIC, "impossible to reach"); /* some compiler require default to do something */
+ RETURN_ERROR(GENERIC, "impossible to reach"); /* some compilers require default to do something */
}
}
@@ -1516,7 +1516,7 @@ unsigned ZSTD_getDictID_fromDict(const void* dict, size_t dictSize)
* This could for one of the following reasons :
* - The frame does not require a dictionary (most common case).
* - The frame was built with dictID intentionally removed.
- * Needed dictionary is a hidden information.
+ * Needed dictionary is a hidden piece of information.
* Note : this use case also happens when using a non-conformant dictionary.
* - `srcSize` is too small, and as a result, frame header could not be decoded.
* Note : possible if `srcSize < ZSTD_FRAMEHEADERSIZE_MAX`.
@@ -2170,7 +2170,7 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB
default:
assert(0); /* impossible */
- RETURN_ERROR(GENERIC, "impossible to reach"); /* some compiler require default to do something */
+ RETURN_ERROR(GENERIC, "impossible to reach"); /* some compilers require default to do something */
} }
/* result */
diff --git a/lib/decompress/zstd_decompress_block.c b/lib/decompress/zstd_decompress_block.c
index 6f8a75f538f..7c046dab506 100644
--- a/lib/decompress/zstd_decompress_block.c
+++ b/lib/decompress/zstd_decompress_block.c
@@ -90,7 +90,7 @@ static void ZSTD_allocateLiteralsBuffer(ZSTD_DCtx* dctx, void* const dst, const
dctx->litBufferEnd = dctx->litBuffer + litSize - ZSTD_LITBUFFEREXTRASIZE;
}
else {
- /* initially this will be stored entirely in dst during huffman decoding, it will partially shifted to litExtraBuffer after */
+ /* initially this will be stored entirely in dst during huffman decoding, it will partially be shifted to litExtraBuffer after */
dctx->litBuffer = (BYTE*)dst + expectedWriteSize - litSize;
dctx->litBufferEnd = (BYTE*)dst + expectedWriteSize;
}
@@ -511,10 +511,10 @@ void ZSTD_buildFSETable_body(ZSTD_seqSymbol* dt,
}
}
/* Now we spread those positions across the table.
- * The benefit of doing it in two stages is that we avoid the the
+ * The benefit of doing it in two stages is that we avoid the
* variable size inner loop, which caused lots of branch misses.
* Now we can run through all the positions without any branch misses.
- * We unroll the loop twice, since that is what emperically worked best.
+ * We unroll the loop twice, since that is what empirically worked best.
*/
{
size_t position = 0;
@@ -1189,7 +1189,7 @@ ZSTD_decodeSequence(seqState_t* seqState, const ZSTD_longOffset_e longOffsets)
U32 const ofnbBits = ofDInfo->nbBits;
/*
* As gcc has better branch and block analyzers, sometimes it is only
- * valuable to mark likelyness for clang, it gives around 3-4% of
+ * valuable to mark likeliness for clang, it gives around 3-4% of
* performance.
*/
diff --git a/lib/dictBuilder/cover.c b/lib/dictBuilder/cover.c
index 949eb539b51..724675d304b 100644
--- a/lib/dictBuilder/cover.c
+++ b/lib/dictBuilder/cover.c
@@ -542,7 +542,7 @@ static void COVER_ctx_destroy(COVER_ctx_t *ctx) {
/**
* Prepare a context for dictionary building.
- * The context is only dependent on the parameter `d` and can used multiple
+ * The context is only dependent on the parameter `d` and can be used multiple
* times.
* Returns 0 on success or error code on error.
* The context must be destroyed with `COVER_ctx_destroy()`.
diff --git a/lib/dictBuilder/fastcover.c b/lib/dictBuilder/fastcover.c
index 3352859ada4..63a2cee7228 100644
--- a/lib/dictBuilder/fastcover.c
+++ b/lib/dictBuilder/fastcover.c
@@ -304,7 +304,7 @@ FASTCOVER_computeFrequency(U32* freqs, const FASTCOVER_ctx_t* ctx)
/**
* Prepare a context for dictionary building.
- * The context is only dependent on the parameter `d` and can used multiple
+ * The context is only dependent on the parameter `d` and can be used multiple
* times.
* Returns 0 on success or error code on error.
* The context must be destroyed with `FASTCOVER_ctx_destroy()`.
diff --git a/lib/dll/example/README.md b/lib/dll/example/README.md
index 9e30fd59331..46aec798005 100644
--- a/lib/dll/example/README.md
+++ b/lib/dll/example/README.md
@@ -46,7 +46,7 @@ The compiled executable will require ZSTD DLL which is available at `dll\libzstd
Open `example\fullbench-dll.sln` to compile `fullbench-dll` that uses a
dynamic ZSTD library from the `dll` directory. The solution works with Visual C++
2010 or newer. When one will open the solution with Visual C++ newer than 2010
-then the solution will upgraded to the current version.
+then the solution will be upgraded to the current version.
## Using ZSTD DLL with Visual C++
diff --git a/lib/legacy/zstd_v02.c b/lib/legacy/zstd_v02.c
index 2f473a75734..6eadd3948b7 100644
--- a/lib/legacy/zstd_v02.c
+++ b/lib/legacy/zstd_v02.c
@@ -433,7 +433,7 @@ MEM_STATIC size_t BIT_lookBits(BIT_DStream_t* bitD, U32 nbBits)
}
/*! BIT_lookBitsFast :
-* unsafe version; only works only if nbBits >= 1 */
+* unsafe version; only works if nbBits >= 1 */
MEM_STATIC size_t BIT_lookBitsFast(BIT_DStream_t* bitD, U32 nbBits)
{
const U32 bitMask = sizeof(bitD->bitContainer)*8 - 1;
@@ -453,7 +453,7 @@ MEM_STATIC size_t BIT_readBits(BIT_DStream_t* bitD, U32 nbBits)
}
/*!BIT_readBitsFast :
-* unsafe version; only works only if nbBits >= 1 */
+* unsafe version; only works if nbBits >= 1 */
MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, U32 nbBits)
{
size_t value = BIT_lookBitsFast(bitD, nbBits);
diff --git a/lib/legacy/zstd_v03.c b/lib/legacy/zstd_v03.c
index 6625f4df1cb..72fc9140d4a 100644
--- a/lib/legacy/zstd_v03.c
+++ b/lib/legacy/zstd_v03.c
@@ -435,7 +435,7 @@ MEM_STATIC size_t BIT_lookBits(BIT_DStream_t* bitD, U32 nbBits)
}
/*! BIT_lookBitsFast :
-* unsafe version; only works only if nbBits >= 1 */
+* unsafe version; only works if nbBits >= 1 */
MEM_STATIC size_t BIT_lookBitsFast(BIT_DStream_t* bitD, U32 nbBits)
{
const U32 bitMask = sizeof(bitD->bitContainer)*8 - 1;
@@ -455,7 +455,7 @@ MEM_STATIC size_t BIT_readBits(BIT_DStream_t* bitD, U32 nbBits)
}
/*!BIT_readBitsFast :
-* unsafe version; only works only if nbBits >= 1 */
+* unsafe version; only works if nbBits >= 1 */
MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, U32 nbBits)
{
size_t value = BIT_lookBitsFast(bitD, nbBits);
diff --git a/lib/legacy/zstd_v04.c b/lib/legacy/zstd_v04.c
index 8d305c7eae9..a2d281eb97d 100644
--- a/lib/legacy/zstd_v04.c
+++ b/lib/legacy/zstd_v04.c
@@ -700,7 +700,7 @@ MEM_STATIC size_t BIT_lookBits(BIT_DStream_t* bitD, U32 nbBits)
}
/*! BIT_lookBitsFast :
-* unsafe version; only works only if nbBits >= 1 */
+* unsafe version; only works if nbBits >= 1 */
MEM_STATIC size_t BIT_lookBitsFast(BIT_DStream_t* bitD, U32 nbBits)
{
const U32 bitMask = sizeof(bitD->bitContainer)*8 - 1;
@@ -720,7 +720,7 @@ MEM_STATIC size_t BIT_readBits(BIT_DStream_t* bitD, U32 nbBits)
}
/*!BIT_readBitsFast :
-* unsafe version; only works only if nbBits >= 1 */
+* unsafe version; only works if nbBits >= 1 */
MEM_STATIC size_t BIT_readBitsFast(BIT_DStream_t* bitD, U32 nbBits)
{
size_t value = BIT_lookBitsFast(bitD, nbBits);
diff --git a/lib/legacy/zstd_v05.c b/lib/legacy/zstd_v05.c
index fbed0a01531..216c67d0194 100644
--- a/lib/legacy/zstd_v05.c
+++ b/lib/legacy/zstd_v05.c
@@ -826,7 +826,7 @@ MEM_STATIC size_t BITv05_lookBits(BITv05_DStream_t* bitD, U32 nbBits)
}
/*! BITv05_lookBitsFast :
-* unsafe version; only works only if nbBits >= 1 */
+* unsafe version; only works if nbBits >= 1 */
MEM_STATIC size_t BITv05_lookBitsFast(BITv05_DStream_t* bitD, U32 nbBits)
{
const U32 bitMask = sizeof(bitD->bitContainer)*8 - 1;
@@ -846,7 +846,7 @@ MEM_STATIC size_t BITv05_readBits(BITv05_DStream_t* bitD, unsigned nbBits)
}
/*!BITv05_readBitsFast :
-* unsafe version; only works only if nbBits >= 1 */
+* unsafe version; only works if nbBits >= 1 */
MEM_STATIC size_t BITv05_readBitsFast(BITv05_DStream_t* bitD, unsigned nbBits)
{
size_t value = BITv05_lookBitsFast(bitD, nbBits);
diff --git a/lib/legacy/zstd_v06.c b/lib/legacy/zstd_v06.c
index c486ed0ac93..b224355d437 100644
--- a/lib/legacy/zstd_v06.c
+++ b/lib/legacy/zstd_v06.c
@@ -928,7 +928,7 @@ MEM_STATIC size_t BITv06_initDStream(BITv06_DStream_t* bitD, const void* srcBuff
}
/*! BITv06_lookBitsFast() :
-* unsafe version; only works only if nbBits >= 1 */
+* unsafe version; only works if nbBits >= 1 */
MEM_STATIC size_t BITv06_lookBitsFast(const BITv06_DStream_t* bitD, U32 nbBits)
{
U32 const bitMask = sizeof(bitD->bitContainer)*8 - 1;
@@ -948,7 +948,7 @@ MEM_STATIC size_t BITv06_readBits(BITv06_DStream_t* bitD, U32 nbBits)
}
/*! BITv06_readBitsFast() :
-* unsafe version; only works only if nbBits >= 1 */
+* unsafe version; only works if nbBits >= 1 */
MEM_STATIC size_t BITv06_readBitsFast(BITv06_DStream_t* bitD, U32 nbBits)
{
size_t const value = BITv06_lookBitsFast(bitD, nbBits);
diff --git a/lib/legacy/zstd_v07.c b/lib/legacy/zstd_v07.c
index 20d97da5d58..c12a091cb55 100644
--- a/lib/legacy/zstd_v07.c
+++ b/lib/legacy/zstd_v07.c
@@ -596,7 +596,7 @@ MEM_STATIC size_t BITv07_initDStream(BITv07_DStream_t* bitD, const void* srcBuff
}
/*! BITv07_lookBitsFast() :
-* unsafe version; only works only if nbBits >= 1 */
+* unsafe version; only works if nbBits >= 1 */
MEM_STATIC size_t BITv07_lookBitsFast(const BITv07_DStream_t* bitD, U32 nbBits)
{
U32 const bitMask = sizeof(bitD->bitContainer)*8 - 1;
@@ -616,7 +616,7 @@ MEM_STATIC size_t BITv07_readBits(BITv07_DStream_t* bitD, U32 nbBits)
}
/*! BITv07_readBitsFast() :
-* unsafe version; only works only if nbBits >= 1 */
+* unsafe version; only works if nbBits >= 1 */
MEM_STATIC size_t BITv07_readBitsFast(BITv07_DStream_t* bitD, U32 nbBits)
{
size_t const value = BITv07_lookBitsFast(bitD, nbBits);
diff --git a/lib/zdict.h b/lib/zdict.h
index f1e139a40dd..8e21ba0f767 100644
--- a/lib/zdict.h
+++ b/lib/zdict.h
@@ -110,8 +110,8 @@ extern "C" {
* The zstd CLI defaults to a 110KB dictionary. You likely don't need a
* dictionary larger than that. But, most use cases can get away with a
* smaller dictionary. The advanced dictionary builders can automatically
- * shrink the dictionary for you, and select a the smallest size that
- * doesn't hurt compression ratio too much. See the `shrinkDict` parameter.
+ * shrink the dictionary for you, and select the smallest size that doesn't
+ * hurt compression ratio too much. See the `shrinkDict` parameter.
* A smaller dictionary can save memory, and potentially speed up
* compression.
*
diff --git a/lib/zstd.h b/lib/zstd.h
index cea2699b90d..14a78cd931f 100644
--- a/lib/zstd.h
+++ b/lib/zstd.h
@@ -922,7 +922,7 @@ ZSTDLIB_API unsigned ZSTD_getDictID_fromDDict(const ZSTD_DDict* ddict);
* If @return == 0, the dictID could not be decoded.
* This could for one of the following reasons :
* - The frame does not require a dictionary to be decoded (most common case).
- * - The frame was built with dictID intentionally removed. Whatever dictionary is necessary is a hidden information.
+ * - The frame was built with dictID intentionally removed. Whatever dictionary is necessary is a hidden piece of information.
* Note : this use case also happens when using a non-conformant dictionary.
* - `srcSize` is too small, and as a result, the frame header could not be decoded (only possible if `srcSize < ZSTD_FRAMEHEADERSIZE_MAX`).
* - This is not a Zstandard frame.
@@ -1409,7 +1409,7 @@ ZSTD_generateSequences( ZSTD_CCtx* zc,
ZSTDLIB_STATIC_API size_t ZSTD_mergeBlockDelimiters(ZSTD_Sequence* sequences, size_t seqsSize);
/*! ZSTD_compressSequences() :
- * Compress an array of ZSTD_Sequence, associted with @src buffer, into dst.
+ * Compress an array of ZSTD_Sequence, associated with @src buffer, into dst.
* @src contains the entire input (not just the literals).
* If @srcSize > sum(sequence.length), the remaining bytes are considered all literals
* If a dictionary is included, then the cctx should reference the dict. (see: ZSTD_CCtx_refCDict(), ZSTD_CCtx_loadDictionary(), etc.)
@@ -1904,7 +1904,7 @@ ZSTDLIB_STATIC_API size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const vo
* Without validation, providing a sequence that does not conform to the zstd spec will cause
* undefined behavior, and may produce a corrupted block.
*
- * With validation enabled, a if sequence is invalid (see doc/zstd_compression_format.md for
+ * With validation enabled, if sequence is invalid (see doc/zstd_compression_format.md for
* specifics regarding offset/matchlength requirements) then the function will bail out and
* return an error.
*
@@ -2110,7 +2110,7 @@ ZSTDLIB_STATIC_API size_t ZSTD_DCtx_getParameter(ZSTD_DCtx* dctx, ZSTD_dParamete
* in the range [dst, dst + pos) MUST not be modified during decompression
* or you will get data corruption.
*
- * When this flags is enabled zstd won't allocate an output buffer, because
+ * When this flag is enabled zstd won't allocate an output buffer, because
* it can write directly to the ZSTD_outBuffer, but it will still allocate
* an input buffer large enough to fit any compressed block. This will also
* avoid the memcpy() from the internal output buffer to the ZSTD_outBuffer.
diff --git a/programs/benchfn.h b/programs/benchfn.h
index 590f292eaa6..99d13ac47c1 100644
--- a/programs/benchfn.h
+++ b/programs/benchfn.h
@@ -123,7 +123,7 @@ BMK_runTime_t BMK_extract_runTime(BMK_runOutcome_t outcome);
/* when benchmark failed, it means one invocation of `benchFn` failed.
* The failure was detected by `errorFn`, operating on return values of `benchFn`.
* Returns the faulty return value.
- * note : this function will abort() program execution if benchmark did not failed.
+ * note : this function will abort() program execution if benchmark did not fail.
* always check if benchmark failed first !
*/
size_t BMK_extract_errorResult(BMK_runOutcome_t outcome);
diff --git a/programs/fileio.c b/programs/fileio.c
index e37921adadc..3c47e3f5b3b 100644
--- a/programs/fileio.c
+++ b/programs/fileio.c
@@ -885,8 +885,8 @@ static void FIO_adjustParamsForPatchFromMode(FIO_prefs_t* const prefs,
if (cParams.strategy >= ZSTD_btopt) {
DISPLAYLEVEL(1, "[Optimal parser notes] Consider the following to improve patch size at the cost of speed:\n");
DISPLAYLEVEL(1, "- Use --single-thread mode in the zstd cli\n");
- DISPLAYLEVEL(1, "- Set a larger targetLength (eg. --zstd=targetLength=4096)\n");
- DISPLAYLEVEL(1, "- Set a larger chainLog (eg. --zstd=chainLog=%u)\n", ZSTD_CHAINLOG_MAX);
+ DISPLAYLEVEL(1, "- Set a larger targetLength (e.g. --zstd=targetLength=4096)\n");
+ DISPLAYLEVEL(1, "- Set a larger chainLog (e.g. --zstd=chainLog=%u)\n", ZSTD_CHAINLOG_MAX);
DISPLAYLEVEL(1, "Also consider playing around with searchLog and hashLog\n");
}
}
diff --git a/programs/util.c b/programs/util.c
index 55bcff25afa..f53eb03fbec 100644
--- a/programs/util.c
+++ b/programs/util.c
@@ -999,7 +999,7 @@ makeUniqueMirroredDestDirs(char** srcDirNames, unsigned nbFile, const char* outD
trimPath(currDirName)))
uniqueDirNr++;
- /* we need maintain original src dir name instead of trimmed
+ /* we need to maintain original src dir name instead of trimmed
* dir, so we can retrieve the original src dir's mode_t */
uniqueDirNames[uniqueDirNr - 1] = currDirName;
}
diff --git a/programs/zstd.1 b/programs/zstd.1
index 0e6e016e950..8191e35e70e 100644
--- a/programs/zstd.1
+++ b/programs/zstd.1
@@ -210,7 +210,7 @@ If input directory contains "\.\.", the files in this directory will be ignored\
\fB\-\-no\-progress\fR: do not display the progress bar, but keep all other messages\.
.
.IP "\(bu" 4
-\fB\-\-show\-default\-cparams\fR: Shows the default compression parameters that will be used for a particular src file\. If the provided src file is not a regular file (eg\. named pipe), the cli will just output the default parameters\. That is, the parameters that are used when the src size is unknown\.
+\fB\-\-show\-default\-cparams\fR: Shows the default compression parameters that will be used for a particular src file\. If the provided src file is not a regular file (e\.g\. named pipe), the cli will just output the default parameters\. That is, the parameters that are used when the src size is unknown\.
.
.IP "\(bu" 4
\fB\-\-\fR: All arguments after \fB\-\-\fR are treated as files
@@ -392,7 +392,7 @@ Note: If \fBwindowLog\fR is set to larger than 27, \fB\-\-long=windowLog\fR or \
Specify the maximum number of bits for a hash table\.
.
.IP
-Bigger hash tables cause less collisions which usually makes compression faster, but requires more memory during compression\.
+Bigger hash tables cause fewer collisions which usually makes compression faster, but requires more memory during compression\.
.
.IP
The minimum \fIhlog\fR is 6 (64 B) and the maximum is 30 (1 GiB)\.
diff --git a/programs/zstd.1.md b/programs/zstd.1.md
index 569ca1aa0db..10f1bda102f 100644
--- a/programs/zstd.1.md
+++ b/programs/zstd.1.md
@@ -159,11 +159,11 @@ the last one takes effect.
Note: `--long` mode will be automatically activated if chainLog < fileLog
(fileLog being the windowLog required to cover the whole file). You
can also manually force it.
- Node: for all levels, you can use --patch-from in --single-thread mode
- to improve compression ratio at the cost of speed
+ Node: for all levels, you can use --patch-from in --single-thread mode
+ to improve compression ratio at the cost of speed
Note: for level 19, you can get increased compression ratio at the cost
of speed by specifying `--zstd=targetLength=` to be something large
- (i.e 4096), and by setting a large `--zstd=chainLog=`
+ (i.e. 4096), and by setting a large `--zstd=chainLog=`
* `--rsyncable` :
`zstd` will periodically synchronize the compression state to make the
compressed file more rsync-friendly. There is a negligible impact to
@@ -185,7 +185,7 @@ the last one takes effect.
* `-M#`, `--memory=#`:
Set a memory usage limit. By default, Zstandard uses 128 MB for decompression
as the maximum amount of memory the decompressor is allowed to use, but you can
- override this manually if need be in either direction (ie. you can increase or
+ override this manually if need be in either direction (i.e. you can increase or
decrease it).
This is also used during compression when using with --patch-from=. In this case,
@@ -275,7 +275,7 @@ the last one takes effect.
* `--show-default-cparams`:
Shows the default compression parameters that will be used for a
particular src file. If the provided src file is not a regular file
- (eg. named pipe), the cli will just output the default parameters.
+ (e.g. named pipe), the cli will just output the default parameters.
That is, the parameters that are used when the src size is unknown.
* `--`:
All arguments after `--` are treated as files
@@ -493,7 +493,7 @@ The list of available _options_:
- `hashLog`=_hlog_, `hlog`=_hlog_:
Specify the maximum number of bits for a hash table.
- Bigger hash tables cause less collisions which usually makes compression
+ Bigger hash tables cause fewer collisions which usually makes compression
faster, but requires more memory during compression.
The minimum _hlog_ is 6 (64 B) and the maximum is 30 (1 GiB).
diff --git a/programs/zstdcli.c b/programs/zstdcli.c
index 0b9b82a9859..f931b2f384b 100644
--- a/programs/zstdcli.c
+++ b/programs/zstdcli.c
@@ -1423,7 +1423,7 @@ int main(int argCount, const char* argv[])
if (showDefaultCParams) {
if (operation == zom_decompress) {
- DISPLAY("error : can't use --show-default-cparams in decomrpession mode \n");
+ DISPLAY("error : can't use --show-default-cparams in decompression mode \n");
CLEAN_RETURN(1);
}
}
diff --git a/tests/README.md b/tests/README.md
index c6ffb4095a7..2cf0e76c20e 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -45,7 +45,7 @@ optional arguments:
-h, --help show this help message and exit
--directory DIRECTORY
directory with files to benchmark
- --levels LEVELS levels to test eg ('1,2,3')
+ --levels LEVELS levels to test e.g. ('1,2,3')
--iterations ITERATIONS
number of benchmark iterations to run
--emails EMAILS email addresses of people who will be alerted upon
@@ -70,7 +70,7 @@ After `sleepTime` (an optional parameter, default 300 seconds) seconds the scrip
If a new commit is found it is compiled and a speed benchmark for this commit is performed.
The results of the speed benchmark are compared to the previous results.
If compression or decompression speed for one of zstd levels is lower than `lowerLimit` (an optional parameter, default 0.98) the speed benchmark is restarted.
-If second results are also lower than `lowerLimit` the warning e-mail is send to recipients from the list (the `emails` parameter).
+If second results are also lower than `lowerLimit` the warning e-mail is sent to recipients from the list (the `emails` parameter).
Additional remarks:
- To be sure that speed results are accurate the script should be run on a "stable" target system with no other jobs running in parallel
@@ -168,7 +168,7 @@ Full list of arguments
can use all --zstd parameter names and 'cParams' as a shorthand for all parameters used in ZSTD_compressionParameters
(Default: display all params available)
-P# : generated sample compressibility (when no file is provided)
- -t# : Caps runtime of operation in seconds (default : 99999 seconds (about 27 hours ))
+ -t# : Caps runtime of operation in seconds (default: 99999 seconds (about 27 hours))
-v : Prints Benchmarking output
-D : Next argument dictionary file
-s : Benchmark all files separately
diff --git a/tests/automated_benchmarking.py b/tests/automated_benchmarking.py
index e0c03ec2d4a..c9839119366 100644
--- a/tests/automated_benchmarking.py
+++ b/tests/automated_benchmarking.py
@@ -291,7 +291,7 @@ def main(filenames, levels, iterations, builds=None, emails=None, continuous=Fal
parser = argparse.ArgumentParser()
parser.add_argument("--directory", help="directory with files to benchmark", default="golden-compression")
- parser.add_argument("--levels", help="levels to test eg ('1,2,3')", default="1")
+ parser.add_argument("--levels", help="levels to test e.g. ('1,2,3')", default="1")
parser.add_argument("--iterations", help="number of benchmark iterations to run", default="1")
parser.add_argument("--emails", help="email addresses of people who will be alerted upon regression. Only for continuous mode", default=None)
parser.add_argument("--frequency", help="specifies the number of seconds to wait before each successive check for new PRs in continuous mode", default=DEFAULT_MAX_API_CALL_FREQUENCY_SEC)
diff --git a/tests/fuzz/fuzz.py b/tests/fuzz/fuzz.py
index 0c56cccb217..17eac4f6ff4 100755
--- a/tests/fuzz/fuzz.py
+++ b/tests/fuzz/fuzz.py
@@ -636,7 +636,7 @@ def regression(args):
try:
description = """
Runs one or more regression tests.
- The fuzzer should have been built with with
+ The fuzzer should have been built with
LIB_FUZZING_ENGINE='libregression.a'.
Takes input from CORPORA.
"""
diff --git a/tests/fuzz/sequence_compression_api.c b/tests/fuzz/sequence_compression_api.c
index e01daacaa89..4a09d27fb47 100644
--- a/tests/fuzz/sequence_compression_api.c
+++ b/tests/fuzz/sequence_compression_api.c
@@ -294,7 +294,7 @@ int LLVMFuzzerTestOneInput(const uint8_t* src, size_t size)
FUZZ_ASSERT(dictBuffer);
dictBuffer = generatePseudoRandomString(dictBuffer, dictSize);
}
- /* Generate window log first so we dont generate offsets too large */
+ /* Generate window log first so we don't generate offsets too large */
wLog = FUZZ_dataProducer_uint32Range(producer, ZSTD_WINDOWLOG_MIN, ZSTD_WINDOWLOG_MAX_32);
cLevel = FUZZ_dataProducer_int32Range(producer, -3, 22);
mode = (ZSTD_sequenceFormat_e)FUZZ_dataProducer_int32Range(producer, 0, 1);
diff --git a/tests/fuzz/simple_compress.c b/tests/fuzz/simple_compress.c
index 3716d0d0fe1..8e6980b3530 100644
--- a/tests/fuzz/simple_compress.c
+++ b/tests/fuzz/simple_compress.c
@@ -9,7 +9,7 @@
*/
/**
- * This fuzz target attempts to comprss the fuzzed data with the simple
+ * This fuzz target attempts to compress the fuzzed data with the simple
* compression function with an output buffer that may be too small to
* ensure that the compressor never crashes.
*/
diff --git a/tests/fuzzer.c b/tests/fuzzer.c
index 018da622cea..71356d53dbc 100644
--- a/tests/fuzzer.c
+++ b/tests/fuzzer.c
@@ -1063,7 +1063,7 @@ static int basicUnitTests(U32 const seed, double compressibility)
void* recon = (void*)malloc(size);
size_t refPrefixCompressedSize = 0;
- size_t refPrefixLdmComrpessedSize = 0;
+ size_t refPrefixLdmCompressedSize = 0;
size_t reconSize = 0;
ZSTD_CCtx* const cctx = ZSTD_createCCtx();
@@ -1093,20 +1093,20 @@ static int basicUnitTests(U32 const seed, double compressibility)
/* compress on level 1 using refPrefix and ldm */
ZSTD_CCtx_refPrefix(cctx, dict, size);;
CHECK_Z(ZSTD_CCtx_setParameter(cctx, ZSTD_c_enableLongDistanceMatching, 1))
- refPrefixLdmComrpessedSize = ZSTD_compress2(cctx, dst, dstSize, src, size);
- assert(!ZSTD_isError(refPrefixLdmComrpessedSize));
+ refPrefixLdmCompressedSize = ZSTD_compress2(cctx, dst, dstSize, src, size);
+ assert(!ZSTD_isError(refPrefixLdmCompressedSize));
/* test round trip refPrefix + ldm*/
ZSTD_DCtx_refPrefix(dctx, dict, size);
- reconSize = ZSTD_decompressDCtx(dctx, recon, size, dst, refPrefixLdmComrpessedSize);
+ reconSize = ZSTD_decompressDCtx(dctx, recon, size, dst, refPrefixLdmCompressedSize);
assert(!ZSTD_isError(reconSize));
assert(reconSize == size);
assert(!memcmp(recon, src, size));
/* make sure that refPrefixCompressedSize is significantly greater */
- assert(refPrefixCompressedSize > 10 * refPrefixLdmComrpessedSize);
- /* make sure the ldm comrpessed size is less than 1% of original */
- assert((double)refPrefixLdmComrpessedSize / (double)size < 0.01);
+ assert(refPrefixCompressedSize > 10 * refPrefixLdmCompressedSize);
+ /* make sure the ldm compressed size is less than 1% of original */
+ assert((double)refPrefixLdmCompressedSize / (double)size < 0.01);
ZSTD_freeDCtx(dctx);
ZSTD_freeCCtx(cctx);
diff --git a/tests/paramgrill.c b/tests/paramgrill.c
index 033a10181a3..756a4334059 100644
--- a/tests/paramgrill.c
+++ b/tests/paramgrill.c
@@ -555,7 +555,7 @@ static int feasible(const BMK_benchResult_t results, const constraint_t target)
}
/* hill climbing value for part 1 */
-/* Scoring here is a linear reward for all set constraints normalized between 0 to 1
+/* Scoring here is a linear reward for all set constraints normalized between 0 and 1
* (with 0 at 0 and 1 being fully fulfilling the constraint), summed with a logarithmic
* bonus to exceeding the constraint value. We also give linear ratio for compression ratio.
* The constant factors are experimental.
diff --git a/zlibWrapper/README.md b/zlibWrapper/README.md
index e61767c468f..f1cc88c52f2 100644
--- a/zlibWrapper/README.md
+++ b/zlibWrapper/README.md
@@ -43,7 +43,7 @@ This behavior can be changed using `ZWRAP_setDecompressionType(ZWRAP_FORCE_ZLIB)
#### Example
-We have take the file `test/example.c` from [the zlib library distribution](http://zlib.net/) and copied it to [zlibWrapper/examples/example.c](examples/example.c).
+We have taken the file `test/example.c` from [the zlib library distribution](http://zlib.net/) and copied it to [zlibWrapper/examples/example.c](examples/example.c).
After compilation and execution it shows the following results:
```
zlib version 1.2.8 = 0x1280, compile flags = 0x65
From 3a64aa29a641f861c94387da209715a641f26447 Mon Sep 17 00:00:00 2001
From: Dominique Pelle
Date: Sun, 13 Mar 2022 00:08:55 +0100
Subject: [PATCH 112/472] On more mistake (Node -> Note)
---
programs/zstd.1.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/programs/zstd.1.md b/programs/zstd.1.md
index 10f1bda102f..6b8117b89a1 100644
--- a/programs/zstd.1.md
+++ b/programs/zstd.1.md
@@ -159,7 +159,7 @@ the last one takes effect.
Note: `--long` mode will be automatically activated if chainLog < fileLog
(fileLog being the windowLog required to cover the whole file). You
can also manually force it.
- Node: for all levels, you can use --patch-from in --single-thread mode
+ Note: for all levels, you can use --patch-from in --single-thread mode
to improve compression ratio at the cost of speed
Note: for level 19, you can get increased compression ratio at the cost
of speed by specifying `--zstd=targetLength=` to be something large
From 64efba4c5ee1e8c9afe92873f79424c9955b82ba Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Thu, 17 Mar 2022 09:35:11 -0700
Subject: [PATCH 113/472] Software pipeline for
ZSTD_compressBlock_fast_dictMatchState (#3086)
* prefetch dict content inside loop
* ip0/ip1 pipeline
* add L2_4 prefetch to dms pipeline
* Remove L1 prefetch
* Remove L2 prefetching
* Reduce # of gotos
* Cosmetic fixes
* Check final position sometimes
* Track step size as in bc768bc
* Fix nits
---
lib/compress/zstd_fast.c | 164 +++++++++++++++++++++++----------------
1 file changed, 99 insertions(+), 65 deletions(-)
diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c
index 62c4c2cea02..5da108c622a 100644
--- a/lib/compress/zstd_fast.c
+++ b/lib/compress/zstd_fast.c
@@ -380,7 +380,8 @@ size_t ZSTD_compressBlock_fast_dictMatchState_generic(
U32 const stepSize = cParams->targetLength + !(cParams->targetLength);
const BYTE* const base = ms->window.base;
const BYTE* const istart = (const BYTE*)src;
- const BYTE* ip = istart;
+ const BYTE* ip0 = istart;
+ const BYTE* ip1 = ip0 + stepSize; /* we assert below that stepSize >= 1 */
const BYTE* anchor = istart;
const U32 prefixStartIndex = ms->window.dictLimit;
const BYTE* const prefixStart = base + prefixStartIndex;
@@ -397,13 +398,13 @@ size_t ZSTD_compressBlock_fast_dictMatchState_generic(
const BYTE* const dictStart = dictBase + dictStartIndex;
const BYTE* const dictEnd = dms->window.nextSrc;
const U32 dictIndexDelta = prefixStartIndex - (U32)(dictEnd - dictBase);
- const U32 dictAndPrefixLength = (U32)(ip - prefixStart + dictEnd - dictStart);
+ const U32 dictAndPrefixLength = (U32)(istart - prefixStart + dictEnd - dictStart);
const U32 dictHLog = dictCParams->hashLog;
/* if a dictionary is still attached, it necessarily means that
* it is within window size. So we just check it. */
const U32 maxDistance = 1U << cParams->windowLog;
- const U32 endIndex = (U32)((size_t)(ip - base) + srcSize);
+ const U32 endIndex = (U32)((size_t)(istart - base) + srcSize);
assert(endIndex - prefixStartIndex <= maxDistance);
(void)maxDistance; (void)endIndex; /* these variables are not used when assert() is disabled */
@@ -415,101 +416,134 @@ size_t ZSTD_compressBlock_fast_dictMatchState_generic(
/* init */
DEBUGLOG(5, "ZSTD_compressBlock_fast_dictMatchState_generic");
- ip += (dictAndPrefixLength == 0);
+ ip0 += (dictAndPrefixLength == 0);
/* dictMatchState repCode checks don't currently handle repCode == 0
* disabling. */
assert(offset_1 <= dictAndPrefixLength);
assert(offset_2 <= dictAndPrefixLength);
- /* Main Search Loop */
- while (ip < ilimit) { /* < instead of <=, because repcode check at (ip+1) */
+ /* Outer search loop */
+ assert(stepSize >= 1);
+ while (ip1 <= ilimit) { /* repcode check at (ip0 + 1) is safe because ip0 < ip1 */
size_t mLength;
- size_t const h = ZSTD_hashPtr(ip, hlog, mls);
- U32 const curr = (U32)(ip-base);
- U32 const matchIndex = hashTable[h];
- const BYTE* match = base + matchIndex;
- const U32 repIndex = curr + 1 - offset_1;
- const BYTE* repMatch = (repIndex < prefixStartIndex) ?
- dictBase + (repIndex - dictIndexDelta) :
- base + repIndex;
- hashTable[h] = curr; /* update hash table */
-
- if ( ((U32)((prefixStartIndex-1) - repIndex) >= 3) /* intentional underflow : ensure repIndex isn't overlapping dict + prefix */
- && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) {
- const BYTE* const repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend;
- mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixStart) + 4;
- ip++;
- ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, REPCODE1_TO_OFFBASE, mLength);
- } else if ( (matchIndex <= prefixStartIndex) ) {
- size_t const dictHash = ZSTD_hashPtr(ip, dictHLog, mls);
- U32 const dictMatchIndex = dictHashTable[dictHash];
- const BYTE* dictMatch = dictBase + dictMatchIndex;
- if (dictMatchIndex <= dictStartIndex ||
- MEM_read32(dictMatch) != MEM_read32(ip)) {
- assert(stepSize >= 1);
- ip += ((ip-anchor) >> kSearchStrength) + stepSize;
- continue;
- } else {
- /* found a dict match */
- U32 const offset = (U32)(curr-dictMatchIndex-dictIndexDelta);
- mLength = ZSTD_count_2segments(ip+4, dictMatch+4, iend, dictEnd, prefixStart) + 4;
- while (((ip>anchor) & (dictMatch>dictStart))
- && (ip[-1] == dictMatch[-1])) {
- ip--; dictMatch--; mLength++;
+ size_t hash0 = ZSTD_hashPtr(ip0, hlog, mls);
+ const size_t dictHash0 = ZSTD_hashPtr(ip0, dictHLog, mls);
+ U32 dictMatchIndex = dictHashTable[dictHash0];
+ U32 matchIndex = hashTable[hash0];
+ U32 curr = (U32)(ip0 - base);
+ size_t step = stepSize;
+ const size_t kStepIncr = 1 << kSearchStrength;
+ const BYTE* nextStep = ip0 + kStepIncr;
+
+ /* Inner search loop */
+ while (1) {
+ const BYTE* match = base + matchIndex;
+ const U32 repIndex = curr + 1 - offset_1;
+ const BYTE* repMatch = (repIndex < prefixStartIndex) ?
+ dictBase + (repIndex - dictIndexDelta) :
+ base + repIndex;
+ const size_t hash1 = ZSTD_hashPtr(ip1, hlog, mls);
+ const size_t dictHash1 = ZSTD_hashPtr(ip1, dictHLog, mls);
+ hashTable[hash0] = curr; /* update hash table */
+
+ if (((U32) ((prefixStartIndex - 1) - repIndex) >=
+ 3) /* intentional underflow : ensure repIndex isn't overlapping dict + prefix */
+ && (MEM_read32(repMatch) == MEM_read32(ip0 + 1))) {
+ const BYTE* const repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend;
+ mLength = ZSTD_count_2segments(ip0 + 1 + 4, repMatch + 4, iend, repMatchEnd, prefixStart) + 4;
+ ip0++;
+ ZSTD_storeSeq(seqStore, (size_t) (ip0 - anchor), anchor, iend, REPCODE1_TO_OFFBASE, mLength);
+ break;
+ } else if (matchIndex <= prefixStartIndex) {
+ /* We only look for a dict match if the normal matchIndex is invalid */
+ const BYTE* dictMatch = dictBase + dictMatchIndex;
+ if (dictMatchIndex > dictStartIndex &&
+ MEM_read32(dictMatch) == MEM_read32(ip0)) {
+ /* found a dict match */
+ U32 const offset = (U32) (curr - dictMatchIndex - dictIndexDelta);
+ mLength = ZSTD_count_2segments(ip0 + 4, dictMatch + 4, iend, dictEnd, prefixStart) + 4;
+ while (((ip0 > anchor) & (dictMatch > dictStart))
+ && (ip0[-1] == dictMatch[-1])) {
+ ip0--;
+ dictMatch--;
+ mLength++;
+ } /* catch up */
+ offset_2 = offset_1;
+ offset_1 = offset;
+ ZSTD_storeSeq(seqStore, (size_t) (ip0 - anchor), anchor, iend, OFFSET_TO_OFFBASE(offset), mLength);
+ break;
+ }
+ } else if (MEM_read32(match) == MEM_read32(ip0)) {
+ /* found a regular match */
+ U32 const offset = (U32) (ip0 - match);
+ mLength = ZSTD_count(ip0 + 4, match + 4, iend) + 4;
+ while (((ip0 > anchor) & (match > prefixStart))
+ && (ip0[-1] == match[-1])) {
+ ip0--;
+ match--;
+ mLength++;
} /* catch up */
offset_2 = offset_1;
offset_1 = offset;
- ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, OFFSET_TO_OFFBASE(offset), mLength);
+ ZSTD_storeSeq(seqStore, (size_t) (ip0 - anchor), anchor, iend, OFFSET_TO_OFFBASE(offset), mLength);
+ break;
}
- } else if (MEM_read32(match) != MEM_read32(ip)) {
- /* it's not a match, and we're not going to check the dictionary */
- assert(stepSize >= 1);
- ip += ((ip-anchor) >> kSearchStrength) + stepSize;
- continue;
- } else {
- /* found a regular match */
- U32 const offset = (U32)(ip-match);
- mLength = ZSTD_count(ip+4, match+4, iend) + 4;
- while (((ip>anchor) & (match>prefixStart))
- && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */
- offset_2 = offset_1;
- offset_1 = offset;
- ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, OFFSET_TO_OFFBASE(offset), mLength);
- }
+
+ /* Prepare for next iteration */
+ dictMatchIndex = dictHashTable[dictHash1];
+ matchIndex = hashTable[hash1];
+
+ if (ip1 >= nextStep) {
+ step++;
+ nextStep += kStepIncr;
+ }
+ ip0 = ip1;
+ ip1 = ip1 + step;
+ if (ip1 > ilimit) goto _cleanup;
+
+ curr = (U32)(ip0 - base);
+ hash0 = hash1;
+ } /* end inner search loop */
/* match found */
- ip += mLength;
- anchor = ip;
+ assert(mLength);
+ ip0 += mLength;
+ anchor = ip0;
- if (ip <= ilimit) {
+ if (ip0 <= ilimit) {
/* Fill Table */
assert(base+curr+2 > istart); /* check base overflow */
hashTable[ZSTD_hashPtr(base+curr+2, hlog, mls)] = curr+2; /* here because curr+2 could be > iend-8 */
- hashTable[ZSTD_hashPtr(ip-2, hlog, mls)] = (U32)(ip-2-base);
+ hashTable[ZSTD_hashPtr(ip0-2, hlog, mls)] = (U32)(ip0-2-base);
/* check immediate repcode */
- while (ip <= ilimit) {
- U32 const current2 = (U32)(ip-base);
+ while (ip0 <= ilimit) {
+ U32 const current2 = (U32)(ip0-base);
U32 const repIndex2 = current2 - offset_2;
const BYTE* repMatch2 = repIndex2 < prefixStartIndex ?
dictBase - dictIndexDelta + repIndex2 :
base + repIndex2;
if ( ((U32)((prefixStartIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */)
- && (MEM_read32(repMatch2) == MEM_read32(ip)) ) {
+ && (MEM_read32(repMatch2) == MEM_read32(ip0))) {
const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend;
- size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixStart) + 4;
+ size_t const repLength2 = ZSTD_count_2segments(ip0+4, repMatch2+4, iend, repEnd2, prefixStart) + 4;
U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */
ZSTD_storeSeq(seqStore, 0, anchor, iend, REPCODE1_TO_OFFBASE, repLength2);
- hashTable[ZSTD_hashPtr(ip, hlog, mls)] = current2;
- ip += repLength2;
- anchor = ip;
+ hashTable[ZSTD_hashPtr(ip0, hlog, mls)] = current2;
+ ip0 += repLength2;
+ anchor = ip0;
continue;
}
break;
}
}
+
+ /* Prepare for next iteration */
+ assert(ip0 == anchor);
+ ip1 = ip0 + stepSize;
}
+_cleanup:
/* save reps for next block */
rep[0] = offset_1 ? offset_1 : offsetSaved;
rep[1] = offset_2 ? offset_2 : offsetSaved;
From 7fbe60d577802394137201b689263e8aa9a62080 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dirk=20M=C3=BCller?=
Date: Thu, 10 Mar 2022 01:34:12 +0100
Subject: [PATCH 114/472] Split help in long and short version, cleanup
formatting
Adopt the more standard Usage: formatting style
List short and long options alongside where available
Print lists as a table
Use command style description
---
programs/zstdcli.c | 177 +++++++++++-----------
tests/cli-tests/basic/help.sh.stdout.glob | 40 ++---
2 files changed, 101 insertions(+), 116 deletions(-)
diff --git a/programs/zstdcli.c b/programs/zstdcli.c
index a96210ecca2..5586f395644 100644
--- a/programs/zstdcli.c
+++ b/programs/zstdcli.c
@@ -143,155 +143,150 @@ static int exeNameMatch(const char* exeName, const char* test)
*/
static void usage(FILE* f, const char* programName)
{
- DISPLAY_F(f, "Usage : \n");
- DISPLAY_F(f, " %s [args] [FILE(s)] [-o file] \n", programName);
- DISPLAY_F(f, "\n");
- DISPLAY_F(f, "FILE : a filename \n");
- DISPLAY_F(f, " with no FILE, or when FILE is - , read standard input\n");
- DISPLAY_F(f, "Arguments : \n");
+ DISPLAY_F(f, "Usage: %s [OPTION]... [FILE]... [-o file]\n", programName);
+ DISPLAY_F(f, "Compress or uncompress FILEs (with no FILE or when FILE is `-`, read from standard input).\n\n");
+ DISPLAY_F(f, " -o file result stored into `file` (only 1 output file)\n");
#ifndef ZSTD_NOCOMPRESS
- DISPLAY_F(f, " -# : # compression level (1-%d, default: %d) \n", ZSTDCLI_CLEVEL_MAX, ZSTDCLI_CLEVEL_DEFAULT);
+ DISPLAY_F(f, " -1 .. -%d compression level (faster .. better; default: %d)\n", ZSTDCLI_CLEVEL_MAX, ZSTDCLI_CLEVEL_DEFAULT);
#endif
#ifndef ZSTD_NODECOMPRESS
- DISPLAY_F(f, " -d : decompression \n");
+ DISPLAY_F(f, " -d, --decompress decompression\n");
#endif
- DISPLAY_F(f, " -D DICT: use DICT as Dictionary for compression or decompression \n");
- DISPLAY_F(f, " -o file: result stored into `file` (only 1 output file) \n");
- DISPLAY_F(f, " -f : disable input and output checks. Allows overwriting existing files,\n");
- DISPLAY_F(f, " input from console, output to stdout, operating on links,\n");
- DISPLAY_F(f, " block devices, etc.\n");
- DISPLAY_F(f, "--rm : remove source file(s) after successful de/compression \n");
- DISPLAY_F(f, " -k : preserve source file(s) (default) \n");
+ DISPLAY_F(f, " -f, --force disable input and output checks. Allows overwriting existing files,\n");
+ DISPLAY_F(f, " input from console, output to stdout, operating on links,\n");
+ DISPLAY_F(f, " block devices, etc.\n");
+ DISPLAY_F(f, " --rm remove source file(s) after successful de/compression\n");
+ DISPLAY_F(f, " -k, --keep preserve source file(s) (default) \n");
#ifdef ZSTD_GZCOMPRESS
if (exeNameMatch(programName, ZSTD_GZ)) { /* behave like gzip */
- DISPLAY_F(f, " -n : do not store original filename when compressing \n");
+ DISPLAY_F(f, " -n, --no-name do not store original filename when compressing\n");
}
#endif
- DISPLAY_F(f, " -h/-H : display help/long help and exit \n");
+ DISPLAY_F(f, " -D DICT use DICT as Dictionary for compression or decompression\n");
+ DISPLAY_F(f, " -h display usage and exit\n");
+ DISPLAY_F(f, " -H,--help display long help and exit\n");
+ DISPLAY_F(f, "\n");
}
static void usage_advanced(const char* programName)
{
DISPLAYOUT(WELCOME_MESSAGE);
usage(stdout, programName);
- DISPLAYOUT( "\n");
- DISPLAYOUT( "Advanced arguments : \n");
- DISPLAYOUT( " -V : display Version number and exit \n");
+ DISPLAYOUT("Advanced options :\n");
+ DISPLAYOUT(" -V, --version display Version number and exit\n");
- DISPLAYOUT( " -c : write to standard output (even if it is the console), keep original file \n");
+ DISPLAYOUT(" -c, --stdout write to standard output (even if it is the console), keep original file\n");
- DISPLAYOUT( " -v : verbose mode; specify multiple times to increase verbosity \n");
- DISPLAYOUT( " -q : suppress warnings; specify twice to suppress errors too \n");
- DISPLAYOUT( "--[no-]progress : forcibly display, or never display the progress counter.\n");
- DISPLAYOUT( " note: any (de)compressed output to terminal will mix with progress counter text. \n");
+ DISPLAYOUT(" -v, --verbose verbose mode; specify multiple times to increase verbosity\n");
+ DISPLAYOUT(" -q, --quiet suppress warnings; specify twice to suppress errors too\n");
+ DISPLAYOUT(" --[no-]progress forcibly display, or never display the progress counter\n");
+ DISPLAYOUT(" note: any (de)compressed output to terminal will mix with progress counter text\n");
#ifdef UTIL_HAS_CREATEFILELIST
- DISPLAYOUT( " -r : operate recursively on directories \n");
- DISPLAYOUT( "--filelist FILE : read list of files to operate upon from FILE \n");
- DISPLAYOUT( "--output-dir-flat DIR : processed files are stored into DIR \n");
+ DISPLAYOUT(" -r operate recursively on directories\n");
+ DISPLAYOUT(" --filelist FILE read list of files to operate upon from FILE\n");
+ DISPLAYOUT(" --output-dir-flat DIR : processed files are stored into DIR\n");
#endif
#ifdef UTIL_HAS_MIRRORFILELIST
- DISPLAYOUT( "--output-dir-mirror DIR : processed files are stored into DIR respecting original directory structure \n");
+ DISPLAYOUT(" --output-dir-mirror DIR : processed files are stored into DIR respecting original directory structure\n");
#endif
if (AIO_supported())
- DISPLAYOUT( "--[no-]asyncio : use asynchronous IO (default: enabled) \n");
+ DISPLAYOUT(" --[no-]asyncio use asynchronous IO (default: enabled)\n");
#ifndef ZSTD_NOCOMPRESS
- DISPLAYOUT( "--[no-]check : during compression, add XXH64 integrity checksum to frame (default: enabled)");
+ DISPLAYOUT(" --[no-]check during compression, add XXH64 integrity checksum to frame (default: enabled)\n");
#ifndef ZSTD_NODECOMPRESS
- DISPLAYOUT( ". If specified with -d, decompressor will ignore/validate checksums in compressed frame (default: validate).");
+ DISPLAYOUT(" if specified with -d, decompressor will ignore/validate checksums in compressed frame (default: validate)\n");
#endif
#else
#ifdef ZSTD_NOCOMPRESS
- DISPLAYOUT( "--[no-]check : during decompression, ignore/validate checksums in compressed frame (default: validate).");
+ DISPLAYOUT(" --[no-]check during decompression, ignore/validate checksums in compressed frame (default: validate)");
#endif
+ DISPLAYOUT("\n");
#endif /* ZSTD_NOCOMPRESS */
#ifndef ZSTD_NOTRACE
- DISPLAYOUT( "\n");
- DISPLAYOUT( "--trace FILE : log tracing information to FILE.");
+ DISPLAYOUT(" --trace FILE log tracing information to FILE\n");
#endif
- DISPLAYOUT( "\n");
-
- DISPLAYOUT( "-- : All arguments after \"--\" are treated as files \n");
+ DISPLAYOUT(" -- all arguments after \"--\" are treated as files\n");
#ifndef ZSTD_NOCOMPRESS
- DISPLAYOUT( "\n");
- DISPLAYOUT( "Advanced compression arguments : \n");
- DISPLAYOUT( "--ultra : enable levels beyond %i, up to %i (requires more memory) \n", ZSTDCLI_CLEVEL_MAX, ZSTD_maxCLevel());
- DISPLAYOUT( "--long[=#]: enable long distance matching with given window log (default: %u) \n", g_defaultMaxWindowLog);
- DISPLAYOUT( "--fast[=#]: switch to very fast compression levels (default: %u) \n", 1);
+ DISPLAYOUT("\n");
+ DISPLAYOUT("Advanced compression options :\n");
+ DISPLAYOUT(" --ultra enable levels beyond %i, up to %i (requires more memory)\n", ZSTDCLI_CLEVEL_MAX, ZSTD_maxCLevel());
+ DISPLAYOUT(" --long[=#] enable long distance matching with given window log (default: %u)\n", g_defaultMaxWindowLog);
+ DISPLAYOUT(" --fast[=#] switch to very fast compression levels (default: %u)\n", 1);
#ifdef ZSTD_GZCOMPRESS
if (exeNameMatch(programName, ZSTD_GZ)) { /* behave like gzip */
- DISPLAYOUT( "--best : compatibility alias for -9 \n");
- DISPLAYOUT( "--no-name : do not store original filename when compressing \n");
+ DISPLAYOUT(" --best compatibility alias for -9 \n");
+ DISPLAYOUT(" --no-name do not store original filename when compressing\n");
}
#endif
- DISPLAYOUT( "--adapt : dynamically adapt compression level to I/O conditions \n");
- DISPLAYOUT( "--[no-]row-match-finder : force enable/disable usage of fast row-based matchfinder for greedy, lazy, and lazy2 strategies \n");
- DISPLAYOUT( "--patch-from=FILE : specify the file to be used as a reference point for zstd's diff engine. \n");
+ DISPLAYOUT(" --adapt dynamically adapt compression level to I/O conditions\n");
+ DISPLAYOUT(" --[no-]row-match-finder : force enable/disable usage of fast row-based matchfinder for greedy, lazy, and lazy2 strategies\n");
+ DISPLAYOUT(" --patch-from=FILE : specify the file to be used as a reference point for zstd's diff engine. \n");
# ifdef ZSTD_MULTITHREAD
- DISPLAYOUT( " -T# : spawns # compression threads (default: 1, 0==# cores) \n");
- DISPLAYOUT( " -B# : select size of each job (default: 0==automatic) \n");
- DISPLAYOUT( "--single-thread : use a single thread for both I/O and compression (result slightly different than -T1) \n");
- DISPLAYOUT( "--auto-threads={physical,logical} (default: physical} : use either physical cores or logical cores as default when specifying -T0 \n");
- DISPLAYOUT( "--rsyncable : compress using a rsync-friendly method (-B sets block size) \n");
+ DISPLAYOUT(" -T# spawn # compression threads (default: 1, 0==# cores) \n");
+ DISPLAYOUT(" -B# select size of each job (default: 0==automatic) \n");
+ DISPLAYOUT(" --single-thread use a single thread for both I/O and compression (result slightly different than -T1) \n");
+ DISPLAYOUT(" --auto-threads={physical,logical} : use either physical cores or logical cores as default when specifying -T0 (default: physical)\n");
+ DISPLAYOUT(" --rsyncable compress using a rsync-friendly method (-B sets block size) \n");
# endif
- DISPLAYOUT( "--exclude-compressed: only compress files that are not already compressed \n");
- DISPLAYOUT( "--stream-size=# : specify size of streaming input from `stdin` \n");
- DISPLAYOUT( "--size-hint=# optimize compression parameters for streaming input of approximately this size \n");
- DISPLAYOUT( "--target-compressed-block-size=# : generate compressed block of approximately targeted size \n");
- DISPLAYOUT( "--no-dictID : don't write dictID into header (dictionary compression only) \n");
- DISPLAYOUT( "--[no-]compress-literals : force (un)compressed literals \n");
-
- DISPLAYOUT( "--format=zstd : compress files to the .zst format (default) \n");
+ DISPLAYOUT(" --exclude-compressed : only compress files that are not already compressed \n");
+ DISPLAYOUT(" --stream-size=# specify size of streaming input from `stdin` \n");
+ DISPLAYOUT(" --size-hint=# optimize compression parameters for streaming input of approximately this size \n");
+ DISPLAYOUT(" --target-compressed-block-size=# : generate compressed block of approximately targeted size \n");
+ DISPLAYOUT(" --no-dictID don't write dictID into header (dictionary compression only)\n");
+ DISPLAYOUT(" --[no-]compress-literals : force (un)compressed literals\n");
+
+ DISPLAYOUT(" --format=zstd compress files to the .zst format (default)\n");
#ifdef ZSTD_GZCOMPRESS
- DISPLAYOUT( "--format=gzip : compress files to the .gz format \n");
+ DISPLAYOUT(" --format=gzip compress files to the .gz format\n");
#endif
#ifdef ZSTD_LZMACOMPRESS
- DISPLAYOUT( "--format=xz : compress files to the .xz format \n");
- DISPLAYOUT( "--format=lzma : compress files to the .lzma format \n");
+ DISPLAYOUT(" --format=xz compress files to the .xz format\n");
+ DISPLAYOUT(" --format=lzma compress files to the .lzma format\n");
#endif
#ifdef ZSTD_LZ4COMPRESS
- DISPLAYOUT( "--format=lz4 : compress files to the .lz4 format \n");
+ DISPLAYOUT( " --format=lz4 compress files to the .lz4 format\n");
#endif
#endif /* !ZSTD_NOCOMPRESS */
#ifndef ZSTD_NODECOMPRESS
- DISPLAYOUT( "\n");
- DISPLAYOUT( "Advanced decompression arguments : \n");
- DISPLAYOUT( " -l : print information about zstd compressed files \n");
- DISPLAYOUT( "--test : test compressed file integrity \n");
- DISPLAYOUT( " -M# : Set a memory usage limit for decompression \n");
+ DISPLAYOUT("\n");
+ DISPLAYOUT("Advanced decompression options :\n");
+ DISPLAYOUT(" -l print information about zstd compressed files\n");
+ DISPLAYOUT(" --test test compressed file integrity\n");
+ DISPLAYOUT(" -M# Set a memory usage limit for decompression\n");
# if ZSTD_SPARSE_DEFAULT
- DISPLAYOUT( "--[no-]sparse : sparse mode (default: enabled on file, disabled on stdout) \n");
+ DISPLAYOUT(" --[no-]sparse sparse mode (default: enabled on file, disabled on stdout)\n");
# else
- DISPLAYOUT( "--[no-]sparse : sparse mode (default: disabled) \n");
+ DISPLAYOUT(" --[no-]sparse sparse mode (default: disabled)\n");
# endif
#endif /* ZSTD_NODECOMPRESS */
#ifndef ZSTD_NODICT
- DISPLAYOUT( "\n");
- DISPLAYOUT( "Dictionary builder : \n");
- DISPLAYOUT( "--train ## : create a dictionary from a training set of files \n");
- DISPLAYOUT( "--train-cover[=k=#,d=#,steps=#,split=#,shrink[=#]] : use the cover algorithm with optional args \n");
- DISPLAYOUT( "--train-fastcover[=k=#,d=#,f=#,steps=#,split=#,accel=#,shrink[=#]] : use the fast cover algorithm with optional args \n");
- DISPLAYOUT( "--train-legacy[=s=#] : use the legacy algorithm with selectivity (default: %u) \n", g_defaultSelectivityLevel);
- DISPLAYOUT( " -o DICT : DICT is dictionary name (default: %s) \n", g_defaultDictName);
- DISPLAYOUT( "--maxdict=# : limit dictionary to specified size (default: %u) \n", g_defaultMaxDictSize);
- DISPLAYOUT( "--dictID=# : force dictionary ID to specified value (default: random) \n");
+ DISPLAYOUT("\n");
+ DISPLAYOUT("Dictionary builder :\n");
+ DISPLAYOUT(" --train ## create a dictionary from a training set of files\n");
+ DISPLAYOUT(" --train-cover[=k=#,d=#,steps=#,split=#,shrink[=#]] : use the cover algorithm with optional args\n");
+ DISPLAYOUT(" --train-fastcover[=k=#,d=#,f=#,steps=#,split=#,accel=#,shrink[=#]] : use the fast cover algorithm with optional args\n");
+ DISPLAYOUT(" --train-legacy[=s=#] : use the legacy algorithm with selectivity (default: %u)\n", g_defaultSelectivityLevel);
+ DISPLAYOUT(" -o DICT DICT is dictionary name (default: %s)\n", g_defaultDictName);
+ DISPLAYOUT(" --maxdict=# limit dictionary to specified size (default: %u)\n", g_defaultMaxDictSize);
+ DISPLAYOUT(" --dictID=# force dictionary ID to specified value (default: random)\n");
#endif
#ifndef ZSTD_NOBENCH
- DISPLAYOUT( "\n");
- DISPLAYOUT( "Benchmark arguments : \n");
- DISPLAYOUT( " -b# : benchmark file(s), using # compression level (default: %d) \n", ZSTDCLI_CLEVEL_DEFAULT);
- DISPLAYOUT( " -e# : test all compression levels successively from -b# to -e# (default: 1) \n");
- DISPLAYOUT( " -i# : minimum evaluation time in seconds (default: 3s) \n");
- DISPLAYOUT( " -B# : cut file into independent blocks of size # (default: no block) \n");
- DISPLAYOUT( " -S : output one benchmark result per input file (default: consolidated result) \n");
- DISPLAYOUT( "--priority=rt : set process priority to real-time \n");
+ DISPLAYOUT("\n");
+ DISPLAYOUT("Benchmark options : \n");
+ DISPLAYOUT(" -b# benchmark file(s), using # compression level (default: %d)\n", ZSTDCLI_CLEVEL_DEFAULT);
+ DISPLAYOUT(" -e# test all compression levels successively from -b# to -e# (default: 1)\n");
+ DISPLAYOUT(" -i# minimum evaluation time in seconds (default: 3s)\n");
+ DISPLAYOUT(" -B# cut file into independent blocks of size # (default: no block)\n");
+ DISPLAYOUT(" -S output one benchmark result per input file (default: consolidated result)\n");
+ DISPLAYOUT(" --priority=rt set process priority to real-time\n");
#endif
}
@@ -1099,8 +1094,8 @@ int main(int argCount, const char* argv[])
{
/* Display help */
case 'V': printVersion(); CLEAN_RETURN(0); /* Version Only */
- case 'H':
- case 'h': usage_advanced(programName); CLEAN_RETURN(0);
+ case 'H': usage_advanced(programName); CLEAN_RETURN(0);
+ case 'h': usage(stdout, programName); CLEAN_RETURN(0);
/* Compress */
case 'z': operation=zom_compress; argument++; break;
diff --git a/tests/cli-tests/basic/help.sh.stdout.glob b/tests/cli-tests/basic/help.sh.stdout.glob
index 5b2f8e4581d..be3f15400c7 100644
--- a/tests/cli-tests/basic/help.sh.stdout.glob
+++ b/tests/cli-tests/basic/help.sh.stdout.glob
@@ -1,34 +1,24 @@
+ zstd -h
-*** zstd command line interface *-bits v1.*.*, by Yann Collet ***
-Usage :
- zstd *args* *FILE(s)* *-o file*
+Usage: zstd *OPTION*... *FILE*... *-o file*
+Compress or uncompress FILEs (with no FILE or when FILE is `-`, read from standard input).
-FILE : a filename
- with no FILE, or when FILE is - , read standard input
-Arguments :
- -# : # compression level*
- -d : decompression
- -D DICT: use DICT as Dictionary for compression or decompression
- -o file: result stored into `file` (only 1 output file)
- -f : disable input and output checks. Allows overwriting existing files,
- input from console, output to stdout, operating on links,
- block devices, etc.
---rm : remove source file(s) after successful de/compression
- -k : preserve source file(s) (default)
- -h/-H : display help/long help and exit
+ -o file result stored into `file` (only 1 output file)
+ -1 .. -19 compression level (faster .. better; default: 3)
+ -d, --decompress decompression
+ -f, --force disable input and output checks. Allows overwriting existing files,
+ input from console, output to stdout, operating on links,
+ block devices, etc.
+ --rm remove source file(s) after successful de/compression
+ -k, --keep preserve source file(s) (default)
+ -D DICT use DICT as Dictionary for compression or decompression
+ -h display usage and exit
+ -H,--help display long help and exit
-Advanced arguments :
- -V : display Version number and exit
-...
+ zstd -H
...
-Arguments :
-...
-Advanced arguments :
+Advanced options :
...
+ zstd --help
...
-Arguments :
-...
-Advanced arguments :
+Advanced options :
...
From 678bfff4fed62b8d1076f7388343338acef2b4c4 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Tue, 29 Mar 2022 16:45:09 -0700
Subject: [PATCH 115/472] fix minor bug in sequence_compression_api tester
margin was merely slightly too short for extra splitting.
---
tests/fuzz/sequence_compression_api.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/fuzz/sequence_compression_api.c b/tests/fuzz/sequence_compression_api.c
index 4a09d27fb47..cc872a08125 100644
--- a/tests/fuzz/sequence_compression_api.c
+++ b/tests/fuzz/sequence_compression_api.c
@@ -153,7 +153,7 @@ static size_t generateRandomSequences(FUZZ_dataProducer_t* producer,
matchLengthMax = MIN(matchLengthMax, blockSizeMax/2);
}
- while ( nbSeqGenerated < ZSTD_FUZZ_MAX_NBSEQ-2 /* extra room for explicit delimiters */
+ while ( nbSeqGenerated < ZSTD_FUZZ_MAX_NBSEQ - 3 /* extra room for explicit delimiters */
&& bytesGenerated < ZSTD_FUZZ_GENERATED_SRC_MAXSIZE
&& !FUZZ_dataProducer_empty(producer)) {
uint32_t matchLength;
From 3e6bbdd8473a753d2047969ac0053fb2cb4dda23 Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Thu, 31 Mar 2022 12:26:20 -0400
Subject: [PATCH 116/472] Disable visual-2015 tests (#3106)
---
.github/workflows/dev-short-tests.yml | 33 ++++++++++++++-------------
1 file changed, 17 insertions(+), 16 deletions(-)
diff --git a/.github/workflows/dev-short-tests.yml b/.github/workflows/dev-short-tests.yml
index a5479691664..9f51508415c 100644
--- a/.github/workflows/dev-short-tests.yml
+++ b/.github/workflows/dev-short-tests.yml
@@ -214,22 +214,23 @@ jobs:
msbuild "build\VS2010\zstd.sln" /m /verbosity:minimal /property:PlatformToolset=v142
/t:Clean,Build /p:Platform=${{matrix.platform}} /p:Configuration=${{matrix.configuration}}
- visual-2015:
- # only GH actions windows-2016 contains VS 2015
- runs-on: windows-2016
- strategy:
- matrix:
- platform: [x64, Win32]
- configuration: [Debug, Release]
- steps:
- - uses: actions/checkout@v2
- - name: Add MSBuild to PATH
- uses: microsoft/setup-msbuild@v1.0.2
- - name: Build
- working-directory: ${{env.GITHUB_WORKSPACE}}
- run: >
- msbuild "build\VS2010\zstd.sln" /m /verbosity:minimal /property:PlatformToolset=v140
- /t:Clean,Build /p:Platform=${{matrix.platform}} /p:Configuration=${{matrix.configuration}}
+# TODO: fix as part of https://github.com/facebook/zstd/issues/3064
+# visual-2015:
+# # only GH actions windows-2016 contains VS 2015
+# runs-on: windows-2016
+# strategy:
+# matrix:
+# platform: [x64, Win32]
+# configuration: [Debug, Release]
+# steps:
+# - uses: actions/checkout@v2
+# - name: Add MSBuild to PATH
+# uses: microsoft/setup-msbuild@v1.0.2
+# - name: Build
+# working-directory: ${{env.GITHUB_WORKSPACE}}
+# run: >
+# msbuild "build\VS2010\zstd.sln" /m /verbosity:minimal /property:PlatformToolset=v140
+# /t:Clean,Build /p:Platform=${{matrix.platform}} /p:Configuration=${{matrix.configuration}}
minimal-decompressor-macros:
runs-on: ubuntu-latest
From f133bc8c9c345fac5c28b6758e1f133b8b6280fd Mon Sep 17 00:00:00 2001
From: Paul Menzel
Date: Sun, 3 Apr 2022 07:26:49 +0200
Subject: [PATCH 117/472] zstd.1: Remove superfluous *not* in description of
`--long[=#]`
Resolves: https://github.com/facebook/zstd/issues/3101
---
programs/zstd.1.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/programs/zstd.1.md b/programs/zstd.1.md
index 49554ca11fd..3901b58dbdf 100644
--- a/programs/zstd.1.md
+++ b/programs/zstd.1.md
@@ -139,7 +139,7 @@ the last one takes effect.
_note_ : at the time of this writing, `--adapt` can remain stuck at low speed
when combined with multiple worker threads (>=2).
* `--long[=#]`:
- enables long distance matching with `#` `windowLog`, if not `#` is not
+ enables long distance matching with `#` `windowLog`, if `#` is not
present it defaults to `27`.
This increases the window size (`windowLog`) and memory usage for both the
compressor and decompressor.
From 0df2fd6088a02cfce7305ccd44874fa9d94cbbb3 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Wed, 13 Apr 2022 18:51:59 -0700
Subject: [PATCH 118/472] updated man page, providing more details for --train
mode
following questions from #3111.
Note : only the source markdown has been updated,
the actual man page zstd.1 still need to be processed.
---
programs/zstd.1.md | 98 +++++++++++++++++++++++++++++-----------------
1 file changed, 63 insertions(+), 35 deletions(-)
diff --git a/programs/zstd.1.md b/programs/zstd.1.md
index 3901b58dbdf..4e176f39950 100644
--- a/programs/zstd.1.md
+++ b/programs/zstd.1.md
@@ -19,8 +19,8 @@ DESCRIPTION
with command line syntax similar to `gzip (1)` and `xz (1)`.
It is based on the **LZ77** family, with further FSE & huff0 entropy stages.
`zstd` offers highly configurable compression speed,
-with fast modes at > 200 MB/s per core,
-and strong modes nearing lzma compression ratios.
+from fast modes at > 200 MB/s per core,
+to strong modes with excellent compression ratios.
It also features a very fast decoder, with speeds > 500 MB/s per core.
`zstd` command line syntax is generally similar to gzip,
@@ -31,13 +31,12 @@ but features the following differences :
- When compressing a single file, `zstd` displays progress notifications
and result summary by default.
Use `-q` to turn them off.
- - `zstd` does not accept input from console,
- but it properly accepts `stdin` when it's not the console.
- `zstd` displays a short help page when command line is an error.
Use `-q` to turn it off.
+ - `zstd` does not accept input from console,
+ though it does accept `stdin` when it's not the console.
-`zstd` compresses or decompresses each _file_ according to the selected
-operation mode.
+`zstd` processes each _file_ according to the selected operation mode.
If no _files_ are given or _file_ is `-`, `zstd` reads from standard input
and writes the processed data to standard output.
`zstd` will refuse to write compressed data to standard output
@@ -54,8 +53,8 @@ whose name is derived from the source _file_ name:
get the target filename
### Concatenation with .zst files
-It is possible to concatenate `.zst` files as is.
-`zstd` will decompress such files as if they were a single `.zst` file.
+It is possible to concatenate multiple `.zst` files. `zstd` will decompress
+such agglomerated file as if it was a single `.zst` file.
OPTIONS
-------
@@ -85,8 +84,8 @@ the last one takes effect.
Decompress.
* `-t`, `--test`:
Test the integrity of compressed _files_.
- This option is equivalent to `--decompress --stdout` except that the
- decompressed data is discarded instead of being written to standard output.
+ This option is equivalent to `--decompress --stdout > /dev/null`,
+ decompressed data is discarded and checksummed for errors.
No files are created or removed.
* `-b#`:
Benchmark file(s) using compression level #
@@ -96,7 +95,7 @@ the last one takes effect.
* `-l`, `--list`:
Display information related to a zstd compressed file, such as size, ratio, and checksum.
Some of these fields may not be available.
- This command can be augmented with the `-v` modifier.
+ This command's output can be augmented with the `-v` modifier.
### Operation modifiers
@@ -292,10 +291,10 @@ options that intend to mimic the `gzip` behavior:
alias to the option `-9`.
-### Restricted usage of Environment Variables
+### Interactions with Environment Variables
-Using environment variables to set parameters has security implications.
-Therefore, this avenue is intentionally restricted.
+Employing environment variables to set parameters has security implications.
+Therefore, this avenue is intentionally limited.
Only `ZSTD_CLEVEL` and `ZSTD_NBTHREADS` are currently supported.
They set the compression level and number of threads to use during compression, respectively.
@@ -305,8 +304,8 @@ If the value of `ZSTD_CLEVEL` is not a valid integer, it will be ignored with a
`ZSTD_NBTHREADS` can be used to set the number of threads `zstd` will attempt to use during compression.
If the value of `ZSTD_NBTHREADS` is not a valid unsigned integer, it will be ignored with a warning message.
-`ZSTD_NBTHREADS` has a default value of (`1`), and is capped at ZSTDMT_NBWORKERS_MAX==200. `zstd` must be
-compiled with multithread support for this to have any effect.
+`ZSTD_NBTHREADS` has a default value of (`1`), and is capped at ZSTDMT_NBWORKERS_MAX==200.
+`zstd` must be compiled with multithread support for this to have any effect.
They can both be overridden by corresponding command line arguments:
`-#` for compression level and `-T#` for number of compression threads.
@@ -318,27 +317,36 @@ DICTIONARY BUILDER
which greatly improves efficiency on small files and messages.
It's possible to train `zstd` with a set of samples,
the result of which is saved into a file called a `dictionary`.
-Then during compression and decompression, reference the same dictionary,
+Then, during compression and decompression, reference the same dictionary,
using command `-D dictionaryFileName`.
Compression of small files similar to the sample set will be greatly improved.
* `--train FILEs`:
Use FILEs as training set to create a dictionary.
- The training set should contain a lot of small files (> 100),
+ The training set should ideally contain a lot of samples (> 100),
and weight typically 100x the target dictionary size
- (for example, 10 MB for a 100 KB dictionary).
+ (for example, ~10 MB for a 100 KB dictionary).
`--train` can be combined with `-r` to indicate a directory rather than listing all the files,
which can be useful to circumvent shell expansion limits.
+ Since dictionary compression is mostly effective for small files,
+ the expectation is that the training set will only contain small files.
+ In the case where some samples happen to be large,
+ only the first 128 KB of these samples will be used for training.
+
`--train` supports multithreading if `zstd` is compiled with threading support (default).
- Additional parameters can be specified with `--train-fastcover`.
+ Additional advanced parameters can be specified with `--train-fastcover`.
The legacy dictionary builder can be accessed with `--train-legacy`.
The slower cover dictionary builder can be accessed with `--train-cover`.
- Default is equivalent to `--train-fastcover=d=8,steps=4`.
-* `-o file`:
- Dictionary saved into `file` (default name: dictionary).
+ Default `--train` is equivalent to `--train-fastcover=d=8,steps=4`.
+
+* `-o FILE`:
+ Dictionary saved into `FILE` (default name: dictionary).
* `--maxdict=#`:
- Limit dictionary to specified size (default: 112640).
+ Limit dictionary to specified size (default: 112640 bytes).
+ As usual, quantities are expressed in bytes by default,
+ and it's possible to employ suffixes (like `KB` or `MB`)
+ to specify larger values.
* `-#`:
Use `#` compression level during training (optional).
Will generate statistics more tuned for selected compression level,
@@ -346,17 +354,37 @@ Compression of small files similar to the sample set will be greatly improved.
* `-B#`:
Split input files into blocks of size # (default: no split)
* `-M#`, `--memory=#`:
- Limit the amount of sample data loaded for training (default: 2 GB). See above for details.
+ Limit the amount of sample data loaded for training (default: 2 GB).
+ Note that the default (2 GB) is also the maximum.
+ This parameter can be useful in situations where the training set size
+ is not well controlled and could be potentially very large.
+ Since speed of the training process is directly correlated to
+ the size of the training sample set,
+ a smaller sample set leads to faster training.
+
+ In situations where the training set is larger than maximum memory,
+ the CLI will randomly select samples among the available ones,
+ up to the maximum allowed memory budget.
+ This is meant to improve dictionary relevance
+ by mitigating the potential impact of clustering,
+ such as selecting only files from the beginning of a list
+ sorted by modification date, or sorted by alphabetical order.
+ The randomization process is deterministic, so
+ training of the same list of files with the same parameters
+ will lead to the creation of the same dictionary.
+
* `--dictID=#`:
- A dictionary ID is a locally unique ID
- that a decoder can use to verify it is using the right dictionary.
+ A dictionary ID is a locally unique ID.
+ The decoder will use this value to verify it is using the right dictionary.
By default, zstd will create a 4-bytes random number ID.
- It's possible to give a precise number instead.
- Short numbers have an advantage : an ID < 256 will only need 1 byte in the
- compressed frame header, and an ID < 65536 will only need 2 bytes.
- This compares favorably to 4 bytes default.
- However, it's up to the dictionary manager to not assign twice the same ID to
+ It's possible to provide an explicit number ID instead.
+ It's up to the dictionary manager to not assign twice the same ID to
2 different dictionaries.
+ Note that short numbers have an advantage :
+ an ID < 256 will only need 1 byte in the compressed frame header,
+ and an ID < 65536 will only need 2 bytes.
+ This compares favorably to 4 bytes default.
+
* `--train-cover[=k#,d=#,steps=#,split=#,shrink[=#]]`:
Select parameters for the default dictionary builder algorithm named cover.
If _d_ is not specified, then it tries _d_ = 6 and _d_ = 8.
@@ -421,7 +449,7 @@ Compression of small files similar to the sample set will be greatly improved.
Use legacy dictionary builder algorithm with the given dictionary
_selectivity_ (default: 9).
The smaller the _selectivity_ value, the denser the dictionary,
- improving its efficiency but reducing its possible maximum size.
+ improving its efficiency but reducing its achievable maximum size.
`--train-legacy=s=#` is also accepted.
Examples:
@@ -452,14 +480,14 @@ BENCHMARK
ADVANCED COMPRESSION OPTIONS
----------------------------
### -B#:
-Select the size of each compression job.
+Specify the size of each compression job.
This parameter is only available when multi-threading is enabled.
Each compression job is run in parallel, so this value indirectly impacts the nb of active threads.
Default job size varies depending on compression level (generally `4 * windowSize`).
`-B#` makes it possible to manually select a custom size.
Note that job size must respect a minimum value which is enforced transparently.
This minimum is either 512 KB, or `overlapSize`, whichever is largest.
-Different job sizes will lead to (slightly) different compressed frames.
+Different job sizes will lead to non-identical compressed frames.
### --zstd[=options]:
`zstd` provides 22 predefined compression levels.
From eb726c6a20f1c83eee5730ca28a4126432a0bb07 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Wed, 13 Apr 2022 18:57:27 -0700
Subject: [PATCH 119/472] updated man pages
had to run the conversion script on Ubuntu, as it doesn't run correctly on macos anymore.
---
programs/zstd.1 | 301 +++++++++++---------------------------------
programs/zstdgrep.1 | 13 +-
programs/zstdless.1 | 9 +-
3 files changed, 77 insertions(+), 246 deletions(-)
diff --git a/programs/zstd.1 b/programs/zstd.1
index 78313c7f39a..c89060c7dac 100644
--- a/programs/zstd.1
+++ b/programs/zstd.1
@@ -1,512 +1,357 @@
-.
-.TH "ZSTD" "1" "February 2022" "zstd 1.5.2" "User Commands"
-.
+.TH "ZSTD" "1" "April 2022" "zstd 1.5.2" "User Commands"
.SH "NAME"
\fBzstd\fR \- zstd, zstdmt, unzstd, zstdcat \- Compress or decompress \.zst files
-.
.SH "SYNOPSIS"
-\fBzstd\fR [\fIOPTIONS\fR] [\-|\fIINPUT\-FILE\fR] [\-o \fIOUTPUT\-FILE\fR]
-.
+.TS
+allbox;
+\fBzstd\fR [\fIOPTIONS\fR] [\- \fIINPUT\-FILE\fR] [\-o \fIOUTPUT\-FILE\fR]
+.TE
.P
\fBzstdmt\fR is equivalent to \fBzstd \-T0\fR
-.
.P
\fBunzstd\fR is equivalent to \fBzstd \-d\fR
-.
.P
\fBzstdcat\fR is equivalent to \fBzstd \-dcf\fR
-.
.SH "DESCRIPTION"
-\fBzstd\fR is a fast lossless compression algorithm and data compression tool, with command line syntax similar to \fBgzip (1)\fR and \fBxz (1)\fR\. It is based on the \fBLZ77\fR family, with further FSE & huff0 entropy stages\. \fBzstd\fR offers highly configurable compression speed, with fast modes at > 200 MB/s per core, and strong modes nearing lzma compression ratios\. It also features a very fast decoder, with speeds > 500 MB/s per core\.
-.
+\fBzstd\fR is a fast lossless compression algorithm and data compression tool, with command line syntax similar to \fBgzip (1)\fR and \fBxz (1)\fR\. It is based on the \fBLZ77\fR family, with further FSE & huff0 entropy stages\. \fBzstd\fR offers highly configurable compression speed, from fast modes at > 200 MB/s per core, to strong modes with excellent compression ratios\. It also features a very fast decoder, with speeds > 500 MB/s per core\.
.P
\fBzstd\fR command line syntax is generally similar to gzip, but features the following differences :
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
Source files are preserved by default\. It\'s possible to remove them automatically by using the \fB\-\-rm\fR command\.
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
When compressing a single file, \fBzstd\fR displays progress notifications and result summary by default\. Use \fB\-q\fR to turn them off\.
-.
-.IP "\(bu" 4
-\fBzstd\fR does not accept input from console, but it properly accepts \fBstdin\fR when it\'s not the console\.
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
\fBzstd\fR displays a short help page when command line is an error\. Use \fB\-q\fR to turn it off\.
-.
+.IP "\[ci]" 4
+\fBzstd\fR does not accept input from console, though it does accept \fBstdin\fR when it\'s not the console\.
.IP "" 0
-.
.P
-\fBzstd\fR compresses or decompresses each \fIfile\fR according to the selected operation mode\. If no \fIfiles\fR are given or \fIfile\fR is \fB\-\fR, \fBzstd\fR reads from standard input and writes the processed data to standard output\. \fBzstd\fR will refuse to write compressed data to standard output if it is a terminal : it will display an error message and skip the \fIfile\fR\. Similarly, \fBzstd\fR will refuse to read compressed data from standard input if it is a terminal\.
-.
+\fBzstd\fR processes each \fIfile\fR according to the selected operation mode\. If no \fIfiles\fR are given or \fIfile\fR is \fB\-\fR, \fBzstd\fR reads from standard input and writes the processed data to standard output\. \fBzstd\fR will refuse to write compressed data to standard output if it is a terminal : it will display an error message and skip the \fIfile\fR\. Similarly, \fBzstd\fR will refuse to read compressed data from standard input if it is a terminal\.
.P
Unless \fB\-\-stdout\fR or \fB\-o\fR is specified, \fIfiles\fR are written to a new file whose name is derived from the source \fIfile\fR name:
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
When compressing, the suffix \fB\.zst\fR is appended to the source filename to get the target filename\.
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
When decompressing, the \fB\.zst\fR suffix is removed from the source filename to get the target filename
-.
.IP "" 0
-.
.SS "Concatenation with \.zst files"
-It is possible to concatenate \fB\.zst\fR files as is\. \fBzstd\fR will decompress such files as if they were a single \fB\.zst\fR file\.
-.
+It is possible to concatenate multiple \fB\.zst\fR files\. \fBzstd\fR will decompress such agglomerated file as if it was a single \fB\.zst\fR file\.
.SH "OPTIONS"
-.
.SS "Integer suffixes and special values"
In most places where an integer argument is expected, an optional suffix is supported to easily indicate large integers\. There must be no space between the integer and the suffix\.
-.
.TP
\fBKiB\fR
-Multiply the integer by 1,024 (2^10)\. \fBKi\fR, \fBK\fR, and \fBKB\fR are accepted as synonyms for \fBKiB\fR\.
-.
+Multiply the integer by 1,024 (2\e^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^20)\. \fBMi\fR, \fBM\fR, and \fBMB\fR are accepted as synonyms for \fBMiB\fR\.
-.
+Multiply the integer by 1,048,576 (2\e^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\.
-.
.TP
\fB\-z\fR, \fB\-\-compress\fR
Compress\. This is the default operation mode when no operation mode option is specified and no other operation mode is implied from the command name (for example, \fBunzstd\fR implies \fB\-\-decompress\fR)\.
-.
.TP
\fB\-d\fR, \fB\-\-decompress\fR, \fB\-\-uncompress\fR
Decompress\.
-.
.TP
\fB\-t\fR, \fB\-\-test\fR
-Test the integrity of compressed \fIfiles\fR\. This option is equivalent to \fB\-\-decompress \-\-stdout\fR except that the decompressed data is discarded instead of being written to standard output\. No files are created or removed\.
-.
+Test the integrity of compressed \fIfiles\fR\. This option is equivalent to \fB\-\-decompress \-\-stdout > /dev/null\fR, decompressed data is discarded and checksummed for errors\. No files are created or removed\.
.TP
\fB\-b#\fR
Benchmark file(s) using compression level #
-.
.TP
\fB\-\-train FILEs\fR
Use FILEs as a training set to create a dictionary\. The training set should contain a lot of small files (> 100)\.
-.
.TP
\fB\-l\fR, \fB\-\-list\fR
-Display information related to a zstd compressed file, such as size, ratio, and checksum\. Some of these fields may not be available\. This command can be augmented with the \fB\-v\fR modifier\.
-.
+Display information related to a zstd compressed file, such as size, ratio, and checksum\. Some of these fields may not be available\. This command\'s output can be augmented with the \fB\-v\fR modifier\.
.SS "Operation modifiers"
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
\fB\-#\fR: \fB#\fR compression level [1\-19] (default: 3)
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
\fB\-\-ultra\fR: unlocks high compression levels 20+ (maximum 22), using a lot more memory\. Note that decompression will also require more memory when using these levels\.
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
\fB\-\-fast[=#]\fR: switch to ultra\-fast compression levels\. If \fB=#\fR is not present, it defaults to \fB1\fR\. The higher the value, the faster the compression speed, at the cost of some compression ratio\. This setting overwrites compression level if one was set previously\. Similarly, if a compression level is set after \fB\-\-fast\fR, it overrides it\.
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
\fB\-T#\fR, \fB\-\-threads=#\fR: Compress using \fB#\fR working threads (default: 1)\. If \fB#\fR is 0, attempt to detect and use the number of physical CPU cores\. In all cases, the nb of threads is capped to \fBZSTDMT_NBWORKERS_MAX\fR, which is either 64 in 32\-bit mode, or 256 for 64\-bit environments\. This modifier does nothing if \fBzstd\fR is compiled without multithread support\.
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
\fB\-\-single\-thread\fR: Does not spawn a thread for compression, use a single thread for both I/O and compression\. In this mode, compression is serialized with I/O, which is slightly slower\. (This is different from \fB\-T1\fR, which spawns 1 compression thread in parallel of I/O)\. This mode is the only one available when multithread support is disabled\. Single\-thread mode features lower memory usage\. Final compressed result is slightly different from \fB\-T1\fR\.
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
\fB\-\-auto\-threads={physical,logical} (default: physical)\fR: When using a default amount of threads via \fB\-T0\fR, choose the default based on the number of detected physical or logical cores\.
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
\fB\-\-adapt[=min=#,max=#]\fR : \fBzstd\fR will dynamically adapt compression level to perceived I/O conditions\. Compression level adaptation can be observed live by using command \fB\-v\fR\. Adaptation can be constrained between supplied \fBmin\fR and \fBmax\fR levels\. The feature works when combined with multi\-threading and \fB\-\-long\fR mode\. It does not work with \fB\-\-single\-thread\fR\. It sets window size to 8 MB by default (can be changed manually, see \fBwlog\fR)\. Due to the chaotic nature of dynamic adaptation, compressed result is not reproducible\. \fInote\fR : at the time of this writing, \fB\-\-adapt\fR can remain stuck at low speed when combined with multiple worker threads (>=2)\.
-.
-.IP "\(bu" 4
-\fB\-\-long[=#]\fR: enables long distance matching with \fB#\fR \fBwindowLog\fR, if not \fB#\fR is not present it defaults to \fB27\fR\. This increases the window size (\fBwindowLog\fR) and memory usage for both the compressor and decompressor\. This setting is designed to improve the compression ratio for files with long matches at a large distance\.
-.
+.IP "\[ci]" 4
+\fB\-\-long[=#]\fR: enables long distance matching with \fB#\fR \fBwindowLog\fR, if \fB#\fR is not present it defaults to \fB27\fR\. This increases the window size (\fBwindowLog\fR) and memory usage for both the compressor and decompressor\. This setting is designed to improve the compression ratio for files with long matches at a large distance\.
.IP
Note: If \fBwindowLog\fR is set to larger than 27, \fB\-\-long=windowLog\fR or \fB\-\-memory=windowSize\fR needs to be passed to the decompressor\.
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
\fB\-D DICT\fR: use \fBDICT\fR as Dictionary to compress or decompress FILE(s)
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
\fB\-\-patch\-from FILE\fR: Specify the file to be used as a reference point for zstd\'s diff engine\. This is effectively dictionary compression with some convenient parameter selection, namely that windowSize > srcSize\.
-.
.IP
-Note: cannot use both this and \-D together Note: \fB\-\-long\fR mode will be automatically activated if chainLog < fileLog (fileLog being the windowLog required to cover the whole file)\. You can also manually force it\. Node: for all levels, you can use \-\-patch\-from in \-\-single\-thread mode to improve compression ratio at the cost of speed Note: for level 19, you can get increased compression ratio at the cost of speed by specifying \fB\-\-zstd=targetLength=\fR to be something large (i\.e 4096), and by setting a large \fB\-\-zstd=chainLog=\fR
-.
-.IP "\(bu" 4
+Note: cannot use both this and \-D together Note: \fB\-\-long\fR mode will be automatically activated if chainLog < fileLog (fileLog being the windowLog required to cover the whole file)\. You can also manually force it\. Note: for all levels, you can use \-\-patch\-from in \-\-single\-thread mode to improve compression ratio at the cost of speed Note: for level 19, you can get increased compression ratio at the cost of speed by specifying \fB\-\-zstd=targetLength=\fR to be something large (i\.e\. 4096), and by setting a large \fB\-\-zstd=chainLog=\fR
+.IP "\[ci]" 4
\fB\-\-rsyncable\fR : \fBzstd\fR will periodically synchronize the compression state to make the compressed file more rsync\-friendly\. There is a negligible impact to compression ratio, and the faster compression levels will see a small compression speed hit\. This feature does not work with \fB\-\-single\-thread\fR\. You probably don\'t want to use it with long range mode, since it will decrease the effectiveness of the synchronization points, but your mileage may vary\.
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
\fB\-C\fR, \fB\-\-[no\-]check\fR: add integrity check computed from uncompressed data (default: enabled)
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
\fB\-\-[no\-]content\-size\fR: enable / disable whether or not the original size of the file is placed in the header of the compressed file\. The default option is \-\-content\-size (meaning that the original size will be placed in the header)\.
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
\fB\-\-no\-dictID\fR: do not store dictionary ID within frame header (dictionary compression)\. The decoder will have to rely on implicit knowledge about which dictionary to use, it won\'t be able to check if it\'s correct\.
-.
-.IP "\(bu" 4
-\fB\-M#\fR, \fB\-\-memory=#\fR: Set a memory usage limit\. By default, Zstandard uses 128 MB for decompression as the maximum amount of memory the decompressor is allowed to use, but you can override this manually if need be in either direction (ie\. you can increase or decrease it)\.
-.
+.IP "\[ci]" 4
+\fB\-M#\fR, \fB\-\-memory=#\fR: Set a memory usage limit\. By default, Zstandard uses 128 MB for decompression as the maximum amount of memory the decompressor is allowed to use, but you can override this manually if need be in either direction (i\.e\. you can increase or decrease it)\.
.IP
This is also used during compression when using with \-\-patch\-from=\. In this case, this parameter overrides that maximum size allowed for a dictionary\. (128 MB)\.
-.
.IP
Additionally, this can be used to limit memory for dictionary training\. This parameter overrides the default limit of 2 GB\. zstd will load training samples up to the memory limit and ignore the rest\.
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
\fB\-\-stream\-size=#\fR : Sets the pledged source size of input coming from a stream\. This value must be exact, as it will be included in the produced frame header\. Incorrect stream sizes will cause an error\. This information will be used to better optimize compression parameters, resulting in better and potentially faster compression, especially for smaller source sizes\.
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
\fB\-\-size\-hint=#\fR: When handling input from a stream, \fBzstd\fR must guess how large the source size will be when optimizing compression parameters\. If the stream size is relatively small, this guess may be a poor one, resulting in a higher compression ratio than expected\. This feature allows for controlling the guess when needed\. Exact guesses result in better compression ratios\. Overestimates result in slightly degraded compression ratios, while underestimates may result in significant degradation\.
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
\fB\-o FILE\fR: save result into \fBFILE\fR
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
\fB\-f\fR, \fB\-\-force\fR: disable input and output checks\. Allows overwriting existing files, input from console, output to stdout, operating on links, block devices, etc\.
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
\fB\-c\fR, \fB\-\-stdout\fR: write to standard output (even if it is the console); keep original files unchanged\.
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
\fB\-\-[no\-]sparse\fR: enable / disable sparse FS support, to make files with many zeroes smaller on disk\. Creating sparse files may save disk space and speed up decompression by reducing the amount of disk I/O\. default: enabled when output is into a file, and disabled when output is stdout\. This setting overrides default and can force sparse mode over stdout\.
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
\fB\-\-rm\fR: remove source file(s) after successful compression or decompression\. If used in combination with \-o, will trigger a confirmation prompt (which can be silenced with \-f), as this is a destructive operation\.
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
\fB\-k\fR, \fB\-\-keep\fR: keep source file(s) after successful compression or decompression\. This is the default behavior\.
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
\fB\-r\fR: operate recursively on directories\. It selects all files in the named directory and all its subdirectories\. This can be useful both to reduce command line typing, and to circumvent shell expansion limitations, when there are a lot of files and naming breaks the maximum size of a command line\.
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
\fB\-\-filelist FILE\fR read a list of files to process as content from \fBFILE\fR\. Format is compatible with \fBls\fR output, with one file per line\.
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
\fB\-\-output\-dir\-flat DIR\fR: resulting files are stored into target \fBDIR\fR directory, instead of same directory as origin file\. Be aware that this command can introduce name collision issues, if multiple files, from different directories, end up having the same name\. Collision resolution ensures first file with a given name will be present in \fBDIR\fR, while in combination with \fB\-f\fR, the last file will be present instead\.
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
\fB\-\-output\-dir\-mirror DIR\fR: similar to \fB\-\-output\-dir\-flat\fR, the output files are stored underneath target \fBDIR\fR directory, but this option will replicate input directory hierarchy into output \fBDIR\fR\.
-.
.IP
If input directory contains "\.\.", the files in this directory will be ignored\. If input directory is an absolute directory (i\.e\. "/var/tmp/abc"), it will be stored into the "output\-dir/var/tmp/abc"\. If there are multiple input files or directories, name collision resolution will follow the same rules as \fB\-\-output\-dir\-flat\fR\.
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
\fB\-\-format=FORMAT\fR: compress and decompress in other formats\. If compiled with support, zstd can compress to or decompress from other compression algorithm formats\. Possibly available options are \fBzstd\fR, \fBgzip\fR, \fBxz\fR, \fBlzma\fR, and \fBlz4\fR\. If no such format is provided, \fBzstd\fR is the default\.
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
\fB\-h\fR/\fB\-H\fR, \fB\-\-help\fR: display help/long help and exit
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
\fB\-V\fR, \fB\-\-version\fR: display version number and exit\. Advanced : \fB\-vV\fR also displays supported formats\. \fB\-vvV\fR also displays POSIX support\. \fB\-q\fR will only display the version number, suitable for machine reading\.
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
\fB\-v\fR, \fB\-\-verbose\fR: verbose mode, display more information
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
\fB\-q\fR, \fB\-\-quiet\fR: suppress warnings, interactivity, and notifications\. specify twice to suppress errors too\.
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
\fB\-\-no\-progress\fR: do not display the progress bar, but keep all other messages\.
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
\fB\-\-show\-default\-cparams\fR: Shows the default compression parameters that will be used for a particular src file\. If the provided src file is not a regular file (e\.g\. named pipe), the cli will just output the default parameters\. That is, the parameters that are used when the src size is unknown\.
-.
-.IP "\(bu" 4
+.IP "\[ci]" 4
\fB\-\-\fR: All arguments after \fB\-\-\fR are treated as files
-.
.IP "" 0
-.
.SS "gzip Operation modifiers"
When invoked via a \fBgzip\fR symlink, \fBzstd\fR will support further options that intend to mimic the \fBgzip\fR behavior:
-.
.TP
\fB\-n\fR, \fB\-\-no\-name\fR
do not store the original filename and timestamps when compressing a file\. This is the default behavior and hence a no\-op\.
-.
.TP
\fB\-\-best\fR
alias to the option \fB\-9\fR\.
-.
-.SS "Restricted usage of Environment Variables"
-Using environment variables to set parameters has security implications\. Therefore, this avenue is intentionally restricted\. Only \fBZSTD_CLEVEL\fR and \fBZSTD_NBTHREADS\fR are currently supported\. They set the compression level and number of threads to use during compression, respectively\.
-.
+.SS "Interactions with Environment Variables"
+Employing environment variables to set parameters has security implications\. Therefore, this avenue is intentionally limited\. Only \fBZSTD_CLEVEL\fR and \fBZSTD_NBTHREADS\fR are currently supported\. They set the compression level and number of threads to use during compression, respectively\.
.P
\fBZSTD_CLEVEL\fR can be used to set the level between 1 and 19 (the "normal" range)\. If the value of \fBZSTD_CLEVEL\fR is not a valid integer, it will be ignored with a warning message\. \fBZSTD_CLEVEL\fR just replaces the default compression level (\fB3\fR)\.
-.
.P
\fBZSTD_NBTHREADS\fR can be used to set the number of threads \fBzstd\fR will attempt to use during compression\. If the value of \fBZSTD_NBTHREADS\fR is not a valid unsigned integer, it will be ignored with a warning message\. \fBZSTD_NBTHREADS\fR has a default value of (\fB1\fR), and is capped at ZSTDMT_NBWORKERS_MAX==200\. \fBzstd\fR must be compiled with multithread support for this to have any effect\.
-.
.P
They can both be overridden by corresponding command line arguments: \fB\-#\fR for compression level and \fB\-T#\fR for number of compression threads\.
-.
.SH "DICTIONARY BUILDER"
-\fBzstd\fR offers \fIdictionary\fR compression, which greatly improves efficiency on small files and messages\. It\'s possible to train \fBzstd\fR with a set of samples, the result of which is saved into a file called a \fBdictionary\fR\. Then during compression and decompression, reference the same dictionary, using command \fB\-D dictionaryFileName\fR\. Compression of small files similar to the sample set will be greatly improved\.
-.
+\fBzstd\fR offers \fIdictionary\fR compression, which greatly improves efficiency on small files and messages\. It\'s possible to train \fBzstd\fR with a set of samples, the result of which is saved into a file called a \fBdictionary\fR\. Then, during compression and decompression, reference the same dictionary, using command \fB\-D dictionaryFileName\fR\. Compression of small files similar to the sample set will be greatly improved\.
.TP
\fB\-\-train FILEs\fR
-Use FILEs as training set to create a dictionary\. The training set should contain a lot of small files (> 100), and weight typically 100x the target dictionary size (for example, 10 MB for a 100 KB dictionary)\. \fB\-\-train\fR can be combined with \fB\-r\fR to indicate a directory rather than listing all the files, which can be useful to circumvent shell expansion limits\.
-.
+Use FILEs as training set to create a dictionary\. The training set should ideally contain a lot of samples (> 100), and weight typically 100x the target dictionary size (for example, ~10 MB for a 100 KB dictionary)\. \fB\-\-train\fR can be combined with \fB\-r\fR to indicate a directory rather than listing all the files, which can be useful to circumvent shell expansion limits\.
.IP
-\fB\-\-train\fR supports multithreading if \fBzstd\fR is compiled with threading support (default)\. Additional parameters can be specified with \fB\-\-train\-fastcover\fR\. The legacy dictionary builder can be accessed with \fB\-\-train\-legacy\fR\. The slower cover dictionary builder can be accessed with \fB\-\-train\-cover\fR\. Default is equivalent to \fB\-\-train\-fastcover=d=8,steps=4\fR\.
-.
+Since dictionary compression is mostly effective for small files, the expectation is that the training set will only contain small files\. In the case where some samples happen to be large, only the first 128 KB of these samples will be used for training\.
+.IP
+\fB\-\-train\fR supports multithreading if \fBzstd\fR is compiled with threading support (default)\. Additional advanced parameters can be specified with \fB\-\-train\-fastcover\fR\. The legacy dictionary builder can be accessed with \fB\-\-train\-legacy\fR\. The slower cover dictionary builder can be accessed with \fB\-\-train\-cover\fR\. Default \fB\-\-train\fR is equivalent to \fB\-\-train\-fastcover=d=8,steps=4\fR\.
.TP
-\fB\-o file\fR
-Dictionary saved into \fBfile\fR (default name: dictionary)\.
-.
+\fB\-o FILE\fR
+Dictionary saved into \fBFILE\fR (default name: dictionary)\.
.TP
\fB\-\-maxdict=#\fR
-Limit dictionary to specified size (default: 112640)\.
-.
+Limit dictionary to specified size (default: 112640 bytes)\. As usual, quantities are expressed in bytes by default, and it\'s possible to employ suffixes (like \fBKB\fR or \fBMB\fR) to specify larger values\.
.TP
\fB\-#\fR
Use \fB#\fR compression level during training (optional)\. Will generate statistics more tuned for selected compression level, resulting in a \fIsmall\fR compression ratio improvement for this level\.
-.
.TP
\fB\-B#\fR
Split input files into blocks of size # (default: no split)
-.
.TP
\fB\-M#\fR, \fB\-\-memory=#\fR
-Limit the amount of sample data loaded for training (default: 2 GB)\. See above for details\.
-.
+Limit the amount of sample data loaded for training (default: 2 GB)\. Note that the default (2 GB) is also the maximum\. This parameter can be useful in situations where the training set size is not well controlled and could be potentially very large\. Since speed of the training process is directly correlated to the size of the training sample set, a smaller sample set leads to faster training\.
+.IP
+In situations where the training set is larger than maximum memory, the CLI will randomly select samples among the available ones, up to the maximum allowed memory budget\. This is meant to improve dictionary relevance by mitigating the potential impact of clustering, such as selecting only files from the beginning of a list sorted by modification date, or sorted by alphabetical order\. The randomization process is deterministic, so training of the same list of files with the same parameters will lead to the creation of the same dictionary\.
.TP
\fB\-\-dictID=#\fR
-A dictionary ID is a locally unique ID that a decoder can use to verify it is using the right dictionary\. By default, zstd will create a 4\-bytes random number ID\. It\'s possible to give a precise number instead\. Short numbers have an advantage : an ID < 256 will only need 1 byte in the compressed frame header, and an ID < 65536 will only need 2 bytes\. This compares favorably to 4 bytes default\. However, it\'s up to the dictionary manager to not assign twice the same ID to 2 different dictionaries\.
-.
+A dictionary ID is a locally unique ID\. The decoder will use this value to verify it is using the right dictionary\. By default, zstd will create a 4\-bytes random number ID\. It\'s possible to provide an explicit number ID instead\. It\'s up to the dictionary manager to not assign twice the same ID to 2 different dictionaries\. Note that short numbers have an advantage : an ID < 256 will only need 1 byte in the compressed frame header, and an ID < 65536 will only need 2 bytes\. This compares favorably to 4 bytes default\.
.TP
\fB\-\-train\-cover[=k#,d=#,steps=#,split=#,shrink[=#]]\fR
Select parameters for the default dictionary builder algorithm named cover\. If \fId\fR is not specified, then it tries \fId\fR = 6 and \fId\fR = 8\. If \fIk\fR is not specified, then it tries \fIsteps\fR values in the range [50, 2000]\. If \fIsteps\fR is not specified, then the default value of 40 is used\. If \fIsplit\fR is not specified or split <= 0, then the default value of 100 is used\. Requires that \fId\fR <= \fIk\fR\. If \fIshrink\fR flag is not used, then the default value for \fIshrinkDict\fR of 0 is used\. If \fIshrink\fR is not specified, then the default value for \fIshrinkDictMaxRegression\fR of 1 is used\.
-.
.IP
Selects segments of size \fIk\fR with highest score to put in the dictionary\. The score of a segment is computed by the sum of the frequencies of all the subsegments of size \fId\fR\. Generally \fId\fR should be in the range [6, 8], occasionally up to 16, but the algorithm will run faster with d <= \fI8\fR\. Good values for \fIk\fR vary widely based on the input data, but a safe range is [2 * \fId\fR, 2000]\. If \fIsplit\fR is 100, all input samples are used for both training and testing to find optimal \fId\fR and \fIk\fR to build dictionary\. Supports multithreading if \fBzstd\fR is compiled with threading support\. Having \fIshrink\fR enabled takes a truncated dictionary of minimum size and doubles in size until compression ratio of the truncated dictionary is at most \fIshrinkDictMaxRegression%\fR worse than the compression ratio of the largest dictionary\.
-.
.IP
Examples:
-.
.IP
\fBzstd \-\-train\-cover FILEs\fR
-.
.IP
\fBzstd \-\-train\-cover=k=50,d=8 FILEs\fR
-.
.IP
\fBzstd \-\-train\-cover=d=8,steps=500 FILEs\fR
-.
.IP
\fBzstd \-\-train\-cover=k=50 FILEs\fR
-.
.IP
\fBzstd \-\-train\-cover=k=50,split=60 FILEs\fR
-.
.IP
\fBzstd \-\-train\-cover=shrink FILEs\fR
-.
.IP
\fBzstd \-\-train\-cover=shrink=2 FILEs\fR
-.
.TP
\fB\-\-train\-fastcover[=k#,d=#,f=#,steps=#,split=#,accel=#]\fR
Same as cover but with extra parameters \fIf\fR and \fIaccel\fR and different default value of split If \fIsplit\fR is not specified, then it tries \fIsplit\fR = 75\. If \fIf\fR is not specified, then it tries \fIf\fR = 20\. Requires that 0 < \fIf\fR < 32\. If \fIaccel\fR is not specified, then it tries \fIaccel\fR = 1\. Requires that 0 < \fIaccel\fR <= 10\. Requires that \fId\fR = 6 or \fId\fR = 8\.
-.
.IP
\fIf\fR is log of size of array that keeps track of frequency of subsegments of size \fId\fR\. The subsegment is hashed to an index in the range [0,2^\fIf\fR \- 1]\. It is possible that 2 different subsegments are hashed to the same index, and they are considered as the same subsegment when computing frequency\. Using a higher \fIf\fR reduces collision but takes longer\.
-.
.IP
Examples:
-.
.IP
\fBzstd \-\-train\-fastcover FILEs\fR
-.
.IP
\fBzstd \-\-train\-fastcover=d=8,f=15,accel=2 FILEs\fR
-.
.TP
\fB\-\-train\-legacy[=selectivity=#]\fR
-Use legacy dictionary builder algorithm with the given dictionary \fIselectivity\fR (default: 9)\. The smaller the \fIselectivity\fR value, the denser the dictionary, improving its efficiency but reducing its possible maximum size\. \fB\-\-train\-legacy=s=#\fR is also accepted\.
-.
+Use legacy dictionary builder algorithm with the given dictionary \fIselectivity\fR (default: 9)\. The smaller the \fIselectivity\fR value, the denser the dictionary, improving its efficiency but reducing its achievable maximum size\. \fB\-\-train\-legacy=s=#\fR is also accepted\.
.IP
Examples:
-.
.IP
\fBzstd \-\-train\-legacy FILEs\fR
-.
.IP
\fBzstd \-\-train\-legacy=selectivity=8 FILEs\fR
-.
.SH "BENCHMARK"
-.
.TP
\fB\-b#\fR
benchmark file(s) using compression level #
-.
.TP
\fB\-e#\fR
benchmark file(s) using multiple compression levels, from \fB\-b#\fR to \fB\-e#\fR (inclusive)
-.
.TP
\fB\-i#\fR
minimum evaluation time, in seconds (default: 3s), benchmark mode only
-.
.TP
\fB\-B#\fR, \fB\-\-block\-size=#\fR
cut file(s) into independent blocks of size # (default: no block)
-.
.TP
\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 "\-B#:"
-Select the size of each compression job\. This parameter is only available when multi\-threading is enabled\. Each compression job is run in parallel, so this value indirectly impacts the nb of active threads\. Default job size varies depending on compression level (generally \fB4 * windowSize\fR)\. \fB\-B#\fR makes it possible to manually select a custom size\. Note that job size must respect a minimum value which is enforced transparently\. This minimum is either 512 KB, or \fBoverlapSize\fR, whichever is largest\. Different job sizes will lead to (slightly) different compressed frames\.
-.
+### \-B#: Specify the size of each compression job\. This parameter is only available when multi\-threading is enabled\. Each compression job is run in parallel, so this value indirectly impacts the nb of active threads\. Default job size varies depending on compression level (generally \fB4 * windowSize\fR)\. \fB\-B#\fR makes it possible to manually select a custom size\. Note that job size must respect a minimum value which is enforced transparently\. This minimum is either 512 KB, or \fBoverlapSize\fR, whichever is largest\. Different job sizes will lead to non\-identical compressed frames\.
.SS "\-\-zstd[=options]:"
\fBzstd\fR provides 22 predefined compression levels\. The selected or default predefined compression level can be changed with advanced compression options\. The \fIoptions\fR are provided as a comma\-separated list\. You may specify only the options you want to change and the rest will be taken from the selected or default compression level\. The list of available \fIoptions\fR:
-.
.TP
\fBstrategy\fR=\fIstrat\fR, \fBstrat\fR=\fIstrat\fR
Specify a strategy used by a match finder\.
-.
.IP
There are 9 strategies numbered from 1 to 9, from faster to stronger: 1=ZSTD_fast, 2=ZSTD_dfast, 3=ZSTD_greedy, 4=ZSTD_lazy, 5=ZSTD_lazy2, 6=ZSTD_btlazy2, 7=ZSTD_btopt, 8=ZSTD_btultra, 9=ZSTD_btultra2\.
-.
.TP
\fBwindowLog\fR=\fIwlog\fR, \fBwlog\fR=\fIwlog\fR
Specify the maximum number of bits for a match distance\.
-.
.IP
The higher number of increases the chance to find a match which usually improves compression ratio\. It also increases memory requirements for the compressor and decompressor\. The minimum \fIwlog\fR is 10 (1 KiB) and the maximum is 30 (1 GiB) on 32\-bit platforms and 31 (2 GiB) on 64\-bit platforms\.
-.
.IP
Note: If \fBwindowLog\fR is set to larger than 27, \fB\-\-long=windowLog\fR or \fB\-\-memory=windowSize\fR needs to be passed to the decompressor\.
-.
.TP
\fBhashLog\fR=\fIhlog\fR, \fBhlog\fR=\fIhlog\fR
Specify the maximum number of bits for a hash table\.
-.
.IP
Bigger hash tables cause fewer collisions which usually makes compression faster, but requires more memory during compression\.
-.
.IP
The minimum \fIhlog\fR is 6 (64 B) and the maximum is 30 (1 GiB)\.
-.
.TP
\fBchainLog\fR=\fIclog\fR, \fBclog\fR=\fIclog\fR
Specify the maximum number of bits for a hash chain or a binary tree\.
-.
.IP
Higher numbers of bits increases the chance to find a match which usually improves compression ratio\. It also slows down compression speed and increases memory requirements for compression\. This option is ignored for the ZSTD_fast strategy\.
-.
.IP
The minimum \fIclog\fR is 6 (64 B) and the maximum is 29 (524 Mib) on 32\-bit platforms and 30 (1 Gib) on 64\-bit platforms\.
-.
.TP
\fBsearchLog\fR=\fIslog\fR, \fBslog\fR=\fIslog\fR
Specify the maximum number of searches in a hash chain or a binary tree using logarithmic scale\.
-.
.IP
More searches increases the chance to find a match which usually increases compression ratio but decreases compression speed\.
-.
.IP
The minimum \fIslog\fR is 1 and the maximum is \'windowLog\' \- 1\.
-.
.TP
\fBminMatch\fR=\fImml\fR, \fBmml\fR=\fImml\fR
Specify the minimum searched length of a match in a hash table\.
-.
.IP
Larger search lengths usually decrease compression ratio but improve decompression speed\.
-.
.IP
The minimum \fImml\fR is 3 and the maximum is 7\.
-.
.TP
\fBtargetLength\fR=\fItlen\fR, \fBtlen\fR=\fItlen\fR
The impact of this field vary depending on selected strategy\.
-.
.IP
For ZSTD_btopt, ZSTD_btultra and ZSTD_btultra2, it specifies the minimum match length that causes match finder to stop searching\. A larger \fBtargetLength\fR usually improves compression ratio but decreases compression speed\. t For ZSTD_fast, it triggers ultra\-fast mode when > 0\. The value represents the amount of data skipped between match sampling\. Impact is reversed : a larger \fBtargetLength\fR increases compression speed but decreases compression ratio\.
-.
.IP
For all other strategies, this field has no impact\.
-.
.IP
The minimum \fItlen\fR is 0 and the maximum is 128 Kib\.
-.
.TP
\fBoverlapLog\fR=\fIovlog\fR, \fBovlog\fR=\fIovlog\fR
Determine \fBoverlapSize\fR, amount of data reloaded from previous job\. This parameter is only available when multithreading is enabled\. Reloading more data improves compression ratio, but decreases speed\.
-.
.IP
The minimum \fIovlog\fR is 0, and the maximum is 9\. 1 means "no overlap", hence completely independent jobs\. 9 means "full overlap", meaning up to \fBwindowSize\fR is reloaded from previous job\. Reducing \fIovlog\fR by 1 reduces the reloaded amount by a factor 2\. For example, 8 means "windowSize/2", and 6 means "windowSize/8"\. Value 0 is special and means "default" : \fIovlog\fR is automatically determined by \fBzstd\fR\. In which case, \fIovlog\fR will range from 6 to 9, depending on selected \fIstrat\fR\.
-.
.TP
\fBldmHashLog\fR=\fIlhlog\fR, \fBlhlog\fR=\fIlhlog\fR
Specify the maximum size for a hash table used for long distance matching\.
-.
.IP
This option is ignored unless long distance matching is enabled\.
-.
.IP
Bigger hash tables usually improve compression ratio at the expense of more memory during compression and a decrease in compression speed\.
-.
.IP
The minimum \fIlhlog\fR is 6 and the maximum is 30 (default: 20)\.
-.
.TP
\fBldmMinMatch\fR=\fIlmml\fR, \fBlmml\fR=\fIlmml\fR
Specify the minimum searched length of a match for long distance matching\.
-.
.IP
This option is ignored unless long distance matching is enabled\.
-.
.IP
Larger/very small values usually decrease compression ratio\.
-.
.IP
The minimum \fIlmml\fR is 4 and the maximum is 4096 (default: 64)\.
-.
.TP
\fBldmBucketSizeLog\fR=\fIlblog\fR, \fBlblog\fR=\fIlblog\fR
Specify the size of each bucket for the hash table used for long distance matching\.
-.
.IP
This option is ignored unless long distance matching is enabled\.
-.
.IP
Larger bucket sizes improve collision resolution but decrease compression speed\.
-.
.IP
The minimum \fIlblog\fR is 1 and the maximum is 8 (default: 3)\.
-.
.TP
\fBldmHashRateLog\fR=\fIlhrlog\fR, \fBlhrlog\fR=\fIlhrlog\fR
Specify the frequency of inserting entries into the long distance matching hash table\.
-.
.IP
This option is ignored unless long distance matching is enabled\.
-.
.IP
Larger values will improve compression speed\. Deviating far from the default value will likely result in a decrease in compression ratio\.
-.
.IP
The default value is \fBwlog \- lhlog\fR\.
-.
.SS "Example"
The following parameters sets advanced compression options to something similar to predefined level 19 for files bigger than 256 KB:
-.
.P
\fB\-\-zstd\fR=wlog=23,clog=23,hlog=22,slog=6,mml=3,tlen=48,strat=6
-.
.SH "BUGS"
Report bugs at: https://github\.com/facebook/zstd/issues
-.
.SH "AUTHOR"
Yann Collet
diff --git a/programs/zstdgrep.1 b/programs/zstdgrep.1
index 563696d339e..4ece2ae5011 100644
--- a/programs/zstdgrep.1
+++ b/programs/zstdgrep.1
@@ -1,26 +1,17 @@
-.
-.TH "ZSTDGREP" "1" "January 2022" "zstd 1.5.2" "User Commands"
-.
+.TH "ZSTDGREP" "1" "April 2022" "zstd 1.5.2" "User Commands"
.SH "NAME"
\fBzstdgrep\fR \- print lines matching a pattern in zstandard\-compressed files
-.
.SH "SYNOPSIS"
-\fBzstdgrep\fR [\fIgrep\-flags\fR] [\-\-] \fIpattern\fR [\fIfiles\fR \.\.\.]
-.
+\fBzstdgrep\fR [\fIgrep\-flags\fR] [\-\-] \fIpattern\fR [\fIfiles\fR \|\.\|\.\|\.]
.SH "DESCRIPTION"
\fBzstdgrep\fR runs \fBgrep (1)\fR on files, or \fBstdin\fR if no files argument is given, after decompressing them with \fBzstdcat (1)\fR\.
-.
.P
The grep\-flags and pattern arguments are passed on to \fBgrep (1)\fR\. If an \fB\-e\fR flag is found in the \fBgrep\-flags\fR, \fBzstdgrep\fR will not look for a pattern argument\.
-.
.P
Note that modern \fBgrep\fR alternatives such as \fBripgrep\fR (\fBrg\fR) support \fBzstd\fR\-compressed files out of the box, and can prove better alternatives than \fBzstdgrep\fR notably for unsupported complex pattern searches\. Note though that such alternatives may also feature some minor command line differences\.
-.
.SH "EXIT STATUS"
In case of missing arguments or missing pattern, 1 will be returned, otherwise 0\.
-.
.SH "SEE ALSO"
\fBzstd (1)\fR
-.
.SH "AUTHORS"
Thomas Klausner \fIwiz@NetBSD\.org\fR
diff --git a/programs/zstdless.1 b/programs/zstdless.1
index ab38e7a7f45..36f21494d08 100644
--- a/programs/zstdless.1
+++ b/programs/zstdless.1
@@ -1,14 +1,9 @@
-.
-.TH "ZSTDLESS" "1" "January 2022" "zstd 1.5.2" "User Commands"
-.
+.TH "ZSTDLESS" "1" "April 2022" "zstd 1.5.2" "User Commands"
.SH "NAME"
\fBzstdless\fR \- view zstandard\-compressed files
-.
.SH "SYNOPSIS"
-\fBzstdless\fR [\fIflags\fR] [\fIfile\fR \.\.\.]
-.
+\fBzstdless\fR [\fIflags\fR] [\fIfile\fR \|\.\|\.\|\.]
.SH "DESCRIPTION"
\fBzstdless\fR runs \fBless (1)\fR on files or stdin, if no files argument is given, after decompressing them with \fBzstdcat (1)\fR\.
-.
.SH "SEE ALSO"
\fBzstd (1)\fR
From 3536262f70abddae45f45455d3735d29282100f6 Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Thu, 14 Apr 2022 14:19:19 -0400
Subject: [PATCH 120/472] Port noDict pipeline
---
lib/compress/zstd_fast.c | 275 ++++++++++++++++++++++++++++++---------
1 file changed, 211 insertions(+), 64 deletions(-)
diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c
index 5da108c622a..7b1d0dd43e6 100644
--- a/lib/compress/zstd_fast.c
+++ b/lib/compress/zstd_fast.c
@@ -587,11 +587,10 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
U32* const hashTable = ms->hashTable;
U32 const hlog = cParams->hashLog;
/* support stepSize of 0 */
- U32 const stepSize = cParams->targetLength + !(cParams->targetLength);
+ U32 const stepSize = cParams->targetLength + !(cParams->targetLength) + 1;
const BYTE* const base = ms->window.base;
const BYTE* const dictBase = ms->window.dictBase;
const BYTE* const istart = (const BYTE*)src;
- const BYTE* ip = istart;
const BYTE* anchor = istart;
const U32 endIndex = (U32)((size_t)(istart - base) + srcSize);
const U32 lowLimit = ZSTD_getLowestMatchIndex(ms, endIndex, cParams->windowLog);
@@ -604,6 +603,29 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
const BYTE* const iend = istart + srcSize;
const BYTE* const ilimit = iend - 8;
U32 offset_1=rep[0], offset_2=rep[1];
+ U32 offsetSaved = 0;
+
+ const BYTE* ip0 = istart;
+ const BYTE* ip1;
+ const BYTE* ip2;
+ const BYTE* ip3;
+ U32 current0;
+
+
+ size_t hash0; /* hash for ip0 */
+ size_t hash1; /* hash for ip1 */
+ U32 idx; /* match idx for ip0 */
+ const BYTE* idxBase; /* base pointer for idx */
+ U32 mval; /* src or dict value at match idx */
+
+ U32 offcode;
+ const BYTE* match0;
+ size_t mLength;
+ const BYTE* matchEnd = 0; /* initialize to avoid warning, assert != 0 later */
+
+ size_t step;
+ const BYTE* nextStep;
+ const size_t kStepIncr = (1 << (kSearchStrength - 1));
(void)hasStep; /* not currently specialized on whether it's accelerated */
@@ -613,75 +635,200 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
if (prefixStartIndex == dictStartIndex)
return ZSTD_compressBlock_fast(ms, seqStore, rep, src, srcSize);
- /* Search Loop */
- while (ip < ilimit) { /* < instead of <=, because (ip+1) */
- const size_t h = ZSTD_hashPtr(ip, hlog, mls);
- const U32 matchIndex = hashTable[h];
- const BYTE* const matchBase = matchIndex < prefixStartIndex ? dictBase : base;
- const BYTE* match = matchBase + matchIndex;
- const U32 curr = (U32)(ip-base);
- const U32 repIndex = curr + 1 - offset_1;
+ /* start each op */
+_start: /* Requires: ip0 */
+
+ step = stepSize;
+ nextStep = ip0 + kStepIncr;
+
+ /* calculate positions, ip0 - anchor == 0, so we skip step calc */
+ ip1 = ip0 + 1;
+ ip2 = ip0 + step;
+ ip3 = ip2 + 1;
+
+ if (ip3 >= ilimit) {
+ goto _cleanup;
+ }
+
+ hash0 = ZSTD_hashPtr(ip0, hlog, mls);
+ hash1 = ZSTD_hashPtr(ip1, hlog, mls);
+
+ idx = hashTable[hash0];
+ idxBase = idx < prefixStartIndex ? dictBase : base;
+
+ do {
+ /* load repcode match for ip[2]*/
+ const U32 current2 = (U32)(ip2 - base);
+ const U32 repIndex = current2 - offset_1;
const BYTE* const repBase = repIndex < prefixStartIndex ? dictBase : base;
- const BYTE* const repMatch = repBase + repIndex;
- hashTable[h] = curr; /* update hash table */
- DEBUGLOG(7, "offset_1 = %u , curr = %u", offset_1, curr);
-
- if ( ( ((U32)((prefixStartIndex-1) - repIndex) >= 3) /* intentional underflow */
- & (offset_1 <= curr+1 - dictStartIndex) ) /* note: we are searching at curr+1 */
- && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) {
- const BYTE* const repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend;
- size_t const rLength = ZSTD_count_2segments(ip+1 +4, repMatch +4, iend, repMatchEnd, prefixStart) + 4;
- ip++;
- ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, REPCODE1_TO_OFFBASE, rLength);
- ip += rLength;
- anchor = ip;
+ U32 rval;
+
+ /* load repcode match for ip[2] */
+ assert(offset_1 > 0);
+ if ( ( ((U32)(prefixStartIndex - repIndex) >= 4) /* intentional underflow */
+ & (offset_1 < current2 - dictStartIndex) ) ) {
+ rval = MEM_read32(repBase + repIndex);
} else {
- if ( (matchIndex < dictStartIndex) ||
- (MEM_read32(match) != MEM_read32(ip)) ) {
- assert(stepSize >= 1);
- ip += ((ip-anchor) >> kSearchStrength) + stepSize;
- continue;
- }
- { const BYTE* const matchEnd = matchIndex < prefixStartIndex ? dictEnd : iend;
- const BYTE* const lowMatchPtr = matchIndex < prefixStartIndex ? dictStart : prefixStart;
- U32 const offset = curr - matchIndex;
- size_t mLength = ZSTD_count_2segments(ip+4, match+4, iend, matchEnd, prefixStart) + 4;
- while (((ip>anchor) & (match>lowMatchPtr)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */
- offset_2 = offset_1; offset_1 = offset; /* update offset history */
- ZSTD_storeSeq(seqStore, (size_t)(ip-anchor), anchor, iend, OFFSET_TO_OFFBASE(offset), mLength);
- ip += mLength;
- anchor = ip;
- } }
-
- if (ip <= ilimit) {
- /* Fill Table */
- hashTable[ZSTD_hashPtr(base+curr+2, hlog, mls)] = curr+2;
- hashTable[ZSTD_hashPtr(ip-2, hlog, mls)] = (U32)(ip-2-base);
- /* check immediate repcode */
- while (ip <= ilimit) {
- U32 const current2 = (U32)(ip-base);
- U32 const repIndex2 = current2 - offset_2;
- const BYTE* const repMatch2 = repIndex2 < prefixStartIndex ? dictBase + repIndex2 : base + repIndex2;
- if ( (((U32)((prefixStartIndex-1) - repIndex2) >= 3) & (offset_2 <= curr - dictStartIndex)) /* intentional overflow */
- && (MEM_read32(repMatch2) == MEM_read32(ip)) ) {
- const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend;
- size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixStart) + 4;
- { U32 const tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; } /* swap offset_2 <=> offset_1 */
- ZSTD_storeSeq(seqStore, 0 /*litlen*/, anchor, iend, REPCODE1_TO_OFFBASE, repLength2);
- hashTable[ZSTD_hashPtr(ip, hlog, mls)] = current2;
- ip += repLength2;
- anchor = ip;
- continue;
- }
- break;
- } } }
+ rval = MEM_read32(ip2) ^ 1; /* guaranteed to not match. */
+ }
+
+ /* write back hash table entry */
+ current0 = (U32)(ip0 - base);
+ hashTable[hash0] = current0;
+
+ /* check repcode at ip[2] */
+ if (MEM_read32(ip2) == rval) {
+ ip0 = ip2;
+ match0 = repBase + repIndex;
+ matchEnd = repIndex < prefixStartIndex ? dictEnd : iend;
+ assert((match0 != prefixStart) & (match0 != dictStart));
+ mLength = ip0[-1] == match0[-1];
+ ip0 -= mLength;
+ match0 -= mLength;
+ offcode = REPCODE1_TO_OFFBASE;
+ mLength += 4;
+ goto _match;
+ }
+
+ /* load match for ip[0] */
+ if (idx >= dictStartIndex) {
+ mval = MEM_read32(idxBase + idx);
+ } else {
+ mval = MEM_read32(ip0) ^ 1; /* guaranteed to not match. */
+ }
+
+ /* check match at ip[0] */
+ if (MEM_read32(ip0) == mval) {
+ /* found a match! */
+ goto _offset;
+ }
+
+ /* lookup ip[1] */
+ idx = hashTable[hash1];
+ idxBase = idx < prefixStartIndex ? dictBase : base;
+
+ /* hash ip[2] */
+ hash0 = hash1;
+ hash1 = ZSTD_hashPtr(ip2, hlog, mls);
+
+ /* advance to next positions */
+ ip0 = ip1;
+ ip1 = ip2;
+ ip2 = ip3;
+
+ /* write back hash table entry */
+ current0 = (U32)(ip0 - base);
+ hashTable[hash0] = current0;
+
+ /* load match for ip[0] */
+ if (idx >= dictStartIndex) {
+ mval = MEM_read32(idxBase + idx);
+ } else {
+ mval = MEM_read32(ip0) ^ 1; /* guaranteed to not match. */
+ }
+
+ /* check match at ip[0] */
+ if (MEM_read32(ip0) == mval) {
+ /* found a match! */
+ goto _offset;
+ }
+
+ /* lookup ip[1] */
+ idx = hashTable[hash1];
+ idxBase = idx < prefixStartIndex ? dictBase : base;
+
+ /* hash ip[2] */
+ hash0 = hash1;
+ hash1 = ZSTD_hashPtr(ip2, hlog, mls);
+
+ /* advance to next positions */
+ ip0 = ip1;
+ ip1 = ip2;
+ ip2 = ip0 + step;
+ ip3 = ip1 + step;
+
+ /* calculate step */
+ if (ip2 >= nextStep) {
+ step++;
+ PREFETCH_L1(ip1 + 64);
+ PREFETCH_L1(ip1 + 128);
+ nextStep += kStepIncr;
+ }
+ } while (ip3 < ilimit);
+
+_cleanup:
+ /* Note that there are probably still a couple positions we could search.
+ * However, it seems to be a meaningful performance hit to try to search
+ * them. So let's not. */
/* save reps for next block */
- rep[0] = offset_1;
- rep[1] = offset_2;
+ rep[0] = offset_1 ? offset_1 : offsetSaved;
+ rep[1] = offset_2 ? offset_2 : offsetSaved;
/* Return the last literals size */
return (size_t)(iend - anchor);
+
+_offset: /* Requires: ip0, idx */
+
+ /* Compute the offset code. */
+ { U32 const offset = current0 - idx;
+ const BYTE* const lowMatchPtr = idx < prefixStartIndex ? dictStart : prefixStart;
+ matchEnd = idx < prefixStartIndex ? dictEnd : iend;
+ match0 = idxBase + idx;
+ offset_2 = offset_1;
+ offset_1 = offset;
+ offcode = OFFSET_TO_OFFBASE(offset);
+ mLength = 4;
+
+ /* Count the backwards match length. */
+ while (((ip0>anchor) & (match0>lowMatchPtr)) && (ip0[-1] == match0[-1])) {
+ ip0--;
+ match0--;
+ mLength++;
+ } }
+
+_match: /* Requires: ip0, match0, offcode */
+
+ /* Count the forward length. */
+ assert(matchEnd != 0);
+ mLength += ZSTD_count_2segments(ip0 + mLength, match0 + mLength, iend, matchEnd, prefixStart);
+
+ ZSTD_storeSeq(seqStore, (size_t)(ip0 - anchor), anchor, iend, offcode, mLength);
+
+ ip0 += mLength;
+ anchor = ip0;
+
+ /* write next hash table entry */
+ if (ip1 < ip0) {
+ hashTable[hash1] = (U32)(ip1 - base);
+ }
+
+ /* Fill table and check for immediate repcode. */
+ if (ip0 <= ilimit) {
+ /* Fill Table */
+ assert(base+current0+2 > istart); /* check base overflow */
+ hashTable[ZSTD_hashPtr(base+current0+2, hlog, mls)] = current0+2; /* here because current+2 could be > iend-8 */
+ hashTable[ZSTD_hashPtr(ip0-2, hlog, mls)] = (U32)(ip0-2-base);
+
+ assert(offset_2 > 0);
+ while (ip0 <= ilimit) {
+ U32 const repIndex2 = (U32)(ip0-base) - offset_2;
+ const BYTE* const repMatch2 = repIndex2 < prefixStartIndex ? dictBase + repIndex2 : base + repIndex2;
+ if ( (((U32)((prefixStartIndex-1) - repIndex2) >= 3) & (offset_2 <= (U32)(ip0-base) - dictStartIndex)) /* intentional overflow */
+ && (MEM_read32(repMatch2) == MEM_read32(ip0)) ) {
+ const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend;
+ size_t const repLength2 = ZSTD_count_2segments(ip0+4, repMatch2+4, iend, repEnd2, prefixStart) + 4;
+ { U32 const tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; } /* swap offset_2 <=> offset_1 */
+ ZSTD_storeSeq(seqStore, 0 /*litlen*/, anchor, iend, REPCODE1_TO_OFFBASE, repLength2);
+ hashTable[ZSTD_hashPtr(ip0, hlog, mls)] = (U32)(ip0-base);
+ ip0 += repLength2;
+ anchor = ip0;
+ continue;
+ }
+ break;
+ } }
+
+ goto _start;
}
ZSTD_GEN_FAST_FN(extDict, 4, 0)
From 2820efe7ec931906ce052771afc04ddd12a8cfa8 Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Tue, 19 Apr 2022 11:36:06 -0400
Subject: [PATCH 121/472] Nits
---
lib/compress/zstd_fast.c | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c
index 7b1d0dd43e6..e201e862048 100644
--- a/lib/compress/zstd_fast.c
+++ b/lib/compress/zstd_fast.c
@@ -603,7 +603,6 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
const BYTE* const iend = istart + srcSize;
const BYTE* const ilimit = iend - 8;
U32 offset_1=rep[0], offset_2=rep[1];
- U32 offsetSaved = 0;
const BYTE* ip0 = istart;
const BYTE* ip1;
@@ -616,7 +615,6 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
size_t hash1; /* hash for ip1 */
U32 idx; /* match idx for ip0 */
const BYTE* idxBase; /* base pointer for idx */
- U32 mval; /* src or dict value at match idx */
U32 offcode;
const BYTE* match0;
@@ -657,13 +655,13 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
idxBase = idx < prefixStartIndex ? dictBase : base;
do {
- /* load repcode match for ip[2]*/
+ U32 mval; /* src or dict value at match idx */
+
+ /* load repcode match for ip[2] */
const U32 current2 = (U32)(ip2 - base);
const U32 repIndex = current2 - offset_1;
const BYTE* const repBase = repIndex < prefixStartIndex ? dictBase : base;
U32 rval;
-
- /* load repcode match for ip[2] */
assert(offset_1 > 0);
if ( ( ((U32)(prefixStartIndex - repIndex) >= 4) /* intentional underflow */
& (offset_1 < current2 - dictStartIndex) ) ) {
@@ -762,13 +760,13 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
* them. So let's not. */
/* save reps for next block */
- rep[0] = offset_1 ? offset_1 : offsetSaved;
- rep[1] = offset_2 ? offset_2 : offsetSaved;
+ rep[0] = offset_1;
+ rep[1] = offset_2;
/* Return the last literals size */
return (size_t)(iend - anchor);
-_offset: /* Requires: ip0, idx */
+_offset: /* Requires: ip0, idx, idxBase */
/* Compute the offset code. */
{ U32 const offset = current0 - idx;
@@ -787,7 +785,7 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
mLength++;
} }
-_match: /* Requires: ip0, match0, offcode */
+_match: /* Requires: ip0, match0, offcode, matchEnd */
/* Count the forward length. */
assert(matchEnd != 0);
From 809f65291266de966ba4262220992f5f7e7903a0 Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Wed, 20 Apr 2022 11:50:00 -0400
Subject: [PATCH 122/472] Optimize repcode predicate, hardcode hasStep == 0
scenario, cosmetic fixes
---
lib/compress/zstd_fast.c | 157 +++++++++++++++++++++------------------
1 file changed, 84 insertions(+), 73 deletions(-)
diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c
index e201e862048..c6e9dd33aaa 100644
--- a/lib/compress/zstd_fast.c
+++ b/lib/compress/zstd_fast.c
@@ -587,7 +587,7 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
U32* const hashTable = ms->hashTable;
U32 const hlog = cParams->hashLog;
/* support stepSize of 0 */
- U32 const stepSize = cParams->targetLength + !(cParams->targetLength) + 1;
+ size_t const stepSize = hasStep ? (cParams->targetLength + !(cParams->targetLength) + 1) : 2;
const BYTE* const base = ms->window.base;
const BYTE* const dictBase = ms->window.dictBase;
const BYTE* const istart = (const BYTE*)src;
@@ -625,14 +625,18 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
const BYTE* nextStep;
const size_t kStepIncr = (1 << (kSearchStrength - 1));
- (void)hasStep; /* not currently specialized on whether it's accelerated */
-
DEBUGLOG(5, "ZSTD_compressBlock_fast_extDict_generic (offset_1=%u)", offset_1);
/* switch to "regular" variant if extDict is invalidated due to maxDistance */
if (prefixStartIndex == dictStartIndex)
return ZSTD_compressBlock_fast(ms, seqStore, rep, src, srcSize);
+ { U32 const curr = (U32)(ip0 - base);
+ U32 const maxRep = curr - dictStartIndex;
+ if (offset_2 >= maxRep) offset_2 = 0;
+ if (offset_1 >= maxRep) offset_1 = 0;
+ }
+
/* start each op */
_start: /* Requires: ip0 */
@@ -655,51 +659,44 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
idxBase = idx < prefixStartIndex ? dictBase : base;
do {
- U32 mval; /* src or dict value at match idx */
-
- /* load repcode match for ip[2] */
- const U32 current2 = (U32)(ip2 - base);
- const U32 repIndex = current2 - offset_1;
- const BYTE* const repBase = repIndex < prefixStartIndex ? dictBase : base;
- U32 rval;
- assert(offset_1 > 0);
- if ( ( ((U32)(prefixStartIndex - repIndex) >= 4) /* intentional underflow */
- & (offset_1 < current2 - dictStartIndex) ) ) {
- rval = MEM_read32(repBase + repIndex);
- } else {
- rval = MEM_read32(ip2) ^ 1; /* guaranteed to not match. */
- }
-
- /* write back hash table entry */
- current0 = (U32)(ip0 - base);
- hashTable[hash0] = current0;
-
- /* check repcode at ip[2] */
- if (MEM_read32(ip2) == rval) {
- ip0 = ip2;
- match0 = repBase + repIndex;
- matchEnd = repIndex < prefixStartIndex ? dictEnd : iend;
- assert((match0 != prefixStart) & (match0 != dictStart));
- mLength = ip0[-1] == match0[-1];
- ip0 -= mLength;
- match0 -= mLength;
- offcode = REPCODE1_TO_OFFBASE;
- mLength += 4;
- goto _match;
- }
+ { /* load repcode match for ip[2] */
+ U32 const current2 = (U32)(ip2 - base);
+ U32 const repIndex = current2 - offset_1;
+ const BYTE* const repBase = repIndex < prefixStartIndex ? dictBase : base;
+ U32 rval;
+ if ( ((U32)(prefixStartIndex - repIndex) >= 4) /* intentional underflow */
+ & (offset_1 > 0) ) {
+ rval = MEM_read32(repBase + repIndex);
+ } else {
+ rval = MEM_read32(ip2) ^ 1; /* guaranteed to not match. */
+ }
- /* load match for ip[0] */
- if (idx >= dictStartIndex) {
- mval = MEM_read32(idxBase + idx);
- } else {
- mval = MEM_read32(ip0) ^ 1; /* guaranteed to not match. */
- }
-
- /* check match at ip[0] */
- if (MEM_read32(ip0) == mval) {
- /* found a match! */
- goto _offset;
- }
+ /* write back hash table entry */
+ current0 = (U32)(ip0 - base);
+ hashTable[hash0] = current0;
+
+ /* check repcode at ip[2] */
+ if (MEM_read32(ip2) == rval) {
+ ip0 = ip2;
+ match0 = repBase + repIndex;
+ matchEnd = repIndex < prefixStartIndex ? dictEnd : iend;
+ assert((match0 != prefixStart) & (match0 != dictStart));
+ mLength = ip0[-1] == match0[-1];
+ ip0 -= mLength;
+ match0 -= mLength;
+ offcode = REPCODE1_TO_OFFBASE;
+ mLength += 4;
+ goto _match;
+ } }
+
+ { /* load match for ip[0] */
+ U32 const mval = idx >= dictStartIndex ? MEM_read32(idxBase + idx) : MEM_read32(ip0) ^ 1; /* guaranteed not to match */
+
+ /* check match at ip[0] */
+ if (MEM_read32(ip0) == mval) {
+ /* found a match! */
+ goto _offset;
+ } }
/* lookup ip[1] */
idx = hashTable[hash1];
@@ -718,18 +715,14 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
current0 = (U32)(ip0 - base);
hashTable[hash0] = current0;
- /* load match for ip[0] */
- if (idx >= dictStartIndex) {
- mval = MEM_read32(idxBase + idx);
- } else {
- mval = MEM_read32(ip0) ^ 1; /* guaranteed to not match. */
- }
+ { /* load match for ip[0] */
+ U32 const mval = idx >= dictStartIndex ? MEM_read32(idxBase + idx) : MEM_read32(ip0) ^ 1; /* guaranteed not to match */
- /* check match at ip[0] */
- if (MEM_read32(ip0) == mval) {
- /* found a match! */
- goto _offset;
- }
+ /* check match at ip[0] */
+ if (MEM_read32(ip0) == mval) {
+ /* found a match! */
+ goto _offset;
+ } }
/* lookup ip[1] */
idx = hashTable[hash1];
@@ -760,8 +753,8 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
* them. So let's not. */
/* save reps for next block */
- rep[0] = offset_1;
- rep[1] = offset_2;
+ rep[0] = offset_1 ? offset_1 : rep[0];
+ rep[1] = offset_2 ? offset_2 : rep[1];
/* Return the last literals size */
return (size_t)(iend - anchor);
@@ -808,11 +801,10 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
hashTable[ZSTD_hashPtr(base+current0+2, hlog, mls)] = current0+2; /* here because current+2 could be > iend-8 */
hashTable[ZSTD_hashPtr(ip0-2, hlog, mls)] = (U32)(ip0-2-base);
- assert(offset_2 > 0);
while (ip0 <= ilimit) {
U32 const repIndex2 = (U32)(ip0-base) - offset_2;
const BYTE* const repMatch2 = repIndex2 < prefixStartIndex ? dictBase + repIndex2 : base + repIndex2;
- if ( (((U32)((prefixStartIndex-1) - repIndex2) >= 3) & (offset_2 <= (U32)(ip0-base) - dictStartIndex)) /* intentional overflow */
+ if ( (((U32)((prefixStartIndex-1) - repIndex2) >= 3) & (offset_2 > 0)) /* intentional underflow */
&& (MEM_read32(repMatch2) == MEM_read32(ip0)) ) {
const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend;
size_t const repLength2 = ZSTD_count_2segments(ip0+4, repMatch2+4, iend, repEnd2, prefixStart) + 4;
@@ -829,6 +821,11 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
goto _start;
}
+ZSTD_GEN_FAST_FN(extDict, 4, 1)
+ZSTD_GEN_FAST_FN(extDict, 5, 1)
+ZSTD_GEN_FAST_FN(extDict, 6, 1)
+ZSTD_GEN_FAST_FN(extDict, 7, 1)
+
ZSTD_GEN_FAST_FN(extDict, 4, 0)
ZSTD_GEN_FAST_FN(extDict, 5, 0)
ZSTD_GEN_FAST_FN(extDict, 6, 0)
@@ -839,16 +836,30 @@ size_t ZSTD_compressBlock_fast_extDict(
void const* src, size_t srcSize)
{
U32 const mls = ms->cParams.minMatch;
- switch(mls)
- {
- default: /* includes case 3 */
- case 4 :
- return ZSTD_compressBlock_fast_extDict_4_0(ms, seqStore, rep, src, srcSize);
- case 5 :
- return ZSTD_compressBlock_fast_extDict_5_0(ms, seqStore, rep, src, srcSize);
- case 6 :
- return ZSTD_compressBlock_fast_extDict_6_0(ms, seqStore, rep, src, srcSize);
- case 7 :
- return ZSTD_compressBlock_fast_extDict_7_0(ms, seqStore, rep, src, srcSize);
+ assert(ms->dictMatchState == NULL);
+ if (ms->cParams.targetLength > 1) {
+ switch (mls) {
+ default: /* includes case 3 */
+ case 4 :
+ return ZSTD_compressBlock_fast_extDict_4_1(ms, seqStore, rep, src, srcSize);
+ case 5 :
+ return ZSTD_compressBlock_fast_extDict_5_1(ms, seqStore, rep, src, srcSize);
+ case 6 :
+ return ZSTD_compressBlock_fast_extDict_6_1(ms, seqStore, rep, src, srcSize);
+ case 7 :
+ return ZSTD_compressBlock_fast_extDict_7_1(ms, seqStore, rep, src, srcSize);
+ }
+ } else {
+ switch (mls) {
+ default: /* includes case 3 */
+ case 4 :
+ return ZSTD_compressBlock_fast_extDict_4_0(ms, seqStore, rep, src, srcSize);
+ case 5 :
+ return ZSTD_compressBlock_fast_extDict_5_0(ms, seqStore, rep, src, srcSize);
+ case 6 :
+ return ZSTD_compressBlock_fast_extDict_6_0(ms, seqStore, rep, src, srcSize);
+ case 7 :
+ return ZSTD_compressBlock_fast_extDict_7_0(ms, seqStore, rep, src, srcSize);
+ }
}
}
From 05796796fd0a9bc972abf3097557f68d6ae93ef4 Mon Sep 17 00:00:00 2001
From: cuishuang
Date: Tue, 26 Apr 2022 17:40:23 +0800
Subject: [PATCH 123/472] fix some typos
Signed-off-by: cuishuang
---
contrib/freestanding_lib/freestanding.py | 4 ++--
doc/decompressor_errata.md | 2 +-
lib/common/xxhash.h | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/contrib/freestanding_lib/freestanding.py b/contrib/freestanding_lib/freestanding.py
index cd9d6377498..4a02dea147b 100755
--- a/contrib/freestanding_lib/freestanding.py
+++ b/contrib/freestanding_lib/freestanding.py
@@ -340,7 +340,7 @@ def _preprocess_once(self):
if macro2 is not None and not resolved:
assert ifdef and defined and op == '&&' and cmp is not None
- # If the statment is true, but we have a single value check, then
+ # If the statement is true, but we have a single value check, then
# check the value.
defined_value = self._defs[macro]
are_ints = True
@@ -690,7 +690,7 @@ def main(name, args):
parser.add_argument("--rewrite-include", default=[], dest="rewritten_includes", action="append", help="Rewrite an include REGEX=NEW (e.g. '=')")
parser.add_argument("--sed", default=[], dest="seds", action="append", help="Apply a sed replacement. Format: `s/REGEX/FORMAT/[g]`. REGEX is a Python regex. FORMAT is a Python format string formatted by the regex dict.")
parser.add_argument("-D", "--define", default=[], dest="defs", action="append", help="Pre-define this macro (can be passed multiple times)")
- parser.add_argument("-U", "--undefine", default=[], dest="undefs", action="append", help="Pre-undefine this macro (can be passed mutliple times)")
+ parser.add_argument("-U", "--undefine", default=[], dest="undefs", action="append", help="Pre-undefine this macro (can be passed multiple times)")
parser.add_argument("-R", "--replace", default=[], dest="replaces", action="append", help="Pre-define this macro and replace the first ifndef block with its definition")
parser.add_argument("-E", "--exclude", default=[], dest="excludes", action="append", help="Exclude all lines between 'BEGIN ' and 'END '")
args = parser.parse_args(args)
diff --git a/doc/decompressor_errata.md b/doc/decompressor_errata.md
index 9c11264018d..b162e7fd6e7 100644
--- a/doc/decompressor_errata.md
+++ b/doc/decompressor_errata.md
@@ -27,7 +27,7 @@ The zstd decoder incorrectly rejected blocks of type `Compressed_Block` that enc
This type of block was never generated by the reference compressor.
-Additionally, these blocks were disallowed by the spec up until spec version 0.3.2 when the restriciton was lifted by [PR#1689](https://github.com/facebook/zstd/pull/1689).
+Additionally, these blocks were disallowed by the spec up until spec version 0.3.2 when the restriction was lifted by [PR#1689](https://github.com/facebook/zstd/pull/1689).
> A Compressed_Block has the extra restriction that Block_Size is always strictly less than the decompressed size. If this condition cannot be respected, the block must be sent uncompressed instead (Raw_Block).
diff --git a/lib/common/xxhash.h b/lib/common/xxhash.h
index acac7ad62ed..f87102a1d49 100644
--- a/lib/common/xxhash.h
+++ b/lib/common/xxhash.h
@@ -3026,7 +3026,7 @@ enum XXH_VECTOR_TYPE /* fake enum */ {
* have more than 2 NEON (F0/F1) micro-ops. If you are only using NEON instructions,
* you are only using 2/3 of the CPU bandwidth.
*
- * This is even more noticable on the more advanced cores like the A76 which
+ * This is even more noticeable on the more advanced cores like the A76 which
* can dispatch 8 micro-ops per cycle, but still only 2 NEON micro-ops at once.
*
* Therefore, @ref XXH3_NEON_LANES lanes will be processed using NEON, and the
From 518cb83833074d304dfcaa93cfc16039ea4683c8 Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Tue, 26 Apr 2022 17:54:25 -0400
Subject: [PATCH 124/472] Hardcode repcode safety check, fix cosmetic nits
---
lib/compress/zstd_fast.c | 37 +++++++++++++++++++++++++++----------
1 file changed, 27 insertions(+), 10 deletions(-)
diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c
index c6e9dd33aaa..49a2add759c 100644
--- a/lib/compress/zstd_fast.c
+++ b/lib/compress/zstd_fast.c
@@ -581,13 +581,13 @@ size_t ZSTD_compressBlock_fast_dictMatchState(
static size_t ZSTD_compressBlock_fast_extDict_generic(
ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
- void const* src, size_t srcSize, U32 const mls, U32 const hasStep)
+ void const* src, size_t srcSize, U32 const mls, U32 const commonScenario)
{
const ZSTD_compressionParameters* const cParams = &ms->cParams;
U32* const hashTable = ms->hashTable;
U32 const hlog = cParams->hashLog;
/* support stepSize of 0 */
- size_t const stepSize = hasStep ? (cParams->targetLength + !(cParams->targetLength) + 1) : 2;
+ size_t const stepSize = commonScenario ? 2 : (cParams->targetLength + !(cParams->targetLength) + 1);
const BYTE* const base = ms->window.base;
const BYTE* const dictBase = ms->window.dictBase;
const BYTE* const istart = (const BYTE*)src;
@@ -633,9 +633,13 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
{ U32 const curr = (U32)(ip0 - base);
U32 const maxRep = curr - dictStartIndex;
- if (offset_2 >= maxRep) offset_2 = 0;
- if (offset_1 >= maxRep) offset_1 = 0;
- }
+ if (commonScenario) {
+ assert(offset_2 < maxRep);
+ assert(offset_1 < maxRep);
+ } else {
+ if (offset_2 >= maxRep) offset_2 = 0;
+ if (offset_1 >= maxRep) offset_1 = 0;
+ } }
/* start each op */
_start: /* Requires: ip0 */
@@ -665,7 +669,7 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
const BYTE* const repBase = repIndex < prefixStartIndex ? dictBase : base;
U32 rval;
if ( ((U32)(prefixStartIndex - repIndex) >= 4) /* intentional underflow */
- & (offset_1 > 0) ) {
+ & (commonScenario | (offset_1 > 0)) ) {
rval = MEM_read32(repBase + repIndex);
} else {
rval = MEM_read32(ip2) ^ 1; /* guaranteed to not match. */
@@ -690,7 +694,9 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
} }
{ /* load match for ip[0] */
- U32 const mval = idx >= dictStartIndex ? MEM_read32(idxBase + idx) : MEM_read32(ip0) ^ 1; /* guaranteed not to match */
+ U32 const mval = idx >= dictStartIndex ?
+ MEM_read32(idxBase + idx) :
+ MEM_read32(ip0) ^ 1; /* guaranteed not to match */
/* check match at ip[0] */
if (MEM_read32(ip0) == mval) {
@@ -716,7 +722,9 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
hashTable[hash0] = current0;
{ /* load match for ip[0] */
- U32 const mval = idx >= dictStartIndex ? MEM_read32(idxBase + idx) : MEM_read32(ip0) ^ 1; /* guaranteed not to match */
+ U32 const mval = idx >= dictStartIndex ?
+ MEM_read32(idxBase + idx) :
+ MEM_read32(ip0) ^ 1; /* guaranteed not to match */
/* check match at ip[0] */
if (MEM_read32(ip0) == mval) {
@@ -804,7 +812,7 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
while (ip0 <= ilimit) {
U32 const repIndex2 = (U32)(ip0-base) - offset_2;
const BYTE* const repMatch2 = repIndex2 < prefixStartIndex ? dictBase + repIndex2 : base + repIndex2;
- if ( (((U32)((prefixStartIndex-1) - repIndex2) >= 3) & (offset_2 > 0)) /* intentional underflow */
+ if ( (((U32)((prefixStartIndex-1) - repIndex2) >= 3) & (commonScenario | (offset_2 > 0))) /* intentional underflow */
&& (MEM_read32(repMatch2) == MEM_read32(ip0)) ) {
const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend;
size_t const repLength2 = ZSTD_count_2segments(ip0+4, repMatch2+4, iend, repEnd2, prefixStart) + 4;
@@ -836,8 +844,17 @@ size_t ZSTD_compressBlock_fast_extDict(
void const* src, size_t srcSize)
{
U32 const mls = ms->cParams.minMatch;
+ const BYTE* const istart = (const BYTE*)src;
+ const BYTE* const base = ms->window.base;
+ const U32 endIndex = (U32)((size_t)(istart - base) + srcSize);
+ const U32 lowLimit = ZSTD_getLowestMatchIndex(ms, endIndex, ms->cParams.windowLog);
+ U32 const curr = (U32)(istart - base);
+ U32 const maxRep = curr - lowLimit;
+ U32 const repOffsetsAreValid = (rep[0] < maxRep) & (rep[1] < maxRep);
+
+ assert((rep[0] > 0) & (rep[1] > 0));
assert(ms->dictMatchState == NULL);
- if (ms->cParams.targetLength > 1) {
+ if ((ms->cParams.targetLength <= 1) & repOffsetsAreValid) {
switch (mls) {
default: /* includes case 3 */
case 4 :
From 6a2e1f7c69f32427afc2f0273d3f4fe923a98a94 Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Wed, 27 Apr 2022 18:16:21 -0400
Subject: [PATCH 125/472] Revert "Hardcode repcode safety check, fix cosmetic
nits"
This reverts commit 518cb83833074d304dfcaa93cfc16039ea4683c8.
---
lib/compress/zstd_fast.c | 37 ++++++++++---------------------------
1 file changed, 10 insertions(+), 27 deletions(-)
diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c
index 49a2add759c..c6e9dd33aaa 100644
--- a/lib/compress/zstd_fast.c
+++ b/lib/compress/zstd_fast.c
@@ -581,13 +581,13 @@ size_t ZSTD_compressBlock_fast_dictMatchState(
static size_t ZSTD_compressBlock_fast_extDict_generic(
ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
- void const* src, size_t srcSize, U32 const mls, U32 const commonScenario)
+ void const* src, size_t srcSize, U32 const mls, U32 const hasStep)
{
const ZSTD_compressionParameters* const cParams = &ms->cParams;
U32* const hashTable = ms->hashTable;
U32 const hlog = cParams->hashLog;
/* support stepSize of 0 */
- size_t const stepSize = commonScenario ? 2 : (cParams->targetLength + !(cParams->targetLength) + 1);
+ size_t const stepSize = hasStep ? (cParams->targetLength + !(cParams->targetLength) + 1) : 2;
const BYTE* const base = ms->window.base;
const BYTE* const dictBase = ms->window.dictBase;
const BYTE* const istart = (const BYTE*)src;
@@ -633,13 +633,9 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
{ U32 const curr = (U32)(ip0 - base);
U32 const maxRep = curr - dictStartIndex;
- if (commonScenario) {
- assert(offset_2 < maxRep);
- assert(offset_1 < maxRep);
- } else {
- if (offset_2 >= maxRep) offset_2 = 0;
- if (offset_1 >= maxRep) offset_1 = 0;
- } }
+ if (offset_2 >= maxRep) offset_2 = 0;
+ if (offset_1 >= maxRep) offset_1 = 0;
+ }
/* start each op */
_start: /* Requires: ip0 */
@@ -669,7 +665,7 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
const BYTE* const repBase = repIndex < prefixStartIndex ? dictBase : base;
U32 rval;
if ( ((U32)(prefixStartIndex - repIndex) >= 4) /* intentional underflow */
- & (commonScenario | (offset_1 > 0)) ) {
+ & (offset_1 > 0) ) {
rval = MEM_read32(repBase + repIndex);
} else {
rval = MEM_read32(ip2) ^ 1; /* guaranteed to not match. */
@@ -694,9 +690,7 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
} }
{ /* load match for ip[0] */
- U32 const mval = idx >= dictStartIndex ?
- MEM_read32(idxBase + idx) :
- MEM_read32(ip0) ^ 1; /* guaranteed not to match */
+ U32 const mval = idx >= dictStartIndex ? MEM_read32(idxBase + idx) : MEM_read32(ip0) ^ 1; /* guaranteed not to match */
/* check match at ip[0] */
if (MEM_read32(ip0) == mval) {
@@ -722,9 +716,7 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
hashTable[hash0] = current0;
{ /* load match for ip[0] */
- U32 const mval = idx >= dictStartIndex ?
- MEM_read32(idxBase + idx) :
- MEM_read32(ip0) ^ 1; /* guaranteed not to match */
+ U32 const mval = idx >= dictStartIndex ? MEM_read32(idxBase + idx) : MEM_read32(ip0) ^ 1; /* guaranteed not to match */
/* check match at ip[0] */
if (MEM_read32(ip0) == mval) {
@@ -812,7 +804,7 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
while (ip0 <= ilimit) {
U32 const repIndex2 = (U32)(ip0-base) - offset_2;
const BYTE* const repMatch2 = repIndex2 < prefixStartIndex ? dictBase + repIndex2 : base + repIndex2;
- if ( (((U32)((prefixStartIndex-1) - repIndex2) >= 3) & (commonScenario | (offset_2 > 0))) /* intentional underflow */
+ if ( (((U32)((prefixStartIndex-1) - repIndex2) >= 3) & (offset_2 > 0)) /* intentional underflow */
&& (MEM_read32(repMatch2) == MEM_read32(ip0)) ) {
const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend;
size_t const repLength2 = ZSTD_count_2segments(ip0+4, repMatch2+4, iend, repEnd2, prefixStart) + 4;
@@ -844,17 +836,8 @@ size_t ZSTD_compressBlock_fast_extDict(
void const* src, size_t srcSize)
{
U32 const mls = ms->cParams.minMatch;
- const BYTE* const istart = (const BYTE*)src;
- const BYTE* const base = ms->window.base;
- const U32 endIndex = (U32)((size_t)(istart - base) + srcSize);
- const U32 lowLimit = ZSTD_getLowestMatchIndex(ms, endIndex, ms->cParams.windowLog);
- U32 const curr = (U32)(istart - base);
- U32 const maxRep = curr - lowLimit;
- U32 const repOffsetsAreValid = (rep[0] < maxRep) & (rep[1] < maxRep);
-
- assert((rep[0] > 0) & (rep[1] > 0));
assert(ms->dictMatchState == NULL);
- if ((ms->cParams.targetLength <= 1) & repOffsetsAreValid) {
+ if (ms->cParams.targetLength > 1) {
switch (mls) {
default: /* includes case 3 */
case 4 :
From ce6b69f5c593a4ee06f8a09517141dbd1ee12621 Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Thu, 28 Apr 2022 14:49:45 -0400
Subject: [PATCH 126/472] Final nit
---
lib/compress/zstd_fast.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c
index c6e9dd33aaa..959a392a48f 100644
--- a/lib/compress/zstd_fast.c
+++ b/lib/compress/zstd_fast.c
@@ -690,7 +690,9 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
} }
{ /* load match for ip[0] */
- U32 const mval = idx >= dictStartIndex ? MEM_read32(idxBase + idx) : MEM_read32(ip0) ^ 1; /* guaranteed not to match */
+ U32 const mval = idx >= dictStartIndex ?
+ MEM_read32(idxBase + idx) :
+ MEM_read32(ip0) ^ 1; /* guaranteed not to match */
/* check match at ip[0] */
if (MEM_read32(ip0) == mval) {
@@ -716,7 +718,9 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
hashTable[hash0] = current0;
{ /* load match for ip[0] */
- U32 const mval = idx >= dictStartIndex ? MEM_read32(idxBase + idx) : MEM_read32(ip0) ^ 1; /* guaranteed not to match */
+ U32 const mval = idx >= dictStartIndex ?
+ MEM_read32(idxBase + idx) :
+ MEM_read32(ip0) ^ 1; /* guaranteed not to match */
/* check match at ip[0] */
if (MEM_read32(ip0) == mval) {
From ac371be27b443e488de824a7a0e365fd6d4ac536 Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Thu, 28 Apr 2022 18:05:39 -0400
Subject: [PATCH 127/472] Remove hasStep variant (not enough wins to justify
the code size increase)
---
lib/compress/zstd_fast.c | 44 +++++++++++++---------------------------
1 file changed, 14 insertions(+), 30 deletions(-)
diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c
index 959a392a48f..198ccaee36f 100644
--- a/lib/compress/zstd_fast.c
+++ b/lib/compress/zstd_fast.c
@@ -587,7 +587,7 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
U32* const hashTable = ms->hashTable;
U32 const hlog = cParams->hashLog;
/* support stepSize of 0 */
- size_t const stepSize = hasStep ? (cParams->targetLength + !(cParams->targetLength) + 1) : 2;
+ size_t const stepSize = cParams->targetLength + !(cParams->targetLength) + 1;
const BYTE* const base = ms->window.base;
const BYTE* const dictBase = ms->window.dictBase;
const BYTE* const istart = (const BYTE*)src;
@@ -625,6 +625,8 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
const BYTE* nextStep;
const size_t kStepIncr = (1 << (kSearchStrength - 1));
+ (void)hasStep; /* not currently specialized on whether it's accelerated */
+
DEBUGLOG(5, "ZSTD_compressBlock_fast_extDict_generic (offset_1=%u)", offset_1);
/* switch to "regular" variant if extDict is invalidated due to maxDistance */
@@ -825,11 +827,6 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
goto _start;
}
-ZSTD_GEN_FAST_FN(extDict, 4, 1)
-ZSTD_GEN_FAST_FN(extDict, 5, 1)
-ZSTD_GEN_FAST_FN(extDict, 6, 1)
-ZSTD_GEN_FAST_FN(extDict, 7, 1)
-
ZSTD_GEN_FAST_FN(extDict, 4, 0)
ZSTD_GEN_FAST_FN(extDict, 5, 0)
ZSTD_GEN_FAST_FN(extDict, 6, 0)
@@ -841,29 +838,16 @@ size_t ZSTD_compressBlock_fast_extDict(
{
U32 const mls = ms->cParams.minMatch;
assert(ms->dictMatchState == NULL);
- if (ms->cParams.targetLength > 1) {
- switch (mls) {
- default: /* includes case 3 */
- case 4 :
- return ZSTD_compressBlock_fast_extDict_4_1(ms, seqStore, rep, src, srcSize);
- case 5 :
- return ZSTD_compressBlock_fast_extDict_5_1(ms, seqStore, rep, src, srcSize);
- case 6 :
- return ZSTD_compressBlock_fast_extDict_6_1(ms, seqStore, rep, src, srcSize);
- case 7 :
- return ZSTD_compressBlock_fast_extDict_7_1(ms, seqStore, rep, src, srcSize);
- }
- } else {
- switch (mls) {
- default: /* includes case 3 */
- case 4 :
- return ZSTD_compressBlock_fast_extDict_4_0(ms, seqStore, rep, src, srcSize);
- case 5 :
- return ZSTD_compressBlock_fast_extDict_5_0(ms, seqStore, rep, src, srcSize);
- case 6 :
- return ZSTD_compressBlock_fast_extDict_6_0(ms, seqStore, rep, src, srcSize);
- case 7 :
- return ZSTD_compressBlock_fast_extDict_7_0(ms, seqStore, rep, src, srcSize);
- }
+ switch(mls)
+ {
+ default: /* includes case 3 */
+ case 4 :
+ return ZSTD_compressBlock_fast_extDict_4_0(ms, seqStore, rep, src, srcSize);
+ case 5 :
+ return ZSTD_compressBlock_fast_extDict_5_0(ms, seqStore, rep, src, srcSize);
+ case 6 :
+ return ZSTD_compressBlock_fast_extDict_6_0(ms, seqStore, rep, src, srcSize);
+ case 7 :
+ return ZSTD_compressBlock_fast_extDict_7_0(ms, seqStore, rep, src, srcSize);
}
}
From df6eefb3bbe18901875ffb7eef5bdb5e84066d7e Mon Sep 17 00:00:00 2001
From: Eli Schwartz
Date: Mon, 9 Aug 2021 23:19:52 -0400
Subject: [PATCH 128/472] meson: avoid rebuilding some libzstd files in the
test programs
The poolTests program already linked to libzstd, and later to
libtestcommon with included libzstd objects. So this was redundant.
Minus 4 compile steps.
---
build/meson/tests/meson.build | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/build/meson/tests/meson.build b/build/meson/tests/meson.build
index 14f45982ab1..cf876a395a4 100644
--- a/build/meson/tests/meson.build
+++ b/build/meson/tests/meson.build
@@ -116,11 +116,7 @@ decodecorpus = executable('decodecorpus',
dependencies: [ testcommon_dep, libm_dep ],
install: false)
-poolTests_sources = [join_paths(zstd_rootdir, 'tests/poolTests.c'),
- join_paths(zstd_rootdir, 'lib/common/pool.c'),
- join_paths(zstd_rootdir, 'lib/common/threading.c'),
- join_paths(zstd_rootdir, 'lib/common/zstd_common.c'),
- join_paths(zstd_rootdir, 'lib/common/error_private.c')]
+poolTests_sources = [join_paths(zstd_rootdir, 'tests/poolTests.c')]
poolTests = executable('poolTests',
poolTests_sources,
include_directories: test_includes,
From 8d522b8a9da21edc7b3b85faa4daeb495ff56a85 Mon Sep 17 00:00:00 2001
From: Eli Schwartz
Date: Mon, 9 Aug 2021 22:53:15 -0400
Subject: [PATCH 129/472] meson: avoid rebuilding some libzstd sources in the
programs
These need to be explicitly included as we use their private symbols,
but we don't need to recompile them when we can reuse the existing
objects.
Minus 7 compile steps.
---
build/meson/programs/meson.build | 23 +++++++++++++----------
1 file changed, 13 insertions(+), 10 deletions(-)
diff --git a/build/meson/programs/meson.build b/build/meson/programs/meson.build
index 0704155d845..55ebf166cad 100644
--- a/build/meson/programs/meson.build
+++ b/build/meson/programs/meson.build
@@ -19,12 +19,7 @@ zstd_programs_sources = [join_paths(zstd_rootdir, 'programs/zstdcli.c'),
join_paths(zstd_rootdir, 'programs/benchzstd.c'),
join_paths(zstd_rootdir, 'programs/datagen.c'),
join_paths(zstd_rootdir, 'programs/dibio.c'),
- join_paths(zstd_rootdir, 'programs/zstdcli_trace.c'),
- # needed due to use of private symbol + -fvisibility=hidden
- join_paths(zstd_rootdir, 'lib/common/xxhash.c'),
- join_paths(zstd_rootdir, 'lib/common/pool.c'),
- join_paths(zstd_rootdir, 'lib/common/zstd_common.c'),
- join_paths(zstd_rootdir, 'lib/common/error_private.c')]
+ join_paths(zstd_rootdir, 'programs/zstdcli_trace.c')]
zstd_deps = [ libzstd_dep ]
zstd_c_args = libzstd_debug_cflags
@@ -75,6 +70,12 @@ zstd = executable('zstd',
zstd_programs_sources,
c_args: zstd_c_args,
dependencies: zstd_deps,
+ # needed due to use of private symbol + -fvisibility=hidden
+ objects: libzstd.extract_objects(
+ join_paths(zstd_rootdir, 'lib/common/xxhash.c'),
+ join_paths(zstd_rootdir, 'lib/common/pool.c'),
+ join_paths(zstd_rootdir, 'lib/common/zstd_common.c'),
+ join_paths(zstd_rootdir, 'lib/common/error_private.c')),
export_dynamic: export_dynamic_on_windows, # Since Meson 0.45.0
install: true)
@@ -82,16 +83,18 @@ zstd_frugal_sources = [join_paths(zstd_rootdir, 'programs/zstdcli.c'),
join_paths(zstd_rootdir, 'programs/timefn.c'),
join_paths(zstd_rootdir, 'programs/util.c'),
join_paths(zstd_rootdir, 'programs/fileio.c'),
- join_paths(zstd_rootdir, 'programs/fileio_asyncio.c'),
- join_paths(zstd_rootdir, 'lib/common/pool.c'),
- join_paths(zstd_rootdir, 'lib/common/zstd_common.c'),
- join_paths(zstd_rootdir, 'lib/common/error_private.c')]
+ join_paths(zstd_rootdir, 'programs/fileio_asyncio.c')]
# Minimal target, with only zstd compression and decompression.
# No bench. No legacy.
executable('zstd-frugal',
zstd_frugal_sources,
dependencies: zstd_frugal_deps,
+ # needed due to use of private symbol + -fvisibility=hidden
+ objects: libzstd.extract_objects(
+ join_paths(zstd_rootdir, 'lib/common/pool.c'),
+ join_paths(zstd_rootdir, 'lib/common/zstd_common.c'),
+ join_paths(zstd_rootdir, 'lib/common/error_private.c')),
c_args: zstd_frugal_c_args,
install: true)
From 6548ec7440712eb531f4148ed0568cf90fa1e523 Mon Sep 17 00:00:00 2001
From: Eli Schwartz
Date: Thu, 28 Apr 2022 18:22:55 -0400
Subject: [PATCH 130/472] meson: for internal linkage, link to both libzstd and
a static copy of it
Partial, Meson-only implementation of #2976 for non-MSVC builds.
Due to the prevalence of private symbol reuse, linking to a shared
library is simply utterly unreliable, but we still want to defer to the
shared library for installable applications. By linking to both, we can
share symbols where possible, and statically link where needed.
This means we no longer need to manually track every file that needs to
be extracted and reused.
The flip side is that MSVC completely does not support this, so for MSVC
builds we just link to a full static copy even where
-Ddefault_library=shared.
As a side benefit, by using library inclusion rather than including
extra explicit object files, the zstd program shrinks in size slightly
(~4kb).
---
build/meson/lib/meson.build | 29 +++++++++++++++++++++++++++++
build/meson/programs/meson.build | 15 ++-------------
build/meson/tests/meson.build | 2 +-
3 files changed, 32 insertions(+), 14 deletions(-)
diff --git a/build/meson/lib/meson.build b/build/meson/lib/meson.build
index 2f4126374dc..467007491f5 100644
--- a/build/meson/lib/meson.build
+++ b/build/meson/lib/meson.build
@@ -126,6 +126,35 @@ libzstd = library('zstd',
libzstd_dep = declare_dependency(link_with: libzstd,
include_directories: libzstd_includes)
+# we link to both:
+# - the shared library (for public symbols)
+# - the static library (for private symbols)
+#
+# this is needed because internally private symbols are used all the time, and
+# -fvisibility=hidden means those cannot be found
+if get_option('default_library') == 'static'
+ libzstd_static = libzstd
+ libzstd_internal_dep = libzstd_dep
+else
+ if get_option('default_library') == 'shared'
+ libzstd_static = static_library('zstd_objlib',
+ objects: libzstd.extract_all_objects(recursive: true),
+ build_by_default: false)
+ else
+ libzstd_static = libzstd.get_static_lib()
+ endif
+
+ if cc_id == compiler_msvc
+ # msvc does not actually support linking to both, but errors out with:
+ # error LNK2005: ZSTD_ already defined in zstd.lib(zstd-1.dll)
+ libzstd_internal_dep = declare_dependency(link_with: libzstd_static)
+ else
+ libzstd_internal_dep = declare_dependency(link_with: libzstd,
+ # the static library must be linked after the shared one
+ dependencies: declare_dependency(link_with: libzstd_static))
+ endif
+endif
+
pkgconfig.generate(libzstd,
name: 'libzstd',
filebase: 'libzstd',
diff --git a/build/meson/programs/meson.build b/build/meson/programs/meson.build
index 55ebf166cad..8df3a5dcbdd 100644
--- a/build/meson/programs/meson.build
+++ b/build/meson/programs/meson.build
@@ -21,10 +21,10 @@ zstd_programs_sources = [join_paths(zstd_rootdir, 'programs/zstdcli.c'),
join_paths(zstd_rootdir, 'programs/dibio.c'),
join_paths(zstd_rootdir, 'programs/zstdcli_trace.c')]
-zstd_deps = [ libzstd_dep ]
+zstd_deps = [ libzstd_internal_dep ]
zstd_c_args = libzstd_debug_cflags
-zstd_frugal_deps = [ libzstd_dep ]
+zstd_frugal_deps = [ libzstd_internal_dep ]
zstd_frugal_c_args = [ '-DZSTD_NOBENCH', '-DZSTD_NODICT', '-DZSTD_NOTRACE' ]
if use_multi_thread
@@ -70,12 +70,6 @@ zstd = executable('zstd',
zstd_programs_sources,
c_args: zstd_c_args,
dependencies: zstd_deps,
- # needed due to use of private symbol + -fvisibility=hidden
- objects: libzstd.extract_objects(
- join_paths(zstd_rootdir, 'lib/common/xxhash.c'),
- join_paths(zstd_rootdir, 'lib/common/pool.c'),
- join_paths(zstd_rootdir, 'lib/common/zstd_common.c'),
- join_paths(zstd_rootdir, 'lib/common/error_private.c')),
export_dynamic: export_dynamic_on_windows, # Since Meson 0.45.0
install: true)
@@ -90,11 +84,6 @@ zstd_frugal_sources = [join_paths(zstd_rootdir, 'programs/zstdcli.c'),
executable('zstd-frugal',
zstd_frugal_sources,
dependencies: zstd_frugal_deps,
- # needed due to use of private symbol + -fvisibility=hidden
- objects: libzstd.extract_objects(
- join_paths(zstd_rootdir, 'lib/common/pool.c'),
- join_paths(zstd_rootdir, 'lib/common/zstd_common.c'),
- join_paths(zstd_rootdir, 'lib/common/error_private.c')),
c_args: zstd_frugal_c_args,
install: true)
diff --git a/build/meson/tests/meson.build b/build/meson/tests/meson.build
index cf876a395a4..22f43209ad7 100644
--- a/build/meson/tests/meson.build
+++ b/build/meson/tests/meson.build
@@ -38,7 +38,7 @@ testcommon_sources = [join_paths(zstd_rootdir, 'programs/datagen.c'),
testcommon = static_library('testcommon',
testcommon_sources,
# needed due to use of private symbol + -fvisibility=hidden
- objects: libzstd.extract_all_objects(recursive: false))
+ link_with: libzstd_static)
testcommon_dep = declare_dependency(link_with: testcommon,
dependencies: libzstd_deps,
From 3be9a81e46d7b0672588ecd167ff5ba9cc19a2ec Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Wed, 4 May 2022 16:05:37 -0400
Subject: [PATCH 131/472] Update results.csv
---
tests/regression/results.csv | 190 +++++++++++++++++------------------
1 file changed, 95 insertions(+), 95 deletions(-)
diff --git a/tests/regression/results.csv b/tests/regression/results.csv
index 3385c504931..e6a4af271a9 100644
--- a/tests/regression/results.csv
+++ b/tests/regression/results.csv
@@ -59,15 +59,15 @@ silesia, uncompressed literals optimal, compress
silesia, huffman literals, compress cctx, 6172178
silesia, multithreaded with advanced params, compress cctx, 4842075
github, level -5, compress cctx, 204411
-github, level -5 with dict, compress cctx, 47294
+github, level -5 with dict, compress cctx, 52059
github, level -3, compress cctx, 193253
-github, level -3 with dict, compress cctx, 48047
+github, level -3 with dict, compress cctx, 46787
github, level -1, compress cctx, 175468
-github, level -1 with dict, compress cctx, 43527
+github, level -1 with dict, compress cctx, 43585
github, level 0, compress cctx, 136332
github, level 0 with dict, compress cctx, 41534
github, level 1, compress cctx, 142365
-github, level 1 with dict, compress cctx, 42157
+github, level 1 with dict, compress cctx, 42259
github, level 3, compress cctx, 136332
github, level 3 with dict, compress cctx, 41534
github, level 4, compress cctx, 136199
@@ -188,15 +188,15 @@ github, uncompressed literals optimal, zstdcli,
github, huffman literals, zstdcli, 144365
github, multithreaded with advanced params, zstdcli, 167911
github.tar, level -5, zstdcli, 52114
-github.tar, level -5 with dict, zstdcli, 46502
+github.tar, level -5 with dict, zstdcli, 51074
github.tar, level -3, zstdcli, 45682
-github.tar, level -3 with dict, zstdcli, 42181
+github.tar, level -3 with dict, zstdcli, 44660
github.tar, level -1, zstdcli, 42564
-github.tar, level -1 with dict, zstdcli, 41140
+github.tar, level -1 with dict, zstdcli, 41155
github.tar, level 0, zstdcli, 38835
github.tar, level 0 with dict, zstdcli, 37999
github.tar, level 1, zstdcli, 39204
-github.tar, level 1 with dict, zstdcli, 38288
+github.tar, level 1 with dict, zstdcli, 38093
github.tar, level 3, zstdcli, 38835
github.tar, level 3 with dict, zstdcli, 37999
github.tar, level 4, zstdcli, 38897
@@ -312,8 +312,8 @@ github, level 1, advanced
github, level 1 with dict, advanced one pass, 41682
github, level 1 with dict dms, advanced one pass, 41682
github, level 1 with dict dds, advanced one pass, 41682
-github, level 1 with dict copy, advanced one pass, 41674
-github, level 1 with dict load, advanced one pass, 43755
+github, level 1 with dict copy, advanced one pass, 41698
+github, level 1 with dict load, advanced one pass, 43814
github, level 3, advanced one pass, 136332
github, level 3 with dict, advanced one pass, 41148
github, level 3 with dict dms, advanced one pass, 41148
@@ -422,11 +422,11 @@ github, uncompressed literals optimal, advanced
github, huffman literals, advanced one pass, 142365
github, multithreaded with advanced params, advanced one pass, 165911
github.tar, level -5, advanced one pass, 52110
-github.tar, level -5 with dict, advanced one pass, 46498
+github.tar, level -5 with dict, advanced one pass, 51070
github.tar, level -3, advanced one pass, 45678
-github.tar, level -3 with dict, advanced one pass, 42177
+github.tar, level -3 with dict, advanced one pass, 44656
github.tar, level -1, advanced one pass, 42560
-github.tar, level -1 with dict, advanced one pass, 41136
+github.tar, level -1 with dict, advanced one pass, 41151
github.tar, level 0, advanced one pass, 38831
github.tar, level 0 with dict, advanced one pass, 37995
github.tar, level 0 with dict dms, advanced one pass, 38003
@@ -434,11 +434,11 @@ github.tar, level 0 with dict dds, advanced
github.tar, level 0 with dict copy, advanced one pass, 37995
github.tar, level 0 with dict load, advanced one pass, 37956
github.tar, level 1, advanced one pass, 39200
-github.tar, level 1 with dict, advanced one pass, 38284
+github.tar, level 1 with dict, advanced one pass, 38089
github.tar, level 1 with dict dms, advanced one pass, 38294
github.tar, level 1 with dict dds, advanced one pass, 38294
-github.tar, level 1 with dict copy, advanced one pass, 38284
-github.tar, level 1 with dict load, advanced one pass, 38724
+github.tar, level 1 with dict copy, advanced one pass, 38089
+github.tar, level 1 with dict load, advanced one pass, 38364
github.tar, level 3, advanced one pass, 38831
github.tar, level 3 with dict, advanced one pass, 37995
github.tar, level 3 with dict dms, advanced one pass, 38003
@@ -630,8 +630,8 @@ github, level 1, advanced
github, level 1 with dict, advanced one pass small out, 41682
github, level 1 with dict dms, advanced one pass small out, 41682
github, level 1 with dict dds, advanced one pass small out, 41682
-github, level 1 with dict copy, advanced one pass small out, 41674
-github, level 1 with dict load, advanced one pass small out, 43755
+github, level 1 with dict copy, advanced one pass small out, 41698
+github, level 1 with dict load, advanced one pass small out, 43814
github, level 3, advanced one pass small out, 136332
github, level 3 with dict, advanced one pass small out, 41148
github, level 3 with dict dms, advanced one pass small out, 41148
@@ -740,11 +740,11 @@ github, uncompressed literals optimal, advanced
github, huffman literals, advanced one pass small out, 142365
github, multithreaded with advanced params, advanced one pass small out, 165911
github.tar, level -5, advanced one pass small out, 52110
-github.tar, level -5 with dict, advanced one pass small out, 46498
+github.tar, level -5 with dict, advanced one pass small out, 51070
github.tar, level -3, advanced one pass small out, 45678
-github.tar, level -3 with dict, advanced one pass small out, 42177
+github.tar, level -3 with dict, advanced one pass small out, 44656
github.tar, level -1, advanced one pass small out, 42560
-github.tar, level -1 with dict, advanced one pass small out, 41136
+github.tar, level -1 with dict, advanced one pass small out, 41151
github.tar, level 0, advanced one pass small out, 38831
github.tar, level 0 with dict, advanced one pass small out, 37995
github.tar, level 0 with dict dms, advanced one pass small out, 38003
@@ -752,11 +752,11 @@ github.tar, level 0 with dict dds, advanced
github.tar, level 0 with dict copy, advanced one pass small out, 37995
github.tar, level 0 with dict load, advanced one pass small out, 37956
github.tar, level 1, advanced one pass small out, 39200
-github.tar, level 1 with dict, advanced one pass small out, 38284
+github.tar, level 1 with dict, advanced one pass small out, 38089
github.tar, level 1 with dict dms, advanced one pass small out, 38294
github.tar, level 1 with dict dds, advanced one pass small out, 38294
-github.tar, level 1 with dict copy, advanced one pass small out, 38284
-github.tar, level 1 with dict load, advanced one pass small out, 38724
+github.tar, level 1 with dict copy, advanced one pass small out, 38089
+github.tar, level 1 with dict load, advanced one pass small out, 38364
github.tar, level 3, advanced one pass small out, 38831
github.tar, level 3 with dict, advanced one pass small out, 37995
github.tar, level 3 with dict dms, advanced one pass small out, 38003
@@ -864,11 +864,11 @@ github.tar, uncompressed literals, advanced
github.tar, uncompressed literals optimal, advanced one pass small out, 35397
github.tar, huffman literals, advanced one pass small out, 38853
github.tar, multithreaded with advanced params, advanced one pass small out, 41525
-silesia, level -5, advanced streaming, 6963781
-silesia, level -3, advanced streaming, 6610376
-silesia, level -1, advanced streaming, 6179294
+silesia, level -5, advanced streaming, 6852424
+silesia, level -3, advanced streaming, 6503413
+silesia, level -1, advanced streaming, 6172179
silesia, level 0, advanced streaming, 4842075
-silesia, level 1, advanced streaming, 5310178
+silesia, level 1, advanced streaming, 5306426
silesia, level 3, advanced streaming, 4842075
silesia, level 4, advanced streaming, 4779186
silesia, level 5 row 1, advanced streaming, 4666323
@@ -896,13 +896,13 @@ silesia, small chain log, advanced
silesia, explicit params, advanced streaming, 4795452
silesia, uncompressed literals, advanced streaming, 5120566
silesia, uncompressed literals optimal, advanced streaming, 4319518
-silesia, huffman literals, advanced streaming, 5327881
+silesia, huffman literals, advanced streaming, 5321346
silesia, multithreaded with advanced params, advanced streaming, 5120566
-silesia.tar, level -5, advanced streaming, 7043687
-silesia.tar, level -3, advanced streaming, 6671317
-silesia.tar, level -1, advanced streaming, 6187457
+silesia.tar, level -5, advanced streaming, 6853609
+silesia.tar, level -3, advanced streaming, 6505969
+silesia.tar, level -1, advanced streaming, 6179028
silesia.tar, level 0, advanced streaming, 4859271
-silesia.tar, level 1, advanced streaming, 5333896
+silesia.tar, level 1, advanced streaming, 5327377
silesia.tar, level 3, advanced streaming, 4859271
silesia.tar, level 4, advanced streaming, 4797470
silesia.tar, level 5 row 1, advanced streaming, 4677748
@@ -930,7 +930,7 @@ silesia.tar, small chain log, advanced
silesia.tar, explicit params, advanced streaming, 4806873
silesia.tar, uncompressed literals, advanced streaming, 5127423
silesia.tar, uncompressed literals optimal, advanced streaming, 4310141
-silesia.tar, huffman literals, advanced streaming, 5349624
+silesia.tar, huffman literals, advanced streaming, 5341688
silesia.tar, multithreaded with advanced params, advanced streaming, 5122567
github, level -5, advanced streaming, 204411
github, level -5 with dict, advanced streaming, 46718
@@ -948,8 +948,8 @@ github, level 1, advanced
github, level 1 with dict, advanced streaming, 41682
github, level 1 with dict dms, advanced streaming, 41682
github, level 1 with dict dds, advanced streaming, 41682
-github, level 1 with dict copy, advanced streaming, 41674
-github, level 1 with dict load, advanced streaming, 43755
+github, level 1 with dict copy, advanced streaming, 41698
+github, level 1 with dict load, advanced streaming, 43814
github, level 3, advanced streaming, 136332
github, level 3 with dict, advanced streaming, 41148
github, level 3 with dict dms, advanced streaming, 41148
@@ -1057,24 +1057,24 @@ github, uncompressed literals, advanced
github, uncompressed literals optimal, advanced streaming, 157227
github, huffman literals, advanced streaming, 142365
github, multithreaded with advanced params, advanced streaming, 165911
-github.tar, level -5, advanced streaming, 51420
-github.tar, level -5 with dict, advanced streaming, 45495
-github.tar, level -3, advanced streaming, 45077
-github.tar, level -3 with dict, advanced streaming, 41627
-github.tar, level -1, advanced streaming, 42536
-github.tar, level -1 with dict, advanced streaming, 41198
+github.tar, level -5, advanced streaming, 52110
+github.tar, level -5 with dict, advanced streaming, 51070
+github.tar, level -3, advanced streaming, 45678
+github.tar, level -3 with dict, advanced streaming, 44656
+github.tar, level -1, advanced streaming, 42560
+github.tar, level -1 with dict, advanced streaming, 41151
github.tar, level 0, advanced streaming, 38831
github.tar, level 0 with dict, advanced streaming, 37995
github.tar, level 0 with dict dms, advanced streaming, 38003
github.tar, level 0 with dict dds, advanced streaming, 38003
github.tar, level 0 with dict copy, advanced streaming, 37995
github.tar, level 0 with dict load, advanced streaming, 37956
-github.tar, level 1, advanced streaming, 39270
-github.tar, level 1 with dict, advanced streaming, 38316
-github.tar, level 1 with dict dms, advanced streaming, 38326
-github.tar, level 1 with dict dds, advanced streaming, 38326
-github.tar, level 1 with dict copy, advanced streaming, 38316
-github.tar, level 1 with dict load, advanced streaming, 38761
+github.tar, level 1, advanced streaming, 39200
+github.tar, level 1 with dict, advanced streaming, 38089
+github.tar, level 1 with dict dms, advanced streaming, 38294
+github.tar, level 1 with dict dds, advanced streaming, 38294
+github.tar, level 1 with dict copy, advanced streaming, 38089
+github.tar, level 1 with dict load, advanced streaming, 38364
github.tar, level 3, advanced streaming, 38831
github.tar, level 3 with dict, advanced streaming, 37995
github.tar, level 3 with dict dms, advanced streaming, 38003
@@ -1180,13 +1180,13 @@ github.tar, small chain log, advanced
github.tar, explicit params, advanced streaming, 41385
github.tar, uncompressed literals, advanced streaming, 41525
github.tar, uncompressed literals optimal, advanced streaming, 35397
-github.tar, huffman literals, advanced streaming, 38874
+github.tar, huffman literals, advanced streaming, 38853
github.tar, multithreaded with advanced params, advanced streaming, 41525
-silesia, level -5, old streaming, 6963781
-silesia, level -3, old streaming, 6610376
-silesia, level -1, old streaming, 6179294
+silesia, level -5, old streaming, 6852424
+silesia, level -3, old streaming, 6503413
+silesia, level -1, old streaming, 6172179
silesia, level 0, old streaming, 4842075
-silesia, level 1, old streaming, 5310178
+silesia, level 1, old streaming, 5306426
silesia, level 3, old streaming, 4842075
silesia, level 4, old streaming, 4779186
silesia, level 5, old streaming, 4666323
@@ -1199,12 +1199,12 @@ silesia, level 19, old stre
silesia, no source size, old streaming, 4842039
silesia, uncompressed literals, old streaming, 4842075
silesia, uncompressed literals optimal, old streaming, 4296686
-silesia, huffman literals, old streaming, 6179294
-silesia.tar, level -5, old streaming, 7043687
-silesia.tar, level -3, old streaming, 6671317
-silesia.tar, level -1, old streaming, 6187457
+silesia, huffman literals, old streaming, 6172179
+silesia.tar, level -5, old streaming, 6853609
+silesia.tar, level -3, old streaming, 6505969
+silesia.tar, level -1, old streaming, 6179028
silesia.tar, level 0, old streaming, 4859271
-silesia.tar, level 1, old streaming, 5333896
+silesia.tar, level 1, old streaming, 5327377
silesia.tar, level 3, old streaming, 4859271
silesia.tar, level 4, old streaming, 4797470
silesia.tar, level 5, old streaming, 4677748
@@ -1217,7 +1217,7 @@ silesia.tar, level 19, old stre
silesia.tar, no source size, old streaming, 4859267
silesia.tar, uncompressed literals, old streaming, 4859271
silesia.tar, uncompressed literals optimal, old streaming, 4267266
-silesia.tar, huffman literals, old streaming, 6187457
+silesia.tar, huffman literals, old streaming, 6179028
github, level -5, old streaming, 204411
github, level -5 with dict, old streaming, 46718
github, level -3, old streaming, 193253
@@ -1251,16 +1251,16 @@ github, no source size with dict, old stre
github, uncompressed literals, old streaming, 136332
github, uncompressed literals optimal, old streaming, 134064
github, huffman literals, old streaming, 175468
-github.tar, level -5, old streaming, 51420
-github.tar, level -5 with dict, old streaming, 45495
-github.tar, level -3, old streaming, 45077
-github.tar, level -3 with dict, old streaming, 41627
-github.tar, level -1, old streaming, 42536
-github.tar, level -1 with dict, old streaming, 41198
+github.tar, level -5, old streaming, 52110
+github.tar, level -5 with dict, old streaming, 51070
+github.tar, level -3, old streaming, 45678
+github.tar, level -3 with dict, old streaming, 44656
+github.tar, level -1, old streaming, 42560
+github.tar, level -1 with dict, old streaming, 41151
github.tar, level 0, old streaming, 38831
github.tar, level 0 with dict, old streaming, 37995
-github.tar, level 1, old streaming, 39270
-github.tar, level 1 with dict, old streaming, 38316
+github.tar, level 1, old streaming, 39200
+github.tar, level 1 with dict, old streaming, 38089
github.tar, level 3, old streaming, 38831
github.tar, level 3 with dict, old streaming, 37995
github.tar, level 4, old streaming, 38893
@@ -1283,12 +1283,12 @@ github.tar, no source size, old stre
github.tar, no source size with dict, old streaming, 38000
github.tar, uncompressed literals, old streaming, 38831
github.tar, uncompressed literals optimal, old streaming, 32134
-github.tar, huffman literals, old streaming, 42536
-silesia, level -5, old streaming advanced, 6963781
-silesia, level -3, old streaming advanced, 6610376
-silesia, level -1, old streaming advanced, 6179294
+github.tar, huffman literals, old streaming, 42560
+silesia, level -5, old streaming advanced, 6852424
+silesia, level -3, old streaming advanced, 6503413
+silesia, level -1, old streaming advanced, 6172179
silesia, level 0, old streaming advanced, 4842075
-silesia, level 1, old streaming advanced, 5310178
+silesia, level 1, old streaming advanced, 5306426
silesia, level 3, old streaming advanced, 4842075
silesia, level 4, old streaming advanced, 4779186
silesia, level 5, old streaming advanced, 4666323
@@ -1308,13 +1308,13 @@ silesia, small chain log, old stre
silesia, explicit params, old streaming advanced, 4795452
silesia, uncompressed literals, old streaming advanced, 4842075
silesia, uncompressed literals optimal, old streaming advanced, 4296686
-silesia, huffman literals, old streaming advanced, 6179294
+silesia, huffman literals, old streaming advanced, 6172179
silesia, multithreaded with advanced params, old streaming advanced, 4842075
-silesia.tar, level -5, old streaming advanced, 7043687
-silesia.tar, level -3, old streaming advanced, 6671317
-silesia.tar, level -1, old streaming advanced, 6187457
+silesia.tar, level -5, old streaming advanced, 6853609
+silesia.tar, level -3, old streaming advanced, 6505969
+silesia.tar, level -1, old streaming advanced, 6179028
silesia.tar, level 0, old streaming advanced, 4859271
-silesia.tar, level 1, old streaming advanced, 5333896
+silesia.tar, level 1, old streaming advanced, 5327377
silesia.tar, level 3, old streaming advanced, 4859271
silesia.tar, level 4, old streaming advanced, 4797470
silesia.tar, level 5, old streaming advanced, 4677748
@@ -1334,7 +1334,7 @@ silesia.tar, small chain log, old stre
silesia.tar, explicit params, old streaming advanced, 4806873
silesia.tar, uncompressed literals, old streaming advanced, 4859271
silesia.tar, uncompressed literals optimal, old streaming advanced, 4267266
-silesia.tar, huffman literals, old streaming advanced, 6187457
+silesia.tar, huffman literals, old streaming advanced, 6179028
silesia.tar, multithreaded with advanced params, old streaming advanced, 4859271
github, level -5, old streaming advanced, 213265
github, level -5 with dict, old streaming advanced, 49562
@@ -1377,16 +1377,16 @@ github, uncompressed literals, old stre
github, uncompressed literals optimal, old streaming advanced, 134064
github, huffman literals, old streaming advanced, 181107
github, multithreaded with advanced params, old streaming advanced, 141104
-github.tar, level -5, old streaming advanced, 51420
-github.tar, level -5 with dict, old streaming advanced, 46091
-github.tar, level -3, old streaming advanced, 45077
-github.tar, level -3 with dict, old streaming advanced, 42222
-github.tar, level -1, old streaming advanced, 42536
-github.tar, level -1 with dict, old streaming advanced, 41494
+github.tar, level -5, old streaming advanced, 52110
+github.tar, level -5 with dict, old streaming advanced, 50985
+github.tar, level -3, old streaming advanced, 45678
+github.tar, level -3 with dict, old streaming advanced, 44729
+github.tar, level -1, old streaming advanced, 42560
+github.tar, level -1 with dict, old streaming advanced, 41589
github.tar, level 0, old streaming advanced, 38831
github.tar, level 0 with dict, old streaming advanced, 38013
-github.tar, level 1, old streaming advanced, 39270
-github.tar, level 1 with dict, old streaming advanced, 38934
+github.tar, level 1, old streaming advanced, 39200
+github.tar, level 1 with dict, old streaming advanced, 38359
github.tar, level 3, old streaming advanced, 38831
github.tar, level 3 with dict, old streaming advanced, 38013
github.tar, level 4, old streaming advanced, 38893
@@ -1416,7 +1416,7 @@ github.tar, small chain log, old stre
github.tar, explicit params, old streaming advanced, 41385
github.tar, uncompressed literals, old streaming advanced, 38831
github.tar, uncompressed literals optimal, old streaming advanced, 32134
-github.tar, huffman literals, old streaming advanced, 42536
+github.tar, huffman literals, old streaming advanced, 42560
github.tar, multithreaded with advanced params, old streaming advanced, 38831
github, level -5 with dict, old streaming cdict, 46718
github, level -3 with dict, old streaming cdict, 45395
@@ -1433,11 +1433,11 @@ github, level 13 with dict, old stre
github, level 16 with dict, old streaming cdict, 37577
github, level 19 with dict, old streaming cdict, 37576
github, no source size with dict, old streaming cdict, 40654
-github.tar, level -5 with dict, old streaming cdict, 46276
-github.tar, level -3 with dict, old streaming cdict, 42354
-github.tar, level -1 with dict, old streaming cdict, 41662
+github.tar, level -5 with dict, old streaming cdict, 51189
+github.tar, level -3 with dict, old streaming cdict, 44821
+github.tar, level -1 with dict, old streaming cdict, 41775
github.tar, level 0 with dict, old streaming cdict, 37956
-github.tar, level 1 with dict, old streaming cdict, 38761
+github.tar, level 1 with dict, old streaming cdict, 38364
github.tar, level 3 with dict, old streaming cdict, 37956
github.tar, level 4 with dict, old streaming cdict, 37927
github.tar, level 5 with dict, old streaming cdict, 38999
@@ -1463,11 +1463,11 @@ github, level 13 with dict, old stre
github, level 16 with dict, old streaming advanced cdict, 40789
github, level 19 with dict, old streaming advanced cdict, 37576
github, no source size with dict, old streaming advanced cdict, 40608
-github.tar, level -5 with dict, old streaming advanced cdict, 44307
-github.tar, level -3 with dict, old streaming advanced cdict, 41359
-github.tar, level -1 with dict, old streaming advanced cdict, 41322
+github.tar, level -5 with dict, old streaming advanced cdict, 50854
+github.tar, level -3 with dict, old streaming advanced cdict, 44571
+github.tar, level -1 with dict, old streaming advanced cdict, 41477
github.tar, level 0 with dict, old streaming advanced cdict, 38013
-github.tar, level 1 with dict, old streaming advanced cdict, 39002
+github.tar, level 1 with dict, old streaming advanced cdict, 38168
github.tar, level 3 with dict, old streaming advanced cdict, 38013
github.tar, level 4 with dict, old streaming advanced cdict, 38063
github.tar, level 5 with dict, old streaming advanced cdict, 38997
From 83049cb3fe980f529bf595bd819252ee2b6667de Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Mon, 9 May 2022 18:28:03 -0400
Subject: [PATCH 132/472] Unbreak FreeBSD CI
---
.cirrus.yml | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/.cirrus.yml b/.cirrus.yml
index fe17aacea5d..27ca65e8d29 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -2,9 +2,8 @@ task:
name: FreeBSD (shortest)
freebsd_instance:
matrix:
+ image_family: freebsd-13-0
image_family: freebsd-12-2
- # The stable 11.3 image causes "Agent is not responding" so use a snapshot
- image_family: freebsd-11-3-snap
install_script: pkg install -y gmake coreutils
script: |
MOREFLAGS="-Werror" gmake -j all
From 97aabc496e57821cb46893bd0a73f48ff6bc20b7 Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Mon, 9 May 2022 17:17:11 -0400
Subject: [PATCH 133/472] Correct and clarify repcode offset history logic
---
lib/compress/zstd_double_fast.c | 19 +++++++++++--------
lib/compress/zstd_fast.c | 32 ++++++++++++++++++++------------
lib/compress/zstd_lazy.c | 17 +++++++++++------
3 files changed, 42 insertions(+), 26 deletions(-)
diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c
index 610a1b3ecbe..d619f119bbd 100644
--- a/lib/compress/zstd_double_fast.c
+++ b/lib/compress/zstd_double_fast.c
@@ -67,7 +67,7 @@ size_t ZSTD_compressBlock_doubleFast_noDict_generic(
const BYTE* const iend = istart + srcSize;
const BYTE* const ilimit = iend - HASH_READ_SIZE;
U32 offset_1=rep[0], offset_2=rep[1];
- U32 offsetSaved = 0;
+ U32 offsetSaved1 = 0, offsetSaved2 = 0;
size_t mLength;
U32 offset;
@@ -100,8 +100,8 @@ size_t ZSTD_compressBlock_doubleFast_noDict_generic(
U32 const current = (U32)(ip - base);
U32 const windowLow = ZSTD_getLowestPrefixIndex(ms, current, cParams->windowLog);
U32 const maxRep = current - windowLow;
- if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0;
- if (offset_1 > maxRep) offsetSaved = offset_1, offset_1 = 0;
+ if (offset_2 > maxRep) offsetSaved2 = offset_2, offset_2 = 0;
+ if (offset_1 > maxRep) offsetSaved1 = offset_1, offset_1 = 0;
}
/* Outer Loop: one iteration per match found and stored */
@@ -175,9 +175,13 @@ size_t ZSTD_compressBlock_doubleFast_noDict_generic(
} while (ip1 <= ilimit);
_cleanup:
+ /* If rep_offset1 started invalid (offsetSaved1 > 0) and became valid (rep_offset1 > 0),
+ * rotate saved offsets. */
+ offsetSaved2 = ((offsetSaved1 > 0) & (offset_1 > 0)) ? offsetSaved1 : offsetSaved2;
+
/* save reps for next block */
- rep[0] = offset_1 ? offset_1 : offsetSaved;
- rep[1] = offset_2 ? offset_2 : offsetSaved;
+ rep[0] = offset_1 ? offset_1 : offsetSaved1;
+ rep[1] = offset_2 ? offset_2 : offsetSaved2;
/* Return the last literals size */
return (size_t)(iend - anchor);
@@ -275,7 +279,6 @@ size_t ZSTD_compressBlock_doubleFast_dictMatchState_generic(
const BYTE* const iend = istart + srcSize;
const BYTE* const ilimit = iend - HASH_READ_SIZE;
U32 offset_1=rep[0], offset_2=rep[1];
- U32 offsetSaved = 0;
const ZSTD_matchState_t* const dms = ms->dictMatchState;
const ZSTD_compressionParameters* const dictCParams = &dms->cParams;
@@ -461,8 +464,8 @@ size_t ZSTD_compressBlock_doubleFast_dictMatchState_generic(
} /* while (ip < ilimit) */
/* save reps for next block */
- rep[0] = offset_1 ? offset_1 : offsetSaved;
- rep[1] = offset_2 ? offset_2 : offsetSaved;
+ rep[0] = offset_1;
+ rep[1] = offset_2;
/* Return the last literals size */
return (size_t)(iend - anchor);
diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c
index 198ccaee36f..36ea4f4e194 100644
--- a/lib/compress/zstd_fast.c
+++ b/lib/compress/zstd_fast.c
@@ -117,7 +117,7 @@ ZSTD_compressBlock_fast_noDict_generic(
U32 rep_offset1 = rep[0];
U32 rep_offset2 = rep[1];
- U32 offsetSaved = 0;
+ U32 offsetSaved1 = 0, offsetSaved2 = 0;
size_t hash0; /* hash for ip0 */
size_t hash1; /* hash for ip1 */
@@ -141,8 +141,8 @@ ZSTD_compressBlock_fast_noDict_generic(
{ U32 const curr = (U32)(ip0 - base);
U32 const windowLow = ZSTD_getLowestPrefixIndex(ms, curr, cParams->windowLog);
U32 const maxRep = curr - windowLow;
- if (rep_offset2 > maxRep) offsetSaved = rep_offset2, rep_offset2 = 0;
- if (rep_offset1 > maxRep) offsetSaved = rep_offset1, rep_offset1 = 0;
+ if (rep_offset2 > maxRep) offsetSaved2 = rep_offset2, rep_offset2 = 0;
+ if (rep_offset1 > maxRep) offsetSaved1 = rep_offset1, rep_offset1 = 0;
}
/* start each op */
@@ -254,9 +254,13 @@ ZSTD_compressBlock_fast_noDict_generic(
* However, it seems to be a meaningful performance hit to try to search
* them. So let's not. */
+ /* If rep_offset1 started invalid (offsetSaved1 != 0) and became valid (rep_offset1 != 0),
+ * rotate saved offsets. */
+ offsetSaved2 = ((offsetSaved1 > 0) & (rep_offset1 > 0)) ? offsetSaved1 : offsetSaved2;
+
/* save reps for next block */
- rep[0] = rep_offset1 ? rep_offset1 : offsetSaved;
- rep[1] = rep_offset2 ? rep_offset2 : offsetSaved;
+ rep[0] = rep_offset1 ? rep_offset1 : offsetSaved1;
+ rep[1] = rep_offset2 ? rep_offset2 : offsetSaved2;
/* Return the last literals size */
return (size_t)(iend - anchor);
@@ -388,7 +392,6 @@ size_t ZSTD_compressBlock_fast_dictMatchState_generic(
const BYTE* const iend = istart + srcSize;
const BYTE* const ilimit = iend - HASH_READ_SIZE;
U32 offset_1=rep[0], offset_2=rep[1];
- U32 offsetSaved = 0;
const ZSTD_matchState_t* const dms = ms->dictMatchState;
const ZSTD_compressionParameters* const dictCParams = &dms->cParams ;
@@ -545,8 +548,8 @@ size_t ZSTD_compressBlock_fast_dictMatchState_generic(
_cleanup:
/* save reps for next block */
- rep[0] = offset_1 ? offset_1 : offsetSaved;
- rep[1] = offset_2 ? offset_2 : offsetSaved;
+ rep[0] = offset_1;
+ rep[1] = offset_2;
/* Return the last literals size */
return (size_t)(iend - anchor);
@@ -603,6 +606,7 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
const BYTE* const iend = istart + srcSize;
const BYTE* const ilimit = iend - 8;
U32 offset_1=rep[0], offset_2=rep[1];
+ U32 offsetSaved1 = 0, offsetSaved2 = 0;
const BYTE* ip0 = istart;
const BYTE* ip1;
@@ -635,8 +639,8 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
{ U32 const curr = (U32)(ip0 - base);
U32 const maxRep = curr - dictStartIndex;
- if (offset_2 >= maxRep) offset_2 = 0;
- if (offset_1 >= maxRep) offset_1 = 0;
+ if (offset_2 >= maxRep) offsetSaved2 = offset_2, offset_2 = 0;
+ if (offset_1 >= maxRep) offsetSaved1 = offset_1, offset_1 = 0;
}
/* start each op */
@@ -758,9 +762,13 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
* However, it seems to be a meaningful performance hit to try to search
* them. So let's not. */
+ /* If offset_1 started invalid (offsetSaved1 != 0) and became valid (offset_1 != 0),
+ * rotate saved offsets. */
+ offsetSaved2 = ((offsetSaved1 > 0) & (offset_1 > 0)) ? offsetSaved1 : offsetSaved2;
+
/* save reps for next block */
- rep[0] = offset_1 ? offset_1 : rep[0];
- rep[1] = offset_2 ? offset_2 : rep[1];
+ rep[0] = offset_1 ? offset_1 : offsetSaved1;
+ rep[1] = offset_2 ? offset_2 : offsetSaved2;
/* Return the last literals size */
return (size_t)(iend - anchor);
diff --git a/lib/compress/zstd_lazy.c b/lib/compress/zstd_lazy.c
index 7f97e12a5b2..9814943d1a9 100644
--- a/lib/compress/zstd_lazy.c
+++ b/lib/compress/zstd_lazy.c
@@ -1461,7 +1461,8 @@ ZSTD_compressBlock_lazy_generic(
const BYTE* const prefixLowest = base + prefixLowestIndex;
searchMax_f const searchMax = ZSTD_selectLazyVTable(ms, searchMethod, dictMode)->searchMax;
- U32 offset_1 = rep[0], offset_2 = rep[1], savedOffset=0;
+ U32 offset_1 = rep[0], offset_2 = rep[1];
+ U32 offsetSaved1 = 0, offsetSaved2 = 0;
const int isDMS = dictMode == ZSTD_dictMatchState;
const int isDDS = dictMode == ZSTD_dedicatedDictSearch;
@@ -1484,8 +1485,8 @@ ZSTD_compressBlock_lazy_generic(
U32 const curr = (U32)(ip - base);
U32 const windowLow = ZSTD_getLowestPrefixIndex(ms, curr, ms->cParams.windowLog);
U32 const maxRep = curr - windowLow;
- if (offset_2 > maxRep) savedOffset = offset_2, offset_2 = 0;
- if (offset_1 > maxRep) savedOffset = offset_1, offset_1 = 0;
+ if (offset_2 > maxRep) offsetSaved2 = offset_2, offset_2 = 0;
+ if (offset_1 > maxRep) offsetSaved1 = offset_1, offset_1 = 0;
}
if (isDxS) {
/* dictMatchState repCode checks don't currently handle repCode == 0
@@ -1681,9 +1682,13 @@ ZSTD_compressBlock_lazy_generic(
continue; /* faster when present ... (?) */
} } }
- /* Save reps for next block */
- rep[0] = offset_1 ? offset_1 : savedOffset;
- rep[1] = offset_2 ? offset_2 : savedOffset;
+ /* If offset_1 started invalid (offsetSaved1 > 0) and became valid (offset_1 > 0),
+ * rotate saved offsets. */
+ offsetSaved2 = ((offsetSaved1 > 0) & (offset_1 > 0)) ? offsetSaved1 : offsetSaved2;
+
+ /* save reps for next block */
+ rep[0] = offset_1 ? offset_1 : offsetSaved1;
+ rep[1] = offset_2 ? offset_2 : offsetSaved2;
/* Return the last literals size */
return (size_t)(iend - anchor);
From 22875ece616624215ecb86211c4256c39a710a82 Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Mon, 9 May 2022 18:26:10 -0400
Subject: [PATCH 134/472] Nits
---
lib/compress/zstd_double_fast.c | 2 +-
lib/compress/zstd_fast.c | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c
index d619f119bbd..698a97a802f 100644
--- a/lib/compress/zstd_double_fast.c
+++ b/lib/compress/zstd_double_fast.c
@@ -175,7 +175,7 @@ size_t ZSTD_compressBlock_doubleFast_noDict_generic(
} while (ip1 <= ilimit);
_cleanup:
- /* If rep_offset1 started invalid (offsetSaved1 > 0) and became valid (rep_offset1 > 0),
+ /* If offset_1 started invalid (offsetSaved1 > 0) and became valid (offset_1 > 0),
* rotate saved offsets. */
offsetSaved2 = ((offsetSaved1 > 0) & (offset_1 > 0)) ? offsetSaved1 : offsetSaved2;
diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c
index 36ea4f4e194..3d3b7d9cb4f 100644
--- a/lib/compress/zstd_fast.c
+++ b/lib/compress/zstd_fast.c
@@ -254,7 +254,7 @@ ZSTD_compressBlock_fast_noDict_generic(
* However, it seems to be a meaningful performance hit to try to search
* them. So let's not. */
- /* If rep_offset1 started invalid (offsetSaved1 != 0) and became valid (rep_offset1 != 0),
+ /* If rep_offset1 started invalid (offsetSaved1 > 0) and became valid (rep_offset1 > 0),
* rotate saved offsets. */
offsetSaved2 = ((offsetSaved1 > 0) & (rep_offset1 > 0)) ? offsetSaved1 : offsetSaved2;
@@ -762,7 +762,7 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
* However, it seems to be a meaningful performance hit to try to search
* them. So let's not. */
- /* If offset_1 started invalid (offsetSaved1 != 0) and became valid (offset_1 != 0),
+ /* If offset_1 started invalid (offsetSaved1 > 0) and became valid (offset_1 > 0),
* rotate saved offsets. */
offsetSaved2 = ((offsetSaved1 > 0) & (offset_1 > 0)) ? offsetSaved1 : offsetSaved2;
From 040986a4f4a2ba64a3ad9dc76646d8fab4472b37 Mon Sep 17 00:00:00 2001
From: "W. Felix Handte"
Date: Tue, 10 May 2022 14:29:39 -0700
Subject: [PATCH 135/472] ZSTD_fast_noDict: Minimize Checks When Writing Hash
Table for ip1
This commit avoids checking whether a hashtable write is safe in two of the
three match-found paths in `ZSTD_compressBlock_fast_noDict_generic`. This pro-
duces a ~0.5% speed-up in compression.
A comment in the code describes why we can skip this check in the other two
paths (the repcode check and the first match check in the unrolled loop).
A downside is that in the new position where we make this check, we have not
yet computed `mLength`. We therefore have to avoid writing *possibly* dangerous
positions, rather than the old check which only avoids writing *actually*
dangerous positions. This leads to a miniscule loss in ratio (remember that
this scenario can only been triggered in very negative levels or under incomp-
ressibility acceleration).
---
lib/compress/zstd_fast.c | 21 ++++++++++++++++++---
1 file changed, 18 insertions(+), 3 deletions(-)
diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c
index 198ccaee36f..f2028ef3589 100644
--- a/lib/compress/zstd_fast.c
+++ b/lib/compress/zstd_fast.c
@@ -224,6 +224,23 @@ ZSTD_compressBlock_fast_noDict_generic(
/* check match at ip[0] */
if (MEM_read32(ip0) == mval) {
/* found a match! */
+ if (step > 4) {
+ /* We need to avoid writing an index into the hash table >= the
+ * position at which we will pick up our searching after we've
+ * taken this match.
+ *
+ * The minimum possible match has length 4, so the earliest ip0
+ * can be after we take this match will be the current ip0 + 4.
+ * ip1 is ip0 + step - 1. If ip1 is >= ip0 + 4, we can't safely
+ * write this position. The expedient thing to do is just to
+ * write a bad position.
+ *
+ * We perform this check here, separate from the write, because
+ * this is the only match path where this can occur. (In rep-
+ * code and the first match checks, ip1 == ip0 + 1.)
+ */
+ ip1 = base;
+ }
goto _offset;
}
@@ -288,9 +305,7 @@ ZSTD_compressBlock_fast_noDict_generic(
anchor = ip0;
/* write next hash table entry */
- if (ip1 < ip0) {
- hashTable[hash1] = (U32)(ip1 - base);
- }
+ hashTable[hash1] = (U32)(ip1 - base);
/* Fill table and check for immediate repcode. */
if (ip0 <= ilimit) {
From cd1f5829432c6df1a47c26cf9be75495fb2fdb94 Mon Sep 17 00:00:00 2001
From: "W. Felix Handte"
Date: Wed, 11 May 2022 11:27:34 -0400
Subject: [PATCH 136/472] Hoist Hash Table Writes Up into Each Match Found
Block
Refactoring this way avoids the bad write in the case that `step > 4`, and
is a bit more straightforward. It also seems to perform better!
---
lib/compress/zstd_fast.c | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c
index f2028ef3589..11c80af87fe 100644
--- a/lib/compress/zstd_fast.c
+++ b/lib/compress/zstd_fast.c
@@ -182,6 +182,10 @@ ZSTD_compressBlock_fast_noDict_generic(
match0 -= mLength;
offcode = REPCODE1_TO_OFFBASE;
mLength += 4;
+
+ /* first write next hash table entry; we've already calculated it */
+ hashTable[hash1] = (U32)(ip1 - base);
+
goto _match;
}
@@ -195,6 +199,10 @@ ZSTD_compressBlock_fast_noDict_generic(
/* check match at ip[0] */
if (MEM_read32(ip0) == mval) {
/* found a match! */
+
+ /* first write next hash table entry; we've already calculated it */
+ hashTable[hash1] = (U32)(ip1 - base);
+
goto _offset;
}
@@ -224,7 +232,9 @@ ZSTD_compressBlock_fast_noDict_generic(
/* check match at ip[0] */
if (MEM_read32(ip0) == mval) {
/* found a match! */
- if (step > 4) {
+
+ /* first write next hash table entry; we've already calculated it */
+ if (step <= 4) {
/* We need to avoid writing an index into the hash table >= the
* position at which we will pick up our searching after we've
* taken this match.
@@ -239,8 +249,9 @@ ZSTD_compressBlock_fast_noDict_generic(
* this is the only match path where this can occur. (In rep-
* code and the first match checks, ip1 == ip0 + 1.)
*/
- ip1 = base;
+ hashTable[hash1] = (U32)(ip1 - base);
}
+
goto _offset;
}
@@ -304,9 +315,6 @@ ZSTD_compressBlock_fast_noDict_generic(
ip0 += mLength;
anchor = ip0;
- /* write next hash table entry */
- hashTable[hash1] = (U32)(ip1 - base);
-
/* Fill table and check for immediate repcode. */
if (ip0 <= ilimit) {
/* Fill Table */
From 1dd046a50783ebc4d0eb10928e71b2dd7c71cd4c Mon Sep 17 00:00:00 2001
From: "W. Felix Handte"
Date: Wed, 11 May 2022 12:38:20 -0400
Subject: [PATCH 137/472] Fix Comments Slightly
---
lib/compress/zstd_fast.c | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c
index 11c80af87fe..f51911eb99d 100644
--- a/lib/compress/zstd_fast.c
+++ b/lib/compress/zstd_fast.c
@@ -183,7 +183,9 @@ ZSTD_compressBlock_fast_noDict_generic(
offcode = REPCODE1_TO_OFFBASE;
mLength += 4;
- /* first write next hash table entry; we've already calculated it */
+ /* First write next hash table entry; we've already calculated it.
+ * This write is known to be safe because the ip1 is before the
+ * repcode (ip2). */
hashTable[hash1] = (U32)(ip1 - base);
goto _match;
@@ -200,7 +202,9 @@ ZSTD_compressBlock_fast_noDict_generic(
if (MEM_read32(ip0) == mval) {
/* found a match! */
- /* first write next hash table entry; we've already calculated it */
+ /* First write next hash table entry; we've already calculated it.
+ * This write is known to be safe because the ip1 == ip0 + 1, so
+ * we know we will resume searching after ip1 */
hashTable[hash1] = (U32)(ip1 - base);
goto _offset;
@@ -242,12 +246,7 @@ ZSTD_compressBlock_fast_noDict_generic(
* The minimum possible match has length 4, so the earliest ip0
* can be after we take this match will be the current ip0 + 4.
* ip1 is ip0 + step - 1. If ip1 is >= ip0 + 4, we can't safely
- * write this position. The expedient thing to do is just to
- * write a bad position.
- *
- * We perform this check here, separate from the write, because
- * this is the only match path where this can occur. (In rep-
- * code and the first match checks, ip1 == ip0 + 1.)
+ * write this position.
*/
hashTable[hash1] = (U32)(ip1 - base);
}
From 1bc8019e10a8ec4c7c1ad3dc2abf9355a1add1f4 Mon Sep 17 00:00:00 2001
From: "W. Felix Handte"
Date: Wed, 11 May 2022 10:27:35 -0700
Subject: [PATCH 138/472] Update results.csv
---
tests/regression/results.csv | 180 +++++++++++++++++------------------
1 file changed, 90 insertions(+), 90 deletions(-)
diff --git a/tests/regression/results.csv b/tests/regression/results.csv
index e6a4af271a9..73069c5d6b2 100644
--- a/tests/regression/results.csv
+++ b/tests/regression/results.csv
@@ -1,9 +1,9 @@
Data, Config, Method, Total compressed size
-silesia.tar, level -5, compress simple, 6853608
-silesia.tar, level -3, compress simple, 6505969
-silesia.tar, level -1, compress simple, 6179026
+silesia.tar, level -5, compress simple, 6861055
+silesia.tar, level -3, compress simple, 6505483
+silesia.tar, level -1, compress simple, 6179047
silesia.tar, level 0, compress simple, 4854086
-silesia.tar, level 1, compress simple, 5327373
+silesia.tar, level 1, compress simple, 5327717
silesia.tar, level 3, compress simple, 4854086
silesia.tar, level 4, compress simple, 4791503
silesia.tar, level 5, compress simple, 4677740
@@ -15,8 +15,8 @@ silesia.tar, level 16, compress
silesia.tar, level 19, compress simple, 4267266
silesia.tar, uncompressed literals, compress simple, 4854086
silesia.tar, uncompressed literals optimal, compress simple, 4267266
-silesia.tar, huffman literals, compress simple, 6179026
-github.tar, level -5, compress simple, 52110
+silesia.tar, huffman literals, compress simple, 6179047
+github.tar, level -5, compress simple, 52115
github.tar, level -3, compress simple, 45678
github.tar, level -1, compress simple, 42560
github.tar, level 0, compress simple, 38831
@@ -33,11 +33,11 @@ github.tar, level 19, compress
github.tar, uncompressed literals, compress simple, 38831
github.tar, uncompressed literals optimal, compress simple, 32134
github.tar, huffman literals, compress simple, 42560
-silesia, level -5, compress cctx, 6852424
-silesia, level -3, compress cctx, 6503413
-silesia, level -1, compress cctx, 6172178
+silesia, level -5, compress cctx, 6857372
+silesia, level -3, compress cctx, 6503412
+silesia, level -1, compress cctx, 6172202
silesia, level 0, compress cctx, 4842075
-silesia, level 1, compress cctx, 5306426
+silesia, level 1, compress cctx, 5306632
silesia, level 3, compress cctx, 4842075
silesia, level 4, compress cctx, 4779186
silesia, level 5, compress cctx, 4666323
@@ -56,9 +56,9 @@ silesia, small chain log, compress
silesia, explicit params, compress cctx, 4794052
silesia, uncompressed literals, compress cctx, 4842075
silesia, uncompressed literals optimal, compress cctx, 4296686
-silesia, huffman literals, compress cctx, 6172178
+silesia, huffman literals, compress cctx, 6172202
silesia, multithreaded with advanced params, compress cctx, 4842075
-github, level -5, compress cctx, 204411
+github, level -5, compress cctx, 204407
github, level -5 with dict, compress cctx, 52059
github, level -3, compress cctx, 193253
github, level -3 with dict, compress cctx, 46787
@@ -97,11 +97,11 @@ github, uncompressed literals, compress
github, uncompressed literals optimal, compress cctx, 134064
github, huffman literals, compress cctx, 175468
github, multithreaded with advanced params, compress cctx, 141069
-silesia, level -5, zstdcli, 6852472
-silesia, level -3, zstdcli, 6503461
-silesia, level -1, zstdcli, 6172226
+silesia, level -5, zstdcli, 6857420
+silesia, level -3, zstdcli, 6503460
+silesia, level -1, zstdcli, 6172250
silesia, level 0, zstdcli, 4842123
-silesia, level 1, zstdcli, 5306474
+silesia, level 1, zstdcli, 5306680
silesia, level 3, zstdcli, 4842123
silesia, level 4, zstdcli, 4779234
silesia, level 5, zstdcli, 4666371
@@ -120,13 +120,13 @@ silesia, small chain log, zstdcli,
silesia, explicit params, zstdcli, 4795432
silesia, uncompressed literals, zstdcli, 5120614
silesia, uncompressed literals optimal, zstdcli, 4319566
-silesia, huffman literals, zstdcli, 5321394
+silesia, huffman literals, zstdcli, 5321417
silesia, multithreaded with advanced params, zstdcli, 5120614
-silesia.tar, level -5, zstdcli, 6853994
-silesia.tar, level -3, zstdcli, 6506742
-silesia.tar, level -1, zstdcli, 6179765
+silesia.tar, level -5, zstdcli, 6862049
+silesia.tar, level -3, zstdcli, 6506509
+silesia.tar, level -1, zstdcli, 6179789
silesia.tar, level 0, zstdcli, 4854164
-silesia.tar, level 1, zstdcli, 5328534
+silesia.tar, level 1, zstdcli, 5329010
silesia.tar, level 3, zstdcli, 4854164
silesia.tar, level 4, zstdcli, 4792352
silesia.tar, level 5, zstdcli, 4678682
@@ -146,9 +146,9 @@ silesia.tar, small chain log, zstdcli,
silesia.tar, explicit params, zstdcli, 4820713
silesia.tar, uncompressed literals, zstdcli, 5122571
silesia.tar, uncompressed literals optimal, zstdcli, 4310145
-silesia.tar, huffman literals, zstdcli, 5342054
+silesia.tar, huffman literals, zstdcli, 5342074
silesia.tar, multithreaded with advanced params, zstdcli, 5122571
-github, level -5, zstdcli, 206411
+github, level -5, zstdcli, 206407
github, level -5 with dict, zstdcli, 48718
github, level -3, zstdcli, 195253
github, level -3 with dict, zstdcli, 47395
@@ -187,8 +187,8 @@ github, uncompressed literals, zstdcli,
github, uncompressed literals optimal, zstdcli, 159227
github, huffman literals, zstdcli, 144365
github, multithreaded with advanced params, zstdcli, 167911
-github.tar, level -5, zstdcli, 52114
-github.tar, level -5 with dict, zstdcli, 51074
+github.tar, level -5, zstdcli, 52119
+github.tar, level -5 with dict, zstdcli, 50978
github.tar, level -3, zstdcli, 45682
github.tar, level -3 with dict, zstdcli, 44660
github.tar, level -1, zstdcli, 42564
@@ -228,11 +228,11 @@ github.tar, uncompressed literals, zstdcli,
github.tar, uncompressed literals optimal, zstdcli, 35401
github.tar, huffman literals, zstdcli, 38857
github.tar, multithreaded with advanced params, zstdcli, 41529
-silesia, level -5, advanced one pass, 6852424
-silesia, level -3, advanced one pass, 6503413
-silesia, level -1, advanced one pass, 6172178
+silesia, level -5, advanced one pass, 6857372
+silesia, level -3, advanced one pass, 6503412
+silesia, level -1, advanced one pass, 6172202
silesia, level 0, advanced one pass, 4842075
-silesia, level 1, advanced one pass, 5306426
+silesia, level 1, advanced one pass, 5306632
silesia, level 3, advanced one pass, 4842075
silesia, level 4, advanced one pass, 4779186
silesia, level 5 row 1, advanced one pass, 4666323
@@ -260,13 +260,13 @@ silesia, small chain log, advanced
silesia, explicit params, advanced one pass, 4795432
silesia, uncompressed literals, advanced one pass, 5120566
silesia, uncompressed literals optimal, advanced one pass, 4319518
-silesia, huffman literals, advanced one pass, 5321346
+silesia, huffman literals, advanced one pass, 5321369
silesia, multithreaded with advanced params, advanced one pass, 5120566
-silesia.tar, level -5, advanced one pass, 6853608
-silesia.tar, level -3, advanced one pass, 6505969
-silesia.tar, level -1, advanced one pass, 6179026
+silesia.tar, level -5, advanced one pass, 6861055
+silesia.tar, level -3, advanced one pass, 6505483
+silesia.tar, level -1, advanced one pass, 6179047
silesia.tar, level 0, advanced one pass, 4854086
-silesia.tar, level 1, advanced one pass, 5327373
+silesia.tar, level 1, advanced one pass, 5327717
silesia.tar, level 3, advanced one pass, 4854086
silesia.tar, level 4, advanced one pass, 4791503
silesia.tar, level 5 row 1, advanced one pass, 4677740
@@ -294,9 +294,9 @@ silesia.tar, small chain log, advanced
silesia.tar, explicit params, advanced one pass, 4806855
silesia.tar, uncompressed literals, advanced one pass, 5122473
silesia.tar, uncompressed literals optimal, advanced one pass, 4310141
-silesia.tar, huffman literals, advanced one pass, 5341685
+silesia.tar, huffman literals, advanced one pass, 5341705
silesia.tar, multithreaded with advanced params, advanced one pass, 5122567
-github, level -5, advanced one pass, 204411
+github, level -5, advanced one pass, 204407
github, level -5 with dict, advanced one pass, 46718
github, level -3, advanced one pass, 193253
github, level -3 with dict, advanced one pass, 45395
@@ -421,8 +421,8 @@ github, uncompressed literals, advanced
github, uncompressed literals optimal, advanced one pass, 157227
github, huffman literals, advanced one pass, 142365
github, multithreaded with advanced params, advanced one pass, 165911
-github.tar, level -5, advanced one pass, 52110
-github.tar, level -5 with dict, advanced one pass, 51070
+github.tar, level -5, advanced one pass, 52115
+github.tar, level -5 with dict, advanced one pass, 50974
github.tar, level -3, advanced one pass, 45678
github.tar, level -3 with dict, advanced one pass, 44656
github.tar, level -1, advanced one pass, 42560
@@ -546,11 +546,11 @@ github.tar, uncompressed literals, advanced
github.tar, uncompressed literals optimal, advanced one pass, 35397
github.tar, huffman literals, advanced one pass, 38853
github.tar, multithreaded with advanced params, advanced one pass, 41525
-silesia, level -5, advanced one pass small out, 6852424
-silesia, level -3, advanced one pass small out, 6503413
-silesia, level -1, advanced one pass small out, 6172178
+silesia, level -5, advanced one pass small out, 6857372
+silesia, level -3, advanced one pass small out, 6503412
+silesia, level -1, advanced one pass small out, 6172202
silesia, level 0, advanced one pass small out, 4842075
-silesia, level 1, advanced one pass small out, 5306426
+silesia, level 1, advanced one pass small out, 5306632
silesia, level 3, advanced one pass small out, 4842075
silesia, level 4, advanced one pass small out, 4779186
silesia, level 5 row 1, advanced one pass small out, 4666323
@@ -578,13 +578,13 @@ silesia, small chain log, advanced
silesia, explicit params, advanced one pass small out, 4795432
silesia, uncompressed literals, advanced one pass small out, 5120566
silesia, uncompressed literals optimal, advanced one pass small out, 4319518
-silesia, huffman literals, advanced one pass small out, 5321346
+silesia, huffman literals, advanced one pass small out, 5321369
silesia, multithreaded with advanced params, advanced one pass small out, 5120566
-silesia.tar, level -5, advanced one pass small out, 6853608
-silesia.tar, level -3, advanced one pass small out, 6505969
-silesia.tar, level -1, advanced one pass small out, 6179026
+silesia.tar, level -5, advanced one pass small out, 6861055
+silesia.tar, level -3, advanced one pass small out, 6505483
+silesia.tar, level -1, advanced one pass small out, 6179047
silesia.tar, level 0, advanced one pass small out, 4854086
-silesia.tar, level 1, advanced one pass small out, 5327373
+silesia.tar, level 1, advanced one pass small out, 5327717
silesia.tar, level 3, advanced one pass small out, 4854086
silesia.tar, level 4, advanced one pass small out, 4791503
silesia.tar, level 5 row 1, advanced one pass small out, 4677740
@@ -612,9 +612,9 @@ silesia.tar, small chain log, advanced
silesia.tar, explicit params, advanced one pass small out, 4806855
silesia.tar, uncompressed literals, advanced one pass small out, 5122473
silesia.tar, uncompressed literals optimal, advanced one pass small out, 4310141
-silesia.tar, huffman literals, advanced one pass small out, 5341685
+silesia.tar, huffman literals, advanced one pass small out, 5341705
silesia.tar, multithreaded with advanced params, advanced one pass small out, 5122567
-github, level -5, advanced one pass small out, 204411
+github, level -5, advanced one pass small out, 204407
github, level -5 with dict, advanced one pass small out, 46718
github, level -3, advanced one pass small out, 193253
github, level -3 with dict, advanced one pass small out, 45395
@@ -739,8 +739,8 @@ github, uncompressed literals, advanced
github, uncompressed literals optimal, advanced one pass small out, 157227
github, huffman literals, advanced one pass small out, 142365
github, multithreaded with advanced params, advanced one pass small out, 165911
-github.tar, level -5, advanced one pass small out, 52110
-github.tar, level -5 with dict, advanced one pass small out, 51070
+github.tar, level -5, advanced one pass small out, 52115
+github.tar, level -5 with dict, advanced one pass small out, 50974
github.tar, level -3, advanced one pass small out, 45678
github.tar, level -3 with dict, advanced one pass small out, 44656
github.tar, level -1, advanced one pass small out, 42560
@@ -864,11 +864,11 @@ github.tar, uncompressed literals, advanced
github.tar, uncompressed literals optimal, advanced one pass small out, 35397
github.tar, huffman literals, advanced one pass small out, 38853
github.tar, multithreaded with advanced params, advanced one pass small out, 41525
-silesia, level -5, advanced streaming, 6852424
-silesia, level -3, advanced streaming, 6503413
-silesia, level -1, advanced streaming, 6172179
+silesia, level -5, advanced streaming, 6854744
+silesia, level -3, advanced streaming, 6503319
+silesia, level -1, advanced streaming, 6172207
silesia, level 0, advanced streaming, 4842075
-silesia, level 1, advanced streaming, 5306426
+silesia, level 1, advanced streaming, 5306388
silesia, level 3, advanced streaming, 4842075
silesia, level 4, advanced streaming, 4779186
silesia, level 5 row 1, advanced streaming, 4666323
@@ -896,13 +896,13 @@ silesia, small chain log, advanced
silesia, explicit params, advanced streaming, 4795452
silesia, uncompressed literals, advanced streaming, 5120566
silesia, uncompressed literals optimal, advanced streaming, 4319518
-silesia, huffman literals, advanced streaming, 5321346
+silesia, huffman literals, advanced streaming, 5321370
silesia, multithreaded with advanced params, advanced streaming, 5120566
-silesia.tar, level -5, advanced streaming, 6853609
-silesia.tar, level -3, advanced streaming, 6505969
-silesia.tar, level -1, advanced streaming, 6179028
+silesia.tar, level -5, advanced streaming, 6856523
+silesia.tar, level -3, advanced streaming, 6505954
+silesia.tar, level -1, advanced streaming, 6179056
silesia.tar, level 0, advanced streaming, 4859271
-silesia.tar, level 1, advanced streaming, 5327377
+silesia.tar, level 1, advanced streaming, 5327708
silesia.tar, level 3, advanced streaming, 4859271
silesia.tar, level 4, advanced streaming, 4797470
silesia.tar, level 5 row 1, advanced streaming, 4677748
@@ -930,9 +930,9 @@ silesia.tar, small chain log, advanced
silesia.tar, explicit params, advanced streaming, 4806873
silesia.tar, uncompressed literals, advanced streaming, 5127423
silesia.tar, uncompressed literals optimal, advanced streaming, 4310141
-silesia.tar, huffman literals, advanced streaming, 5341688
+silesia.tar, huffman literals, advanced streaming, 5341712
silesia.tar, multithreaded with advanced params, advanced streaming, 5122567
-github, level -5, advanced streaming, 204411
+github, level -5, advanced streaming, 204407
github, level -5 with dict, advanced streaming, 46718
github, level -3, advanced streaming, 193253
github, level -3 with dict, advanced streaming, 45395
@@ -1057,8 +1057,8 @@ github, uncompressed literals, advanced
github, uncompressed literals optimal, advanced streaming, 157227
github, huffman literals, advanced streaming, 142365
github, multithreaded with advanced params, advanced streaming, 165911
-github.tar, level -5, advanced streaming, 52110
-github.tar, level -5 with dict, advanced streaming, 51070
+github.tar, level -5, advanced streaming, 52152
+github.tar, level -5 with dict, advanced streaming, 51045
github.tar, level -3, advanced streaming, 45678
github.tar, level -3 with dict, advanced streaming, 44656
github.tar, level -1, advanced streaming, 42560
@@ -1182,11 +1182,11 @@ github.tar, uncompressed literals, advanced
github.tar, uncompressed literals optimal, advanced streaming, 35397
github.tar, huffman literals, advanced streaming, 38853
github.tar, multithreaded with advanced params, advanced streaming, 41525
-silesia, level -5, old streaming, 6852424
-silesia, level -3, old streaming, 6503413
-silesia, level -1, old streaming, 6172179
+silesia, level -5, old streaming, 6854744
+silesia, level -3, old streaming, 6503319
+silesia, level -1, old streaming, 6172207
silesia, level 0, old streaming, 4842075
-silesia, level 1, old streaming, 5306426
+silesia, level 1, old streaming, 5306388
silesia, level 3, old streaming, 4842075
silesia, level 4, old streaming, 4779186
silesia, level 5, old streaming, 4666323
@@ -1199,12 +1199,12 @@ silesia, level 19, old stre
silesia, no source size, old streaming, 4842039
silesia, uncompressed literals, old streaming, 4842075
silesia, uncompressed literals optimal, old streaming, 4296686
-silesia, huffman literals, old streaming, 6172179
-silesia.tar, level -5, old streaming, 6853609
-silesia.tar, level -3, old streaming, 6505969
-silesia.tar, level -1, old streaming, 6179028
+silesia, huffman literals, old streaming, 6172207
+silesia.tar, level -5, old streaming, 6856523
+silesia.tar, level -3, old streaming, 6505954
+silesia.tar, level -1, old streaming, 6179056
silesia.tar, level 0, old streaming, 4859271
-silesia.tar, level 1, old streaming, 5327377
+silesia.tar, level 1, old streaming, 5327708
silesia.tar, level 3, old streaming, 4859271
silesia.tar, level 4, old streaming, 4797470
silesia.tar, level 5, old streaming, 4677748
@@ -1217,8 +1217,8 @@ silesia.tar, level 19, old stre
silesia.tar, no source size, old streaming, 4859267
silesia.tar, uncompressed literals, old streaming, 4859271
silesia.tar, uncompressed literals optimal, old streaming, 4267266
-silesia.tar, huffman literals, old streaming, 6179028
-github, level -5, old streaming, 204411
+silesia.tar, huffman literals, old streaming, 6179056
+github, level -5, old streaming, 204407
github, level -5 with dict, old streaming, 46718
github, level -3, old streaming, 193253
github, level -3 with dict, old streaming, 45395
@@ -1251,8 +1251,8 @@ github, no source size with dict, old stre
github, uncompressed literals, old streaming, 136332
github, uncompressed literals optimal, old streaming, 134064
github, huffman literals, old streaming, 175468
-github.tar, level -5, old streaming, 52110
-github.tar, level -5 with dict, old streaming, 51070
+github.tar, level -5, old streaming, 52152
+github.tar, level -5 with dict, old streaming, 51045
github.tar, level -3, old streaming, 45678
github.tar, level -3 with dict, old streaming, 44656
github.tar, level -1, old streaming, 42560
@@ -1284,11 +1284,11 @@ github.tar, no source size with dict, old stre
github.tar, uncompressed literals, old streaming, 38831
github.tar, uncompressed literals optimal, old streaming, 32134
github.tar, huffman literals, old streaming, 42560
-silesia, level -5, old streaming advanced, 6852424
-silesia, level -3, old streaming advanced, 6503413
-silesia, level -1, old streaming advanced, 6172179
+silesia, level -5, old streaming advanced, 6854744
+silesia, level -3, old streaming advanced, 6503319
+silesia, level -1, old streaming advanced, 6172207
silesia, level 0, old streaming advanced, 4842075
-silesia, level 1, old streaming advanced, 5306426
+silesia, level 1, old streaming advanced, 5306388
silesia, level 3, old streaming advanced, 4842075
silesia, level 4, old streaming advanced, 4779186
silesia, level 5, old streaming advanced, 4666323
@@ -1308,13 +1308,13 @@ silesia, small chain log, old stre
silesia, explicit params, old streaming advanced, 4795452
silesia, uncompressed literals, old streaming advanced, 4842075
silesia, uncompressed literals optimal, old streaming advanced, 4296686
-silesia, huffman literals, old streaming advanced, 6172179
+silesia, huffman literals, old streaming advanced, 6172207
silesia, multithreaded with advanced params, old streaming advanced, 4842075
-silesia.tar, level -5, old streaming advanced, 6853609
-silesia.tar, level -3, old streaming advanced, 6505969
-silesia.tar, level -1, old streaming advanced, 6179028
+silesia.tar, level -5, old streaming advanced, 6856523
+silesia.tar, level -3, old streaming advanced, 6505954
+silesia.tar, level -1, old streaming advanced, 6179056
silesia.tar, level 0, old streaming advanced, 4859271
-silesia.tar, level 1, old streaming advanced, 5327377
+silesia.tar, level 1, old streaming advanced, 5327708
silesia.tar, level 3, old streaming advanced, 4859271
silesia.tar, level 4, old streaming advanced, 4797470
silesia.tar, level 5, old streaming advanced, 4677748
@@ -1334,7 +1334,7 @@ silesia.tar, small chain log, old stre
silesia.tar, explicit params, old streaming advanced, 4806873
silesia.tar, uncompressed literals, old streaming advanced, 4859271
silesia.tar, uncompressed literals optimal, old streaming advanced, 4267266
-silesia.tar, huffman literals, old streaming advanced, 6179028
+silesia.tar, huffman literals, old streaming advanced, 6179056
silesia.tar, multithreaded with advanced params, old streaming advanced, 4859271
github, level -5, old streaming advanced, 213265
github, level -5 with dict, old streaming advanced, 49562
@@ -1377,8 +1377,8 @@ github, uncompressed literals, old stre
github, uncompressed literals optimal, old streaming advanced, 134064
github, huffman literals, old streaming advanced, 181107
github, multithreaded with advanced params, old streaming advanced, 141104
-github.tar, level -5, old streaming advanced, 52110
-github.tar, level -5 with dict, old streaming advanced, 50985
+github.tar, level -5, old streaming advanced, 52152
+github.tar, level -5 with dict, old streaming advanced, 50988
github.tar, level -3, old streaming advanced, 45678
github.tar, level -3 with dict, old streaming advanced, 44729
github.tar, level -1, old streaming advanced, 42560
@@ -1433,7 +1433,7 @@ github, level 13 with dict, old stre
github, level 16 with dict, old streaming cdict, 37577
github, level 19 with dict, old streaming cdict, 37576
github, no source size with dict, old streaming cdict, 40654
-github.tar, level -5 with dict, old streaming cdict, 51189
+github.tar, level -5 with dict, old streaming cdict, 51191
github.tar, level -3 with dict, old streaming cdict, 44821
github.tar, level -1 with dict, old streaming cdict, 41775
github.tar, level 0 with dict, old streaming cdict, 37956
From 3620a0a56589c952d248dfccdc96a86b948e9432 Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Thu, 12 May 2022 12:53:15 -0400
Subject: [PATCH 139/472] Nits
---
lib/compress/zstd_double_fast.c | 6 +++---
lib/compress/zstd_fast.c | 23 +++++++++++++++++------
lib/compress/zstd_lazy.c | 6 +++---
3 files changed, 23 insertions(+), 12 deletions(-)
diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c
index 698a97a802f..d8412ef0b7e 100644
--- a/lib/compress/zstd_double_fast.c
+++ b/lib/compress/zstd_double_fast.c
@@ -175,9 +175,9 @@ size_t ZSTD_compressBlock_doubleFast_noDict_generic(
} while (ip1 <= ilimit);
_cleanup:
- /* If offset_1 started invalid (offsetSaved1 > 0) and became valid (offset_1 > 0),
- * rotate saved offsets. */
- offsetSaved2 = ((offsetSaved1 > 0) & (offset_1 > 0)) ? offsetSaved1 : offsetSaved2;
+ /* If offset_1 started invalid (offsetSaved1 != 0) and became valid (offset_1 != 0),
+ * rotate saved offsets. See comment in ZSTD_compressBlock_fast_noDict for more context. */
+ offsetSaved2 = ((offsetSaved1 != 0) && (offset_1 != 0)) ? offsetSaved1 : offsetSaved2;
/* save reps for next block */
rep[0] = offset_1 ? offset_1 : offsetSaved1;
diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c
index 3d3b7d9cb4f..c7d48eb2414 100644
--- a/lib/compress/zstd_fast.c
+++ b/lib/compress/zstd_fast.c
@@ -254,9 +254,20 @@ ZSTD_compressBlock_fast_noDict_generic(
* However, it seems to be a meaningful performance hit to try to search
* them. So let's not. */
- /* If rep_offset1 started invalid (offsetSaved1 > 0) and became valid (rep_offset1 > 0),
- * rotate saved offsets. */
- offsetSaved2 = ((offsetSaved1 > 0) & (rep_offset1 > 0)) ? offsetSaved1 : offsetSaved2;
+ /* When the repcodes are outside of the prefix, we set them to zero before the loop.
+ * When the offsets are still zero, we need to restore them after the block to have a correct
+ * repcode history. If only one offset was invalid, it is easy. The tricky case is when both
+ * offsets were invalid. We need to figure out which offset to refill with.
+ * - If both offsets are zero they are in the same order.
+ * - If both offsets are non-zero, we won't restore the offsets from `offsetSaved[12]`.
+ * - If only one is zero, we need to decide which offset to restore.
+ * - If rep_offset1 is non-zero, then rep_offset2 must be offsetSaved1.
+ * - It is impossible for rep_offset2 to be non-zero.
+ *
+ * So if rep_offset1 started invalid (offsetSaved1 != 0) and became valid (rep_offset1 != 0), then
+ * set rep[0] = rep_offset1 and rep[1] = offsetSaved1.
+ */
+ offsetSaved2 = ((offsetSaved1 != 0) && (rep_offset1 != 0)) ? offsetSaved1 : offsetSaved2;
/* save reps for next block */
rep[0] = rep_offset1 ? rep_offset1 : offsetSaved1;
@@ -762,9 +773,9 @@ static size_t ZSTD_compressBlock_fast_extDict_generic(
* However, it seems to be a meaningful performance hit to try to search
* them. So let's not. */
- /* If offset_1 started invalid (offsetSaved1 > 0) and became valid (offset_1 > 0),
- * rotate saved offsets. */
- offsetSaved2 = ((offsetSaved1 > 0) & (offset_1 > 0)) ? offsetSaved1 : offsetSaved2;
+ /* If offset_1 started invalid (offsetSaved1 != 0) and became valid (offset_1 != 0),
+ * rotate saved offsets. See comment in ZSTD_compressBlock_fast_noDict for more context. */
+ offsetSaved2 = ((offsetSaved1 != 0) && (offset_1 != 0)) ? offsetSaved1 : offsetSaved2;
/* save reps for next block */
rep[0] = offset_1 ? offset_1 : offsetSaved1;
diff --git a/lib/compress/zstd_lazy.c b/lib/compress/zstd_lazy.c
index 9814943d1a9..912b59b9c60 100644
--- a/lib/compress/zstd_lazy.c
+++ b/lib/compress/zstd_lazy.c
@@ -1682,9 +1682,9 @@ ZSTD_compressBlock_lazy_generic(
continue; /* faster when present ... (?) */
} } }
- /* If offset_1 started invalid (offsetSaved1 > 0) and became valid (offset_1 > 0),
- * rotate saved offsets. */
- offsetSaved2 = ((offsetSaved1 > 0) & (offset_1 > 0)) ? offsetSaved1 : offsetSaved2;
+ /* If offset_1 started invalid (offsetSaved1 != 0) and became valid (offset_1 != 0),
+ * rotate saved offsets. See comment in ZSTD_compressBlock_fast_noDict for more context. */
+ offsetSaved2 = ((offsetSaved1 != 0) && (offset_1 != 0)) ? offsetSaved1 : offsetSaved2;
/* save reps for next block */
rep[0] = offset_1 ? offset_1 : offsetSaved1;
From 14894d63c1d96667e7bb62666a52f3e20e63e23d Mon Sep 17 00:00:00 2001
From: Talha Khan
Date: Fri, 20 May 2022 16:53:48 +0800
Subject: [PATCH 140/472] Typo in man
---
programs/zstd.1 | 2 +-
programs/zstd.1.md | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/programs/zstd.1 b/programs/zstd.1
index c89060c7dac..5d037b81597 100644
--- a/programs/zstd.1
+++ b/programs/zstd.1
@@ -250,7 +250,7 @@ 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
+\fBOutput Format:\fR CompressionLevel#Filename : InputSize \-> 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"
diff --git a/programs/zstd.1.md b/programs/zstd.1.md
index 4e176f39950..d415211d6d1 100644
--- a/programs/zstd.1.md
+++ b/programs/zstd.1.md
@@ -473,7 +473,7 @@ BENCHMARK
* `--priority=rt`:
set process priority to real-time
-**Output Format:** CompressionLevel#Filename : IntputSize -> OutputSize (CompressionRatio), CompressionSpeed, DecompressionSpeed
+**Output Format:** CompressionLevel#Filename : InputSize -> 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.
From e11783b04d1c49678bb4f95a4ecaa26323bd823d Mon Sep 17 00:00:00 2001
From: Danila Kutenin
Date: Sun, 22 May 2022 10:34:33 +0000
Subject: [PATCH 141/472] [lazy] Optimize ZSTD_row_getMatchMask for level 8-10
We found that movemask is not used properly or consumes too much CPU.
This effort helps to optimize the movemask emulation on ARM.
For level 8-9 we saw 3-5% improvements. For level 10 we say 1.5%
improvement.
The key idea is not to use pure movemasks but to have groups of bits.
For rowEntries == 16, 32 we are going to have groups of size 4 and 2
respectively. It means that each bit will be duplicated within the group
Then we do AND to have only one bit set in the group so that iteration
with lowering bit `a &= (a - 1)` works as well.
Also, aarch64 does not have rotate instructions for 16 bit, only for 32
and 64, that's why we see more improvements for level 8-9.
vshrn_n_u16 instruction is used to achieve that: vshrn_n_u16 shifts by
4 every u16 and narrows to 8 lower bits. See the picture below. It's
also used in
[Folly](https://github.com/facebook/folly/blob/c5702590080aa5d0e8d666d91861d64634065132/folly/container/detail/F14Table.h#L446).
It also uses 2 cycles according to Neoverse-N{1,2} guidelines.
64 bit movemask is already well optimized. We have ongoing experiments
but were not able to validate other implementations work reliably faster.
---
lib/compress/zstd_lazy.c | 95 +++++++++++++++++++++++++---------------
1 file changed, 60 insertions(+), 35 deletions(-)
diff --git a/lib/compress/zstd_lazy.c b/lib/compress/zstd_lazy.c
index 912b59b9c60..d897832bae0 100644
--- a/lib/compress/zstd_lazy.c
+++ b/lib/compress/zstd_lazy.c
@@ -974,20 +974,45 @@ ZSTD_row_getSSEMask(int nbChunks, const BYTE* const src, const BYTE tag, const U
}
#endif
-/* Returns a ZSTD_VecMask (U32) that has the nth bit set to 1 if the newly-computed "tag" matches
- * the hash at the nth position in a row of the tagTable.
- * Each row is a circular buffer beginning at the value of "head". So we must rotate the "matches" bitfield
- * to match up with the actual layout of the entries within the hashTable */
+/* Returns the mask width of bits group of which will be set to 1. Given not all
+ * architectures have easy movemask instruction, this helps to iterate over
+ * groups of bits easier and faster.
+ */
+FORCE_INLINE_TEMPLATE U32
+ZSTD_row_matchMaskGroupWidth(const U32 rowEntries) {
+ assert((rowEntries == 16) || (rowEntries == 32) || rowEntries == 64);
+ assert(rowEntries <= ZSTD_ROW_HASH_MAX_ENTRIES);
+#if defined(ZSTD_ARCH_ARM_NEON)
+ if (rowEntries == 16) {
+ return 4;
+ }
+ if (rowEntries == 32) {
+ return 2;
+ }
+ if (rowEntries == 64) {
+ return 1;
+ }
+#endif
+ return 1;
+}
+
+/* Returns a ZSTD_VecMask (U64) that has the nth group (determined by
+ * ZSTD_row_matchMaskGroupWidth) of bits set to 1 if the newly-computed "tag"
+ * matches the hash at the nth position in a row of the tagTable.
+ * Each row is a circular buffer beginning at the value of "headGrouped". So we
+ * must rotate the "matches" bitfield to match up with the actual layout of the
+ * entries within the hashTable */
FORCE_INLINE_TEMPLATE ZSTD_VecMask
-ZSTD_row_getMatchMask(const BYTE* const tagRow, const BYTE tag, const U32 head, const U32 rowEntries)
+ZSTD_row_getMatchMask(const BYTE* const tagRow, const BYTE tag, const U32 headGrouped, const U32 rowEntries)
{
const BYTE* const src = tagRow + ZSTD_ROW_HASH_TAG_OFFSET;
assert((rowEntries == 16) || (rowEntries == 32) || rowEntries == 64);
assert(rowEntries <= ZSTD_ROW_HASH_MAX_ENTRIES);
+ assert(ZSTD_row_matchMaskGroupWidth(rowEntries) * rowEntries <= sizeof(ZSTD_VecMask) * 8);
#if defined(ZSTD_ARCH_X86_SSE2)
- return ZSTD_row_getSSEMask(rowEntries / 16, src, tag, head);
+ return ZSTD_row_getSSEMask(rowEntries / 16, src, tag, headGrouped);
#else /* SW or NEON-LE */
@@ -995,30 +1020,29 @@ ZSTD_row_getMatchMask(const BYTE* const tagRow, const BYTE tag, const U32 head,
/* This NEON path only works for little endian - otherwise use SWAR below */
if (MEM_isLittleEndian()) {
if (rowEntries == 16) {
+ /* vshrn_n_u16 shifts by 4 every u16 and narrows to 8 lower bits.
+ * After that groups of 4 bits represent the equalMask. We lower
+ * all bits except the highest in these groups by doing AND with
+ * 0x88 = 0b10001000.
+ */
const uint8x16_t chunk = vld1q_u8(src);
const uint16x8_t equalMask = vreinterpretq_u16_u8(vceqq_u8(chunk, vdupq_n_u8(tag)));
- const uint16x8_t t0 = vshlq_n_u16(equalMask, 7);
- const uint32x4_t t1 = vreinterpretq_u32_u16(vsriq_n_u16(t0, t0, 14));
- const uint64x2_t t2 = vreinterpretq_u64_u32(vshrq_n_u32(t1, 14));
- const uint8x16_t t3 = vreinterpretq_u8_u64(vsraq_n_u64(t2, t2, 28));
- const U16 hi = (U16)vgetq_lane_u8(t3, 8);
- const U16 lo = (U16)vgetq_lane_u8(t3, 0);
- return ZSTD_rotateRight_U16((hi << 8) | lo, head);
+ const uint8x8_t res = vshrn_n_u16(equalMask, 4);
+ const U64 matches = vget_lane_u64(vreinterpret_u64_u8(res), 0);
+ return ZSTD_rotateRight_U64(matches, headGrouped) & 0x8888888888888888ull;
} else if (rowEntries == 32) {
- const uint16x8x2_t chunk = vld2q_u16((const U16*)(const void*)src);
+ /* Same idea as with rowEntries == 16 but doing AND with
+ * 0x55 = 0b01010101.
+ */
+ const uint16x8x2_t chunk = vld2q_u16((const uint16_t*)(const void*)src);
const uint8x16_t chunk0 = vreinterpretq_u8_u16(chunk.val[0]);
const uint8x16_t chunk1 = vreinterpretq_u8_u16(chunk.val[1]);
- const uint8x16_t equalMask0 = vceqq_u8(chunk0, vdupq_n_u8(tag));
- const uint8x16_t equalMask1 = vceqq_u8(chunk1, vdupq_n_u8(tag));
- const int8x8_t pack0 = vqmovn_s16(vreinterpretq_s16_u8(equalMask0));
- const int8x8_t pack1 = vqmovn_s16(vreinterpretq_s16_u8(equalMask1));
- const uint8x8_t t0 = vreinterpret_u8_s8(pack0);
- const uint8x8_t t1 = vreinterpret_u8_s8(pack1);
- const uint8x8_t t2 = vsri_n_u8(t1, t0, 2);
- const uint8x8x2_t t3 = vuzp_u8(t2, t0);
- const uint8x8_t t4 = vsri_n_u8(t3.val[1], t3.val[0], 4);
- const U32 matches = vget_lane_u32(vreinterpret_u32_u8(t4), 0);
- return ZSTD_rotateRight_U32(matches, head);
+ const uint8x16_t dup = vdupq_n_u8(tag);
+ const uint8x8_t t0 = vshrn_n_u16(vreinterpretq_u16_u8(vceqq_u8(chunk0, dup)), 6);
+ const uint8x8_t t1 = vshrn_n_u16(vreinterpretq_u16_u8(vceqq_u8(chunk1, dup)), 6);
+ const uint8x8_t res = vsli_n_u8(t0, t1, 4);
+ const U64 matches = vget_lane_u64(vreinterpret_u64_u8(res), 0) ;
+ return ZSTD_rotateRight_U64(matches, headGrouped) & 0x5555555555555555ull;
} else { /* rowEntries == 64 */
const uint8x16x4_t chunk = vld4q_u8(src);
const uint8x16_t dup = vdupq_n_u8(tag);
@@ -1033,7 +1057,7 @@ ZSTD_row_getMatchMask(const BYTE* const tagRow, const BYTE tag, const U32 head,
const uint8x16_t t3 = vsriq_n_u8(t2, t2, 4);
const uint8x8_t t4 = vshrn_n_u16(vreinterpretq_u16_u8(t3), 4);
const U64 matches = vget_lane_u64(vreinterpret_u64_u8(t4), 0);
- return ZSTD_rotateRight_U64(matches, head);
+ return ZSTD_rotateRight_U64(matches, headGrouped);
}
}
# endif /* ZSTD_ARCH_ARM_NEON */
@@ -1071,11 +1095,11 @@ ZSTD_row_getMatchMask(const BYTE* const tagRow, const BYTE tag, const U32 head,
}
matches = ~matches;
if (rowEntries == 16) {
- return ZSTD_rotateRight_U16((U16)matches, head);
+ return ZSTD_rotateRight_U16((U16)matches, headGrouped);
} else if (rowEntries == 32) {
- return ZSTD_rotateRight_U32((U32)matches, head);
+ return ZSTD_rotateRight_U32((U32)matches, headGrouped);
} else {
- return ZSTD_rotateRight_U64((U64)matches, head);
+ return ZSTD_rotateRight_U64((U64)matches, headGrouped);
}
}
#endif
@@ -1123,6 +1147,7 @@ size_t ZSTD_RowFindBestMatch(
const U32 rowEntries = (1U << rowLog);
const U32 rowMask = rowEntries - 1;
const U32 cappedSearchLog = MIN(cParams->searchLog, rowLog); /* nb of searches is capped at nb entries per row */
+ const U32 groupWidth = ZSTD_row_matchMaskGroupWidth(rowEntries);
U32 nbAttempts = 1U << cappedSearchLog;
size_t ml=4-1;
@@ -1165,15 +1190,15 @@ size_t ZSTD_RowFindBestMatch(
U32 const tag = hash & ZSTD_ROW_HASH_TAG_MASK;
U32* const row = hashTable + relRow;
BYTE* tagRow = (BYTE*)(tagTable + relRow);
- U32 const head = *tagRow & rowMask;
+ U32 const headGrouped = (*tagRow & rowMask) * groupWidth;
U32 matchBuffer[ZSTD_ROW_HASH_MAX_ENTRIES];
size_t numMatches = 0;
size_t currMatch = 0;
- ZSTD_VecMask matches = ZSTD_row_getMatchMask(tagRow, (BYTE)tag, head, rowEntries);
+ ZSTD_VecMask matches = ZSTD_row_getMatchMask(tagRow, (BYTE)tag, headGrouped, rowEntries);
/* Cycle through the matches and prefetch */
for (; (matches > 0) && (nbAttempts > 0); --nbAttempts, matches &= (matches - 1)) {
- U32 const matchPos = (head + ZSTD_VecMask_next(matches)) & rowMask;
+ U32 const matchPos = ((headGrouped + ZSTD_VecMask_next(matches)) / groupWidth) & rowMask;
U32 const matchIndex = row[matchPos];
assert(numMatches < rowEntries);
if (matchIndex < lowLimit)
@@ -1234,14 +1259,14 @@ size_t ZSTD_RowFindBestMatch(
const U32 dmsSize = (U32)(dmsEnd - dmsBase);
const U32 dmsIndexDelta = dictLimit - dmsSize;
- { U32 const head = *dmsTagRow & rowMask;
+ { U32 const headGrouped = (*dmsTagRow & rowMask) * groupWidth;
U32 matchBuffer[ZSTD_ROW_HASH_MAX_ENTRIES];
size_t numMatches = 0;
size_t currMatch = 0;
- ZSTD_VecMask matches = ZSTD_row_getMatchMask(dmsTagRow, (BYTE)dmsTag, head, rowEntries);
+ ZSTD_VecMask matches = ZSTD_row_getMatchMask(dmsTagRow, (BYTE)dmsTag, headGrouped, rowEntries);
for (; (matches > 0) && (nbAttempts > 0); --nbAttempts, matches &= (matches - 1)) {
- U32 const matchPos = (head + ZSTD_VecMask_next(matches)) & rowMask;
+ U32 const matchPos = ((headGrouped + ZSTD_VecMask_next(matches)) / groupWidth) & rowMask;
U32 const matchIndex = dmsRow[matchPos];
if (matchIndex < dmsLowestIndex)
break;
From 778f639be961ab6c83ab84e0f3fd3ecb0fd09f4f Mon Sep 17 00:00:00 2001
From: Danila Kutenin
Date: Sun, 22 May 2022 10:50:33 +0000
Subject: [PATCH 142/472] Disable unused variable warning
---
lib/compress/zstd_lazy.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/lib/compress/zstd_lazy.c b/lib/compress/zstd_lazy.c
index d897832bae0..5e9a06b9eee 100644
--- a/lib/compress/zstd_lazy.c
+++ b/lib/compress/zstd_lazy.c
@@ -982,6 +982,7 @@ FORCE_INLINE_TEMPLATE U32
ZSTD_row_matchMaskGroupWidth(const U32 rowEntries) {
assert((rowEntries == 16) || (rowEntries == 32) || rowEntries == 64);
assert(rowEntries <= ZSTD_ROW_HASH_MAX_ENTRIES);
+ (void)rowEntries;
#if defined(ZSTD_ARCH_ARM_NEON)
if (rowEntries == 16) {
return 4;
From 6b561d230fd71de732346838752051c349c52f2e Mon Sep 17 00:00:00 2001
From: Danila Kutenin
Date: Mon, 23 May 2022 14:49:35 +0000
Subject: [PATCH 143/472] Move NEON version to a separate function and fix
indentation
---
lib/compress/zstd_lazy.c | 129 +++++++++++++++++++++------------------
1 file changed, 69 insertions(+), 60 deletions(-)
diff --git a/lib/compress/zstd_lazy.c b/lib/compress/zstd_lazy.c
index 5e9a06b9eee..6404d29f998 100644
--- a/lib/compress/zstd_lazy.c
+++ b/lib/compress/zstd_lazy.c
@@ -954,6 +954,29 @@ void ZSTD_row_update(ZSTD_matchState_t* const ms, const BYTE* ip) {
ZSTD_row_update_internal(ms, ip, mls, rowLog, rowMask, 0 /* don't use cache */);
}
+/* Returns the mask width of bits group of which will be set to 1. Given not all
+ * architectures have easy movemask instruction, this helps to iterate over
+ * groups of bits easier and faster.
+ */
+FORCE_INLINE_TEMPLATE U32
+ZSTD_row_matchMaskGroupWidth(const U32 rowEntries)
+{
+ assert((rowEntries == 16) || (rowEntries == 32) || rowEntries == 64);
+ assert(rowEntries <= ZSTD_ROW_HASH_MAX_ENTRIES);
+#if defined(ZSTD_ARCH_ARM_NEON)
+ if (rowEntries == 16) {
+ return 4;
+ }
+ if (rowEntries == 32) {
+ return 2;
+ }
+ if (rowEntries == 64) {
+ return 1;
+ }
+#endif
+ return 1;
+}
+
#if defined(ZSTD_ARCH_X86_SSE2)
FORCE_INLINE_TEMPLATE ZSTD_VecMask
ZSTD_row_getSSEMask(int nbChunks, const BYTE* const src, const BYTE tag, const U32 head)
@@ -974,28 +997,53 @@ ZSTD_row_getSSEMask(int nbChunks, const BYTE* const src, const BYTE tag, const U
}
#endif
-/* Returns the mask width of bits group of which will be set to 1. Given not all
- * architectures have easy movemask instruction, this helps to iterate over
- * groups of bits easier and faster.
- */
-FORCE_INLINE_TEMPLATE U32
-ZSTD_row_matchMaskGroupWidth(const U32 rowEntries) {
- assert((rowEntries == 16) || (rowEntries == 32) || rowEntries == 64);
- assert(rowEntries <= ZSTD_ROW_HASH_MAX_ENTRIES);
- (void)rowEntries;
#if defined(ZSTD_ARCH_ARM_NEON)
- if (rowEntries == 16) {
- return 4;
- }
- if (rowEntries == 32) {
- return 2;
- }
- if (rowEntries == 64) {
- return 1;
- }
-#endif
- return 1;
+FORCE_INLINE_TEMPLATE ZSTD_VecMask
+ZSTD_row_getNEONMask(const U32 rowEntries, const BYTE* const src, const BYTE tag, const U32 headGrouped)
+{
+ assert((rowEntries == 16) || (rowEntries == 32) || rowEntries == 64);
+ if (rowEntries == 16) {
+ /* vshrn_n_u16 shifts by 4 every u16 and narrows to 8 lower bits.
+ * After that groups of 4 bits represent the equalMask. We lower
+ * all bits except the highest in these groups by doing AND with
+ * 0x88 = 0b10001000.
+ */
+ const uint8x16_t chunk = vld1q_u8(src);
+ const uint16x8_t equalMask = vreinterpretq_u16_u8(vceqq_u8(chunk, vdupq_n_u8(tag)));
+ const uint8x8_t res = vshrn_n_u16(equalMask, 4);
+ const U64 matches = vget_lane_u64(vreinterpret_u64_u8(res), 0);
+ return ZSTD_rotateRight_U64(matches, headGrouped) & 0x8888888888888888ull;
+ } else if (rowEntries == 32) {
+ /* Same idea as with rowEntries == 16 but doing AND with
+ * 0x55 = 0b01010101.
+ */
+ const uint16x8x2_t chunk = vld2q_u16((const uint16_t*)(const void*)src);
+ const uint8x16_t chunk0 = vreinterpretq_u8_u16(chunk.val[0]);
+ const uint8x16_t chunk1 = vreinterpretq_u8_u16(chunk.val[1]);
+ const uint8x16_t dup = vdupq_n_u8(tag);
+ const uint8x8_t t0 = vshrn_n_u16(vreinterpretq_u16_u8(vceqq_u8(chunk0, dup)), 6);
+ const uint8x8_t t1 = vshrn_n_u16(vreinterpretq_u16_u8(vceqq_u8(chunk1, dup)), 6);
+ const uint8x8_t res = vsli_n_u8(t0, t1, 4);
+ const U64 matches = vget_lane_u64(vreinterpret_u64_u8(res), 0) ;
+ return ZSTD_rotateRight_U64(matches, headGrouped) & 0x5555555555555555ull;
+ } else { /* rowEntries == 64 */
+ const uint8x16x4_t chunk = vld4q_u8(src);
+ const uint8x16_t dup = vdupq_n_u8(tag);
+ const uint8x16_t cmp0 = vceqq_u8(chunk.val[0], dup);
+ const uint8x16_t cmp1 = vceqq_u8(chunk.val[1], dup);
+ const uint8x16_t cmp2 = vceqq_u8(chunk.val[2], dup);
+ const uint8x16_t cmp3 = vceqq_u8(chunk.val[3], dup);
+
+ const uint8x16_t t0 = vsriq_n_u8(cmp1, cmp0, 1);
+ const uint8x16_t t1 = vsriq_n_u8(cmp3, cmp2, 1);
+ const uint8x16_t t2 = vsriq_n_u8(t1, t0, 2);
+ const uint8x16_t t3 = vsriq_n_u8(t2, t2, 4);
+ const uint8x8_t t4 = vshrn_n_u16(vreinterpretq_u16_u8(t3), 4);
+ const U64 matches = vget_lane_u64(vreinterpret_u64_u8(t4), 0);
+ return ZSTD_rotateRight_U64(matches, headGrouped);
+ }
}
+#endif
/* Returns a ZSTD_VecMask (U64) that has the nth group (determined by
* ZSTD_row_matchMaskGroupWidth) of bits set to 1 if the newly-computed "tag"
@@ -1020,46 +1068,7 @@ ZSTD_row_getMatchMask(const BYTE* const tagRow, const BYTE tag, const U32 headGr
# if defined(ZSTD_ARCH_ARM_NEON)
/* This NEON path only works for little endian - otherwise use SWAR below */
if (MEM_isLittleEndian()) {
- if (rowEntries == 16) {
- /* vshrn_n_u16 shifts by 4 every u16 and narrows to 8 lower bits.
- * After that groups of 4 bits represent the equalMask. We lower
- * all bits except the highest in these groups by doing AND with
- * 0x88 = 0b10001000.
- */
- const uint8x16_t chunk = vld1q_u8(src);
- const uint16x8_t equalMask = vreinterpretq_u16_u8(vceqq_u8(chunk, vdupq_n_u8(tag)));
- const uint8x8_t res = vshrn_n_u16(equalMask, 4);
- const U64 matches = vget_lane_u64(vreinterpret_u64_u8(res), 0);
- return ZSTD_rotateRight_U64(matches, headGrouped) & 0x8888888888888888ull;
- } else if (rowEntries == 32) {
- /* Same idea as with rowEntries == 16 but doing AND with
- * 0x55 = 0b01010101.
- */
- const uint16x8x2_t chunk = vld2q_u16((const uint16_t*)(const void*)src);
- const uint8x16_t chunk0 = vreinterpretq_u8_u16(chunk.val[0]);
- const uint8x16_t chunk1 = vreinterpretq_u8_u16(chunk.val[1]);
- const uint8x16_t dup = vdupq_n_u8(tag);
- const uint8x8_t t0 = vshrn_n_u16(vreinterpretq_u16_u8(vceqq_u8(chunk0, dup)), 6);
- const uint8x8_t t1 = vshrn_n_u16(vreinterpretq_u16_u8(vceqq_u8(chunk1, dup)), 6);
- const uint8x8_t res = vsli_n_u8(t0, t1, 4);
- const U64 matches = vget_lane_u64(vreinterpret_u64_u8(res), 0) ;
- return ZSTD_rotateRight_U64(matches, headGrouped) & 0x5555555555555555ull;
- } else { /* rowEntries == 64 */
- const uint8x16x4_t chunk = vld4q_u8(src);
- const uint8x16_t dup = vdupq_n_u8(tag);
- const uint8x16_t cmp0 = vceqq_u8(chunk.val[0], dup);
- const uint8x16_t cmp1 = vceqq_u8(chunk.val[1], dup);
- const uint8x16_t cmp2 = vceqq_u8(chunk.val[2], dup);
- const uint8x16_t cmp3 = vceqq_u8(chunk.val[3], dup);
-
- const uint8x16_t t0 = vsriq_n_u8(cmp1, cmp0, 1);
- const uint8x16_t t1 = vsriq_n_u8(cmp3, cmp2, 1);
- const uint8x16_t t2 = vsriq_n_u8(t1, t0, 2);
- const uint8x16_t t3 = vsriq_n_u8(t2, t2, 4);
- const uint8x8_t t4 = vshrn_n_u16(vreinterpretq_u16_u8(t3), 4);
- const U64 matches = vget_lane_u64(vreinterpret_u64_u8(t4), 0);
- return ZSTD_rotateRight_U64(matches, headGrouped);
- }
+ return ZSTD_row_getNEONMask(rowEntries, src, tag, headGrouped);
}
# endif /* ZSTD_ARCH_ARM_NEON */
/* SWAR */
From 9166c6ae204950b22c8fd5c0fb0cd2ee4c4b5abf Mon Sep 17 00:00:00 2001
From: Danila Kutenin
Date: Mon, 23 May 2022 14:51:47 +0000
Subject: [PATCH 144/472] Again unused error warning. Fixed
---
lib/compress/zstd_lazy.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/lib/compress/zstd_lazy.c b/lib/compress/zstd_lazy.c
index 6404d29f998..6f8d11e145b 100644
--- a/lib/compress/zstd_lazy.c
+++ b/lib/compress/zstd_lazy.c
@@ -963,6 +963,7 @@ ZSTD_row_matchMaskGroupWidth(const U32 rowEntries)
{
assert((rowEntries == 16) || (rowEntries == 32) || rowEntries == 64);
assert(rowEntries <= ZSTD_ROW_HASH_MAX_ENTRIES);
+ (void)rowEntries;
#if defined(ZSTD_ARCH_ARM_NEON)
if (rowEntries == 16) {
return 4;
From f33ccd2d1b7e7a1b20ca721c5d05193b2eb5e637 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Tue, 24 May 2022 04:47:49 -0700
Subject: [PATCH 145/472] fix small error in format documentation example
reported by @dkcasset
fix #3142
---
doc/zstd_compression_format.md | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/doc/zstd_compression_format.md b/doc/zstd_compression_format.md
index cd53b7f9421..419de38eb81 100644
--- a/doc/zstd_compression_format.md
+++ b/doc/zstd_compression_format.md
@@ -945,14 +945,14 @@ sequences are applied to them:
|:--------------:|:-----------------:|:------------------:|:------------------:|:------------------:|:-----------------------:|
| | | 1 | 4 | 8 | starting values |
| 1114 | 11 | 1111 | 1 | 4 | non-repeat |
-| 1 | 22 | 1111 | 1 | 4 | repeat 1; no change |
+| 1 | 22 | 1111 | 1 | 4 | repeat 1: no change |
| 2225 | 22 | 2222 | 1111 | 1 | non-repeat |
| 1114 | 111 | 1111 | 2222 | 1111 | non-repeat |
| 3336 | 33 | 3333 | 1111 | 2222 | non-repeat |
-| 2 | 22 | 1111 | 3333 | 2222 | repeat 2; swap 1 & 2 |
-| 3 | 33 | 2222 | 1111 | 3333 | repeat 3; rotate 3 to 1 |
-| 3 | 0 | 2221 | 2222 | 1111 | insert resolved offset |
-| 1 | 0 | 2222 | 2221 | 3333 | repeat 2 |
+| 2 | 22 | 1111 | 3333 | 2222 | repeat 2: swap 1 & 2 |
+| 3 | 33 | 2222 | 1111 | 3333 | repeat 3: rotate 3 to 1 |
+| 3 | 0 | 2221 | 2222 | 1111 | special case : insert `repeat1 - 1` |
+| 1 | 0 | 2222 | 2221 | 1111 | == repeat 2 |
Skippable Frames
From d7249dafb43825dc2048a6bdce4e3c9b7606e0ae Mon Sep 17 00:00:00 2001
From: Jun He
Date: Wed, 25 May 2022 22:26:41 +0800
Subject: [PATCH 146/472] common: apply two stage copy to aarch64
On aarch64 ZSTD_wildcopy uses a simple loop to do
16B based memory copy. There is existing optimized
two stage copy that can achieve better performance.
By applying this to aarch64 it is also observed ~1%
uplift in silesia corpus.
Signed-off-by: Jun He
Change-Id: Ic1253308e7a8a7df2d08963ba544e086c81ce8be
---
lib/common/zstd_internal.h | 7 -------
1 file changed, 7 deletions(-)
diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h
index 8e2b84a2365..e76b8e19d64 100644
--- a/lib/common/zstd_internal.h
+++ b/lib/common/zstd_internal.h
@@ -235,12 +235,6 @@ void ZSTD_wildcopy(void* dst, const void* src, ptrdiff_t length, ZSTD_overlap_e
* one COPY16() in the first call. Then, do two calls per loop since
* at that point it is more likely to have a high trip count.
*/
-#ifdef __aarch64__
- do {
- COPY16(op, ip);
- }
- while (op < oend);
-#else
ZSTD_copy16(op, ip);
if (16 >= length) return;
op += 16;
@@ -250,7 +244,6 @@ void ZSTD_wildcopy(void* dst, const void* src, ptrdiff_t length, ZSTD_overlap_e
COPY16(op, ip);
}
while (op < oend);
-#endif
}
}
From 95073b1af1c15a9c3a5344d2e98ff27f41c1d872 Mon Sep 17 00:00:00 2001
From: Ma Lin
Date: Mon, 30 May 2022 08:18:54 +0800
Subject: [PATCH 147/472] fix leaking thread handles on Windows
On Windows, thread handle should be closed explicitly.
Co-authored-by: luben karavelov
---
lib/common/threading.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/lib/common/threading.c b/lib/common/threading.c
index 92cf57c195a..4f69f766286 100644
--- a/lib/common/threading.c
+++ b/lib/common/threading.c
@@ -63,6 +63,8 @@ int ZSTD_pthread_join(ZSTD_pthread_t thread, void **value_ptr)
if (!thread.handle) return 0;
result = WaitForSingleObject(thread.handle, INFINITE);
+ CloseHandle(thread.handle);
+
switch (result) {
case WAIT_OBJECT_0:
if (value_ptr) *value_ptr = thread.arg;
From 5081ccb05620bdec6e07464213f871bc0381a63f Mon Sep 17 00:00:00 2001
From: ihsinme
Date: Mon, 30 May 2022 14:08:19 +0300
Subject: [PATCH 148/472] Update zstd_compress.c
---
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 e43bbec44fc..f60fb4351d8 100644
--- a/lib/compress/zstd_compress.c
+++ b/lib/compress/zstd_compress.c
@@ -5873,7 +5873,7 @@ ZSTD_copySequencesToSeqStoreExplicitBlockDelim(ZSTD_CCtx* cctx,
dictSize = 0;
}
ZSTD_memcpy(updatedRepcodes.rep, cctx->blockState.prevCBlock->rep, sizeof(repcodes_t));
- for (; (inSeqs[idx].matchLength != 0 || inSeqs[idx].offset != 0) && idx < inSeqsSize; ++idx) {
+ for (; idx < inSeqsSize && (inSeqs[idx].matchLength != 0 || inSeqs[idx].offset != 0); ++idx) {
U32 const litLength = inSeqs[idx].litLength;
U32 const ll0 = (litLength == 0);
U32 const matchLength = inSeqs[idx].matchLength;
From 2491c65937d42561ef9238422929213751adcc10 Mon Sep 17 00:00:00 2001
From: Jun He
Date: Mon, 23 May 2022 14:25:10 +0800
Subject: [PATCH 149/472] dec: adjust seqSymbol load on aarch64
ZSTD_seqSymbol is a structure with total of 64 bits
wide. So it can be loaded in one operation and
extract its fields by simply shifting or extracting
on aarch64.
GCC doesn't recognize this and generates more
unnecessary ldr/ldrb/ldrh operations that cause
performance drop.
With this change it is observed 2~4% uplift of
silesia and 2.5~6% of cantrbry @L8 on Arm N1.
Signed-off-by: Jun He
Change-Id: I7748909204cf78a17eb9d4f2333692d53239daa8
---
lib/decompress/zstd_decompress_block.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/lib/decompress/zstd_decompress_block.c b/lib/decompress/zstd_decompress_block.c
index 7c046dab506..466e83b6be1 100644
--- a/lib/decompress/zstd_decompress_block.c
+++ b/lib/decompress/zstd_decompress_block.c
@@ -1170,9 +1170,27 @@ FORCE_INLINE_TEMPLATE seq_t
ZSTD_decodeSequence(seqState_t* seqState, const ZSTD_longOffset_e longOffsets)
{
seq_t seq;
+ /*
+ * ZSTD_seqSymbol is a structure with a total of 64 bits wide. So it can be
+ * loaded in one operation and extracted its fields by simply shifting or
+ * bit-extracting on aarch64.
+ * GCC doesn't recognize this and generates more unnecessary ldr/ldrb/ldrh
+ * operations that cause performance drop. This can be avoided by using this
+ * ZSTD_memcpy hack.
+ */
+#if defined(__aarch64__) && (defined(__GNUC__) && !defined(__clang__))
+ ZSTD_seqSymbol llDInfoS, mlDInfoS, ofDInfoS;
+ ZSTD_seqSymbol* const llDInfo = &llDInfoS;
+ ZSTD_seqSymbol* const mlDInfo = &mlDInfoS;
+ ZSTD_seqSymbol* const ofDInfo = &ofDInfoS;
+ ZSTD_memcpy(llDInfo, seqState->stateLL.table + seqState->stateLL.state, sizeof(ZSTD_seqSymbol));
+ ZSTD_memcpy(mlDInfo, seqState->stateML.table + seqState->stateML.state, sizeof(ZSTD_seqSymbol));
+ ZSTD_memcpy(ofDInfo, seqState->stateOffb.table + seqState->stateOffb.state, sizeof(ZSTD_seqSymbol));
+#else
const ZSTD_seqSymbol* const llDInfo = seqState->stateLL.table + seqState->stateLL.state;
const ZSTD_seqSymbol* const mlDInfo = seqState->stateML.table + seqState->stateML.state;
const ZSTD_seqSymbol* const ofDInfo = seqState->stateOffb.table + seqState->stateOffb.state;
+#endif
seq.matchLength = mlDInfo->baseValue;
seq.litLength = llDInfo->baseValue;
{ U32 const ofBase = ofDInfo->baseValue;
From 7c05b9aec3e18bf733cccff6dda97ea269bb8594 Mon Sep 17 00:00:00 2001
From: Nick Terrell
Date: Mon, 6 Jun 2022 11:56:13 -0700
Subject: [PATCH 150/472] Remove expensive assert in --rsyncable hot loop
This assert slows the loop down by 10x. We can get similar
coverage by asserting at the beginning & end of the loop.
We need this fix because Debian compiles zstd with asserts
enabled. Separately, we should ask them why, and if they would
consider disabling asserts in their builds. Since we don't
optimize for assert enabled builds.
Fixes Issue #3150.
---
lib/compress/zstdmt_compress.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c
index 4ac2249bd3e..0c10eb603df 100644
--- a/lib/compress/zstdmt_compress.c
+++ b/lib/compress/zstdmt_compress.c
@@ -1761,17 +1761,24 @@ findSynchronizationPoint(ZSTDMT_CCtx const* mtctx, ZSTD_inBuffer const input)
* then a block will be emitted anyways, but this is okay, since if we
* are already synchronized we will remain synchronized.
*/
+ assert(pos < RSYNC_LENGTH || ZSTD_rollingHash_compute(istart + pos - RSYNC_LENGTH, RSYNC_LENGTH) == hash);
for (; pos < syncPoint.toLoad; ++pos) {
BYTE const toRemove = pos < RSYNC_LENGTH ? prev[pos] : istart[pos - RSYNC_LENGTH];
- assert(pos < RSYNC_LENGTH || ZSTD_rollingHash_compute(istart + pos - RSYNC_LENGTH, RSYNC_LENGTH) == hash);
+ /* This assert is very expensive, and Debian compiles with asserts enabled.
+ * So disable it for now. We can get similar coverage by checking it at the
+ * beginning & end of the loop.
+ * assert(pos < RSYNC_LENGTH || ZSTD_rollingHash_compute(istart + pos - RSYNC_LENGTH, RSYNC_LENGTH) == hash);
+ */
hash = ZSTD_rollingHash_rotate(hash, toRemove, istart[pos], primePower);
assert(mtctx->inBuff.filled + pos >= RSYNC_MIN_BLOCK_SIZE);
if ((hash & hitMask) == hitMask) {
syncPoint.toLoad = pos + 1;
syncPoint.flush = 1;
+ ++pos; /* for assert */
break;
}
}
+ assert(pos < RSYNC_LENGTH || ZSTD_rollingHash_compute(istart + pos - RSYNC_LENGTH, RSYNC_LENGTH) == hash);
return syncPoint;
}
From 27bf96e72bb786a94f7fef13b7f2e5d7ee1d6d48 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Tue, 7 Jun 2022 17:44:20 -0700
Subject: [PATCH 151/472] updated --single-thread man
---
programs/zstd.1 | 261 ++++++++++++++++++++++++++++++++++++---------
programs/zstd.1.md | 13 +--
2 files changed, 218 insertions(+), 56 deletions(-)
diff --git a/programs/zstd.1 b/programs/zstd.1
index 5d037b81597..dbdc30172f8 100644
--- a/programs/zstd.1
+++ b/programs/zstd.1
@@ -1,357 +1,518 @@
-.TH "ZSTD" "1" "April 2022" "zstd 1.5.2" "User Commands"
+.
+.TH "ZSTD" "1" "June 2022" "zstd 1.5.2" "User Commands"
+.
.SH "NAME"
\fBzstd\fR \- zstd, zstdmt, unzstd, zstdcat \- Compress or decompress \.zst files
+.
.SH "SYNOPSIS"
-.TS
-allbox;
-\fBzstd\fR [\fIOPTIONS\fR] [\- \fIINPUT\-FILE\fR] [\-o \fIOUTPUT\-FILE\fR]
-.TE
+\fBzstd\fR [\fIOPTIONS\fR] [\-|\fIINPUT\-FILE\fR] [\-o \fIOUTPUT\-FILE\fR]
+.
.P
\fBzstdmt\fR is equivalent to \fBzstd \-T0\fR
+.
.P
\fBunzstd\fR is equivalent to \fBzstd \-d\fR
+.
.P
\fBzstdcat\fR is equivalent to \fBzstd \-dcf\fR
+.
.SH "DESCRIPTION"
\fBzstd\fR is a fast lossless compression algorithm and data compression tool, with command line syntax similar to \fBgzip (1)\fR and \fBxz (1)\fR\. It is based on the \fBLZ77\fR family, with further FSE & huff0 entropy stages\. \fBzstd\fR offers highly configurable compression speed, from fast modes at > 200 MB/s per core, to strong modes with excellent compression ratios\. It also features a very fast decoder, with speeds > 500 MB/s per core\.
+.
.P
\fBzstd\fR command line syntax is generally similar to gzip, but features the following differences :
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
Source files are preserved by default\. It\'s possible to remove them automatically by using the \fB\-\-rm\fR command\.
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
When compressing a single file, \fBzstd\fR displays progress notifications and result summary by default\. Use \fB\-q\fR to turn them off\.
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fBzstd\fR displays a short help page when command line is an error\. Use \fB\-q\fR to turn it off\.
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fBzstd\fR does not accept input from console, though it does accept \fBstdin\fR when it\'s not the console\.
+.
.IP "" 0
+.
.P
\fBzstd\fR processes each \fIfile\fR according to the selected operation mode\. If no \fIfiles\fR are given or \fIfile\fR is \fB\-\fR, \fBzstd\fR reads from standard input and writes the processed data to standard output\. \fBzstd\fR will refuse to write compressed data to standard output if it is a terminal : it will display an error message and skip the \fIfile\fR\. Similarly, \fBzstd\fR will refuse to read compressed data from standard input if it is a terminal\.
+.
.P
Unless \fB\-\-stdout\fR or \fB\-o\fR is specified, \fIfiles\fR are written to a new file whose name is derived from the source \fIfile\fR name:
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
When compressing, the suffix \fB\.zst\fR is appended to the source filename to get the target filename\.
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
When decompressing, the \fB\.zst\fR suffix is removed from the source filename to get the target filename
+.
.IP "" 0
+.
.SS "Concatenation with \.zst files"
It is possible to concatenate multiple \fB\.zst\fR files\. \fBzstd\fR will decompress such agglomerated file as if it was a single \fB\.zst\fR file\.
+.
.SH "OPTIONS"
+.
.SS "Integer suffixes and special values"
In most places where an integer argument is expected, an optional suffix is supported to easily indicate large integers\. There must be no space between the integer and the suffix\.
+.
.TP
\fBKiB\fR
-Multiply the integer by 1,024 (2\e^10)\. \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^20)\. \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\.
+.
.TP
\fB\-z\fR, \fB\-\-compress\fR
Compress\. This is the default operation mode when no operation mode option is specified and no other operation mode is implied from the command name (for example, \fBunzstd\fR implies \fB\-\-decompress\fR)\.
+.
.TP
\fB\-d\fR, \fB\-\-decompress\fR, \fB\-\-uncompress\fR
Decompress\.
+.
.TP
\fB\-t\fR, \fB\-\-test\fR
Test the integrity of compressed \fIfiles\fR\. This option is equivalent to \fB\-\-decompress \-\-stdout > /dev/null\fR, decompressed data is discarded and checksummed for errors\. No files are created or removed\.
+.
.TP
\fB\-b#\fR
Benchmark file(s) using compression level #
+.
.TP
\fB\-\-train FILEs\fR
Use FILEs as a training set to create a dictionary\. The training set should contain a lot of small files (> 100)\.
+.
.TP
\fB\-l\fR, \fB\-\-list\fR
Display information related to a zstd compressed file, such as size, ratio, and checksum\. Some of these fields may not be available\. This command\'s output can be augmented with the \fB\-v\fR modifier\.
+.
.SS "Operation modifiers"
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fB\-#\fR: \fB#\fR compression level [1\-19] (default: 3)
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fB\-\-ultra\fR: unlocks high compression levels 20+ (maximum 22), using a lot more memory\. Note that decompression will also require more memory when using these levels\.
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fB\-\-fast[=#]\fR: switch to ultra\-fast compression levels\. If \fB=#\fR is not present, it defaults to \fB1\fR\. The higher the value, the faster the compression speed, at the cost of some compression ratio\. This setting overwrites compression level if one was set previously\. Similarly, if a compression level is set after \fB\-\-fast\fR, it overrides it\.
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fB\-T#\fR, \fB\-\-threads=#\fR: Compress using \fB#\fR working threads (default: 1)\. If \fB#\fR is 0, attempt to detect and use the number of physical CPU cores\. In all cases, the nb of threads is capped to \fBZSTDMT_NBWORKERS_MAX\fR, which is either 64 in 32\-bit mode, or 256 for 64\-bit environments\. This modifier does nothing if \fBzstd\fR is compiled without multithread support\.
-.IP "\[ci]" 4
-\fB\-\-single\-thread\fR: Does not spawn a thread for compression, use a single thread for both I/O and compression\. In this mode, compression is serialized with I/O, which is slightly slower\. (This is different from \fB\-T1\fR, which spawns 1 compression thread in parallel of I/O)\. This mode is the only one available when multithread support is disabled\. Single\-thread mode features lower memory usage\. Final compressed result is slightly different from \fB\-T1\fR\.
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
+\fB\-\-single\-thread\fR: Use a single thread for both I/O and compression\. As compression is serialized with I/O, this can be slightly slower\. Single\-thread mode features significantly lower memory usage, which can be useful for systems with limited amount of memory, such as 32\-bit systems\. Note 1 : this mode is the only available one when multithread support is disabled\. Note 2 : this mode is different from \fB\-T1\fR, which spawns 1 compression thread in parallel with I/O\. Final compressed result is also slightly different from \fB\-T1\fR\.
+.
+.IP "\(bu" 4
\fB\-\-auto\-threads={physical,logical} (default: physical)\fR: When using a default amount of threads via \fB\-T0\fR, choose the default based on the number of detected physical or logical cores\.
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fB\-\-adapt[=min=#,max=#]\fR : \fBzstd\fR will dynamically adapt compression level to perceived I/O conditions\. Compression level adaptation can be observed live by using command \fB\-v\fR\. Adaptation can be constrained between supplied \fBmin\fR and \fBmax\fR levels\. The feature works when combined with multi\-threading and \fB\-\-long\fR mode\. It does not work with \fB\-\-single\-thread\fR\. It sets window size to 8 MB by default (can be changed manually, see \fBwlog\fR)\. Due to the chaotic nature of dynamic adaptation, compressed result is not reproducible\. \fInote\fR : at the time of this writing, \fB\-\-adapt\fR can remain stuck at low speed when combined with multiple worker threads (>=2)\.
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fB\-\-long[=#]\fR: enables long distance matching with \fB#\fR \fBwindowLog\fR, if \fB#\fR is not present it defaults to \fB27\fR\. This increases the window size (\fBwindowLog\fR) and memory usage for both the compressor and decompressor\. This setting is designed to improve the compression ratio for files with long matches at a large distance\.
+.
.IP
Note: If \fBwindowLog\fR is set to larger than 27, \fB\-\-long=windowLog\fR or \fB\-\-memory=windowSize\fR needs to be passed to the decompressor\.
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fB\-D DICT\fR: use \fBDICT\fR as Dictionary to compress or decompress FILE(s)
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fB\-\-patch\-from FILE\fR: Specify the file to be used as a reference point for zstd\'s diff engine\. This is effectively dictionary compression with some convenient parameter selection, namely that windowSize > srcSize\.
+.
.IP
Note: cannot use both this and \-D together Note: \fB\-\-long\fR mode will be automatically activated if chainLog < fileLog (fileLog being the windowLog required to cover the whole file)\. You can also manually force it\. Note: for all levels, you can use \-\-patch\-from in \-\-single\-thread mode to improve compression ratio at the cost of speed Note: for level 19, you can get increased compression ratio at the cost of speed by specifying \fB\-\-zstd=targetLength=\fR to be something large (i\.e\. 4096), and by setting a large \fB\-\-zstd=chainLog=\fR
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fB\-\-rsyncable\fR : \fBzstd\fR will periodically synchronize the compression state to make the compressed file more rsync\-friendly\. There is a negligible impact to compression ratio, and the faster compression levels will see a small compression speed hit\. This feature does not work with \fB\-\-single\-thread\fR\. You probably don\'t want to use it with long range mode, since it will decrease the effectiveness of the synchronization points, but your mileage may vary\.
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fB\-C\fR, \fB\-\-[no\-]check\fR: add integrity check computed from uncompressed data (default: enabled)
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fB\-\-[no\-]content\-size\fR: enable / disable whether or not the original size of the file is placed in the header of the compressed file\. The default option is \-\-content\-size (meaning that the original size will be placed in the header)\.
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fB\-\-no\-dictID\fR: do not store dictionary ID within frame header (dictionary compression)\. The decoder will have to rely on implicit knowledge about which dictionary to use, it won\'t be able to check if it\'s correct\.
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fB\-M#\fR, \fB\-\-memory=#\fR: Set a memory usage limit\. By default, Zstandard uses 128 MB for decompression as the maximum amount of memory the decompressor is allowed to use, but you can override this manually if need be in either direction (i\.e\. you can increase or decrease it)\.
+.
.IP
This is also used during compression when using with \-\-patch\-from=\. In this case, this parameter overrides that maximum size allowed for a dictionary\. (128 MB)\.
+.
.IP
Additionally, this can be used to limit memory for dictionary training\. This parameter overrides the default limit of 2 GB\. zstd will load training samples up to the memory limit and ignore the rest\.
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fB\-\-stream\-size=#\fR : Sets the pledged source size of input coming from a stream\. This value must be exact, as it will be included in the produced frame header\. Incorrect stream sizes will cause an error\. This information will be used to better optimize compression parameters, resulting in better and potentially faster compression, especially for smaller source sizes\.
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fB\-\-size\-hint=#\fR: When handling input from a stream, \fBzstd\fR must guess how large the source size will be when optimizing compression parameters\. If the stream size is relatively small, this guess may be a poor one, resulting in a higher compression ratio than expected\. This feature allows for controlling the guess when needed\. Exact guesses result in better compression ratios\. Overestimates result in slightly degraded compression ratios, while underestimates may result in significant degradation\.
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fB\-o FILE\fR: save result into \fBFILE\fR
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fB\-f\fR, \fB\-\-force\fR: disable input and output checks\. Allows overwriting existing files, input from console, output to stdout, operating on links, block devices, etc\.
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fB\-c\fR, \fB\-\-stdout\fR: write to standard output (even if it is the console); keep original files unchanged\.
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fB\-\-[no\-]sparse\fR: enable / disable sparse FS support, to make files with many zeroes smaller on disk\. Creating sparse files may save disk space and speed up decompression by reducing the amount of disk I/O\. default: enabled when output is into a file, and disabled when output is stdout\. This setting overrides default and can force sparse mode over stdout\.
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fB\-\-rm\fR: remove source file(s) after successful compression or decompression\. If used in combination with \-o, will trigger a confirmation prompt (which can be silenced with \-f), as this is a destructive operation\.
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fB\-k\fR, \fB\-\-keep\fR: keep source file(s) after successful compression or decompression\. This is the default behavior\.
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fB\-r\fR: operate recursively on directories\. It selects all files in the named directory and all its subdirectories\. This can be useful both to reduce command line typing, and to circumvent shell expansion limitations, when there are a lot of files and naming breaks the maximum size of a command line\.
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fB\-\-filelist FILE\fR read a list of files to process as content from \fBFILE\fR\. Format is compatible with \fBls\fR output, with one file per line\.
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fB\-\-output\-dir\-flat DIR\fR: resulting files are stored into target \fBDIR\fR directory, instead of same directory as origin file\. Be aware that this command can introduce name collision issues, if multiple files, from different directories, end up having the same name\. Collision resolution ensures first file with a given name will be present in \fBDIR\fR, while in combination with \fB\-f\fR, the last file will be present instead\.
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fB\-\-output\-dir\-mirror DIR\fR: similar to \fB\-\-output\-dir\-flat\fR, the output files are stored underneath target \fBDIR\fR directory, but this option will replicate input directory hierarchy into output \fBDIR\fR\.
+.
.IP
If input directory contains "\.\.", the files in this directory will be ignored\. If input directory is an absolute directory (i\.e\. "/var/tmp/abc"), it will be stored into the "output\-dir/var/tmp/abc"\. If there are multiple input files or directories, name collision resolution will follow the same rules as \fB\-\-output\-dir\-flat\fR\.
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fB\-\-format=FORMAT\fR: compress and decompress in other formats\. If compiled with support, zstd can compress to or decompress from other compression algorithm formats\. Possibly available options are \fBzstd\fR, \fBgzip\fR, \fBxz\fR, \fBlzma\fR, and \fBlz4\fR\. If no such format is provided, \fBzstd\fR is the default\.
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fB\-h\fR/\fB\-H\fR, \fB\-\-help\fR: display help/long help and exit
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fB\-V\fR, \fB\-\-version\fR: display version number and exit\. Advanced : \fB\-vV\fR also displays supported formats\. \fB\-vvV\fR also displays POSIX support\. \fB\-q\fR will only display the version number, suitable for machine reading\.
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fB\-v\fR, \fB\-\-verbose\fR: verbose mode, display more information
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fB\-q\fR, \fB\-\-quiet\fR: suppress warnings, interactivity, and notifications\. specify twice to suppress errors too\.
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fB\-\-no\-progress\fR: do not display the progress bar, but keep all other messages\.
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fB\-\-show\-default\-cparams\fR: Shows the default compression parameters that will be used for a particular src file\. If the provided src file is not a regular file (e\.g\. named pipe), the cli will just output the default parameters\. That is, the parameters that are used when the src size is unknown\.
-.IP "\[ci]" 4
+.
+.IP "\(bu" 4
\fB\-\-\fR: All arguments after \fB\-\-\fR are treated as files
+.
.IP "" 0
+.
.SS "gzip Operation modifiers"
When invoked via a \fBgzip\fR symlink, \fBzstd\fR will support further options that intend to mimic the \fBgzip\fR behavior:
+.
.TP
\fB\-n\fR, \fB\-\-no\-name\fR
do not store the original filename and timestamps when compressing a file\. This is the default behavior and hence a no\-op\.
+.
.TP
\fB\-\-best\fR
alias to the option \fB\-9\fR\.
+.
.SS "Interactions with Environment Variables"
Employing environment variables to set parameters has security implications\. Therefore, this avenue is intentionally limited\. Only \fBZSTD_CLEVEL\fR and \fBZSTD_NBTHREADS\fR are currently supported\. They set the compression level and number of threads to use during compression, respectively\.
+.
.P
\fBZSTD_CLEVEL\fR can be used to set the level between 1 and 19 (the "normal" range)\. If the value of \fBZSTD_CLEVEL\fR is not a valid integer, it will be ignored with a warning message\. \fBZSTD_CLEVEL\fR just replaces the default compression level (\fB3\fR)\.
+.
.P
\fBZSTD_NBTHREADS\fR can be used to set the number of threads \fBzstd\fR will attempt to use during compression\. If the value of \fBZSTD_NBTHREADS\fR is not a valid unsigned integer, it will be ignored with a warning message\. \fBZSTD_NBTHREADS\fR has a default value of (\fB1\fR), and is capped at ZSTDMT_NBWORKERS_MAX==200\. \fBzstd\fR must be compiled with multithread support for this to have any effect\.
+.
.P
They can both be overridden by corresponding command line arguments: \fB\-#\fR for compression level and \fB\-T#\fR for number of compression threads\.
+.
.SH "DICTIONARY BUILDER"
\fBzstd\fR offers \fIdictionary\fR compression, which greatly improves efficiency on small files and messages\. It\'s possible to train \fBzstd\fR with a set of samples, the result of which is saved into a file called a \fBdictionary\fR\. Then, during compression and decompression, reference the same dictionary, using command \fB\-D dictionaryFileName\fR\. Compression of small files similar to the sample set will be greatly improved\.
+.
.TP
\fB\-\-train FILEs\fR
Use FILEs as training set to create a dictionary\. The training set should ideally contain a lot of samples (> 100), and weight typically 100x the target dictionary size (for example, ~10 MB for a 100 KB dictionary)\. \fB\-\-train\fR can be combined with \fB\-r\fR to indicate a directory rather than listing all the files, which can be useful to circumvent shell expansion limits\.
+.
.IP
Since dictionary compression is mostly effective for small files, the expectation is that the training set will only contain small files\. In the case where some samples happen to be large, only the first 128 KB of these samples will be used for training\.
+.
.IP
\fB\-\-train\fR supports multithreading if \fBzstd\fR is compiled with threading support (default)\. Additional advanced parameters can be specified with \fB\-\-train\-fastcover\fR\. The legacy dictionary builder can be accessed with \fB\-\-train\-legacy\fR\. The slower cover dictionary builder can be accessed with \fB\-\-train\-cover\fR\. Default \fB\-\-train\fR is equivalent to \fB\-\-train\-fastcover=d=8,steps=4\fR\.
+.
.TP
\fB\-o FILE\fR
Dictionary saved into \fBFILE\fR (default name: dictionary)\.
+.
.TP
\fB\-\-maxdict=#\fR
Limit dictionary to specified size (default: 112640 bytes)\. As usual, quantities are expressed in bytes by default, and it\'s possible to employ suffixes (like \fBKB\fR or \fBMB\fR) to specify larger values\.
+.
.TP
\fB\-#\fR
Use \fB#\fR compression level during training (optional)\. Will generate statistics more tuned for selected compression level, resulting in a \fIsmall\fR compression ratio improvement for this level\.
+.
.TP
\fB\-B#\fR
Split input files into blocks of size # (default: no split)
+.
.TP
\fB\-M#\fR, \fB\-\-memory=#\fR
Limit the amount of sample data loaded for training (default: 2 GB)\. Note that the default (2 GB) is also the maximum\. This parameter can be useful in situations where the training set size is not well controlled and could be potentially very large\. Since speed of the training process is directly correlated to the size of the training sample set, a smaller sample set leads to faster training\.
+.
.IP
In situations where the training set is larger than maximum memory, the CLI will randomly select samples among the available ones, up to the maximum allowed memory budget\. This is meant to improve dictionary relevance by mitigating the potential impact of clustering, such as selecting only files from the beginning of a list sorted by modification date, or sorted by alphabetical order\. The randomization process is deterministic, so training of the same list of files with the same parameters will lead to the creation of the same dictionary\.
+.
.TP
\fB\-\-dictID=#\fR
A dictionary ID is a locally unique ID\. The decoder will use this value to verify it is using the right dictionary\. By default, zstd will create a 4\-bytes random number ID\. It\'s possible to provide an explicit number ID instead\. It\'s up to the dictionary manager to not assign twice the same ID to 2 different dictionaries\. Note that short numbers have an advantage : an ID < 256 will only need 1 byte in the compressed frame header, and an ID < 65536 will only need 2 bytes\. This compares favorably to 4 bytes default\.
+.
.TP
\fB\-\-train\-cover[=k#,d=#,steps=#,split=#,shrink[=#]]\fR
Select parameters for the default dictionary builder algorithm named cover\. If \fId\fR is not specified, then it tries \fId\fR = 6 and \fId\fR = 8\. If \fIk\fR is not specified, then it tries \fIsteps\fR values in the range [50, 2000]\. If \fIsteps\fR is not specified, then the default value of 40 is used\. If \fIsplit\fR is not specified or split <= 0, then the default value of 100 is used\. Requires that \fId\fR <= \fIk\fR\. If \fIshrink\fR flag is not used, then the default value for \fIshrinkDict\fR of 0 is used\. If \fIshrink\fR is not specified, then the default value for \fIshrinkDictMaxRegression\fR of 1 is used\.
+.
.IP
Selects segments of size \fIk\fR with highest score to put in the dictionary\. The score of a segment is computed by the sum of the frequencies of all the subsegments of size \fId\fR\. Generally \fId\fR should be in the range [6, 8], occasionally up to 16, but the algorithm will run faster with d <= \fI8\fR\. Good values for \fIk\fR vary widely based on the input data, but a safe range is [2 * \fId\fR, 2000]\. If \fIsplit\fR is 100, all input samples are used for both training and testing to find optimal \fId\fR and \fIk\fR to build dictionary\. Supports multithreading if \fBzstd\fR is compiled with threading support\. Having \fIshrink\fR enabled takes a truncated dictionary of minimum size and doubles in size until compression ratio of the truncated dictionary is at most \fIshrinkDictMaxRegression%\fR worse than the compression ratio of the largest dictionary\.
+.
.IP
Examples:
+.
.IP
\fBzstd \-\-train\-cover FILEs\fR
+.
.IP
\fBzstd \-\-train\-cover=k=50,d=8 FILEs\fR
+.
.IP
\fBzstd \-\-train\-cover=d=8,steps=500 FILEs\fR
+.
.IP
\fBzstd \-\-train\-cover=k=50 FILEs\fR
+.
.IP
\fBzstd \-\-train\-cover=k=50,split=60 FILEs\fR
+.
.IP
\fBzstd \-\-train\-cover=shrink FILEs\fR
+.
.IP
\fBzstd \-\-train\-cover=shrink=2 FILEs\fR
+.
.TP
\fB\-\-train\-fastcover[=k#,d=#,f=#,steps=#,split=#,accel=#]\fR
Same as cover but with extra parameters \fIf\fR and \fIaccel\fR and different default value of split If \fIsplit\fR is not specified, then it tries \fIsplit\fR = 75\. If \fIf\fR is not specified, then it tries \fIf\fR = 20\. Requires that 0 < \fIf\fR < 32\. If \fIaccel\fR is not specified, then it tries \fIaccel\fR = 1\. Requires that 0 < \fIaccel\fR <= 10\. Requires that \fId\fR = 6 or \fId\fR = 8\.
+.
.IP
\fIf\fR is log of size of array that keeps track of frequency of subsegments of size \fId\fR\. The subsegment is hashed to an index in the range [0,2^\fIf\fR \- 1]\. It is possible that 2 different subsegments are hashed to the same index, and they are considered as the same subsegment when computing frequency\. Using a higher \fIf\fR reduces collision but takes longer\.
+.
.IP
Examples:
+.
.IP
\fBzstd \-\-train\-fastcover FILEs\fR
+.
.IP
\fBzstd \-\-train\-fastcover=d=8,f=15,accel=2 FILEs\fR
+.
.TP
\fB\-\-train\-legacy[=selectivity=#]\fR
Use legacy dictionary builder algorithm with the given dictionary \fIselectivity\fR (default: 9)\. The smaller the \fIselectivity\fR value, the denser the dictionary, improving its efficiency but reducing its achievable maximum size\. \fB\-\-train\-legacy=s=#\fR is also accepted\.
+.
.IP
Examples:
+.
.IP
\fBzstd \-\-train\-legacy FILEs\fR
+.
.IP
\fBzstd \-\-train\-legacy=selectivity=8 FILEs\fR
+.
.SH "BENCHMARK"
+.
.TP
\fB\-b#\fR
benchmark file(s) using compression level #
+.
.TP
\fB\-e#\fR
benchmark file(s) using multiple compression levels, from \fB\-b#\fR to \fB\-e#\fR (inclusive)
+.
.TP
\fB\-i#\fR
minimum evaluation time, in seconds (default: 3s), benchmark mode only
+.
.TP
\fB\-B#\fR, \fB\-\-block\-size=#\fR
cut file(s) into independent blocks of size # (default: no block)
+.
.TP
\fB\-\-priority=rt\fR
set process priority to real\-time
+.
.P
\fBOutput Format:\fR CompressionLevel#Filename : InputSize \-> 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"
-### \-B#: Specify the size of each compression job\. This parameter is only available when multi\-threading is enabled\. Each compression job is run in parallel, so this value indirectly impacts the nb of active threads\. Default job size varies depending on compression level (generally \fB4 * windowSize\fR)\. \fB\-B#\fR makes it possible to manually select a custom size\. Note that job size must respect a minimum value which is enforced transparently\. This minimum is either 512 KB, or \fBoverlapSize\fR, whichever is largest\. Different job sizes will lead to non\-identical compressed frames\.
+.
+.SS "\-B#:"
+Specify the size of each compression job\. This parameter is only available when multi\-threading is enabled\. Each compression job is run in parallel, so this value indirectly impacts the nb of active threads\. Default job size varies depending on compression level (generally \fB4 * windowSize\fR)\. \fB\-B#\fR makes it possible to manually select a custom size\. Note that job size must respect a minimum value which is enforced transparently\. This minimum is either 512 KB, or \fBoverlapSize\fR, whichever is largest\. Different job sizes will lead to non\-identical compressed frames\.
+.
.SS "\-\-zstd[=options]:"
\fBzstd\fR provides 22 predefined compression levels\. The selected or default predefined compression level can be changed with advanced compression options\. The \fIoptions\fR are provided as a comma\-separated list\. You may specify only the options you want to change and the rest will be taken from the selected or default compression level\. The list of available \fIoptions\fR:
+.
.TP
\fBstrategy\fR=\fIstrat\fR, \fBstrat\fR=\fIstrat\fR
Specify a strategy used by a match finder\.
+.
.IP
There are 9 strategies numbered from 1 to 9, from faster to stronger: 1=ZSTD_fast, 2=ZSTD_dfast, 3=ZSTD_greedy, 4=ZSTD_lazy, 5=ZSTD_lazy2, 6=ZSTD_btlazy2, 7=ZSTD_btopt, 8=ZSTD_btultra, 9=ZSTD_btultra2\.
+.
.TP
\fBwindowLog\fR=\fIwlog\fR, \fBwlog\fR=\fIwlog\fR
Specify the maximum number of bits for a match distance\.
+.
.IP
The higher number of increases the chance to find a match which usually improves compression ratio\. It also increases memory requirements for the compressor and decompressor\. The minimum \fIwlog\fR is 10 (1 KiB) and the maximum is 30 (1 GiB) on 32\-bit platforms and 31 (2 GiB) on 64\-bit platforms\.
+.
.IP
Note: If \fBwindowLog\fR is set to larger than 27, \fB\-\-long=windowLog\fR or \fB\-\-memory=windowSize\fR needs to be passed to the decompressor\.
+.
.TP
\fBhashLog\fR=\fIhlog\fR, \fBhlog\fR=\fIhlog\fR
Specify the maximum number of bits for a hash table\.
+.
.IP
Bigger hash tables cause fewer collisions which usually makes compression faster, but requires more memory during compression\.
+.
.IP
The minimum \fIhlog\fR is 6 (64 B) and the maximum is 30 (1 GiB)\.
+.
.TP
\fBchainLog\fR=\fIclog\fR, \fBclog\fR=\fIclog\fR
Specify the maximum number of bits for a hash chain or a binary tree\.
+.
.IP
Higher numbers of bits increases the chance to find a match which usually improves compression ratio\. It also slows down compression speed and increases memory requirements for compression\. This option is ignored for the ZSTD_fast strategy\.
+.
.IP
The minimum \fIclog\fR is 6 (64 B) and the maximum is 29 (524 Mib) on 32\-bit platforms and 30 (1 Gib) on 64\-bit platforms\.
+.
.TP
\fBsearchLog\fR=\fIslog\fR, \fBslog\fR=\fIslog\fR
Specify the maximum number of searches in a hash chain or a binary tree using logarithmic scale\.
+.
.IP
More searches increases the chance to find a match which usually increases compression ratio but decreases compression speed\.
+.
.IP
The minimum \fIslog\fR is 1 and the maximum is \'windowLog\' \- 1\.
+.
.TP
\fBminMatch\fR=\fImml\fR, \fBmml\fR=\fImml\fR
Specify the minimum searched length of a match in a hash table\.
+.
.IP
Larger search lengths usually decrease compression ratio but improve decompression speed\.
+.
.IP
The minimum \fImml\fR is 3 and the maximum is 7\.
+.
.TP
\fBtargetLength\fR=\fItlen\fR, \fBtlen\fR=\fItlen\fR
The impact of this field vary depending on selected strategy\.
+.
.IP
For ZSTD_btopt, ZSTD_btultra and ZSTD_btultra2, it specifies the minimum match length that causes match finder to stop searching\. A larger \fBtargetLength\fR usually improves compression ratio but decreases compression speed\. t For ZSTD_fast, it triggers ultra\-fast mode when > 0\. The value represents the amount of data skipped between match sampling\. Impact is reversed : a larger \fBtargetLength\fR increases compression speed but decreases compression ratio\.
+.
.IP
For all other strategies, this field has no impact\.
+.
.IP
The minimum \fItlen\fR is 0 and the maximum is 128 Kib\.
+.
.TP
\fBoverlapLog\fR=\fIovlog\fR, \fBovlog\fR=\fIovlog\fR
Determine \fBoverlapSize\fR, amount of data reloaded from previous job\. This parameter is only available when multithreading is enabled\. Reloading more data improves compression ratio, but decreases speed\.
+.
.IP
The minimum \fIovlog\fR is 0, and the maximum is 9\. 1 means "no overlap", hence completely independent jobs\. 9 means "full overlap", meaning up to \fBwindowSize\fR is reloaded from previous job\. Reducing \fIovlog\fR by 1 reduces the reloaded amount by a factor 2\. For example, 8 means "windowSize/2", and 6 means "windowSize/8"\. Value 0 is special and means "default" : \fIovlog\fR is automatically determined by \fBzstd\fR\. In which case, \fIovlog\fR will range from 6 to 9, depending on selected \fIstrat\fR\.
+.
.TP
\fBldmHashLog\fR=\fIlhlog\fR, \fBlhlog\fR=\fIlhlog\fR
Specify the maximum size for a hash table used for long distance matching\.
+.
.IP
This option is ignored unless long distance matching is enabled\.
+.
.IP
Bigger hash tables usually improve compression ratio at the expense of more memory during compression and a decrease in compression speed\.
+.
.IP
The minimum \fIlhlog\fR is 6 and the maximum is 30 (default: 20)\.
+.
.TP
\fBldmMinMatch\fR=\fIlmml\fR, \fBlmml\fR=\fIlmml\fR
Specify the minimum searched length of a match for long distance matching\.
+.
.IP
This option is ignored unless long distance matching is enabled\.
+.
.IP
Larger/very small values usually decrease compression ratio\.
+.
.IP
The minimum \fIlmml\fR is 4 and the maximum is 4096 (default: 64)\.
+.
.TP
\fBldmBucketSizeLog\fR=\fIlblog\fR, \fBlblog\fR=\fIlblog\fR
Specify the size of each bucket for the hash table used for long distance matching\.
+.
.IP
This option is ignored unless long distance matching is enabled\.
+.
.IP
Larger bucket sizes improve collision resolution but decrease compression speed\.
+.
.IP
The minimum \fIlblog\fR is 1 and the maximum is 8 (default: 3)\.
+.
.TP
\fBldmHashRateLog\fR=\fIlhrlog\fR, \fBlhrlog\fR=\fIlhrlog\fR
Specify the frequency of inserting entries into the long distance matching hash table\.
+.
.IP
This option is ignored unless long distance matching is enabled\.
+.
.IP
Larger values will improve compression speed\. Deviating far from the default value will likely result in a decrease in compression ratio\.
+.
.IP
The default value is \fBwlog \- lhlog\fR\.
+.
.SS "Example"
The following parameters sets advanced compression options to something similar to predefined level 19 for files bigger than 256 KB:
+.
.P
\fB\-\-zstd\fR=wlog=23,clog=23,hlog=22,slog=6,mml=3,tlen=48,strat=6
+.
.SH "BUGS"
Report bugs at: https://github\.com/facebook/zstd/issues
+.
.SH "AUTHOR"
Yann Collet
diff --git a/programs/zstd.1.md b/programs/zstd.1.md
index d415211d6d1..c3a87817676 100644
--- a/programs/zstd.1.md
+++ b/programs/zstd.1.md
@@ -118,12 +118,13 @@ the last one takes effect.
which is either 64 in 32-bit mode, or 256 for 64-bit environments.
This modifier does nothing if `zstd` is compiled without multithread support.
* `--single-thread`:
- Does not spawn a thread for compression, use a single thread for both I/O and compression.
- In this mode, compression is serialized with I/O, which is slightly slower.
- (This is different from `-T1`, which spawns 1 compression thread in parallel of I/O).
- This mode is the only one available when multithread support is disabled.
- Single-thread mode features lower memory usage.
- Final compressed result is slightly different from `-T1`.
+ Use a single thread for both I/O and compression.
+ As compression is serialized with I/O, this can be slightly slower.
+ Single-thread mode features significantly lower memory usage,
+ which can be useful for systems with limited amount of memory, such as 32-bit systems.
+ Note 1 : this mode is the only available one when multithread support is disabled.
+ Note 2 : this mode is different from `-T1`, which spawns 1 compression thread in parallel with I/O.
+ Final compressed result is also slightly different from `-T1`.
* `--auto-threads={physical,logical} (default: physical)`:
When using a default amount of threads via `-T0`, choose the default based on the number
of detected physical or logical cores.
From 31bd6402c63a33e81513de6511c6447c63456625 Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Thu, 9 Jun 2022 11:39:30 -0400
Subject: [PATCH 152/472] Bugfix for huge dictionaries
---
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 e43bbec44fc..6fe46f5b78f 100644
--- a/lib/compress/zstd_compress.c
+++ b/lib/compress/zstd_compress.c
@@ -4219,7 +4219,7 @@ static size_t ZSTD_loadDictionaryContent(ZSTD_matchState_t* ms,
* Dictionaries right at the edge will immediately trigger overflow
* correction, but I don't want to insert extra constraints here.
*/
- U32 const maxDictSize = ZSTD_CURRENT_MAX - 1;
+ U32 const maxDictSize = ZSTD_CURRENT_MAX - ZSTD_WINDOW_START_INDEX;
/* We must have cleared our windows when our source is this large. */
assert(ZSTD_window_isEmpty(ms->window));
if (loadLdmDict)
From 05f3f415ce3ca73abc82936752829a3ee75242e2 Mon Sep 17 00:00:00 2001
From: Daniel Kutenin
Date: Mon, 13 Jun 2022 09:16:24 +0100
Subject: [PATCH 153/472] Fix big endian ARM NEON path
It is not using the NEON acceleration but the bit grouping was applied
---
lib/compress/zstd_lazy.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/lib/compress/zstd_lazy.c b/lib/compress/zstd_lazy.c
index 6f8d11e145b..e54b43c0cd4 100644
--- a/lib/compress/zstd_lazy.c
+++ b/lib/compress/zstd_lazy.c
@@ -965,6 +965,10 @@ ZSTD_row_matchMaskGroupWidth(const U32 rowEntries)
assert(rowEntries <= ZSTD_ROW_HASH_MAX_ENTRIES);
(void)rowEntries;
#if defined(ZSTD_ARCH_ARM_NEON)
+ /* NEON path only works for little endian */
+ if (!MEM_isLittleEndian()) {
+ return 1;
+ }
if (rowEntries == 16) {
return 4;
}
From e0c4863c5c81c7aecdb7e1560e081346747a495b Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Mon, 13 Jun 2022 17:23:33 -0700
Subject: [PATCH 154/472] largeNbDicts bugfix + improvements
---
contrib/largeNbDicts/largeNbDicts.c | 30 +++++++++++++++++++++++------
1 file changed, 24 insertions(+), 6 deletions(-)
diff --git a/contrib/largeNbDicts/largeNbDicts.c b/contrib/largeNbDicts/largeNbDicts.c
index ae5383d25cb..034753d3c9f 100644
--- a/contrib/largeNbDicts/largeNbDicts.c
+++ b/contrib/largeNbDicts/largeNbDicts.c
@@ -654,7 +654,8 @@ static int benchMem(slice_collection_t dstBlocks,
slice_collection_t srcBlocks,
ddict_collection_t ddictionaries,
cdict_collection_t cdictionaries,
- unsigned nbRounds, int benchCompression)
+ unsigned nbRounds, int benchCompression,
+ const char* exeName)
{
assert(dstBlocks.nbSlices == srcBlocks.nbSlices);
@@ -703,6 +704,22 @@ static int benchMem(slice_collection_t dstBlocks,
}
DISPLAY("\n");
+ char* csvFileName = malloc(strlen(exeName) + 5);
+ strcpy(csvFileName, exeName);
+ strcat(csvFileName, ".csv");
+ FILE* csvFile = fopen(csvFileName, "r");
+ if (!csvFile) {
+ csvFile = fopen(csvFileName, "wt");
+ assert(csvFile);
+ fprintf(csvFile, "%s\n", exeName);
+ } else {
+ csvFile = fopen(csvFileName, "at");
+ assert(csvFile);
+ }
+ fprintf(csvFile, "%.1f\n", bestSpeed);
+ fclose(csvFile);
+ free(csvFileName);
+
freeDecompressInstructions(di);
freeCompressInstructions(ci);
BMK_freeTimedFnState(benchState);
@@ -721,7 +738,8 @@ int bench(const char** fileNameTable, unsigned nbFiles,
size_t blockSize, int clevel,
unsigned nbDictMax, unsigned nbBlocks,
unsigned nbRounds, int benchCompression,
- ZSTD_dictContentType_e dictContentType, ZSTD_CCtx_params* cctxParams)
+ ZSTD_dictContentType_e dictContentType, ZSTD_CCtx_params* cctxParams,
+ const char* exeName)
{
int result = 0;
@@ -806,7 +824,7 @@ int bench(const char** fileNameTable, unsigned nbFiles,
CONTROL(ddictionaries.ddicts != NULL);
if (benchCompression) {
- size_t const dictMem = ZSTD_estimateCDictSize(dictBuffer.size, DICT_LOAD_METHOD);
+ size_t const dictMem = ZSTD_sizeof_CDict(cdictionaries.cdicts[0]);
size_t const allDictMem = dictMem * nbDicts;
DISPLAYLEVEL(3, "generating %u dictionaries, using %.1f MB of memory \n",
nbDicts, (double)allDictMem / (1 MB));
@@ -816,7 +834,7 @@ int bench(const char** fileNameTable, unsigned nbFiles,
buffer_collection_t resultCollection = createBufferCollection_fromSliceCollection(srcSlices);
CONTROL(resultCollection.buffer.ptr != NULL);
- result = benchMem(dstSlices, resultCollection.slices, ddictionaries, cdictionaries, nbRounds, benchCompression);
+ result = benchMem(dstSlices, resultCollection.slices, ddictionaries, cdictionaries, nbRounds, benchCompression, exeName);
freeBufferCollection(resultCollection);
} else {
@@ -830,7 +848,7 @@ int bench(const char** fileNameTable, unsigned nbFiles,
buffer_collection_t resultCollection = createBufferCollection_fromSliceCollectionSizes(srcSlices);
CONTROL(resultCollection.buffer.ptr != NULL);
- result = benchMem(resultCollection.slices, dstSlices, ddictionaries, cdictionaries, nbRounds, benchCompression);
+ result = benchMem(resultCollection.slices, dstSlices, ddictionaries, cdictionaries, nbRounds, benchCompression, exeName);
freeBufferCollection(resultCollection);
}
@@ -988,7 +1006,7 @@ int main (int argc, const char** argv)
ZSTD_CCtxParams_setParameter(cctxParams, ZSTD_c_nbWorkers, 0);
ZSTD_CCtxParams_setParameter(cctxParams, ZSTD_c_forceAttachDict, dictAttachPref);
- int result = bench(filenameTable->fileNames, (unsigned)filenameTable->tableSize, dictionary, blockSize, cLevel, nbDicts, nbBlocks, nbRounds, benchCompression, dictContentType, cctxParams);
+ int result = bench(filenameTable->fileNames, (unsigned)filenameTable->tableSize, dictionary, blockSize, cLevel, nbDicts, nbBlocks, nbRounds, benchCompression, dictContentType, cctxParams, exeName);
UTIL_freeFileNamesTable(filenameTable);
free(nameTable);
From f7ebbcd0cc090d8012c906a009521558f70245a7 Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Tue, 14 Jun 2022 14:52:51 -0700
Subject: [PATCH 155/472] Support advanced API so forceCopy/forceAttach works
properly
---
contrib/largeNbDicts/largeNbDicts.c | 20 +++++++++++---------
1 file changed, 11 insertions(+), 9 deletions(-)
diff --git a/contrib/largeNbDicts/largeNbDicts.c b/contrib/largeNbDicts/largeNbDicts.c
index 034753d3c9f..9be059a6e2f 100644
--- a/contrib/largeNbDicts/largeNbDicts.c
+++ b/contrib/largeNbDicts/largeNbDicts.c
@@ -577,11 +577,12 @@ typedef struct {
cdict_collection_t dictionaries;
} compressInstructions;
-compressInstructions createCompressInstructions(cdict_collection_t dictionaries)
+compressInstructions createCompressInstructions(cdict_collection_t dictionaries, ZSTD_CCtx_params* cctxParams)
{
compressInstructions ci;
ci.cctx = ZSTD_createCCtx();
CONTROL(ci.cctx != NULL);
+ ZSTD_CCtx_setParametersUsingCCtxParams(ci.cctx, cctxParams);
ci.nbDicts = dictionaries.nbCDict;
ci.dictNb = 0;
ci.dictionaries = dictionaries;
@@ -622,10 +623,10 @@ size_t compress(const void* src, size_t srcSize, void* dst, size_t dstCapacity,
compressInstructions* const ci = (compressInstructions*) payload;
(void)dstCapacity;
- ZSTD_compress_usingCDict(ci->cctx,
- dst, srcSize,
- src, srcSize,
- ci->dictionaries.cdicts[ci->dictNb]);
+ ZSTD_CCtx_refCDict(ci->cctx, ci->dictionaries.cdicts[ci->dictNb]);
+ ZSTD_compress2(ci->cctx,
+ dst, srcSize,
+ src, srcSize);
ci->dictNb = ci->dictNb + 1;
if (ci->dictNb >= ci->nbDicts) ci->dictNb = 0;
@@ -655,9 +656,10 @@ static int benchMem(slice_collection_t dstBlocks,
ddict_collection_t ddictionaries,
cdict_collection_t cdictionaries,
unsigned nbRounds, int benchCompression,
- const char* exeName)
+ const char* exeName, ZSTD_CCtx_params* cctxParams)
{
assert(dstBlocks.nbSlices == srcBlocks.nbSlices);
+ if (benchCompression) assert(cctxParams);
unsigned const ms_per_round = RUN_TIME_DEFAULT_MS;
unsigned const total_time_ms = nbRounds * ms_per_round;
@@ -668,7 +670,7 @@ static int benchMem(slice_collection_t dstBlocks,
BMK_createTimedFnState(total_time_ms, ms_per_round);
decompressInstructions di = createDecompressInstructions(ddictionaries);
- compressInstructions ci = createCompressInstructions(cdictionaries);
+ compressInstructions ci = createCompressInstructions(cdictionaries, cctxParams);
void* payload = benchCompression ? (void*)&ci : (void*)&di;
BMK_benchParams_t const bp = {
.benchFn = benchCompression ? compress : decompress,
@@ -834,7 +836,7 @@ int bench(const char** fileNameTable, unsigned nbFiles,
buffer_collection_t resultCollection = createBufferCollection_fromSliceCollection(srcSlices);
CONTROL(resultCollection.buffer.ptr != NULL);
- result = benchMem(dstSlices, resultCollection.slices, ddictionaries, cdictionaries, nbRounds, benchCompression, exeName);
+ result = benchMem(dstSlices, resultCollection.slices, ddictionaries, cdictionaries, nbRounds, benchCompression, exeName, cctxParams);
freeBufferCollection(resultCollection);
} else {
@@ -848,7 +850,7 @@ int bench(const char** fileNameTable, unsigned nbFiles,
buffer_collection_t resultCollection = createBufferCollection_fromSliceCollectionSizes(srcSlices);
CONTROL(resultCollection.buffer.ptr != NULL);
- result = benchMem(resultCollection.slices, dstSlices, ddictionaries, cdictionaries, nbRounds, benchCompression, exeName);
+ result = benchMem(resultCollection.slices, dstSlices, ddictionaries, cdictionaries, nbRounds, benchCompression, exeName, NULL);
freeBufferCollection(resultCollection);
}
From 2bbdc9f40e6663311028dec241cdc6ab3b5c7e33 Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Tue, 14 Jun 2022 14:57:54 -0700
Subject: [PATCH 156/472] Fix FILE handle leak
---
contrib/largeNbDicts/largeNbDicts.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/contrib/largeNbDicts/largeNbDicts.c b/contrib/largeNbDicts/largeNbDicts.c
index 9be059a6e2f..a87220b7920 100644
--- a/contrib/largeNbDicts/largeNbDicts.c
+++ b/contrib/largeNbDicts/largeNbDicts.c
@@ -715,6 +715,7 @@ static int benchMem(slice_collection_t dstBlocks,
assert(csvFile);
fprintf(csvFile, "%s\n", exeName);
} else {
+ free(csvFileName);
csvFile = fopen(csvFileName, "at");
assert(csvFile);
}
From 24364057bcb6b29a65c6ab91a3da6a32add141eb Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Tue, 14 Jun 2022 19:18:49 -0400
Subject: [PATCH 157/472] fix typo
Co-authored-by: Nick Terrell
---
contrib/largeNbDicts/largeNbDicts.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/contrib/largeNbDicts/largeNbDicts.c b/contrib/largeNbDicts/largeNbDicts.c
index a87220b7920..25b154c309f 100644
--- a/contrib/largeNbDicts/largeNbDicts.c
+++ b/contrib/largeNbDicts/largeNbDicts.c
@@ -715,7 +715,7 @@ static int benchMem(slice_collection_t dstBlocks,
assert(csvFile);
fprintf(csvFile, "%s\n", exeName);
} else {
- free(csvFileName);
+ fclose(csvFile);
csvFile = fopen(csvFileName, "at");
assert(csvFile);
}
From b33ef91694f0b8b13471da0a39e584c4000f89fe Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Sun, 19 Jun 2022 11:12:16 -0700
Subject: [PATCH 158/472] updated documentation regarding build systems
---
README.md | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 69720ba2ccf..da8622d7276 100644
--- a/README.md
+++ b/README.md
@@ -124,14 +124,23 @@ Dictionary gains are mostly effective in the first few KB. Then, the compression
## Build instructions
+`make` is the officially maintained build system of this project.
+All other build systems are "compatible" and 3rd-party maintained,
+they may feature small differences in advanced options.
+When your system allows it, prefer using `make` to build `zstd` and `libzstd`.
+
### Makefile
If your system is compatible with standard `make` (or `gmake`),
invoking `make` in root directory will generate `zstd` cli in root directory.
+It will also create `libzstd` into `lib/`.
Other available options include:
- `make install` : create and install zstd cli, library and man pages
-- `make check` : create and run `zstd`, tests its behavior on local platform
+- `make check` : create and run `zstd`, test its behavior on local platform
+
+The `Makefile` follows the [GNU Standard Makefile conventions](https://www.gnu.org/prep/standards/html_node/Makefile-Conventions.html),
+allowing staged install, standard flags, directory variables and command variables.
### cmake
From 574ecbb0fcbddd1937d104d199247052042d9a16 Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Sun, 19 Jun 2022 11:38:06 -0700
Subject: [PATCH 159/472] display a warning message when using C90 clock_t for
MT speed measurements.
---
programs/benchzstd.c | 9 ++++++++-
programs/timefn.h | 1 +
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/programs/benchzstd.c b/programs/benchzstd.c
index fa2659efbbb..6ceca020c59 100644
--- a/programs/benchzstd.c
+++ b/programs/benchzstd.c
@@ -13,7 +13,7 @@
* Tuning parameters
****************************************/
#ifndef BMK_TIMETEST_DEFAULT_S /* default minimum time per test */
-#define BMK_TIMETEST_DEFAULT_S 3
+# define BMK_TIMETEST_DEFAULT_S 3
#endif
@@ -387,6 +387,13 @@ BMK_benchMemAdvancedNoAlloc(
RDG_genBuffer(compressedBuffer, maxCompressedSize, 0.10, 0.50, 1);
}
+#if defined(UTIL_TIME_USES_C90_CLOCK)
+ if (adv->nbWorkers > 1) {
+ OUTPUTLEVEL(2, "Warning : time measurements restricted to C90 clock_t. \n")
+ OUTPUTLEVEL(2, "Warning : using C90 clock_t leads to incorrect measurements in multithreading mode. \n")
+ }
+#endif
+
/* Bench */
{ U64 const crcOrig = (adv->mode == BMK_decodeOnly) ? 0 : XXH64(srcBuffer, srcSize, 0);
# define NB_MARKS 4
diff --git a/programs/timefn.h b/programs/timefn.h
index 3fcd78a28ec..8ba8ed787bc 100644
--- a/programs/timefn.h
+++ b/programs/timefn.h
@@ -65,6 +65,7 @@ extern "C" {
#else /* relies on standard C90 (note : clock_t measurements can be wrong when using multi-threading) */
+ #define UTIL_TIME_USES_C90_CLOCK
typedef clock_t UTIL_time_t;
#define UTIL_TIME_INITIALIZER 0
From eceecc5b2cade40e2ffe7e4ff4c7d2e16883961a Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Sun, 19 Jun 2022 14:52:32 -0700
Subject: [PATCH 160/472] removed explicit compilation standard from cmake
script
it's not expected to be useful
and can actually lead to subtle side effects
such as #3163.
---
.../cmake/CMakeModules/AddZstdCompilationFlags.cmake | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/build/cmake/CMakeModules/AddZstdCompilationFlags.cmake b/build/cmake/CMakeModules/AddZstdCompilationFlags.cmake
index e23b9d603eb..8d04458c3eb 100644
--- a/build/cmake/CMakeModules/AddZstdCompilationFlags.cmake
+++ b/build/cmake/CMakeModules/AddZstdCompilationFlags.cmake
@@ -22,10 +22,12 @@ endfunction()
macro(ADD_ZSTD_COMPILATION_FLAGS)
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang" OR MINGW) #Not only UNIX but also WIN32 for MinGW
- #Set c++11 by default
- EnableCompilerFlag("-std=c++11" false true)
- #Set c99 by default
- EnableCompilerFlag("-std=c99" true false)
+ # It's possible to select the exact standard used for compilation.
+ # It's not necessary, but can be employed for specific purposes.
+ # Note that zstd source code is compatible with both C++98 and above
+ # and C-gnu90 (c90 + long long + variadic macros ) and above
+ # EnableCompilerFlag("-std=c++11" false true) # Set C++ compilation to c++11 standard
+ # EnableCompilerFlag("-std=c99" true false) # Set C compiation to c99 standard
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND MSVC)
# clang-cl normally maps -Wall to -Weverything.
EnableCompilerFlag("/clang:-Wall" true true)
@@ -48,7 +50,7 @@ macro(ADD_ZSTD_COMPILATION_FLAGS)
if (CMAKE_GENERATOR MATCHES "Visual Studio" AND ACTIVATE_MULTITHREADED_COMPILATION)
EnableCompilerFlag("/MP" true true)
endif ()
-
+
# UNICODE SUPPORT
EnableCompilerFlag("/D_UNICODE" true true)
EnableCompilerFlag("/DUNICODE" true true)
From 15f3605135ef7647402c1639af3b50de30e613af Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Mon, 20 Jun 2022 15:02:41 -0700
Subject: [PATCH 161/472] removed gnu99 statement from meson recipe
---
build/meson/meson.build | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/build/meson/meson.build b/build/meson/meson.build
index e0ea3dff9ce..f264760a34c 100644
--- a/build/meson/meson.build
+++ b/build/meson/meson.build
@@ -12,7 +12,10 @@ project('zstd',
['c', 'cpp'],
license: ['BSD', 'GPLv2'],
default_options : [
- 'c_std=gnu99',
+ # There shouldn't be any need to force a C standard convention for zstd
+ # but in case one would want that anyway, this can be done here.
+ # 'c_std=gnu99',
+ # c++11 standard is useful for pzstd
'cpp_std=c++11',
'buildtype=release',
'warning_level=3',
From f6ef14329f396eb8b2c1290790e7547d070d9511 Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Tue, 21 Jun 2022 14:27:19 -0700
Subject: [PATCH 162/472] "Short cache" optimization for level 1-4 DMS (+5-30%
compression speed) (#3152)
* first attempt at fast DMS short cache
* significant wins for some scenarios
* fix all clang regressions
* nits
* fix 1.5% gcc11 regression on hot 110Kdict scenario
* fix CI
* nit
* Add tags to doublefast hash table
* use tags in doublefast DMS
* Fix CI
* Clean up some hardcoded logic / constants
* Switch forCCtx to an enum
* nit
* add short cache to ip+1 long search
* Move tag size into hashLog
* Minor nits
* Truncate dictionaries greater than 16MB in short cache mode
* Helper function for tag comparison
* Cap short cache hashLog at 24 to prevent overflow
* size_t dictTagsMatch -> int dictTagsMatch
* nit
* Clean up and comment dictionary truncation
* Move ZSTD_tableFillPurpose_e next to ZSTD_dictTableLoadMethod_e
* Comment and expand helper functions
* Asserts and documentation
* nit
---
lib/compress/zstd_compress.c | 94 +++++++++++++++++------
lib/compress/zstd_compress_internal.h | 53 +++++++++++--
lib/compress/zstd_double_fast.c | 80 ++++++++++++++++----
lib/compress/zstd_double_fast.h | 3 +-
lib/compress/zstd_fast.c | 105 ++++++++++++++++++++------
lib/compress/zstd_fast.h | 3 +-
lib/compress/zstd_ldm.c | 4 +-
7 files changed, 273 insertions(+), 69 deletions(-)
diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
index ec2a2fe2704..46a9dbe6690 100644
--- a/lib/compress/zstd_compress.c
+++ b/lib/compress/zstd_compress.c
@@ -275,6 +275,12 @@ static ZSTD_paramSwitch_e ZSTD_resolveEnableLdm(ZSTD_paramSwitch_e mode,
return (cParams->strategy >= ZSTD_btopt && cParams->windowLog >= 27) ? ZSTD_ps_enable : ZSTD_ps_disable;
}
+/* Returns 1 if compression parameters are such that CDict hashtable and chaintable indices are tagged.
+ * If so, the tags need to be removed in ZSTD_resetCCtx_byCopyingCDict. */
+static int ZSTD_CDictIndicesAreTagged(const ZSTD_compressionParameters* const cParams) {
+ return cParams->strategy == ZSTD_fast || cParams->strategy == ZSTD_dfast;
+}
+
static ZSTD_CCtx_params ZSTD_makeCCtxParamsFromCParams(
ZSTD_compressionParameters cParams)
{
@@ -1367,6 +1373,13 @@ ZSTD_adjustCParams_internal(ZSTD_compressionParameters cPar,
if (cPar.windowLog < ZSTD_WINDOWLOG_ABSOLUTEMIN)
cPar.windowLog = ZSTD_WINDOWLOG_ABSOLUTEMIN; /* minimum wlog required for valid frame header */
+ if (mode == ZSTD_cpm_createCDict && ZSTD_CDictIndicesAreTagged(&cPar)) {
+ U32 const maxShortCacheHashLog = 32 - ZSTD_SHORT_CACHE_TAG_BITS;
+ if (cPar.hashLog > maxShortCacheHashLog) {
+ cPar.hashLog = maxShortCacheHashLog;
+ }
+ }
+
return cPar;
}
@@ -2096,6 +2109,22 @@ ZSTD_resetCCtx_byAttachingCDict(ZSTD_CCtx* cctx,
return 0;
}
+static void ZSTD_copyCDictTableIntoCCtx(U32* dst, U32 const* src, size_t tableSize,
+ ZSTD_compressionParameters const* cParams) {
+ if (ZSTD_CDictIndicesAreTagged(cParams)){
+ /* Remove tags from the CDict table if they are present.
+ * See docs on "short cache" in zstd_compress_internal.h for context. */
+ size_t i;
+ for (i = 0; i < tableSize; i++) {
+ U32 const taggedIndex = src[i];
+ U32 const index = taggedIndex >> ZSTD_SHORT_CACHE_TAG_BITS;
+ dst[i] = index;
+ }
+ } else {
+ ZSTD_memcpy(dst, src, tableSize * sizeof(U32));
+ }
+}
+
static size_t ZSTD_resetCCtx_byCopyingCDict(ZSTD_CCtx* cctx,
const ZSTD_CDict* cdict,
ZSTD_CCtx_params params,
@@ -2131,14 +2160,15 @@ static size_t ZSTD_resetCCtx_byCopyingCDict(ZSTD_CCtx* cctx,
: 0;
size_t const hSize = (size_t)1 << cdict_cParams->hashLog;
- ZSTD_memcpy(cctx->blockState.matchState.hashTable,
- cdict->matchState.hashTable,
- hSize * sizeof(U32));
+ ZSTD_copyCDictTableIntoCCtx(cctx->blockState.matchState.hashTable,
+ cdict->matchState.hashTable,
+ hSize, cdict_cParams);
+
/* Do not copy cdict's chainTable if cctx has parameters such that it would not use chainTable */
if (ZSTD_allocateChainTable(cctx->appliedParams.cParams.strategy, cctx->appliedParams.useRowMatchFinder, 0 /* forDDSDict */)) {
- ZSTD_memcpy(cctx->blockState.matchState.chainTable,
- cdict->matchState.chainTable,
- chainSize * sizeof(U32));
+ ZSTD_copyCDictTableIntoCCtx(cctx->blockState.matchState.chainTable,
+ cdict->matchState.chainTable,
+ chainSize, cdict_cParams);
}
/* copy tag table */
if (ZSTD_rowMatchFinderUsed(cdict_cParams->strategy, cdict->useRowMatchFinder)) {
@@ -4205,7 +4235,8 @@ static size_t ZSTD_loadDictionaryContent(ZSTD_matchState_t* ms,
ZSTD_cwksp* ws,
ZSTD_CCtx_params const* params,
const void* src, size_t srcSize,
- ZSTD_dictTableLoadMethod_e dtlm)
+ ZSTD_dictTableLoadMethod_e dtlm,
+ ZSTD_tableFillPurpose_e tfp)
{
const BYTE* ip = (const BYTE*) src;
const BYTE* const iend = ip + srcSize;
@@ -4214,22 +4245,37 @@ static size_t ZSTD_loadDictionaryContent(ZSTD_matchState_t* ms,
/* Assert that the ms params match the params we're being given */
ZSTD_assertEqualCParams(params->cParams, ms->cParams);
- if (srcSize > ZSTD_CHUNKSIZE_MAX) {
+ { /* Ensure large dictionaries can't cause index overflow */
+
/* Allow the dictionary to set indices up to exactly ZSTD_CURRENT_MAX.
* Dictionaries right at the edge will immediately trigger overflow
* correction, but I don't want to insert extra constraints here.
*/
- U32 const maxDictSize = ZSTD_CURRENT_MAX - ZSTD_WINDOW_START_INDEX;
- /* We must have cleared our windows when our source is this large. */
- assert(ZSTD_window_isEmpty(ms->window));
- if (loadLdmDict)
- assert(ZSTD_window_isEmpty(ls->window));
+ U32 maxDictSize = ZSTD_CURRENT_MAX - ZSTD_WINDOW_START_INDEX;
+
+ int const CDictTaggedIndices = ZSTD_CDictIndicesAreTagged(¶ms->cParams);
+ if (CDictTaggedIndices && tfp == ZSTD_tfp_forCDict) {
+ /* Some dictionary matchfinders in zstd use "short cache",
+ * which treats the lower ZSTD_SHORT_CACHE_TAG_BITS of each
+ * CDict hashtable entry as a tag rather than as part of an index.
+ * When short cache is used, we need to truncate the dictionary
+ * so that its indices don't overlap with the tag. */
+ U32 const shortCacheMaxDictSize = (1u << (32 - ZSTD_SHORT_CACHE_TAG_BITS)) - ZSTD_WINDOW_START_INDEX;
+ maxDictSize = MIN(maxDictSize, shortCacheMaxDictSize);
+ assert(!loadLdmDict);
+ }
+
/* If the dictionary is too large, only load the suffix of the dictionary. */
if (srcSize > maxDictSize) {
ip = iend - maxDictSize;
src = ip;
srcSize = maxDictSize;
- }
+ } }
+
+ if (srcSize > ZSTD_CHUNKSIZE_MAX) {
+ /* We must have cleared our windows when our source is this large. */
+ assert(ZSTD_window_isEmpty(ms->window));
+ if (loadLdmDict) assert(ZSTD_window_isEmpty(ls->window));
}
DEBUGLOG(4, "ZSTD_loadDictionaryContent(): useRowMatchFinder=%d", (int)params->useRowMatchFinder);
@@ -4252,10 +4298,10 @@ static size_t ZSTD_loadDictionaryContent(ZSTD_matchState_t* ms,
switch(params->cParams.strategy)
{
case ZSTD_fast:
- ZSTD_fillHashTable(ms, iend, dtlm);
+ ZSTD_fillHashTable(ms, iend, dtlm, tfp);
break;
case ZSTD_dfast:
- ZSTD_fillDoubleHashTable(ms, iend, dtlm);
+ ZSTD_fillDoubleHashTable(ms, iend, dtlm, tfp);
break;
case ZSTD_greedy:
@@ -4421,6 +4467,7 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_compressedBlockState_t* bs,
ZSTD_CCtx_params const* params,
const void* dict, size_t dictSize,
ZSTD_dictTableLoadMethod_e dtlm,
+ ZSTD_tableFillPurpose_e tfp,
void* workspace)
{
const BYTE* dictPtr = (const BYTE*)dict;
@@ -4439,7 +4486,7 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_compressedBlockState_t* bs,
{
size_t const dictContentSize = (size_t)(dictEnd - dictPtr);
FORWARD_IF_ERROR(ZSTD_loadDictionaryContent(
- ms, NULL, ws, params, dictPtr, dictContentSize, dtlm), "");
+ ms, NULL, ws, params, dictPtr, dictContentSize, dtlm, tfp), "");
}
return dictID;
}
@@ -4455,6 +4502,7 @@ ZSTD_compress_insertDictionary(ZSTD_compressedBlockState_t* bs,
const void* dict, size_t dictSize,
ZSTD_dictContentType_e dictContentType,
ZSTD_dictTableLoadMethod_e dtlm,
+ ZSTD_tableFillPurpose_e tfp,
void* workspace)
{
DEBUGLOG(4, "ZSTD_compress_insertDictionary (dictSize=%u)", (U32)dictSize);
@@ -4467,13 +4515,13 @@ ZSTD_compress_insertDictionary(ZSTD_compressedBlockState_t* bs,
/* dict restricted modes */
if (dictContentType == ZSTD_dct_rawContent)
- return ZSTD_loadDictionaryContent(ms, ls, ws, params, dict, dictSize, dtlm);
+ return ZSTD_loadDictionaryContent(ms, ls, ws, params, dict, dictSize, dtlm, tfp);
if (MEM_readLE32(dict) != ZSTD_MAGIC_DICTIONARY) {
if (dictContentType == ZSTD_dct_auto) {
DEBUGLOG(4, "raw content dictionary detected");
return ZSTD_loadDictionaryContent(
- ms, ls, ws, params, dict, dictSize, dtlm);
+ ms, ls, ws, params, dict, dictSize, dtlm, tfp);
}
RETURN_ERROR_IF(dictContentType == ZSTD_dct_fullDict, dictionary_wrong, "");
assert(0); /* impossible */
@@ -4481,7 +4529,7 @@ ZSTD_compress_insertDictionary(ZSTD_compressedBlockState_t* bs,
/* dict as full zstd dictionary */
return ZSTD_loadZstdDictionary(
- bs, ms, ws, params, dict, dictSize, dtlm, workspace);
+ bs, ms, ws, params, dict, dictSize, dtlm, tfp, workspace);
}
#define ZSTD_USE_CDICT_PARAMS_SRCSIZE_CUTOFF (128 KB)
@@ -4524,11 +4572,11 @@ static size_t ZSTD_compressBegin_internal(ZSTD_CCtx* cctx,
cctx->blockState.prevCBlock, &cctx->blockState.matchState,
&cctx->ldmState, &cctx->workspace, &cctx->appliedParams, cdict->dictContent,
cdict->dictContentSize, cdict->dictContentType, dtlm,
- cctx->entropyWorkspace)
+ ZSTD_tfp_forCCtx, cctx->entropyWorkspace)
: ZSTD_compress_insertDictionary(
cctx->blockState.prevCBlock, &cctx->blockState.matchState,
&cctx->ldmState, &cctx->workspace, &cctx->appliedParams, dict, dictSize,
- dictContentType, dtlm, cctx->entropyWorkspace);
+ dictContentType, dtlm, ZSTD_tfp_forCCtx, cctx->entropyWorkspace);
FORWARD_IF_ERROR(dictID, "ZSTD_compress_insertDictionary failed");
assert(dictID <= UINT_MAX);
cctx->dictID = (U32)dictID;
@@ -4832,7 +4880,7 @@ static size_t ZSTD_initCDict_internal(
{ size_t const dictID = ZSTD_compress_insertDictionary(
&cdict->cBlockState, &cdict->matchState, NULL, &cdict->workspace,
¶ms, cdict->dictContent, cdict->dictContentSize,
- dictContentType, ZSTD_dtlm_full, cdict->entropyWorkspace);
+ dictContentType, ZSTD_dtlm_full, ZSTD_tfp_forCDict, cdict->entropyWorkspace);
FORWARD_IF_ERROR(dictID, "ZSTD_compress_insertDictionary failed");
assert(dictID <= (size_t)(U32)-1);
cdict->dictID = (U32)dictID;
diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h
index bbae53303e6..870bcc8be90 100644
--- a/lib/compress/zstd_compress_internal.h
+++ b/lib/compress/zstd_compress_internal.h
@@ -434,6 +434,7 @@ struct ZSTD_CCtx_s {
};
typedef enum { ZSTD_dtlm_fast, ZSTD_dtlm_full } ZSTD_dictTableLoadMethod_e;
+typedef enum { ZSTD_tfp_forCCtx, ZSTD_tfp_forCDict } ZSTD_tableFillPurpose_e;
typedef enum {
ZSTD_noDict = 0,
@@ -745,32 +746,36 @@ ZSTD_count_2segments(const BYTE* ip, const BYTE* match,
* Hashes
***************************************/
static const U32 prime3bytes = 506832829U;
-static U32 ZSTD_hash3(U32 u, U32 h) { return ((u << (32-24)) * prime3bytes) >> (32-h) ; }
+static U32 ZSTD_hash3(U32 u, U32 h) { assert(h <= 32); return ((u << (32-24)) * prime3bytes) >> (32-h) ; }
MEM_STATIC size_t ZSTD_hash3Ptr(const void* ptr, U32 h) { return ZSTD_hash3(MEM_readLE32(ptr), h); } /* only in zstd_opt.h */
static const U32 prime4bytes = 2654435761U;
-static U32 ZSTD_hash4(U32 u, U32 h) { return (u * prime4bytes) >> (32-h) ; }
+static U32 ZSTD_hash4(U32 u, U32 h) { assert(h <= 32); return (u * prime4bytes) >> (32-h) ; }
static size_t ZSTD_hash4Ptr(const void* ptr, U32 h) { return ZSTD_hash4(MEM_read32(ptr), h); }
static const U64 prime5bytes = 889523592379ULL;
-static size_t ZSTD_hash5(U64 u, U32 h) { return (size_t)(((u << (64-40)) * prime5bytes) >> (64-h)) ; }
+static size_t ZSTD_hash5(U64 u, U32 h) { assert(h <= 64); return (size_t)(((u << (64-40)) * prime5bytes) >> (64-h)) ; }
static size_t ZSTD_hash5Ptr(const void* p, U32 h) { return ZSTD_hash5(MEM_readLE64(p), h); }
static const U64 prime6bytes = 227718039650203ULL;
-static size_t ZSTD_hash6(U64 u, U32 h) { return (size_t)(((u << (64-48)) * prime6bytes) >> (64-h)) ; }
+static size_t ZSTD_hash6(U64 u, U32 h) { assert(h <= 64); return (size_t)(((u << (64-48)) * prime6bytes) >> (64-h)) ; }
static size_t ZSTD_hash6Ptr(const void* p, U32 h) { return ZSTD_hash6(MEM_readLE64(p), h); }
static const U64 prime7bytes = 58295818150454627ULL;
-static size_t ZSTD_hash7(U64 u, U32 h) { return (size_t)(((u << (64-56)) * prime7bytes) >> (64-h)) ; }
+static size_t ZSTD_hash7(U64 u, U32 h) { assert(h <= 64); return (size_t)(((u << (64-56)) * prime7bytes) >> (64-h)) ; }
static size_t ZSTD_hash7Ptr(const void* p, U32 h) { return ZSTD_hash7(MEM_readLE64(p), h); }
static const U64 prime8bytes = 0xCF1BBCDCB7A56463ULL;
-static size_t ZSTD_hash8(U64 u, U32 h) { return (size_t)(((u) * prime8bytes) >> (64-h)) ; }
+static size_t ZSTD_hash8(U64 u, U32 h) { assert(h <= 64); return (size_t)(((u) * prime8bytes) >> (64-h)) ; }
static size_t ZSTD_hash8Ptr(const void* p, U32 h) { return ZSTD_hash8(MEM_readLE64(p), h); }
MEM_STATIC FORCE_INLINE_ATTR
size_t ZSTD_hashPtr(const void* p, U32 hBits, U32 mls)
{
+ /* Although some of these hashes do support hBits up to 64, some do not.
+ * To be on the safe side, always avoid hBits > 32. */
+ assert(hBits <= 32);
+
switch(mls)
{
default:
@@ -1264,6 +1269,42 @@ MEM_STATIC void ZSTD_debugTable(const U32* table, U32 max)
#endif
+/* Short Cache */
+
+/* Normally, zstd matchfinders follow this flow:
+ * 1. Compute hash at ip
+ * 2. Load index from hashTable[hash]
+ * 3. Check if *ip == *(base + index)
+ * In dictionary compression, loading *(base + index) is often an L2 or even L3 miss.
+ *
+ * Short cache is an optimization which allows us to avoid step 3 most of the time
+ * when the data doesn't actually match. With short cache, the flow becomes:
+ * 1. Compute (hash, currentTag) at ip. currentTag is an 8-bit independent hash at ip.
+ * 2. Load (index, matchTag) from hashTable[hash]. See ZSTD_writeTaggedIndex to understand how this works.
+ * 3. Only if currentTag == matchTag, check *ip == *(base + index). Otherwise, continue.
+ *
+ * Currently, short cache is only implemented in CDict hashtables. Thus, its use is limited to
+ * dictMatchState matchfinders.
+ */
+#define ZSTD_SHORT_CACHE_TAG_BITS 8
+#define ZSTD_SHORT_CACHE_TAG_MASK ((1u << ZSTD_SHORT_CACHE_TAG_BITS) - 1)
+
+/* Helper function for ZSTD_fillHashTable and ZSTD_fillDoubleHashTable.
+ * Unpacks hashAndTag into (hash, tag), then packs (index, tag) into hashTable[hash]. */
+MEM_STATIC void ZSTD_writeTaggedIndex(U32* const hashTable, size_t hashAndTag, U32 index) {
+ size_t const hash = hashAndTag >> ZSTD_SHORT_CACHE_TAG_BITS;
+ U32 const tag = (U32)(hashAndTag & ZSTD_SHORT_CACHE_TAG_MASK);
+ assert(index >> (32 - ZSTD_SHORT_CACHE_TAG_BITS) == 0);
+ hashTable[hash] = (index << ZSTD_SHORT_CACHE_TAG_BITS) | tag;
+}
+
+/* Helper function for short cache matchfinders.
+ * Unpacks tag1 and tag2 from lower bits of packedTag1 and packedTag2, then checks if the tags match. */
+MEM_STATIC int ZSTD_comparePackedTags(size_t packedTag1, size_t packedTag2) {
+ U32 const tag1 = packedTag1 & ZSTD_SHORT_CACHE_TAG_MASK;
+ U32 const tag2 = packedTag2 & ZSTD_SHORT_CACHE_TAG_MASK;
+ return tag1 == tag2;
+}
#if defined (__cplusplus)
}
diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c
index d8412ef0b7e..6697ba0a91c 100644
--- a/lib/compress/zstd_double_fast.c
+++ b/lib/compress/zstd_double_fast.c
@@ -11,8 +11,43 @@
#include "zstd_compress_internal.h"
#include "zstd_double_fast.h"
+static void ZSTD_fillDoubleHashTableForCDict(ZSTD_matchState_t* ms,
+ void const* end, ZSTD_dictTableLoadMethod_e dtlm)
+{
+ const ZSTD_compressionParameters* const cParams = &ms->cParams;
+ U32* const hashLarge = ms->hashTable;
+ U32 const hBitsL = cParams->hashLog + ZSTD_SHORT_CACHE_TAG_BITS;
+ U32 const mls = cParams->minMatch;
+ U32* const hashSmall = ms->chainTable;
+ U32 const hBitsS = cParams->chainLog + ZSTD_SHORT_CACHE_TAG_BITS;
+ const BYTE* const base = ms->window.base;
+ const BYTE* ip = base + ms->nextToUpdate;
+ const BYTE* const iend = ((const BYTE*)end) - HASH_READ_SIZE;
+ const U32 fastHashFillStep = 3;
-void ZSTD_fillDoubleHashTable(ZSTD_matchState_t* ms,
+ /* Always insert every fastHashFillStep position into the hash tables.
+ * Insert the other positions into the large hash table if their entry
+ * is empty.
+ */
+ for (; ip + fastHashFillStep - 1 <= iend; ip += fastHashFillStep) {
+ U32 const curr = (U32)(ip - base);
+ U32 i;
+ for (i = 0; i < fastHashFillStep; ++i) {
+ size_t const smHashAndTag = ZSTD_hashPtr(ip + i, hBitsS, mls);
+ size_t const lgHashAndTag = ZSTD_hashPtr(ip + i, hBitsL, 8);
+ if (i == 0) {
+ ZSTD_writeTaggedIndex(hashSmall, smHashAndTag, curr + i);
+ }
+ if (i == 0 || hashLarge[lgHashAndTag >> ZSTD_SHORT_CACHE_TAG_BITS] == 0) {
+ ZSTD_writeTaggedIndex(hashLarge, lgHashAndTag, curr + i);
+ }
+ /* Only load extra positions for ZSTD_dtlm_full */
+ if (dtlm == ZSTD_dtlm_fast)
+ break;
+ } }
+}
+
+static void ZSTD_fillDoubleHashTableForCCtx(ZSTD_matchState_t* ms,
void const* end, ZSTD_dictTableLoadMethod_e dtlm)
{
const ZSTD_compressionParameters* const cParams = &ms->cParams;
@@ -43,7 +78,19 @@ void ZSTD_fillDoubleHashTable(ZSTD_matchState_t* ms,
/* Only load extra positions for ZSTD_dtlm_full */
if (dtlm == ZSTD_dtlm_fast)
break;
- } }
+ } }
+}
+
+void ZSTD_fillDoubleHashTable(ZSTD_matchState_t* ms,
+ const void* const end,
+ ZSTD_dictTableLoadMethod_e dtlm,
+ ZSTD_tableFillPurpose_e tfp)
+{
+ if (tfp == ZSTD_tfp_forCDict) {
+ ZSTD_fillDoubleHashTableForCDict(ms, end, dtlm);
+ } else {
+ ZSTD_fillDoubleHashTableForCCtx(ms, end, dtlm);
+ }
}
@@ -289,8 +336,8 @@ size_t ZSTD_compressBlock_doubleFast_dictMatchState_generic(
const BYTE* const dictStart = dictBase + dictStartIndex;
const BYTE* const dictEnd = dms->window.nextSrc;
const U32 dictIndexDelta = prefixLowestIndex - (U32)(dictEnd - dictBase);
- const U32 dictHBitsL = dictCParams->hashLog;
- const U32 dictHBitsS = dictCParams->chainLog;
+ const U32 dictHBitsL = dictCParams->hashLog + ZSTD_SHORT_CACHE_TAG_BITS;
+ const U32 dictHBitsS = dictCParams->chainLog + ZSTD_SHORT_CACHE_TAG_BITS;
const U32 dictAndPrefixLength = (U32)((ip - prefixLowest) + (dictEnd - dictStart));
DEBUGLOG(5, "ZSTD_compressBlock_doubleFast_dictMatchState_generic");
@@ -312,8 +359,12 @@ size_t ZSTD_compressBlock_doubleFast_dictMatchState_generic(
U32 offset;
size_t const h2 = ZSTD_hashPtr(ip, hBitsL, 8);
size_t const h = ZSTD_hashPtr(ip, hBitsS, mls);
- size_t const dictHL = ZSTD_hashPtr(ip, dictHBitsL, 8);
- size_t const dictHS = ZSTD_hashPtr(ip, dictHBitsS, mls);
+ size_t const dictHashAndTagL = ZSTD_hashPtr(ip, dictHBitsL, 8);
+ size_t const dictHashAndTagS = ZSTD_hashPtr(ip, dictHBitsS, mls);
+ U32 const dictMatchIndexAndTagL = dictHashLong[dictHashAndTagL >> ZSTD_SHORT_CACHE_TAG_BITS];
+ U32 const dictMatchIndexAndTagS = dictHashSmall[dictHashAndTagS >> ZSTD_SHORT_CACHE_TAG_BITS];
+ int const dictTagsMatchL = ZSTD_comparePackedTags(dictMatchIndexAndTagL, dictHashAndTagL);
+ int const dictTagsMatchS = ZSTD_comparePackedTags(dictMatchIndexAndTagS, dictHashAndTagS);
U32 const curr = (U32)(ip-base);
U32 const matchIndexL = hashLong[h2];
U32 matchIndexS = hashSmall[h];
@@ -343,9 +394,9 @@ size_t ZSTD_compressBlock_doubleFast_dictMatchState_generic(
while (((ip>anchor) & (matchLong>prefixLowest)) && (ip[-1] == matchLong[-1])) { ip--; matchLong--; mLength++; } /* catch up */
goto _match_found;
}
- } else {
+ } else if (dictTagsMatchL) {
/* check dictMatchState long match */
- U32 const dictMatchIndexL = dictHashLong[dictHL];
+ U32 const dictMatchIndexL = dictMatchIndexAndTagL >> ZSTD_SHORT_CACHE_TAG_BITS;
const BYTE* dictMatchL = dictBase + dictMatchIndexL;
assert(dictMatchL < dictEnd);
@@ -361,9 +412,9 @@ size_t ZSTD_compressBlock_doubleFast_dictMatchState_generic(
if (MEM_read32(match) == MEM_read32(ip)) {
goto _search_next_long;
}
- } else {
+ } else if (dictTagsMatchS) {
/* check dictMatchState short match */
- U32 const dictMatchIndexS = dictHashSmall[dictHS];
+ U32 const dictMatchIndexS = dictMatchIndexAndTagS >> ZSTD_SHORT_CACHE_TAG_BITS;
match = dictBase + dictMatchIndexS;
matchIndexS = dictMatchIndexS + dictIndexDelta;
@@ -378,10 +429,11 @@ size_t ZSTD_compressBlock_doubleFast_dictMatchState_generic(
continue;
_search_next_long:
-
{ size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8);
- size_t const dictHLNext = ZSTD_hashPtr(ip+1, dictHBitsL, 8);
+ size_t const dictHashAndTagL3 = ZSTD_hashPtr(ip+1, dictHBitsL, 8);
U32 const matchIndexL3 = hashLong[hl3];
+ U32 const dictMatchIndexAndTagL3 = dictHashLong[dictHashAndTagL3 >> ZSTD_SHORT_CACHE_TAG_BITS];
+ int const dictTagsMatchL3 = ZSTD_comparePackedTags(dictMatchIndexAndTagL3, dictHashAndTagL3);
const BYTE* matchL3 = base + matchIndexL3;
hashLong[hl3] = curr + 1;
@@ -394,9 +446,9 @@ size_t ZSTD_compressBlock_doubleFast_dictMatchState_generic(
while (((ip>anchor) & (matchL3>prefixLowest)) && (ip[-1] == matchL3[-1])) { ip--; matchL3--; mLength++; } /* catch up */
goto _match_found;
}
- } else {
+ } else if (dictTagsMatchL3) {
/* check dict long +1 match */
- U32 const dictMatchIndexL3 = dictHashLong[dictHLNext];
+ U32 const dictMatchIndexL3 = dictMatchIndexAndTagL3 >> ZSTD_SHORT_CACHE_TAG_BITS;
const BYTE* dictMatchL3 = dictBase + dictMatchIndexL3;
assert(dictMatchL3 < dictEnd);
if (dictMatchL3 > dictStart && MEM_read64(dictMatchL3) == MEM_read64(ip+1)) {
diff --git a/lib/compress/zstd_double_fast.h b/lib/compress/zstd_double_fast.h
index e16b7b03a32..6d8ee8c651f 100644
--- a/lib/compress/zstd_double_fast.h
+++ b/lib/compress/zstd_double_fast.h
@@ -19,7 +19,8 @@ extern "C" {
#include "zstd_compress_internal.h" /* ZSTD_CCtx, size_t */
void ZSTD_fillDoubleHashTable(ZSTD_matchState_t* ms,
- void const* end, ZSTD_dictTableLoadMethod_e dtlm);
+ void const* end, ZSTD_dictTableLoadMethod_e dtlm,
+ ZSTD_tableFillPurpose_e tfp);
size_t ZSTD_compressBlock_doubleFast(
ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
void const* src, size_t srcSize);
diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c
index b0ec972c682..de7336907e3 100644
--- a/lib/compress/zstd_fast.c
+++ b/lib/compress/zstd_fast.c
@@ -11,8 +11,42 @@
#include "zstd_compress_internal.h" /* ZSTD_hashPtr, ZSTD_count, ZSTD_storeSeq */
#include "zstd_fast.h"
+static void ZSTD_fillHashTableForCDict(ZSTD_matchState_t* ms,
+ const void* const end,
+ ZSTD_dictTableLoadMethod_e dtlm)
+{
+ const ZSTD_compressionParameters* const cParams = &ms->cParams;
+ U32* const hashTable = ms->hashTable;
+ U32 const hBits = cParams->hashLog + ZSTD_SHORT_CACHE_TAG_BITS;
+ U32 const mls = cParams->minMatch;
+ const BYTE* const base = ms->window.base;
+ const BYTE* ip = base + ms->nextToUpdate;
+ const BYTE* const iend = ((const BYTE*)end) - HASH_READ_SIZE;
+ const U32 fastHashFillStep = 3;
-void ZSTD_fillHashTable(ZSTD_matchState_t* ms,
+ /* Currently, we always use ZSTD_dtlm_full for filling CDict tables.
+ * Feel free to remove this assert if there's a good reason! */
+ assert(dtlm == ZSTD_dtlm_full);
+
+ /* Always insert every fastHashFillStep position into the hash table.
+ * Insert the other positions if their hash entry is empty.
+ */
+ for ( ; ip + fastHashFillStep < iend + 2; ip += fastHashFillStep) {
+ U32 const curr = (U32)(ip - base);
+ { size_t const hashAndTag = ZSTD_hashPtr(ip, hBits, mls);
+ ZSTD_writeTaggedIndex(hashTable, hashAndTag, curr); }
+
+ if (dtlm == ZSTD_dtlm_fast) continue;
+ /* Only load extra positions for ZSTD_dtlm_full */
+ { U32 p;
+ for (p = 1; p < fastHashFillStep; ++p) {
+ size_t const hashAndTag = ZSTD_hashPtr(ip + p, hBits, mls);
+ if (hashTable[hashAndTag >> ZSTD_SHORT_CACHE_TAG_BITS] == 0) { /* not yet filled */
+ ZSTD_writeTaggedIndex(hashTable, hashAndTag, curr + p);
+ } } } }
+}
+
+static void ZSTD_fillHashTableForCCtx(ZSTD_matchState_t* ms,
const void* const end,
ZSTD_dictTableLoadMethod_e dtlm)
{
@@ -25,6 +59,10 @@ void ZSTD_fillHashTable(ZSTD_matchState_t* ms,
const BYTE* const iend = ((const BYTE*)end) - HASH_READ_SIZE;
const U32 fastHashFillStep = 3;
+ /* Currently, we always use ZSTD_dtlm_fast for filling CCtx tables.
+ * Feel free to remove this assert if there's a good reason! */
+ assert(dtlm == ZSTD_dtlm_fast);
+
/* Always insert every fastHashFillStep position into the hash table.
* Insert the other positions if their hash entry is empty.
*/
@@ -42,6 +80,18 @@ void ZSTD_fillHashTable(ZSTD_matchState_t* ms,
} } } }
}
+void ZSTD_fillHashTable(ZSTD_matchState_t* ms,
+ const void* const end,
+ ZSTD_dictTableLoadMethod_e dtlm,
+ ZSTD_tableFillPurpose_e tfp)
+{
+ if (tfp == ZSTD_tfp_forCDict) {
+ ZSTD_fillHashTableForCDict(ms, end, dtlm);
+ } else {
+ ZSTD_fillHashTableForCCtx(ms, end, dtlm);
+ }
+}
+
/**
* If you squint hard enough (and ignore repcodes), the search operation at any
@@ -435,7 +485,7 @@ size_t ZSTD_compressBlock_fast_dictMatchState_generic(
const BYTE* const dictEnd = dms->window.nextSrc;
const U32 dictIndexDelta = prefixStartIndex - (U32)(dictEnd - dictBase);
const U32 dictAndPrefixLength = (U32)(istart - prefixStart + dictEnd - dictStart);
- const U32 dictHLog = dictCParams->hashLog;
+ const U32 dictHBits = dictCParams->hashLog + ZSTD_SHORT_CACHE_TAG_BITS;
/* if a dictionary is still attached, it necessarily means that
* it is within window size. So we just check it. */
@@ -463,8 +513,11 @@ size_t ZSTD_compressBlock_fast_dictMatchState_generic(
while (ip1 <= ilimit) { /* repcode check at (ip0 + 1) is safe because ip0 < ip1 */
size_t mLength;
size_t hash0 = ZSTD_hashPtr(ip0, hlog, mls);
- const size_t dictHash0 = ZSTD_hashPtr(ip0, dictHLog, mls);
- U32 dictMatchIndex = dictHashTable[dictHash0];
+
+ size_t const dictHashAndTag0 = ZSTD_hashPtr(ip0, dictHBits, mls);
+ U32 dictMatchIndexAndTag = dictHashTable[dictHashAndTag0 >> ZSTD_SHORT_CACHE_TAG_BITS];
+ int dictTagsMatch = ZSTD_comparePackedTags(dictMatchIndexAndTag, dictHashAndTag0);
+
U32 matchIndex = hashTable[hash0];
U32 curr = (U32)(ip0 - base);
size_t step = stepSize;
@@ -479,7 +532,7 @@ size_t ZSTD_compressBlock_fast_dictMatchState_generic(
dictBase + (repIndex - dictIndexDelta) :
base + repIndex;
const size_t hash1 = ZSTD_hashPtr(ip1, hlog, mls);
- const size_t dictHash1 = ZSTD_hashPtr(ip1, dictHLog, mls);
+ size_t const dictHashAndTag1 = ZSTD_hashPtr(ip1, dictHBits, mls);
hashTable[hash0] = curr; /* update hash table */
if (((U32) ((prefixStartIndex - 1) - repIndex) >=
@@ -490,26 +543,33 @@ size_t ZSTD_compressBlock_fast_dictMatchState_generic(
ip0++;
ZSTD_storeSeq(seqStore, (size_t) (ip0 - anchor), anchor, iend, REPCODE1_TO_OFFBASE, mLength);
break;
- } else if (matchIndex <= prefixStartIndex) {
- /* We only look for a dict match if the normal matchIndex is invalid */
+ }
+
+ if (dictTagsMatch) {
+ /* Found a possible dict match */
+ const U32 dictMatchIndex = dictMatchIndexAndTag >> ZSTD_SHORT_CACHE_TAG_BITS;
const BYTE* dictMatch = dictBase + dictMatchIndex;
if (dictMatchIndex > dictStartIndex &&
MEM_read32(dictMatch) == MEM_read32(ip0)) {
- /* found a dict match */
- U32 const offset = (U32) (curr - dictMatchIndex - dictIndexDelta);
- mLength = ZSTD_count_2segments(ip0 + 4, dictMatch + 4, iend, dictEnd, prefixStart) + 4;
- while (((ip0 > anchor) & (dictMatch > dictStart))
- && (ip0[-1] == dictMatch[-1])) {
- ip0--;
- dictMatch--;
- mLength++;
- } /* catch up */
- offset_2 = offset_1;
- offset_1 = offset;
- ZSTD_storeSeq(seqStore, (size_t) (ip0 - anchor), anchor, iend, OFFSET_TO_OFFBASE(offset), mLength);
- break;
+ /* To replicate extDict parse behavior, we only use dict matches when the normal matchIndex is invalid */
+ if (matchIndex <= prefixStartIndex) {
+ U32 const offset = (U32) (curr - dictMatchIndex - dictIndexDelta);
+ mLength = ZSTD_count_2segments(ip0 + 4, dictMatch + 4, iend, dictEnd, prefixStart) + 4;
+ while (((ip0 > anchor) & (dictMatch > dictStart))
+ && (ip0[-1] == dictMatch[-1])) {
+ ip0--;
+ dictMatch--;
+ mLength++;
+ } /* catch up */
+ offset_2 = offset_1;
+ offset_1 = offset;
+ ZSTD_storeSeq(seqStore, (size_t) (ip0 - anchor), anchor, iend, OFFSET_TO_OFFBASE(offset), mLength);
+ break;
+ }
}
- } else if (MEM_read32(match) == MEM_read32(ip0)) {
+ }
+
+ if (matchIndex > prefixStartIndex && MEM_read32(match) == MEM_read32(ip0)) {
/* found a regular match */
U32 const offset = (U32) (ip0 - match);
mLength = ZSTD_count(ip0 + 4, match + 4, iend) + 4;
@@ -526,7 +586,8 @@ size_t ZSTD_compressBlock_fast_dictMatchState_generic(
}
/* Prepare for next iteration */
- dictMatchIndex = dictHashTable[dictHash1];
+ dictMatchIndexAndTag = dictHashTable[dictHashAndTag1 >> ZSTD_SHORT_CACHE_TAG_BITS];
+ dictTagsMatch = ZSTD_comparePackedTags(dictMatchIndexAndTag, dictHashAndTag1);
matchIndex = hashTable[hash1];
if (ip1 >= nextStep) {
diff --git a/lib/compress/zstd_fast.h b/lib/compress/zstd_fast.h
index 0d4a0c1090f..3bfeb2c5f83 100644
--- a/lib/compress/zstd_fast.h
+++ b/lib/compress/zstd_fast.h
@@ -19,7 +19,8 @@ extern "C" {
#include "zstd_compress_internal.h"
void ZSTD_fillHashTable(ZSTD_matchState_t* ms,
- void const* end, ZSTD_dictTableLoadMethod_e dtlm);
+ void const* end, ZSTD_dictTableLoadMethod_e dtlm,
+ ZSTD_tableFillPurpose_e tfp);
size_t ZSTD_compressBlock_fast(
ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
void const* src, size_t srcSize);
diff --git a/lib/compress/zstd_ldm.c b/lib/compress/zstd_ldm.c
index e1d2f741014..c14c62454f4 100644
--- a/lib/compress/zstd_ldm.c
+++ b/lib/compress/zstd_ldm.c
@@ -242,11 +242,11 @@ static size_t ZSTD_ldm_fillFastTables(ZSTD_matchState_t* ms,
switch(ms->cParams.strategy)
{
case ZSTD_fast:
- ZSTD_fillHashTable(ms, iend, ZSTD_dtlm_fast);
+ ZSTD_fillHashTable(ms, iend, ZSTD_dtlm_fast, ZSTD_tfp_forCCtx);
break;
case ZSTD_dfast:
- ZSTD_fillDoubleHashTable(ms, iend, ZSTD_dtlm_fast);
+ ZSTD_fillDoubleHashTable(ms, iend, ZSTD_dtlm_fast, ZSTD_tfp_forCCtx);
break;
case ZSTD_greedy:
From 91aeade7352a31b96944ee68112e748d0c42635c Mon Sep 17 00:00:00 2001
From: Yann Collet
Date: Tue, 21 Jun 2022 18:14:11 -0700
Subject: [PATCH 163/472] Streaming decompression can detect incorrect header
ID sooner
Streaming decompression used to wait for a minimum of 5 bytes before attempting decoding.
This meant that, in the case that only a few bytes (<5) were provided,
and assuming these bytes are incorrect,
there would be no error reported.
The streaming API would simply request more data, waiting for at least 5 bytes.
This PR makes it possible to detect incorrect Frame IDs as soon as the first byte is provided.
Fix #3169
---
lib/decompress/zstd_decompress.c | 48 +++++++++++++++++++++++++-------
tests/zstreamtest.c | 9 ++++++
2 files changed, 47 insertions(+), 10 deletions(-)
diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c
index 85f4d2202e9..5bd412df436 100644
--- a/lib/decompress/zstd_decompress.c
+++ b/lib/decompress/zstd_decompress.c
@@ -79,11 +79,11 @@
*************************************/
#define DDICT_HASHSET_MAX_LOAD_FACTOR_COUNT_MULT 4
-#define DDICT_HASHSET_MAX_LOAD_FACTOR_SIZE_MULT 3 /* These two constants represent SIZE_MULT/COUNT_MULT load factor without using a float.
- * Currently, that means a 0.75 load factor.
- * So, if count * COUNT_MULT / size * SIZE_MULT != 0, then we've exceeded
- * the load factor of the ddict hash set.
- */
+#define DDICT_HASHSET_MAX_LOAD_FACTOR_SIZE_MULT 3 /* These two constants represent SIZE_MULT/COUNT_MULT load factor without using a float.
+ * Currently, that means a 0.75 load factor.
+ * So, if count * COUNT_MULT / size * SIZE_MULT != 0, then we've exceeded
+ * the load factor of the ddict hash set.
+ */
#define DDICT_HASHSET_TABLE_BASE_SIZE 64
#define DDICT_HASHSET_RESIZE_FACTOR 2
@@ -439,16 +439,40 @@ size_t ZSTD_frameHeaderSize(const void* src, size_t srcSize)
* note : only works for formats ZSTD_f_zstd1 and ZSTD_f_zstd1_magicless
* @return : 0, `zfhPtr` is correctly filled,
* >0, `srcSize` is too small, value is wanted `srcSize` amount,
- * or an error code, which can be tested using ZSTD_isError() */
+** or an error code, which can be tested using ZSTD_isError() */
size_t ZSTD_getFrameHeader_advanced(ZSTD_frameHeader* zfhPtr, const void* src, size_t srcSize, ZSTD_format_e format)
{
const BYTE* ip = (const BYTE*)src;
size_t const minInputSize = ZSTD_startingInputLength(format);
- ZSTD_memset(zfhPtr, 0, sizeof(*zfhPtr)); /* not strictly necessary, but static analyzer do not understand that zfhPtr is only going to be read only if return value is zero, since they are 2 different signals */
- if (srcSize < minInputSize) return minInputSize;
- RETURN_ERROR_IF(src==NULL, GENERIC, "invalid parameter");
+ DEBUGLOG(5, "ZSTD_getFrameHeader_advanced: minInputSize = %zu, srcSize = %zu", minInputSize, srcSize);
+
+ if (srcSize > 0) {
+ /* note : technically could be considered an assert(), since it's an invalid entry */
+ RETURN_ERROR_IF(src==NULL, GENERIC, "invalid parameter : src==NULL, but srcSize>0");
+ }
+ if (srcSize < minInputSize) {
+ if (srcSize > 0 && format != ZSTD_f_zstd1_magicless) {
+ /* when receiving less than @minInputSize bytes,
+ * control these bytes at least correspond to a supported magic number
+ * in order to error out early if they don't.
+ **/
+ size_t const toCopy = MIN(4, srcSize);
+ unsigned char hbuf[4]; MEM_writeLE32(hbuf, ZSTD_MAGICNUMBER);
+ assert(src != NULL);
+ ZSTD_memcpy(hbuf, src, toCopy);
+ if ( MEM_readLE32(hbuf) != ZSTD_MAGICNUMBER ) {
+ /* not a zstd frame : let's check if it's a skippable frame */
+ MEM_writeLE32(hbuf, ZSTD_MAGIC_SKIPPABLE_START);
+ ZSTD_memcpy(hbuf, src, toCopy);
+ if ((MEM_readLE32(hbuf) & ZSTD_MAGIC_SKIPPABLE_MASK) != ZSTD_MAGIC_SKIPPABLE_START) {
+ RETURN_ERROR(prefix_unknown,
+ "first bytes don't correspond to any supported magic number");
+ } } }
+ return minInputSize;
+ }
+ ZSTD_memset(zfhPtr, 0, sizeof(*zfhPtr)); /* not strictly necessary, but static analyzers may not understand that zfhPtr will be read only if return value is zero, since they are 2 different signals */
if ( (format != ZSTD_f_zstd1_magicless)
&& (MEM_readLE32(src) != ZSTD_MAGICNUMBER) ) {
if ((MEM_readLE32(src) & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) {
@@ -1981,7 +2005,6 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB
if (zds->refMultipleDDicts && zds->ddictSet) {
ZSTD_DCtx_selectFrameDDict(zds);
}
- DEBUGLOG(5, "header size : %u", (U32)hSize);
if (ZSTD_isError(hSize)) {
#if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1)
U32 const legacyVersion = ZSTD_isLegacy(istart, iend-istart);
@@ -2013,6 +2036,11 @@ size_t ZSTD_decompressStream(ZSTD_DStream* zds, ZSTD_outBuffer* output, ZSTD_inB
zds->lhSize += remainingInput;
}
input->pos = input->size;
+ /* check first few bytes */
+ FORWARD_IF_ERROR(
+ ZSTD_getFrameHeader_advanced(&zds->fParams, zds->headerBuffer, zds->lhSize, zds->format),
+ "First few bytes detected incorrect" );
+ /* return hint input size */
return (MAX((size_t)ZSTD_FRAMEHEADERSIZE_MIN(zds->format), hSize) - zds->lhSize) + ZSTD_blockHeaderSize; /* remaining header bytes + next block header */
}
assert(ip != NULL);
diff --git a/tests/zstreamtest.c b/tests/zstreamtest.c
index 20a05a75c9f..3fcdd5399a4 100644
--- a/tests/zstreamtest.c
+++ b/tests/zstreamtest.c
@@ -424,6 +424,15 @@ static int basicUnitTests(U32 seed, double compressibility)
} }
DISPLAYLEVEL(3, "OK \n");
+ /* check decompression fails early if first bytes are wrong */
+ DISPLAYLEVEL(3, "test%3i : early decompression error if first bytes are incorrect : ", testNb++);
+ { const char buf[3] = { 0 }; /* too short, not enough to start decoding header */
+ ZSTD_inBuffer inb = { buf, sizeof(buf), 0 };
+ size_t const remaining = ZSTD_decompressStream(zd, &outBuff, &inb);
+ if (!ZSTD_isError(remaining)) goto _output_error; /* should have errored out immediately (note: this does not test the exact error code) */
+ }
+ DISPLAYLEVEL(3, "OK \n");
+
/* context size functions */
DISPLAYLEVEL(3, "test%3i : estimate DStream size : ", testNb++);
{ ZSTD_frameHeader fhi;
From 2a128110d05aa5f67c9faee55a4fc538c169c16b Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Tue, 21 Jun 2022 11:59:27 -0400
Subject: [PATCH 164/472] Add prefetchCDictTables CCtxParam
---
lib/compress/zstd_compress.c | 16 ++++++++++++++++
lib/compress/zstd_compress_internal.h | 4 ++++
lib/compress/zstd_double_fast.c | 7 +++++++
lib/compress/zstd_fast.c | 5 +++++
lib/zstd.h | 7 ++++++-
5 files changed, 38 insertions(+), 1 deletion(-)
diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
index 46a9dbe6690..f8ca7d35e9c 100644
--- a/lib/compress/zstd_compress.c
+++ b/lib/compress/zstd_compress.c
@@ -576,6 +576,11 @@ ZSTD_bounds ZSTD_cParam_getBounds(ZSTD_cParameter param)
bounds.upperBound = 1;
return bounds;
+ case ZSTD_c_prefetchCDictTables:
+ bounds.lowerBound = (int)ZSTD_ps_auto;
+ bounds.upperBound = (int)ZSTD_ps_disable;
+ return bounds;
+
default:
bounds.error = ERROR(parameter_unsupported);
return bounds;
@@ -640,6 +645,7 @@ static int ZSTD_isUpdateAuthorized(ZSTD_cParameter param)
case ZSTD_c_useBlockSplitter:
case ZSTD_c_useRowMatchFinder:
case ZSTD_c_deterministicRefPrefix:
+ case ZSTD_c_prefetchCDictTables:
default:
return 0;
}
@@ -695,6 +701,7 @@ size_t ZSTD_CCtx_setParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, int value)
case ZSTD_c_useBlockSplitter:
case ZSTD_c_useRowMatchFinder:
case ZSTD_c_deterministicRefPrefix:
+ case ZSTD_c_prefetchCDictTables:
break;
default: RETURN_ERROR(parameter_unsupported, "unknown parameter");
@@ -921,6 +928,11 @@ size_t ZSTD_CCtxParams_setParameter(ZSTD_CCtx_params* CCtxParams,
CCtxParams->deterministicRefPrefix = !!value;
return CCtxParams->deterministicRefPrefix;
+ case ZSTD_c_prefetchCDictTables:
+ BOUNDCHECK(ZSTD_c_prefetchCDictTables, value);
+ CCtxParams->prefetchCDictTables = (ZSTD_paramSwitch_e)value;
+ return CCtxParams->prefetchCDictTables;
+
default: RETURN_ERROR(parameter_unsupported, "unknown parameter");
}
}
@@ -1053,6 +1065,9 @@ size_t ZSTD_CCtxParams_getParameter(
case ZSTD_c_deterministicRefPrefix:
*value = (int)CCtxParams->deterministicRefPrefix;
break;
+ case ZSTD_c_prefetchCDictTables:
+ *value = (int)CCtxParams->prefetchCDictTables;
+ break;
default: RETURN_ERROR(parameter_unsupported, "unknown parameter");
}
return 0;
@@ -2913,6 +2928,7 @@ static size_t ZSTD_buildSeqStore(ZSTD_CCtx* zc, const void* src, size_t srcSize)
zc->appliedParams.useRowMatchFinder,
dictMode);
ms->ldmSeqStore = NULL;
+ ms->prefetchCDictTables = zc->appliedParams.prefetchCDictTables == ZSTD_ps_enable;
lastLLSize = blockCompressor(ms, &zc->seqStore, zc->blockState.nextCBlock->rep, src, srcSize);
}
{ const BYTE* const lastLiterals = (const BYTE*)src + srcSize - lastLLSize;
diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h
index 870bcc8be90..bd1077f8673 100644
--- a/lib/compress/zstd_compress_internal.h
+++ b/lib/compress/zstd_compress_internal.h
@@ -235,6 +235,7 @@ struct ZSTD_matchState_t {
const ZSTD_matchState_t* dictMatchState;
ZSTD_compressionParameters cParams;
const rawSeqStore_t* ldmSeqStore;
+ int prefetchCDictTables; /* TODO document */
};
typedef struct {
@@ -331,6 +332,9 @@ struct ZSTD_CCtx_params_s {
/* Internal use, for createCCtxParams() and freeCCtxParams() only */
ZSTD_customMem customMem;
+
+ /* TODO document */
+ ZSTD_paramSwitch_e prefetchCDictTables;
}; /* typedef'd to ZSTD_CCtx_params within "zstd.h" */
#define COMPRESS_SEQUENCES_WORKSPACE_SIZE (sizeof(unsigned) * (MaxSeq + 2))
diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c
index 6697ba0a91c..a69fdc8182a 100644
--- a/lib/compress/zstd_double_fast.c
+++ b/lib/compress/zstd_double_fast.c
@@ -345,6 +345,13 @@ size_t ZSTD_compressBlock_doubleFast_dictMatchState_generic(
/* if a dictionary is attached, it must be within window range */
assert(ms->window.dictLimit + (1U << cParams->windowLog) >= endIndex);
+ if (ms->prefetchCDictTables) {
+ size_t const hashTableSize = ((size_t)1) << dictCParams->hashLog;
+ size_t const chainTableSize = ((size_t)1) << dictCParams->chainLog;
+ PREFETCH_AREA(dictHashLong, hashTableSize * sizeof(U32))
+ PREFETCH_AREA(dictHashSmall, chainTableSize * sizeof(U32))
+ }
+
/* init */
ip += (dictAndPrefixLength == 0);
diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c
index de7336907e3..a44057988b8 100644
--- a/lib/compress/zstd_fast.c
+++ b/lib/compress/zstd_fast.c
@@ -500,6 +500,11 @@ size_t ZSTD_compressBlock_fast_dictMatchState_generic(
* when translating a dict index into a local index */
assert(prefixStartIndex >= (U32)(dictEnd - dictBase));
+ if (ms->prefetchCDictTables) {
+ size_t const hashTableSize = ((size_t)1) << dictCParams->hashLog;
+ PREFETCH_AREA(dictHashTable, hashTableSize * sizeof(U32))
+ }
+
/* init */
DEBUGLOG(5, "ZSTD_compressBlock_fast_dictMatchState_generic");
ip0 += (dictAndPrefixLength == 0);
diff --git a/lib/zstd.h b/lib/zstd.h
index 14a78cd931f..221426715e4 100644
--- a/lib/zstd.h
+++ b/lib/zstd.h
@@ -421,6 +421,7 @@ typedef enum {
* ZSTD_c_validateSequences
* ZSTD_c_useBlockSplitter
* ZSTD_c_useRowMatchFinder
+ * ZSTD_c_prefetchCDictTables
* Because they are not stable, it's necessary to define ZSTD_STATIC_LINKING_ONLY to access them.
* note : never ever use experimentalParam? names directly;
* also, the enums values themselves are unstable and can still change.
@@ -439,7 +440,8 @@ typedef enum {
ZSTD_c_experimentalParam12=1009,
ZSTD_c_experimentalParam13=1010,
ZSTD_c_experimentalParam14=1011,
- ZSTD_c_experimentalParam15=1012
+ ZSTD_c_experimentalParam15=1012,
+ ZSTD_c_experimentalParam16=1013
} ZSTD_cParameter;
typedef struct {
@@ -1954,6 +1956,9 @@ ZSTDLIB_STATIC_API size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const vo
*/
#define ZSTD_c_deterministicRefPrefix ZSTD_c_experimentalParam15
+/* TODO document */
+#define ZSTD_c_prefetchCDictTables ZSTD_c_experimentalParam16
+
/*! ZSTD_CCtx_getParameter() :
* Get the requested compression parameter value, selected by enum ZSTD_cParameter,
* and store it into int* value.
From 93b89fb24b1e019ffc2df027993d04e4c0cadfef Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Tue, 21 Jun 2022 18:06:48 -0400
Subject: [PATCH 165/472] Add docs
---
lib/compress/zstd_compress_internal.h | 4 ++--
lib/zstd.h | 22 +++++++++++++++++++++-
2 files changed, 23 insertions(+), 3 deletions(-)
diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h
index bd1077f8673..8c73007c8d5 100644
--- a/lib/compress/zstd_compress_internal.h
+++ b/lib/compress/zstd_compress_internal.h
@@ -235,7 +235,7 @@ struct ZSTD_matchState_t {
const ZSTD_matchState_t* dictMatchState;
ZSTD_compressionParameters cParams;
const rawSeqStore_t* ldmSeqStore;
- int prefetchCDictTables; /* TODO document */
+ int prefetchCDictTables; /* Controls prefetching in some dictMatchState matchfinders */
};
typedef struct {
@@ -333,7 +333,7 @@ struct ZSTD_CCtx_params_s {
/* Internal use, for createCCtxParams() and freeCCtxParams() only */
ZSTD_customMem customMem;
- /* TODO document */
+ /* Controls prefetching in some dictMatchState matchfinders */
ZSTD_paramSwitch_e prefetchCDictTables;
}; /* typedef'd to ZSTD_CCtx_params within "zstd.h" */
diff --git a/lib/zstd.h b/lib/zstd.h
index 221426715e4..14b4b4559b1 100644
--- a/lib/zstd.h
+++ b/lib/zstd.h
@@ -1956,7 +1956,27 @@ ZSTDLIB_STATIC_API size_t ZSTD_CCtx_refPrefix_advanced(ZSTD_CCtx* cctx, const vo
*/
#define ZSTD_c_deterministicRefPrefix ZSTD_c_experimentalParam15
-/* TODO document */
+/* ZSTD_c_prefetchCDictTables
+ * Controlled with ZSTD_paramSwitch_e enum. Default is ZSTD_ps_auto.
+ *
+ * In some situations, zstd uses CDict tables in-place rather than copying them
+ * into the working context. (See docs on ZSTD_dictAttachPref_e above for details).
+ * In such situations, compression speed is seriously impacted when CDict tables are
+ * "cold" (outside CPU cache). This parameter instructs zstd to prefetch CDict tables
+ * when they are used in-place.
+ *
+ * For sufficiently small inputs, the cost of the prefetch will outweigh the benefit.
+ * For sufficiently large inputs, zstd will by default memcpy() CDict tables
+ * into the working context, so there is no need to prefetch. This parameter is
+ * targeted at a middle range of input sizes, where a prefetch is cheap enough to be
+ * useful but memcpy() is too expensive. The exact range of input sizes where this
+ * makes sense is best determined by careful experimentation.
+ *
+ * Note: for this parameter, ZSTD_ps_auto is currently equivalent to ZSTD_ps_disable,
+ * but in the future zstd may conditionally enable this feature via an auto-detection
+ * heuristic for cold CDicts.
+ * Use ZSTD_ps_disable to opt out of prefetching under any circumstances.
+ */
#define ZSTD_c_prefetchCDictTables ZSTD_c_experimentalParam16
/*! ZSTD_CCtx_getParameter() :
From 6bd5ac671352801cd249576d94606bdc0ef235ec Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Wed, 22 Jun 2022 08:59:28 -0700
Subject: [PATCH 166/472] add prefetchCDictTables to largeNbDicts
---
contrib/largeNbDicts/largeNbDicts.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/contrib/largeNbDicts/largeNbDicts.c b/contrib/largeNbDicts/largeNbDicts.c
index 25b154c309f..66850e28cfe 100644
--- a/contrib/largeNbDicts/largeNbDicts.c
+++ b/contrib/largeNbDicts/largeNbDicts.c
@@ -968,6 +968,7 @@ int main (int argc, const char** argv)
unsigned nbBlocks = 0; /* determine nbBlocks automatically, from source and blockSize */
ZSTD_dictContentType_e dictContentType = ZSTD_dct_auto;
ZSTD_dictAttachPref_e dictAttachPref = ZSTD_dictDefaultAttach;
+ ZSTD_paramSwitch_e prefetchCDictTables = ZSTD_ps_auto;
for (int argNb = 1; argNb < argc ; argNb++) {
const char* argument = argv[argNb];
@@ -986,6 +987,7 @@ int main (int argc, const char** argv)
if (longCommandWArg(&argument, "--dedicated-dict-search")) { dedicatedDictSearch = 1; continue; }
if (longCommandWArg(&argument, "--dict-content-type=")) { dictContentType = (int)readU32FromChar(&argument); continue; }
if (longCommandWArg(&argument, "--dict-attach-pref=")) { dictAttachPref = (int)readU32FromChar(&argument); continue; }
+ if (longCommandWArg(&argument, "--prefetch-cdict-tables=")) { prefetchCDictTables = (int)readU32FromChar(&argument); continue; }
if (longCommandWArg(&argument, "-")) { cLevel = (int)readU32FromChar(&argument); continue; }
/* anything that's not a command is a filename */
nameTable[nameIdx++] = argument;
@@ -1008,6 +1010,7 @@ int main (int argc, const char** argv)
ZSTD_CCtxParams_setParameter(cctxParams, ZSTD_c_enableDedicatedDictSearch, dedicatedDictSearch);
ZSTD_CCtxParams_setParameter(cctxParams, ZSTD_c_nbWorkers, 0);
ZSTD_CCtxParams_setParameter(cctxParams, ZSTD_c_forceAttachDict, dictAttachPref);
+ ZSTD_CCtxParams_setParameter(cctxParams, ZSTD_c_prefetchCDictTables, prefetchCDictTables);
int result = bench(filenameTable->fileNames, (unsigned)filenameTable->tableSize, dictionary, blockSize, cLevel, nbDicts, nbBlocks, nbRounds, benchCompression, dictContentType, cctxParams, exeName);
From 747e06f4f6db99d7f4a05f6a0d4a5490da7c944f Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Wed, 22 Jun 2022 17:05:23 -0400
Subject: [PATCH 167/472] Add tests
---
tests/fuzz/zstd_helpers.c | 1 +
tests/fuzzer.c | 1 +
2 files changed, 2 insertions(+)
diff --git a/tests/fuzz/zstd_helpers.c b/tests/fuzz/zstd_helpers.c
index f66579754ff..4f8727df96a 100644
--- a/tests/fuzz/zstd_helpers.c
+++ b/tests/fuzz/zstd_helpers.c
@@ -98,6 +98,7 @@ void FUZZ_setRandomParameters(ZSTD_CCtx *cctx, size_t srcSize, FUZZ_dataProducer
setRand(cctx, ZSTD_c_forceAttachDict, 0, 2, producer);
setRand(cctx, ZSTD_c_useBlockSplitter, 0, 2, producer);
setRand(cctx, ZSTD_c_deterministicRefPrefix, 0, 1, producer);
+ setRand(cctx, ZSTD_c_prefetchCDictTables, 0, 2, producer);
if (FUZZ_dataProducer_uint32Range(producer, 0, 1) == 0) {
setRand(cctx, ZSTD_c_srcSizeHint, ZSTD_SRCSIZEHINT_MIN, 2 * srcSize, producer);
}
diff --git a/tests/fuzzer.c b/tests/fuzzer.c
index 71356d53dbc..81c2d9dba22 100644
--- a/tests/fuzzer.c
+++ b/tests/fuzzer.c
@@ -2041,6 +2041,7 @@ static int basicUnitTests(U32 const seed, double compressibility)
CHECK_Z( ZSTD_CCtx_setParameter(ctxOrig, ZSTD_c_compressionLevel, l) );
CHECK_Z( ZSTD_CCtx_setParameter(ctxOrig, ZSTD_c_enableDedicatedDictSearch, 0) );
CHECK_Z( ZSTD_CCtx_setParameter(ctxOrig, ZSTD_c_forceAttachDict, ZSTD_dictForceAttach) );
+ CHECK_Z( ZSTD_CCtx_setParameter(ctxOrig, ZSTD_c_prefetchCDictTables, seed % 3) );
wdict_cSize = ZSTD_compress2(ctxOrig, compressedBuffer, compressedBufferSize, contentStart, contentSize);
if (wdict_cSize > target_wdict_cSize[l]) {
DISPLAYLEVEL(1, "error : compression with dictionary and compress2 at level %i worse than expected (%u > %u) \n",
From bb4a3c71ef352d2fdb5bb5bfa9b11b72bb3d28d5 Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Wed, 22 Jun 2022 18:02:07 -0700
Subject: [PATCH 168/472] Update README.md for fuzzers (#3174)
* Update README.md for fuzzers
* Add ls corpora/*crash command
* nit
* Clarify wording and add Nick's command
* Minor clarification
---
tests/fuzz/README.md | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/tests/fuzz/README.md b/tests/fuzz/README.md
index 71afa40631a..4ff7fe3ac1d 100644
--- a/tests/fuzz/README.md
+++ b/tests/fuzz/README.md
@@ -50,6 +50,20 @@ See the help of the relevant command for options.
Flags not parsed by `fuzz.py` are passed to the fuzzing engine.
The command used to run the fuzzer is printed for debugging.
+Here's a helpful command to fuzz each target across all cores,
+stopping only if a bug is found:
+```
+for target in $(./fuzz.py list); do
+ ./fuzz.py libfuzzer $target -jobs=10 -workers=10 -max_total_time=1000 || break;
+done
+```
+Alternatively, you can fuzz all targets in parallel, using one core per target:
+```
+python3 ./fuzz.py list | xargs -P$(python3 ./fuzz.py list | wc -l) -I__ sh -c "python3 ./fuzz.py libfuzzer __ 2>&1 | tee __.log"
+```
+Either way, to double-check that no crashes were found, run `ls corpora/*crash`.
+If any crashes were found, you can use the hashes to reproduce them.
+
## LibFuzzer
```
From cb9e3411292133c4e2b96ef331760eefdf895f23 Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Thu, 23 Jun 2022 16:58:03 -0400
Subject: [PATCH 169/472] Nits
---
lib/compress/zstd_compress.c | 2 +-
lib/compress/zstd_compress_internal.h | 6 +++++-
lib/compress/zstd_double_fast.c | 8 ++++----
lib/compress/zstd_fast.c | 4 ++--
4 files changed, 12 insertions(+), 8 deletions(-)
diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
index f8ca7d35e9c..b1f9bba8a9d 100644
--- a/lib/compress/zstd_compress.c
+++ b/lib/compress/zstd_compress.c
@@ -1943,6 +1943,7 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,
/* init params */
zc->blockState.matchState.cParams = params->cParams;
+ zc->blockState.matchState.prefetchCDictTables = params->prefetchCDictTables == ZSTD_ps_enable;
zc->pledgedSrcSizePlusOne = pledgedSrcSize+1;
zc->consumedSrcSize = 0;
zc->producedCSize = 0;
@@ -2928,7 +2929,6 @@ static size_t ZSTD_buildSeqStore(ZSTD_CCtx* zc, const void* src, size_t srcSize)
zc->appliedParams.useRowMatchFinder,
dictMode);
ms->ldmSeqStore = NULL;
- ms->prefetchCDictTables = zc->appliedParams.prefetchCDictTables == ZSTD_ps_enable;
lastLLSize = blockCompressor(ms, &zc->seqStore, zc->blockState.nextCBlock->rep, src, srcSize);
}
{ const BYTE* const lastLiterals = (const BYTE*)src + srcSize - lastLLSize;
diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h
index 8c73007c8d5..4771b15ba6c 100644
--- a/lib/compress/zstd_compress_internal.h
+++ b/lib/compress/zstd_compress_internal.h
@@ -235,7 +235,11 @@ struct ZSTD_matchState_t {
const ZSTD_matchState_t* dictMatchState;
ZSTD_compressionParameters cParams;
const rawSeqStore_t* ldmSeqStore;
- int prefetchCDictTables; /* Controls prefetching in some dictMatchState matchfinders */
+
+ /* Controls prefetching in some dictMatchState matchfinders.
+ * This behavior is controlled from the cctx ms.
+ * This parameter has no effect in the cdict ms. */
+ int prefetchCDictTables;
};
typedef struct {
diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c
index a69fdc8182a..c2dbd54c127 100644
--- a/lib/compress/zstd_double_fast.c
+++ b/lib/compress/zstd_double_fast.c
@@ -346,10 +346,10 @@ size_t ZSTD_compressBlock_doubleFast_dictMatchState_generic(
assert(ms->window.dictLimit + (1U << cParams->windowLog) >= endIndex);
if (ms->prefetchCDictTables) {
- size_t const hashTableSize = ((size_t)1) << dictCParams->hashLog;
- size_t const chainTableSize = ((size_t)1) << dictCParams->chainLog;
- PREFETCH_AREA(dictHashLong, hashTableSize * sizeof(U32))
- PREFETCH_AREA(dictHashSmall, chainTableSize * sizeof(U32))
+ size_t const hashTableBytes = (((size_t)1) << dictCParams->hashLog) * sizeof(U32);
+ size_t const chainTableBytes = (((size_t)1) << dictCParams->chainLog) * sizeof(U32);
+ PREFETCH_AREA(dictHashLong, hashTableBytes)
+ PREFETCH_AREA(dictHashSmall, chainTableBytes)
}
/* init */
diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c
index a44057988b8..291173449bb 100644
--- a/lib/compress/zstd_fast.c
+++ b/lib/compress/zstd_fast.c
@@ -501,8 +501,8 @@ size_t ZSTD_compressBlock_fast_dictMatchState_generic(
assert(prefixStartIndex >= (U32)(dictEnd - dictBase));
if (ms->prefetchCDictTables) {
- size_t const hashTableSize = ((size_t)1) << dictCParams->hashLog;
- PREFETCH_AREA(dictHashTable, hashTableSize * sizeof(U32))
+ size_t const hashTableBytes = (((size_t)1) << dictCParams->hashLog) * sizeof(U32);
+ PREFETCH_AREA(dictHashTable, hashTableBytes)
}
/* init */
From 5c382bf1104109de88dbde534ab7ba384773c1df Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Wed, 29 Jun 2022 13:11:13 -0400
Subject: [PATCH 170/472] 1.5.3 version bump
---
CHANGELOG | 35 +++++++++++++++++++++++++++++++++++
doc/zstd_manual.html | 29 ++++++++++++++++-------------
lib/zstd.h | 2 +-
3 files changed, 52 insertions(+), 14 deletions(-)
diff --git a/CHANGELOG b/CHANGELOG
index 4e0045b950c..e8c40f302e3 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,38 @@
+v1.5.3 (June, 2022)
+perf: 5-30% faster dictionary compression at levels 1-4 (#3086, #3114, #3152, @embg)
+perf: 5-10% faster streaming compression at levels 1-2 (#3114, @embg)
+perf: Remove branch in ZSTD_fast_noDict (#3129, @felixhandte)
+perf: Add option to prefetch CDict tables (#3177, @embg)
+perf: Minor compression ratio improvement (#2983, @Cyan4973)
+perf: Minor speed improvement for Huffman decoding (#3013, @WojciechMula)
+perf: Enable STATIC_BMI2 for gcc/clang (#3080, @TocarIP)
+perf: Optimize ZSTD_row_getMatchMask for ARM levels 8-10 (#3139, #3160, @danlark1)
+perf: aarch64 performance improvements (#3145, #3141, @JunHe77)
+perf: Lazy parameters adaptation (#2974, @Cyan4973)
+cli: Async write for decompression (#2975, @yoniko)
+cli: Use buffered output (#2985, @yoniko)
+cli: Change zstdless behavior to align with zless (#2909, @binhdvo)
+cli: AsyncIO compression (#3021, #3022, @yoniko)
+cli: Print zlib/lz4/lzma library versions in verbose version output (#3030, @terrelln)
+cli: Fix for -r on empty directory (#3027, @brailovich)
+cli: Fix required decompression memory usage reported by -vv + --long (#3042, @u1f35c)
+cli: Fix infinite loop when empty input is passed to trainer (#3081, @terrelln)
+cli: Implement more gzip compatibility (#3059, @dirkmueller)
+cli: Keep original file if -c or --stdout is given (#3052, @dirkmueller)
+bug: Fix for block-splitter (#3033, @Cyan4973)
+bug: Fixes for Sequence Compression API (#3023, #3040, @Cyan4973)
+bug: Fix leaking thread handles on Windows (#3147, @animalize)
+bug: Fix timing issues with cmake/meson builds (#3166, #3167, #3170, @Cyan4973)
+build: Allow user to select legacy level for cmake (#3050, @shadchin)
+build: Enable legacy support by default in cmake (#3079, @niamster)
+build: Meson improvements (#3039, #3122, @eli-schwartz)
+build: Add aarch64 to supported architectures for zstd_trace (#3054, @ooosssososos)
+doc: Split help in long and short version, cleanup formatting (#3094, @dirkmueller)
+doc: Updated man page, providing more details for --train mode (#3112, @Cyan4973)
+doc: Add decompressor errata document (#3092, @terrelln)
+misc: Enable Intel CET (#2992, #2994, @hjl-tools)
+misc: Streaming decompression can detect incorrect header ID sooner (#3175, @Cyan4973)
+
v1.5.2 (Jan, 2022)
perf: Regain Minimal memset()-ing During Reuse of Compression Contexts (@Cyan4973, #2969)
build: Build Zstd with `noexecstack` on All Architectures (@felixhandte, #2964)
diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html
index 4bbb513bbe5..ff9eaca3635 100644
--- a/doc/zstd_manual.html
+++ b/doc/zstd_manual.html
@@ -1,10 +1,10 @@
-zstd 1.5.2 Manual
+zstd 1.5.3 Manual
-zstd 1.5.2 Manual
+zstd 1.5.3 Manual
Contents
@@ -359,6 +359,7 @@ Decompression context
When decompressing many times,
* ZSTD_c_validateSequences
* ZSTD_c_useBlockSplitter
* ZSTD_c_useRowMatchFinder
+ * ZSTD_c_prefetchCDictTables
* Because they are not stable, it's necessary to define ZSTD_STATIC_LINKING_ONLY to access them.
* note : never ever use experimentalParam? names directly;
* also, the enums values themselves are unstable and can still change.
@@ -377,7 +378,8 @@ Decompression context
When decompressing many times,
ZSTD_c_experimentalParam12=1009,
ZSTD_c_experimentalParam13=1010,
ZSTD_c_experimentalParam14=1011,
- ZSTD_c_experimentalParam15=1012
+ ZSTD_c_experimentalParam15=1012,
+ ZSTD_c_experimentalParam16=1013
} ZSTD_cParameter;
typedef struct {
@@ -790,7 +792,7 @@ Streaming decompression functions
unsigned ZSTD_getDictID_fromFrame(const void* src, size_t srcSize);
- Provides the dictID required to decompress the frame stored within `src`.
+
Provides the dictID required to decompressed the frame stored within `src`.
If @return == 0, the dictID could not be decoded.
This could for one of the following reasons :
- The frame does not require a dictionary to be decoded (most common case).
@@ -1132,15 +1134,15 @@
Streaming decompression functions
ZSTD_sf_explicitBlockDelimiters = 1 /* Representation of ZSTD_Sequence contains explicit block delimiters */
} ZSTD_sequenceFormat_e;
- Generate sequences using ZSTD_compress2, given a source buffer.
+
Generate sequences using ZSTD_compress2(), given a source buffer.
Each block will end with a dummy sequence
with offset == 0, matchLength == 0, and litLength == length of last literals.
litLength may be == 0, and if so, then the sequence of (of: 0 ml: 0 ll: 0)
simply acts as a block delimiter.
- zc can be used to insert custom compression params.
- This function invokes ZSTD_compress2
+ @zc can be used to insert custom compression params.
+ This function invokes ZSTD_compress2().
The output of this function can be fed into ZSTD_compressSequences() with CCtx
setting of ZSTD_c_blockDelimiters as ZSTD_sf_explicitBlockDelimiters
@@ -1150,7 +1152,7 @@
Streaming decompression functions
ZSTDLIB_STATIC_API size_t ZSTD_mergeBlockDelimiters(ZSTD_Sequence* sequences, size_t seqsSize);
Given an array of ZSTD_Sequence, remove all sequences that represent block delimiters/last literals
- by merging them into into the literals of the next sequence.
+ by merging them into the literals of the next sequence.
As such, the final generated result has no explicit representation of block boundaries,
and the final last literals segment is not represented in the sequences.
@@ -1161,9 +1163,10 @@
Streaming decompression functions
-ZSTDLIB_STATIC_API size_t ZSTD_compressSequences(ZSTD_CCtx* const cctx, void* dst, size_t dstSize,
- const ZSTD_Sequence* inSeqs, size_t inSeqsSize,
- const void* src, size_t srcSize);
+ZSTDLIB_STATIC_API size_t
+ZSTD_compressSequences( ZSTD_CCtx* cctx, void* dst, size_t dstSize,
+ const ZSTD_Sequence* inSeqs, size_t inSeqsSize,
+ const void* src, size_t srcSize);
Compress an array of ZSTD_Sequence, associated with @src buffer, into dst.
@src contains the entire input (not just the literals).
If @srcSize > sum(sequence.length), the remaining bytes are considered all literals
@@ -1191,7 +1194,7 @@
Streaming decompression functions
Note: Repcodes are, as of now, always re-calculated within this function, so ZSTD_Sequence::rep is unused.
Note 2: Once we integrate ability to ingest repcodes, the explicit block delims mode must respect those repcodes exactly,
and cannot emit an RLE block that disagrees with the repcode history
- @return : final compressed size or a ZSTD error.
+ @return : final compressed size, or a ZSTD error code.
@@ -1199,7 +1202,7 @@ Streaming decompression functions
const void* src, size_t srcSize, unsigned magicVariant);
Generates a zstd skippable frame containing data given by src, and writes it to dst buffer.
- Skippable frames begin with a a 4-byte magic number. There are 16 possible choices of magic number,
+ Skippable frames begin with a 4-byte magic number. There are 16 possible choices of magic number,
ranging from ZSTD_MAGIC_SKIPPABLE_START to ZSTD_MAGIC_SKIPPABLE_START+15.
As such, the parameter magicVariant controls the exact skippable frame magic number variant used, so
the magic number used will be ZSTD_MAGIC_SKIPPABLE_START + magicVariant.
diff --git a/lib/zstd.h b/lib/zstd.h
index 14b4b4559b1..4c2eee69609 100644
--- a/lib/zstd.h
+++ b/lib/zstd.h
@@ -74,7 +74,7 @@ extern "C" {
/*------ Version ------*/
#define ZSTD_VERSION_MAJOR 1
#define ZSTD_VERSION_MINOR 5
-#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)
/*! ZSTD_versionNumber() :
From bb3839a78cf0dbaf2059d47936f9e3b8ff50e9d1 Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Wed, 29 Jun 2022 14:55:14 -0400
Subject: [PATCH 171/472] make -C programs zstd.1
---
programs/zstd.1 | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/programs/zstd.1 b/programs/zstd.1
index dbdc30172f8..84450d8ec34 100644
--- a/programs/zstd.1
+++ b/programs/zstd.1
@@ -1,5 +1,5 @@
.
-.TH "ZSTD" "1" "June 2022" "zstd 1.5.2" "User Commands"
+.TH "ZSTD" "1" "June 2022" "zstd 1.5.3" "User Commands"
.
.SH "NAME"
\fBzstd\fR \- zstd, zstdmt, unzstd, zstdcat \- Compress or decompress \.zst files
From cd9d0a7e6e5aa770b377d738eb94de6671c5487e Mon Sep 17 00:00:00 2001
From: Taylor Braun-Jones
Date: Thu, 30 Jun 2022 13:20:42 -0400
Subject: [PATCH 172/472] Fix ZSTD_BUILD_TESTS=ON build with MSVC
Fixes:
Command line error D8021 : invalid numeric argument '/Wno-deprecated-declarations'
---
build/cmake/tests/CMakeLists.txt | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/build/cmake/tests/CMakeLists.txt b/build/cmake/tests/CMakeLists.txt
index 8bba6ea65a6..6c1ea298a1c 100644
--- a/build/cmake/tests/CMakeLists.txt
+++ b/build/cmake/tests/CMakeLists.txt
@@ -57,7 +57,9 @@ target_link_libraries(datagen libzstd_static)
# fullbench
#
add_executable(fullbench ${PROGRAMS_DIR}/datagen.c ${PROGRAMS_DIR}/util.c ${PROGRAMS_DIR}/timefn.c ${PROGRAMS_DIR}/benchfn.c ${PROGRAMS_DIR}/benchzstd.c ${TESTS_DIR}/fullbench.c)
-set_property(TARGET fullbench APPEND PROPERTY COMPILE_OPTIONS "-Wno-deprecated-declarations")
+if (NOT MSVC)
+ target_compile_options(fullbench PRIVATE "-Wno-deprecated-declarations")
+endif()
target_link_libraries(fullbench libzstd_static)
add_test(NAME fullbench COMMAND fullbench ${ZSTD_FULLBENCH_FLAGS})
@@ -65,7 +67,9 @@ add_test(NAME fullbench COMMAND fullbench ${ZSTD_FULLBENCH_FLAGS})
# fuzzer
#
add_executable(fuzzer ${PROGRAMS_DIR}/datagen.c ${PROGRAMS_DIR}/util.c ${PROGRAMS_DIR}/timefn.c ${TESTS_DIR}/fuzzer.c)
-set_property(TARGET fuzzer APPEND PROPERTY COMPILE_OPTIONS "-Wno-deprecated-declarations")
+if (NOT MSVC)
+ target_compile_options(fuzzer PRIVATE "-Wno-deprecated-declarations")
+endif()
target_link_libraries(fuzzer libzstd_static)
AddTestFlagsOption(ZSTD_FUZZER_FLAGS "$ENV{FUZZERTEST} $ENV{FUZZER_FLAGS}"
"Semicolon-separated list of flags to pass to the fuzzer test (see `fuzzer -h` for usage)")
@@ -78,7 +82,9 @@ add_test(NAME fuzzer COMMAND fuzzer ${ZSTD_FUZZER_FLAGS})
# zstreamtest
#
add_executable(zstreamtest ${PROGRAMS_DIR}/datagen.c ${PROGRAMS_DIR}/util.c ${PROGRAMS_DIR}/timefn.c ${TESTS_DIR}/seqgen.c ${TESTS_DIR}/zstreamtest.c)
-set_property(TARGET zstreamtest APPEND PROPERTY COMPILE_OPTIONS "-Wno-deprecated-declarations")
+if (NOT MSVC)
+ target_compile_options(zstreamtest PRIVATE "-Wno-deprecated-declarations")
+endif()
target_link_libraries(zstreamtest libzstd_static)
AddTestFlagsOption(ZSTD_ZSTREAM_FLAGS "$ENV{ZSTREAM_TESTTIME} $ENV{FUZZER_FLAGS}"
"Semicolon-separated list of flags to pass to the zstreamtest test (see `zstreamtest -h` for usage)")
From cc8c98485a7c1f9837e13662649711dc842f2c02 Mon Sep 17 00:00:00 2001
From: htnhan
Date: Tue, 5 Jul 2022 21:28:33 -0500
Subject: [PATCH 173/472] zstd -lv to show dictID
---
programs/fileio.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/programs/fileio.c b/programs/fileio.c
index 3c47e3f5b3b..86906bed3a3 100644
--- a/programs/fileio.c
+++ b/programs/fileio.c
@@ -2721,6 +2721,7 @@ typedef struct {
int decompUnavailable;
int usesCheck;
U32 nbFiles;
+ unsigned dictID;
} fileInfo_t;
typedef enum {
@@ -2775,6 +2776,7 @@ FIO_analyzeFrames(fileInfo_t* info, FILE* const srcFile)
}
ERROR_IF(ZSTD_getFrameHeader(&header, headerBuffer, numBytesRead) != 0,
info_frame_error, "Error: could not decode frame header");
+ info->dictID = header.dictID;
info->windowSize = header.windowSize;
/* move to the end of the frame header */
{ size_t const headerSize = ZSTD_frameHeaderSize(headerBuffer, numBytesRead);
@@ -2887,6 +2889,7 @@ displayInfo(const char* inFileName, const fileInfo_t* info, int displayLevel)
DISPLAYOUT("# Zstandard Frames: %d\n", info->numActualFrames);
if (info->numSkippableFrames)
DISPLAYOUT("# Skippable Frames: %d\n", info->numSkippableFrames);
+ DISPLAYOUT("DictID: %u\n", info->dictID);
DISPLAYOUT("Window Size: %.*f%s (%llu B)\n",
window_hrs.precision, window_hrs.value, window_hrs.suffix,
(unsigned long long)info->windowSize);
From d7eb829af5428f62b9308917232207d0dc92292c Mon Sep 17 00:00:00 2001
From: htnhan
Date: Fri, 8 Jul 2022 12:20:50 -0500
Subject: [PATCH 174/472] Detect multiple dictIDs in one file
---
programs/fileio.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/programs/fileio.c b/programs/fileio.c
index 86906bed3a3..b8b2818bcd8 100644
--- a/programs/fileio.c
+++ b/programs/fileio.c
@@ -2776,7 +2776,12 @@ FIO_analyzeFrames(fileInfo_t* info, FILE* const srcFile)
}
ERROR_IF(ZSTD_getFrameHeader(&header, headerBuffer, numBytesRead) != 0,
info_frame_error, "Error: could not decode frame header");
- info->dictID = header.dictID;
+ if (info->dictID != 0 && info->dictID != header.dictID) {
+ DISPLAY("WARNING: File contains multiple frames with different dictionary IDs. Showing dictID 0 instead");
+ info->dictID = 0;
+ } else {
+ info->dictID = header.dictID;
+ }
info->windowSize = header.windowSize;
/* move to the end of the frame header */
{ size_t const headerSize = ZSTD_frameHeaderSize(headerBuffer, numBytesRead);
From 962746edffa5340315136af34ac3331eba82c3c8 Mon Sep 17 00:00:00 2001
From: Miles HU
Date: Fri, 8 Jul 2022 15:01:36 -0700
Subject: [PATCH 175/472] T119975957
Signed-off-by: Miles HU
---
lib/compress/zstd_compress.c | 98 --------------------------------
lib/decompress/zstd_decompress.c | 7 ---
lib/zstd.h | 2 -
tests/fuzzer.c | 13 -----
4 files changed, 120 deletions(-)
diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
index b1f9bba8a9d..1e853e215df 100644
--- a/lib/compress/zstd_compress.c
+++ b/lib/compress/zstd_compress.c
@@ -2243,104 +2243,6 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx,
}
}
-/*! ZSTD_copyCCtx_internal() :
- * Duplicate an existing context `srcCCtx` into another one `dstCCtx`.
- * Only works during stage ZSTDcs_init (i.e. after creation, but before first call to ZSTD_compressContinue()).
- * 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,
- ZSTD_frameParameters fParams,
- U64 pledgedSrcSize,
- ZSTD_buffered_policy_e zbuff)
-{
- RETURN_ERROR_IF(srcCCtx->stage!=ZSTDcs_init, stage_wrong,
- "Can't copy a ctx that's not in init stage.");
- DEBUGLOG(5, "ZSTD_copyCCtx_internal");
- ZSTD_memcpy(&dstCCtx->customMem, &srcCCtx->customMem, sizeof(ZSTD_customMem));
- { ZSTD_CCtx_params params = dstCCtx->requestedParams;
- /* Copy only compression parameters related to tables. */
- params.cParams = srcCCtx->appliedParams.cParams;
- assert(srcCCtx->appliedParams.useRowMatchFinder != ZSTD_ps_auto);
- assert(srcCCtx->appliedParams.useBlockSplitter != ZSTD_ps_auto);
- assert(srcCCtx->appliedParams.ldmParams.enableLdm != ZSTD_ps_auto);
- params.useRowMatchFinder = srcCCtx->appliedParams.useRowMatchFinder;
- params.useBlockSplitter = srcCCtx->appliedParams.useBlockSplitter;
- params.ldmParams = srcCCtx->appliedParams.ldmParams;
- params.fParams = fParams;
- ZSTD_resetCCtx_internal(dstCCtx, ¶ms, pledgedSrcSize,
- /* loadedDictSize */ 0,
- ZSTDcrp_leaveDirty, zbuff);
- assert(dstCCtx->appliedParams.cParams.windowLog == srcCCtx->appliedParams.cParams.windowLog);
- assert(dstCCtx->appliedParams.cParams.strategy == srcCCtx->appliedParams.cParams.strategy);
- assert(dstCCtx->appliedParams.cParams.hashLog == srcCCtx->appliedParams.cParams.hashLog);
- assert(dstCCtx->appliedParams.cParams.chainLog == srcCCtx->appliedParams.cParams.chainLog);
- assert(dstCCtx->blockState.matchState.hashLog3 == srcCCtx->blockState.matchState.hashLog3);
- }
-
- ZSTD_cwksp_mark_tables_dirty(&dstCCtx->workspace);
-
- /* copy tables */
- { size_t const chainSize = ZSTD_allocateChainTable(srcCCtx->appliedParams.cParams.strategy,
- srcCCtx->appliedParams.useRowMatchFinder,
- 0 /* forDDSDict */)
- ? ((size_t)1 << srcCCtx->appliedParams.cParams.chainLog)
- : 0;
- size_t const hSize = (size_t)1 << srcCCtx->appliedParams.cParams.hashLog;
- int const h3log = srcCCtx->blockState.matchState.hashLog3;
- size_t const h3Size = h3log ? ((size_t)1 << h3log) : 0;
-
- ZSTD_memcpy(dstCCtx->blockState.matchState.hashTable,
- srcCCtx->blockState.matchState.hashTable,
- hSize * sizeof(U32));
- ZSTD_memcpy(dstCCtx->blockState.matchState.chainTable,
- srcCCtx->blockState.matchState.chainTable,
- chainSize * sizeof(U32));
- ZSTD_memcpy(dstCCtx->blockState.matchState.hashTable3,
- srcCCtx->blockState.matchState.hashTable3,
- h3Size * sizeof(U32));
- }
-
- ZSTD_cwksp_mark_tables_clean(&dstCCtx->workspace);
-
- /* copy dictionary offsets */
- {
- const ZSTD_matchState_t* srcMatchState = &srcCCtx->blockState.matchState;
- ZSTD_matchState_t* dstMatchState = &dstCCtx->blockState.matchState;
- dstMatchState->window = srcMatchState->window;
- dstMatchState->nextToUpdate = srcMatchState->nextToUpdate;
- dstMatchState->loadedDictEnd= srcMatchState->loadedDictEnd;
- }
- dstCCtx->dictID = srcCCtx->dictID;
- dstCCtx->dictContentSize = srcCCtx->dictContentSize;
-
- /* copy block state */
- ZSTD_memcpy(dstCCtx->blockState.prevCBlock, srcCCtx->blockState.prevCBlock, sizeof(*srcCCtx->blockState.prevCBlock));
-
- return 0;
-}
-
-/*! ZSTD_copyCCtx() :
- * Duplicate an existing context `srcCCtx` into another one `dstCCtx`.
- * Only works during stage ZSTDcs_init (i.e. after creation, but before first call to ZSTD_compressContinue()).
- * pledgedSrcSize==0 means "unknown".
-* @return : 0, or an error code */
-size_t ZSTD_copyCCtx(ZSTD_CCtx* dstCCtx, const ZSTD_CCtx* srcCCtx, unsigned long long pledgedSrcSize)
-{
- ZSTD_frameParameters fParams = { 1 /*content*/, 0 /*checksum*/, 0 /*noDictID*/ };
- ZSTD_buffered_policy_e const zbuff = srcCCtx->bufferedPolicy;
- ZSTD_STATIC_ASSERT((U32)ZSTDb_buffered==1);
- if (pledgedSrcSize==0) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN;
- fParams.contentSizeFlag = (pledgedSrcSize != ZSTD_CONTENTSIZE_UNKNOWN);
-
- return ZSTD_copyCCtx_internal(dstCCtx, srcCCtx,
- fParams, pledgedSrcSize,
- zbuff);
-}
-
-
#define ZSTD_ROWSIZE 16
/*! ZSTD_reduceTable() :
* reduce table indexes by `reducerValue`, or squash to zero.
diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c
index 5bd412df436..779028b97ac 100644
--- a/lib/decompress/zstd_decompress.c
+++ b/lib/decompress/zstd_decompress.c
@@ -338,13 +338,6 @@ size_t ZSTD_freeDCtx(ZSTD_DCtx* dctx)
}
}
-/* no longer useful */
-void ZSTD_copyDCtx(ZSTD_DCtx* dstDCtx, const ZSTD_DCtx* srcDCtx)
-{
- size_t const toCopy = (size_t)((char*)(&dstDCtx->inBuff) - (char*)dstDCtx);
- ZSTD_memcpy(dstDCtx, srcDCtx, toCopy); /* no need to copy workspace */
-}
-
/* Given a dctx with a digested frame params, re-selects the correct ZSTD_DDict based on
* the requested dict ID from the frame. If there exists a reference to the correct ZSTD_DDict, then
* accordingly sets the ddict to be used to decompress the frame.
diff --git a/lib/zstd.h b/lib/zstd.h
index 4c2eee69609..81dec7ae958 100644
--- a/lib/zstd.h
+++ b/lib/zstd.h
@@ -2413,7 +2413,6 @@ ZSTDLIB_STATIC_API size_t ZSTD_resetDStream(ZSTD_DStream* zds);
Start by initializing a context.
Use ZSTD_compressBegin(), or ZSTD_compressBegin_usingDict() for dictionary compression.
- It's also possible to duplicate a reference context which has already been initialized, using ZSTD_copyCCtx()
Then, consume your input using ZSTD_compressContinue().
There are some important considerations to keep in mind when using this advanced function :
@@ -2553,7 +2552,6 @@ ZSTDLIB_STATIC_API size_t ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx* dctx);
ZSTDLIB_STATIC_API size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
/* misc */
-ZSTDLIB_STATIC_API void ZSTD_copyDCtx(ZSTD_DCtx* dctx, const ZSTD_DCtx* preparedDCtx);
typedef enum { ZSTDnit_frameHeader, ZSTDnit_blockHeader, ZSTDnit_block, ZSTDnit_lastBlock, ZSTDnit_checksum, ZSTDnit_skippableFrame } ZSTD_nextInputType_e;
ZSTDLIB_STATIC_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx);
diff --git a/tests/fuzzer.c b/tests/fuzzer.c
index 81c2d9dba22..34b70bf597c 100644
--- a/tests/fuzzer.c
+++ b/tests/fuzzer.c
@@ -623,13 +623,10 @@ static int basicUnitTests(U32 const seed, double compressibility)
ZSTD_getDictID_fromDDict;
ZSTD_DStream* (* const funcptr_createDStream)(
ZSTD_customMem customMem) = ZSTD_createDStream_advanced;
- void (* const funcptr_copyDCtx)(
- ZSTD_DCtx* dctx, const ZSTD_DCtx* preparedDCtx) = ZSTD_copyDCtx;
ZSTD_nextInputType_e (* const funcptr_nextInputType)(ZSTD_DCtx* dctx) =
ZSTD_nextInputType;
const void *voidptr_getDictID;
const void *voidptr_createDStream;
- const void *voidptr_copyDCtx;
const void *voidptr_nextInputType;
DEBUG_STATIC_ASSERT(sizeof(funcptr_getDictID) == sizeof(voidptr_getDictID));
memcpy(
@@ -641,7 +638,6 @@ static int basicUnitTests(U32 const seed, double compressibility)
(const void*)&funcptr_createDStream,
sizeof(void*));
memcpy(
- (void*)&voidptr_copyDCtx,
(const void*)&funcptr_copyDCtx,
sizeof(void*));
memcpy(
@@ -650,7 +646,6 @@ static int basicUnitTests(U32 const seed, double compressibility)
sizeof(void*));
DISPLAYLEVEL(3, "%p ", voidptr_getDictID);
DISPLAYLEVEL(3, "%p ", voidptr_createDStream);
- DISPLAYLEVEL(3, "%p ", voidptr_copyDCtx);
DISPLAYLEVEL(3, "%p ", voidptr_nextInputType);
}
DISPLAYLEVEL(3, ": OK \n");
@@ -1878,14 +1873,8 @@ static int basicUnitTests(U32 const seed, double compressibility)
static const size_t dictSize = 551;
assert(dctx != NULL); assert(ctxOrig != NULL); assert(ctxDuplicated != NULL);
- DISPLAYLEVEL(3, "test%3i : copy context too soon : ", testNb++);
- { size_t const copyResult = ZSTD_copyCCtx(ctxDuplicated, ctxOrig, 0);
- if (!ZSTD_isError(copyResult)) goto _output_error; } /* error must be detected */
- DISPLAYLEVEL(3, "OK \n");
-
DISPLAYLEVEL(3, "test%3i : load dictionary into context : ", testNb++);
CHECK( ZSTD_compressBegin_usingDict(ctxOrig, CNBuffer, dictSize, 2) );
- CHECK( ZSTD_copyCCtx(ctxDuplicated, ctxOrig, 0) ); /* Begin_usingDict implies unknown srcSize, so match that */
DISPLAYLEVEL(3, "OK \n");
DISPLAYLEVEL(3, "test%3i : compress with flat dictionary : ", testNb++);
@@ -1946,7 +1935,6 @@ static int basicUnitTests(U32 const seed, double compressibility)
DISPLAYLEVEL(3, "test%3i : check content size on duplicated context : ", testNb++);
{ size_t const testSize = CNBuffSize / 3;
CHECK( ZSTD_compressBegin(ctxOrig, ZSTD_defaultCLevel()) );
- CHECK( ZSTD_copyCCtx(ctxDuplicated, ctxOrig, testSize) );
CHECK_VAR(cSize, ZSTD_compressEnd(ctxDuplicated, compressedBuffer, ZSTD_compressBound(testSize),
(const char*)CNBuffer + dictSize, testSize) );
@@ -4060,7 +4048,6 @@ static int fuzzerTests(U32 seed, unsigned nbTests, unsigned startTest, U32 const
ZSTD_parameters const p = FUZ_makeParams(cPar, fPar);
CHECK_Z ( ZSTD_compressBegin_advanced(refCtx, dict, dictSize, p, 0) );
}
- CHECK_Z( ZSTD_copyCCtx(ctx, refCtx, 0) );
}
{ U32 const nbChunks = (FUZ_rand(&lseed) & 127) + 2;
From a5655e4017c223081b0345710bfef85972f4bd87 Mon Sep 17 00:00:00 2001
From: Miles HU
Date: Tue, 12 Jul 2022 11:17:25 -0700
Subject: [PATCH 176/472] Revert "T119975957"
This reverts commit 962746edffa5340315136af34ac3331eba82c3c8.
---
lib/compress/zstd_compress.c | 98 ++++++++++++++++++++++++++++++++
lib/decompress/zstd_decompress.c | 7 +++
lib/zstd.h | 2 +
tests/fuzzer.c | 13 +++++
4 files changed, 120 insertions(+)
diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
index 1e853e215df..b1f9bba8a9d 100644
--- a/lib/compress/zstd_compress.c
+++ b/lib/compress/zstd_compress.c
@@ -2243,6 +2243,104 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx,
}
}
+/*! ZSTD_copyCCtx_internal() :
+ * Duplicate an existing context `srcCCtx` into another one `dstCCtx`.
+ * Only works during stage ZSTDcs_init (i.e. after creation, but before first call to ZSTD_compressContinue()).
+ * 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,
+ ZSTD_frameParameters fParams,
+ U64 pledgedSrcSize,
+ ZSTD_buffered_policy_e zbuff)
+{
+ RETURN_ERROR_IF(srcCCtx->stage!=ZSTDcs_init, stage_wrong,
+ "Can't copy a ctx that's not in init stage.");
+ DEBUGLOG(5, "ZSTD_copyCCtx_internal");
+ ZSTD_memcpy(&dstCCtx->customMem, &srcCCtx->customMem, sizeof(ZSTD_customMem));
+ { ZSTD_CCtx_params params = dstCCtx->requestedParams;
+ /* Copy only compression parameters related to tables. */
+ params.cParams = srcCCtx->appliedParams.cParams;
+ assert(srcCCtx->appliedParams.useRowMatchFinder != ZSTD_ps_auto);
+ assert(srcCCtx->appliedParams.useBlockSplitter != ZSTD_ps_auto);
+ assert(srcCCtx->appliedParams.ldmParams.enableLdm != ZSTD_ps_auto);
+ params.useRowMatchFinder = srcCCtx->appliedParams.useRowMatchFinder;
+ params.useBlockSplitter = srcCCtx->appliedParams.useBlockSplitter;
+ params.ldmParams = srcCCtx->appliedParams.ldmParams;
+ params.fParams = fParams;
+ ZSTD_resetCCtx_internal(dstCCtx, ¶ms, pledgedSrcSize,
+ /* loadedDictSize */ 0,
+ ZSTDcrp_leaveDirty, zbuff);
+ assert(dstCCtx->appliedParams.cParams.windowLog == srcCCtx->appliedParams.cParams.windowLog);
+ assert(dstCCtx->appliedParams.cParams.strategy == srcCCtx->appliedParams.cParams.strategy);
+ assert(dstCCtx->appliedParams.cParams.hashLog == srcCCtx->appliedParams.cParams.hashLog);
+ assert(dstCCtx->appliedParams.cParams.chainLog == srcCCtx->appliedParams.cParams.chainLog);
+ assert(dstCCtx->blockState.matchState.hashLog3 == srcCCtx->blockState.matchState.hashLog3);
+ }
+
+ ZSTD_cwksp_mark_tables_dirty(&dstCCtx->workspace);
+
+ /* copy tables */
+ { size_t const chainSize = ZSTD_allocateChainTable(srcCCtx->appliedParams.cParams.strategy,
+ srcCCtx->appliedParams.useRowMatchFinder,
+ 0 /* forDDSDict */)
+ ? ((size_t)1 << srcCCtx->appliedParams.cParams.chainLog)
+ : 0;
+ size_t const hSize = (size_t)1 << srcCCtx->appliedParams.cParams.hashLog;
+ int const h3log = srcCCtx->blockState.matchState.hashLog3;
+ size_t const h3Size = h3log ? ((size_t)1 << h3log) : 0;
+
+ ZSTD_memcpy(dstCCtx->blockState.matchState.hashTable,
+ srcCCtx->blockState.matchState.hashTable,
+ hSize * sizeof(U32));
+ ZSTD_memcpy(dstCCtx->blockState.matchState.chainTable,
+ srcCCtx->blockState.matchState.chainTable,
+ chainSize * sizeof(U32));
+ ZSTD_memcpy(dstCCtx->blockState.matchState.hashTable3,
+ srcCCtx->blockState.matchState.hashTable3,
+ h3Size * sizeof(U32));
+ }
+
+ ZSTD_cwksp_mark_tables_clean(&dstCCtx->workspace);
+
+ /* copy dictionary offsets */
+ {
+ const ZSTD_matchState_t* srcMatchState = &srcCCtx->blockState.matchState;
+ ZSTD_matchState_t* dstMatchState = &dstCCtx->blockState.matchState;
+ dstMatchState->window = srcMatchState->window;
+ dstMatchState->nextToUpdate = srcMatchState->nextToUpdate;
+ dstMatchState->loadedDictEnd= srcMatchState->loadedDictEnd;
+ }
+ dstCCtx->dictID = srcCCtx->dictID;
+ dstCCtx->dictContentSize = srcCCtx->dictContentSize;
+
+ /* copy block state */
+ ZSTD_memcpy(dstCCtx->blockState.prevCBlock, srcCCtx->blockState.prevCBlock, sizeof(*srcCCtx->blockState.prevCBlock));
+
+ return 0;
+}
+
+/*! ZSTD_copyCCtx() :
+ * Duplicate an existing context `srcCCtx` into another one `dstCCtx`.
+ * Only works during stage ZSTDcs_init (i.e. after creation, but before first call to ZSTD_compressContinue()).
+ * pledgedSrcSize==0 means "unknown".
+* @return : 0, or an error code */
+size_t ZSTD_copyCCtx(ZSTD_CCtx* dstCCtx, const ZSTD_CCtx* srcCCtx, unsigned long long pledgedSrcSize)
+{
+ ZSTD_frameParameters fParams = { 1 /*content*/, 0 /*checksum*/, 0 /*noDictID*/ };
+ ZSTD_buffered_policy_e const zbuff = srcCCtx->bufferedPolicy;
+ ZSTD_STATIC_ASSERT((U32)ZSTDb_buffered==1);
+ if (pledgedSrcSize==0) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN;
+ fParams.contentSizeFlag = (pledgedSrcSize != ZSTD_CONTENTSIZE_UNKNOWN);
+
+ return ZSTD_copyCCtx_internal(dstCCtx, srcCCtx,
+ fParams, pledgedSrcSize,
+ zbuff);
+}
+
+
#define ZSTD_ROWSIZE 16
/*! ZSTD_reduceTable() :
* reduce table indexes by `reducerValue`, or squash to zero.
diff --git a/lib/decompress/zstd_decompress.c b/lib/decompress/zstd_decompress.c
index 779028b97ac..5bd412df436 100644
--- a/lib/decompress/zstd_decompress.c
+++ b/lib/decompress/zstd_decompress.c
@@ -338,6 +338,13 @@ size_t ZSTD_freeDCtx(ZSTD_DCtx* dctx)
}
}
+/* no longer useful */
+void ZSTD_copyDCtx(ZSTD_DCtx* dstDCtx, const ZSTD_DCtx* srcDCtx)
+{
+ size_t const toCopy = (size_t)((char*)(&dstDCtx->inBuff) - (char*)dstDCtx);
+ ZSTD_memcpy(dstDCtx, srcDCtx, toCopy); /* no need to copy workspace */
+}
+
/* Given a dctx with a digested frame params, re-selects the correct ZSTD_DDict based on
* the requested dict ID from the frame. If there exists a reference to the correct ZSTD_DDict, then
* accordingly sets the ddict to be used to decompress the frame.
diff --git a/lib/zstd.h b/lib/zstd.h
index 81dec7ae958..4c2eee69609 100644
--- a/lib/zstd.h
+++ b/lib/zstd.h
@@ -2413,6 +2413,7 @@ ZSTDLIB_STATIC_API size_t ZSTD_resetDStream(ZSTD_DStream* zds);
Start by initializing a context.
Use ZSTD_compressBegin(), or ZSTD_compressBegin_usingDict() for dictionary compression.
+ It's also possible to duplicate a reference context which has already been initialized, using ZSTD_copyCCtx()
Then, consume your input using ZSTD_compressContinue().
There are some important considerations to keep in mind when using this advanced function :
@@ -2552,6 +2553,7 @@ ZSTDLIB_STATIC_API size_t ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx* dctx);
ZSTDLIB_STATIC_API size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
/* misc */
+ZSTDLIB_STATIC_API void ZSTD_copyDCtx(ZSTD_DCtx* dctx, const ZSTD_DCtx* preparedDCtx);
typedef enum { ZSTDnit_frameHeader, ZSTDnit_blockHeader, ZSTDnit_block, ZSTDnit_lastBlock, ZSTDnit_checksum, ZSTDnit_skippableFrame } ZSTD_nextInputType_e;
ZSTDLIB_STATIC_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx);
diff --git a/tests/fuzzer.c b/tests/fuzzer.c
index 34b70bf597c..81c2d9dba22 100644
--- a/tests/fuzzer.c
+++ b/tests/fuzzer.c
@@ -623,10 +623,13 @@ static int basicUnitTests(U32 const seed, double compressibility)
ZSTD_getDictID_fromDDict;
ZSTD_DStream* (* const funcptr_createDStream)(
ZSTD_customMem customMem) = ZSTD_createDStream_advanced;
+ void (* const funcptr_copyDCtx)(
+ ZSTD_DCtx* dctx, const ZSTD_DCtx* preparedDCtx) = ZSTD_copyDCtx;
ZSTD_nextInputType_e (* const funcptr_nextInputType)(ZSTD_DCtx* dctx) =
ZSTD_nextInputType;
const void *voidptr_getDictID;
const void *voidptr_createDStream;
+ const void *voidptr_copyDCtx;
const void *voidptr_nextInputType;
DEBUG_STATIC_ASSERT(sizeof(funcptr_getDictID) == sizeof(voidptr_getDictID));
memcpy(
@@ -638,6 +641,7 @@ static int basicUnitTests(U32 const seed, double compressibility)
(const void*)&funcptr_createDStream,
sizeof(void*));
memcpy(
+ (void*)&voidptr_copyDCtx,
(const void*)&funcptr_copyDCtx,
sizeof(void*));
memcpy(
@@ -646,6 +650,7 @@ static int basicUnitTests(U32 const seed, double compressibility)
sizeof(void*));
DISPLAYLEVEL(3, "%p ", voidptr_getDictID);
DISPLAYLEVEL(3, "%p ", voidptr_createDStream);
+ DISPLAYLEVEL(3, "%p ", voidptr_copyDCtx);
DISPLAYLEVEL(3, "%p ", voidptr_nextInputType);
}
DISPLAYLEVEL(3, ": OK \n");
@@ -1873,8 +1878,14 @@ static int basicUnitTests(U32 const seed, double compressibility)
static const size_t dictSize = 551;
assert(dctx != NULL); assert(ctxOrig != NULL); assert(ctxDuplicated != NULL);
+ DISPLAYLEVEL(3, "test%3i : copy context too soon : ", testNb++);
+ { size_t const copyResult = ZSTD_copyCCtx(ctxDuplicated, ctxOrig, 0);
+ if (!ZSTD_isError(copyResult)) goto _output_error; } /* error must be detected */
+ DISPLAYLEVEL(3, "OK \n");
+
DISPLAYLEVEL(3, "test%3i : load dictionary into context : ", testNb++);
CHECK( ZSTD_compressBegin_usingDict(ctxOrig, CNBuffer, dictSize, 2) );
+ CHECK( ZSTD_copyCCtx(ctxDuplicated, ctxOrig, 0) ); /* Begin_usingDict implies unknown srcSize, so match that */
DISPLAYLEVEL(3, "OK \n");
DISPLAYLEVEL(3, "test%3i : compress with flat dictionary : ", testNb++);
@@ -1935,6 +1946,7 @@ static int basicUnitTests(U32 const seed, double compressibility)
DISPLAYLEVEL(3, "test%3i : check content size on duplicated context : ", testNb++);
{ size_t const testSize = CNBuffSize / 3;
CHECK( ZSTD_compressBegin(ctxOrig, ZSTD_defaultCLevel()) );
+ CHECK( ZSTD_copyCCtx(ctxDuplicated, ctxOrig, testSize) );
CHECK_VAR(cSize, ZSTD_compressEnd(ctxDuplicated, compressedBuffer, ZSTD_compressBound(testSize),
(const char*)CNBuffer + dictSize, testSize) );
@@ -4048,6 +4060,7 @@ static int fuzzerTests(U32 seed, unsigned nbTests, unsigned startTest, U32 const
ZSTD_parameters const p = FUZ_makeParams(cPar, fPar);
CHECK_Z ( ZSTD_compressBegin_advanced(refCtx, dict, dictSize, p, 0) );
}
+ CHECK_Z( ZSTD_copyCCtx(ctx, refCtx, 0) );
}
{ U32 const nbChunks = (FUZ_rand(&lseed) & 127) + 2;
From 6b233d5d41d65f00ff830abf846d2939f5812f56 Mon Sep 17 00:00:00 2001
From: Miles HU
Date: Wed, 13 Jul 2022 11:00:05 -0700
Subject: [PATCH 177/472] [T124890272] Mark 2 Obsolete Functions(ZSTD_copy*Ctx)
Deprecated in Zstd
The discussion for this task is here: facebook/zstd#3128.
This task can probably be scoped to the first part: marking these functions deprecated.
We'll later look at removal when we roll out v1.6.0.
---
lib/zstd.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lib/zstd.h b/lib/zstd.h
index 4c2eee69609..75acd59233d 100644
--- a/lib/zstd.h
+++ b/lib/zstd.h
@@ -2413,7 +2413,6 @@ ZSTDLIB_STATIC_API size_t ZSTD_resetDStream(ZSTD_DStream* zds);
Start by initializing a context.
Use ZSTD_compressBegin(), or ZSTD_compressBegin_usingDict() for dictionary compression.
- It's also possible to duplicate a reference context which has already been initialized, using ZSTD_copyCCtx()
Then, consume your input using ZSTD_compressContinue().
There are some important considerations to keep in mind when using this advanced function :
@@ -2438,6 +2437,7 @@ ZSTDLIB_STATIC_API size_t ZSTD_resetDStream(ZSTD_DStream* zds);
ZSTDLIB_STATIC_API size_t ZSTD_compressBegin(ZSTD_CCtx* cctx, int compressionLevel);
ZSTDLIB_STATIC_API size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel);
ZSTDLIB_STATIC_API size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict); /**< note: fails if cdict==NULL */
+ZSTD_DEPRECATED("will be deprecated soon")
ZSTDLIB_STATIC_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_STATIC_API size_t ZSTD_compressContinue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
@@ -2553,6 +2553,7 @@ ZSTDLIB_STATIC_API size_t ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx* dctx);
ZSTDLIB_STATIC_API size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
/* misc */
+ZSTD_DEPRECATED("will be deprecated soon")
ZSTDLIB_STATIC_API void ZSTD_copyDCtx(ZSTD_DCtx* dctx, const ZSTD_DCtx* preparedDCtx);
typedef enum { ZSTDnit_frameHeader, ZSTDnit_blockHeader, ZSTDnit_block, ZSTDnit_lastBlock, ZSTDnit_checksum, ZSTDnit_skippableFrame } ZSTD_nextInputType_e;
ZSTDLIB_STATIC_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx);
@@ -2575,7 +2576,6 @@ ZSTDLIB_STATIC_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx);
- It is necessary to init context before starting
+ compression : any ZSTD_compressBegin*() variant, including with dictionary
+ decompression : any ZSTD_decompressBegin*() variant, including with dictionary
- + copyCCtx() and copyDCtx() can be used too
- Block size is limited, it must be <= ZSTD_getBlockSize() <= ZSTD_BLOCKSIZE_MAX == 128 KB
+ If input is larger than a block size, it's necessary to split input data into multiple blocks
+ For inputs larger than a single block, consider using regular ZSTD_compress() instead.
From 6d75b36b7f80c6289d2ec44e63986c0cbdadf03e Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Wed, 13 Jul 2022 16:54:29 -0400
Subject: [PATCH 178/472] Clarify -B docstring
---
programs/README.md | 2 +-
programs/zstd.1 | 2 +-
programs/zstd.1.md | 2 +-
programs/zstdcli.c | 2 +-
zlibWrapper/examples/zwrapbench.c | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/programs/README.md b/programs/README.md
index b88cf78d71a..c192b802693 100644
--- a/programs/README.md
+++ b/programs/README.md
@@ -209,7 +209,7 @@ Benchmark arguments :
-b# : benchmark file(s), using # compression level (default: 3)
-e# : test all compression levels successively from -b# to -e# (default: 1)
-i# : minimum evaluation time in seconds (default: 3s)
- -B# : cut file into independent blocks of size # (default: no block)
+ -B# : cut file into independent chunks of size # (default: no chunking)
-S : output one benchmark result per input file (default: consolidated result)
--priority=rt : set process priority to real-time
```
diff --git a/programs/zstd.1 b/programs/zstd.1
index 84450d8ec34..3ac7f0f3c36 100644
--- a/programs/zstd.1
+++ b/programs/zstd.1
@@ -356,7 +356,7 @@ minimum evaluation time, in seconds (default: 3s), benchmark mode only
.
.TP
\fB\-B#\fR, \fB\-\-block\-size=#\fR
-cut file(s) into independent blocks of size # (default: no block)
+cut file(s) into independent chunks of size # (default: no chunking)
.
.TP
\fB\-\-priority=rt\fR
diff --git a/programs/zstd.1.md b/programs/zstd.1.md
index c3a87817676..af18586ab20 100644
--- a/programs/zstd.1.md
+++ b/programs/zstd.1.md
@@ -470,7 +470,7 @@ BENCHMARK
* `-i#`:
minimum evaluation time, in seconds (default: 3s), benchmark mode only
* `-B#`, `--block-size=#`:
- cut file(s) into independent blocks of size # (default: no block)
+ cut file(s) into independent chunks of size # (default: no chunking)
* `--priority=rt`:
set process priority to real-time
diff --git a/programs/zstdcli.c b/programs/zstdcli.c
index 5586f395644..fbacb908a92 100644
--- a/programs/zstdcli.c
+++ b/programs/zstdcli.c
@@ -284,7 +284,7 @@ static void usage_advanced(const char* programName)
DISPLAYOUT(" -b# benchmark file(s), using # compression level (default: %d)\n", ZSTDCLI_CLEVEL_DEFAULT);
DISPLAYOUT(" -e# test all compression levels successively from -b# to -e# (default: 1)\n");
DISPLAYOUT(" -i# minimum evaluation time in seconds (default: 3s)\n");
- DISPLAYOUT(" -B# cut file into independent blocks of size # (default: no block)\n");
+ DISPLAYOUT(" -B# cut file into independent chunks of size # (default: no chunking)\n");
DISPLAYOUT(" -S output one benchmark result per input file (default: consolidated result)\n");
DISPLAYOUT(" --priority=rt set process priority to real-time\n");
#endif
diff --git a/zlibWrapper/examples/zwrapbench.c b/zlibWrapper/examples/zwrapbench.c
index 5993e51ba02..8673ca348da 100644
--- a/zlibWrapper/examples/zwrapbench.c
+++ b/zlibWrapper/examples/zwrapbench.c
@@ -836,7 +836,7 @@ static int usage(const char* programName)
DISPLAY( " -b# : benchmark file(s), using # compression level (default : %d) \n", ZSTDCLI_CLEVEL_DEFAULT);
DISPLAY( " -e# : test all compression levels from -bX to # (default: %d)\n", ZSTDCLI_CLEVEL_DEFAULT);
DISPLAY( " -i# : minimum evaluation time in seconds (default : 3s)\n");
- DISPLAY( " -B# : cut file into independent blocks of size # (default: no block)\n");
+ DISPLAY( " -B# : cut file into independent chunks of size # (default: no chunking)\n");
return 0;
}
From 43f21a600ec431aa615b09868f1f586b949607fb Mon Sep 17 00:00:00 2001
From: udayanbapat <104396882+udayanbapat@users.noreply.github.com>
Date: Thu, 14 Jul 2022 11:54:34 -0700
Subject: [PATCH 179/472] Intial commit to address 3090. Added support to
decompress empty block. (#3118)
* Intial commit to address 3090. Added support to decompress empty block
* Update zstd_decompress_block.c
Addressed review comments for the case of 'set_basic'
* Update lib/decompress/zstd_decompress_block.c
Co-authored-by: Nick Terrell
* Update lib/decompress/zstd_decompress_block.c
Co-authored-by: Nick Terrell
Co-authored-by: Nick Terrell
---
lib/common/zstd_internal.h | 2 +-
lib/compress/zstd_compress.c | 12 +++++++++---
lib/decompress/zstd_decompress_block.c | 6 ++++--
tests/golden-decompression/empty-block.zst | Bin 0 -> 11 bytes
tests/playTests.sh | 5 ++++-
5 files changed, 18 insertions(+), 7 deletions(-)
create mode 100644 tests/golden-decompression/empty-block.zst
diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h
index e76b8e19d64..038232453f8 100644
--- a/lib/common/zstd_internal.h
+++ b/lib/common/zstd_internal.h
@@ -93,7 +93,7 @@ typedef enum { bt_raw, bt_rle, bt_compressed, bt_reserved } blockType_e;
#define ZSTD_FRAMECHECKSUMSIZE 4
#define MIN_SEQUENCES_SIZE 1 /* nbSeq==0 */
-#define MIN_CBLOCK_SIZE (1 /*litCSize*/ + 1 /* RLE or RAW */ + MIN_SEQUENCES_SIZE /* nbSeq==0 */) /* for a non-null block */
+#define MIN_CBLOCK_SIZE (1 /*litCSize*/ + 1 /* RLE or RAW */) /* for a non-null block */
typedef enum { set_basic, set_rle, set_compressed, set_repeat } symbolEncodingType_e;
diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c
index b1f9bba8a9d..59d441b2a5f 100644
--- a/lib/compress/zstd_compress.c
+++ b/lib/compress/zstd_compress.c
@@ -2863,7 +2863,9 @@ static size_t ZSTD_buildSeqStore(ZSTD_CCtx* zc, const void* src, size_t srcSize)
assert(srcSize <= ZSTD_BLOCKSIZE_MAX);
/* Assert that we have correctly flushed the ctx params into the ms's copy */
ZSTD_assertEqualCParams(zc->appliedParams.cParams, ms->cParams);
- if (srcSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1) {
+ /* TODO: See 3090. We reduced MIN_CBLOCK_SIZE from 3 to 2 so to compensate we are adding
+ * additional 1. We need to revisit and change this logic to be more consistent */
+ if (srcSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1+1) {
if (zc->appliedParams.cParams.strategy >= ZSTD_btopt) {
ZSTD_ldm_skipRawSeqStoreBytes(&zc->externSeqStore, srcSize);
} else {
@@ -4000,7 +4002,9 @@ static size_t ZSTD_compress_frameChunk(ZSTD_CCtx* cctx,
ZSTD_matchState_t* const ms = &cctx->blockState.matchState;
U32 const lastBlock = lastFrameChunk & (blockSize >= remaining);
- RETURN_ERROR_IF(dstCapacity < ZSTD_blockHeaderSize + MIN_CBLOCK_SIZE,
+ /* TODO: See 3090. We reduced MIN_CBLOCK_SIZE from 3 to 2 so to compensate we are adding
+ * additional 1. We need to revisit and change this logic to be more consistent */
+ RETURN_ERROR_IF(dstCapacity < ZSTD_blockHeaderSize + MIN_CBLOCK_SIZE + 1,
dstSize_tooSmall,
"not enough space to store compressed block");
if (remaining < blockSize) blockSize = remaining;
@@ -6215,7 +6219,9 @@ ZSTD_compressSequences_internal(ZSTD_CCtx* cctx,
blockSize -= additionalByteAdjustment;
/* If blocks are too small, emit as a nocompress block */
- if (blockSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1) {
+ /* TODO: See 3090. We reduced MIN_CBLOCK_SIZE from 3 to 2 so to compensate we are adding
+ * additional 1. We need to revisit and change this logic to be more consistent */
+ if (blockSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1+1) {
cBlockSize = ZSTD_noCompressBlock(op, dstCapacity, ip, blockSize, lastBlock);
FORWARD_IF_ERROR(cBlockSize, "Nocompress block failed");
DEBUGLOG(5, "Block too small, writing out nocompress block: cSize: %zu", cBlockSize);
diff --git a/lib/decompress/zstd_decompress_block.c b/lib/decompress/zstd_decompress_block.c
index 466e83b6be1..6df4c3849e0 100644
--- a/lib/decompress/zstd_decompress_block.c
+++ b/lib/decompress/zstd_decompress_block.c
@@ -135,7 +135,7 @@ size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx,
ZSTD_FALLTHROUGH;
case set_compressed:
- RETURN_ERROR_IF(srcSize < 5, corruption_detected, "srcSize >= MIN_CBLOCK_SIZE == 3; here we need up to 5 for case 3");
+ RETURN_ERROR_IF(srcSize < 5, corruption_detected, "srcSize >= MIN_CBLOCK_SIZE == 2; here we need up to 5 for case 3");
{ size_t lhSize, litSize, litCSize;
U32 singleStream=0;
U32 const lhlCode = (istart[0] >> 2) & 3;
@@ -238,6 +238,7 @@ size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx,
break;
case 3:
lhSize = 3;
+ RETURN_ERROR_IF(srcSize<3, corruption_detected, "srcSize >= MIN_CBLOCK_SIZE == 2; here we need lhSize = 3");
litSize = MEM_readLE24(istart) >> 4;
break;
}
@@ -280,12 +281,13 @@ size_t ZSTD_decodeLiteralsBlock(ZSTD_DCtx* dctx,
break;
case 1:
lhSize = 2;
+ RETURN_ERROR_IF(srcSize<3, corruption_detected, "srcSize >= MIN_CBLOCK_SIZE == 2; here we need lhSize+1 = 3");
litSize = MEM_readLE16(istart) >> 4;
break;
case 3:
lhSize = 3;
+ RETURN_ERROR_IF(srcSize<4, corruption_detected, "srcSize >= MIN_CBLOCK_SIZE == 2; here we need lhSize+1 = 4");
litSize = MEM_readLE24(istart) >> 4;
- RETURN_ERROR_IF(srcSize<4, corruption_detected, "srcSize >= MIN_CBLOCK_SIZE == 3; here we need lhSize+1 = 4");
break;
}
RETURN_ERROR_IF(litSize > 0 && dst == NULL, dstSize_tooSmall, "NULL not handled");
diff --git a/tests/golden-decompression/empty-block.zst b/tests/golden-decompression/empty-block.zst
new file mode 100644
index 0000000000000000000000000000000000000000..2a3782aff0d1a6bab62dcdc1af29b448322a84bd
GIT binary patch
literal 11
QcmdPcs{dDkL6iXq028eOKL7v#
literal 0
HcmV?d00001
diff --git a/tests/playTests.sh b/tests/playTests.sh
index 873c3c672cd..50aef5df5a3 100755
--- a/tests/playTests.sh
+++ b/tests/playTests.sh
@@ -418,8 +418,11 @@ println "\n===> decompression only tests "
dd bs=1048576 count=1 if=/dev/zero of=tmp
zstd -d -o tmp1 "$TESTDIR/golden-decompression/rle-first-block.zst"
$DIFF -s tmp1 tmp
-rm -f tmp*
+touch tmp_empty
+zstd -d -o tmp2 "$TESTDIR/golden-decompression/empty-block.zst"
+$DIFF -s tmp2 tmp_empty
+rm -f tmp*
println "\n===> compress multiple files"
println hello > tmp1
From d0c88afe6d5c0a29274df735a044154c6bf6deaa Mon Sep 17 00:00:00 2001
From: Han Zhu
Date: Tue, 19 Jul 2022 13:55:48 -0700
Subject: [PATCH 180/472] [largeNbDicts] Fix decompression segfault in
createCompressInstructions
Benchmarking decompression results in a segfault in `createCompressInstructions`
because `cctxParams` is NULL. Skip running that function if we are not benching
compression.
---
contrib/largeNbDicts/largeNbDicts.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/contrib/largeNbDicts/largeNbDicts.c b/contrib/largeNbDicts/largeNbDicts.c
index 66850e28cfe..f1a80c8e154 100644
--- a/contrib/largeNbDicts/largeNbDicts.c
+++ b/contrib/largeNbDicts/largeNbDicts.c
@@ -670,7 +670,9 @@ static int benchMem(slice_collection_t dstBlocks,
BMK_createTimedFnState(total_time_ms, ms_per_round);
decompressInstructions di = createDecompressInstructions(ddictionaries);
- compressInstructions ci = createCompressInstructions(cdictionaries, cctxParams);
+ compressInstructions ci;
+ if (benchCompression)
+ ci = createCompressInstructions(cdictionaries, cctxParams);
void* payload = benchCompression ? (void*)&ci : (void*)&di;
BMK_benchParams_t const bp = {
.benchFn = benchCompression ? compress : decompress,
From b550f9b77e9fa11d11078735c32ba9847e5df773 Mon Sep 17 00:00:00 2001
From: Han Zhu
Date: Tue, 19 Jul 2022 16:50:28 -0700
Subject: [PATCH 181/472] [largeNbDicts] Print more metrics into csv file
Summary:
Add column headers and data for whether it's a compression or a decompression
run, compression level, nbDicts and dictAttachPref in additional to
compr/decompr speed.
Test Plan:
Example output:
```
./largeNbDicts
Compression/Decompression,Level,nbDicts,dictAttachPref,Speed
Compression,1,1,0,300.9
Compression,1,1,1,296.4
Compression,1,1,2,307.8
Compression,1,10,0,292.3
Compression,1,100,0,293.3
Compression,3,110,0,106.0
Decompression,-1,110,-1,155.6
Decompression,-1,110,-1,709.4
Decompression,-1,120,-1,709.1
Decompression,-1,120,-1,734.6
```
---
contrib/largeNbDicts/largeNbDicts.c | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/contrib/largeNbDicts/largeNbDicts.c b/contrib/largeNbDicts/largeNbDicts.c
index f1a80c8e154..bb1c16d47a0 100644
--- a/contrib/largeNbDicts/largeNbDicts.c
+++ b/contrib/largeNbDicts/largeNbDicts.c
@@ -716,12 +716,28 @@ static int benchMem(slice_collection_t dstBlocks,
csvFile = fopen(csvFileName, "wt");
assert(csvFile);
fprintf(csvFile, "%s\n", exeName);
+ /* Print table headers */
+ fprintf(
+ csvFile,
+ "Compression/Decompression,Level,nbDicts,dictAttachPref,Speed\n");
} else {
fclose(csvFile);
csvFile = fopen(csvFileName, "at");
assert(csvFile);
}
- fprintf(csvFile, "%.1f\n", bestSpeed);
+
+ int cLevel = -1;
+ int dictAttachPref = -1;
+ if (benchCompression) {
+ ZSTD_CCtxParams_getParameter(cctxParams, ZSTD_c_compressionLevel,
+ &cLevel);
+ ZSTD_CCtxParams_getParameter(cctxParams, ZSTD_c_forceAttachDict,
+ &dictAttachPref);
+ }
+ fprintf(csvFile, "%s,%d,%ld,%d,%.1f\n",
+ benchCompression ? "Compression" : "Decompression", cLevel,
+ benchCompression ? ci.nbDicts : di.nbDicts, dictAttachPref,
+ bestSpeed);
fclose(csvFile);
free(csvFileName);
From d993a288e067ea55d406f8fc547d55b9ad6e725b Mon Sep 17 00:00:00 2001
From: Han Zhu
Date: Wed, 20 Jul 2022 11:14:51 -0700
Subject: [PATCH 182/472] [largeNbDicts] Add an option to print out median
speed
Summary:
Added an option -p# where -p0 (default) sets the aggregation method to fastest
speed while -p1 sets the aggregation method to median. Also added a new column
in the csv file to report this option's value.
Test Plan:
``
$ ./largeNbDicts -1 --nbDicts=1 -D ~/benchmarks/html/html_8_16K.32K.dict
~/benchmarks/html/html_8_16K/*
loading 7450 files...
created src buffer of size 83.4 MB
split input into 7450 blocks
loading dictionary /home/zhuhan/benchmarks/html/html_8_16K.32K.dict
compressing at level 1 without dictionary : Ratio=3.03 (28827863 bytes)
compressed using a 32768 bytes dictionary : Ratio=4.28 (20410262 bytes)
generating 1 dictionaries, using 0.1 MB of memory
Compression Speed : 306.0 MB/s
Fastest Speed : 310.6 MB/s
$ ./largeNbDicts -1 --nbDicts=1 -p1 -D ~/benchmarks/html/html_8_16K.32K.dict
~/benchmarks/html/html_8_16K/*
loading 7450 files...
created src buffer of size 83.4 MB
split input into 7450 blocks
loading dictionary /home/zhuhan/benchmarks/html/html_8_16K.32K.dict
compressing at level 1 without dictionary : Ratio=3.03 (28827863 bytes)
compressed using a 32768 bytes dictionary : Ratio=4.28 (20410262 bytes)
generating 1 dictionaries, using 0.1 MB of memory
Compression Speed : 306.9 MB/s
Median Speed : 298.4 MB/s
```
---
contrib/largeNbDicts/largeNbDicts.c | 89 ++++++++++++++++++++++-------
1 file changed, 67 insertions(+), 22 deletions(-)
diff --git a/contrib/largeNbDicts/largeNbDicts.c b/contrib/largeNbDicts/largeNbDicts.c
index bb1c16d47a0..72b4b41e9c2 100644
--- a/contrib/largeNbDicts/largeNbDicts.c
+++ b/contrib/largeNbDicts/largeNbDicts.c
@@ -19,7 +19,7 @@
/*--- Dependencies ---*/
#include /* size_t */
-#include /* malloc, free, abort */
+#include /* malloc, free, abort, qsort*/
#include /* fprintf */
#include /* UINT_MAX */
#include /* assert */
@@ -650,13 +650,40 @@ size_t decompress(const void* src, size_t srcSize, void* dst, size_t dstCapacity
return result;
}
+typedef enum {
+ fastest = 0,
+ median = 1,
+} metricAggregatePref_e;
-static int benchMem(slice_collection_t dstBlocks,
- slice_collection_t srcBlocks,
+/* compareFunction() :
+ * Sort input in decreasing order when used with qsort() */
+int compareFunction(const void *a, const void *b)
+{
+ double x = *(const double *)a;
+ double y = *(const double *)b;
+ if (x < y)
+ return 1;
+ else if (x > y)
+ return -1;
+ return 0;
+}
+
+double aggregateData(double *data, size_t size,
+ metricAggregatePref_e metricAggregatePref)
+{
+ qsort(data, size, sizeof(*data), compareFunction);
+ if (metricAggregatePref == fastest)
+ return data[0];
+ else /* median */
+ return (data[(size - 1) / 2] + data[size / 2]) / 2;
+}
+
+static int benchMem(slice_collection_t dstBlocks, slice_collection_t srcBlocks,
ddict_collection_t ddictionaries,
- cdict_collection_t cdictionaries,
- unsigned nbRounds, int benchCompression,
- const char* exeName, ZSTD_CCtx_params* cctxParams)
+ cdict_collection_t cdictionaries, unsigned nbRounds,
+ int benchCompression, const char *exeName,
+ ZSTD_CCtx_params *cctxParams,
+ metricAggregatePref_e metricAggregatePref)
{
assert(dstBlocks.nbSlices == srcBlocks.nbSlices);
if (benchCompression) assert(cctxParams);
@@ -664,7 +691,7 @@ static int benchMem(slice_collection_t dstBlocks,
unsigned const ms_per_round = RUN_TIME_DEFAULT_MS;
unsigned const total_time_ms = nbRounds * ms_per_round;
- double bestSpeed = 0.;
+ double *const speedPerRound = (double *)malloc(nbRounds * sizeof(double));
BMK_timedFnState_t* const benchState =
BMK_createTimedFnState(total_time_ms, ms_per_round);
@@ -688,6 +715,7 @@ static int benchMem(slice_collection_t dstBlocks,
.blockResults = NULL
};
+ size_t roundNb = 0;
for (;;) {
BMK_runOutcome_t const outcome = BMK_benchTimedFn(benchState, bp);
CONTROL(BMK_isSuccessful_runOutcome(outcome));
@@ -697,16 +725,24 @@ static int benchMem(slice_collection_t dstBlocks,
double const dTime_sec = (double)dTime_ns / 1000000000;
size_t const srcSize = result.sumOfReturn;
double const speed_MBps = (double)srcSize / dTime_sec / (1 MB);
- if (speed_MBps > bestSpeed) bestSpeed = speed_MBps;
+ speedPerRound[roundNb] = speed_MBps;
if (benchCompression)
- DISPLAY("Compression Speed : %.1f MB/s \r", bestSpeed);
+ DISPLAY("Compression Speed : %.1f MB/s \r", speed_MBps);
else
- DISPLAY("Decompression Speed : %.1f MB/s \r", bestSpeed);
+ DISPLAY("Decompression Speed : %.1f MB/s \r", speed_MBps);
fflush(stdout);
if (BMK_isCompleted_TimedFn(benchState)) break;
+ roundNb++;
}
DISPLAY("\n");
+ /* BMK_benchTimedFn may not run exactly nbRounds iterations */
+ double speedAggregated =
+ aggregateData(speedPerRound, roundNb + 1, metricAggregatePref);
+ if (metricAggregatePref == fastest)
+ DISPLAY("Fastest Speed : %.1f MB/s \n", speedAggregated);
+ else
+ DISPLAY("Median Speed : %.1f MB/s \n", speedAggregated);
char* csvFileName = malloc(strlen(exeName) + 5);
strcpy(csvFileName, exeName);
@@ -719,7 +755,7 @@ static int benchMem(slice_collection_t dstBlocks,
/* Print table headers */
fprintf(
csvFile,
- "Compression/Decompression,Level,nbDicts,dictAttachPref,Speed\n");
+ "Compression/Decompression,Level,nbDicts,dictAttachPref,metricAggregatePref,Speed\n");
} else {
fclose(csvFile);
csvFile = fopen(csvFileName, "at");
@@ -734,10 +770,10 @@ static int benchMem(slice_collection_t dstBlocks,
ZSTD_CCtxParams_getParameter(cctxParams, ZSTD_c_forceAttachDict,
&dictAttachPref);
}
- fprintf(csvFile, "%s,%d,%ld,%d,%.1f\n",
+ fprintf(csvFile, "%s,%d,%ld,%d,%d,%.1f\n",
benchCompression ? "Compression" : "Decompression", cLevel,
benchCompression ? ci.nbDicts : di.nbDicts, dictAttachPref,
- bestSpeed);
+ metricAggregatePref, speedAggregated);
fclose(csvFile);
free(csvFileName);
@@ -754,13 +790,11 @@ static int benchMem(slice_collection_t dstBlocks,
* dictionary : optional (can be NULL), file to load as dictionary,
* if none provided : will be calculated on the fly by the program.
* @return : 0 is success, 1+ otherwise */
-int bench(const char** fileNameTable, unsigned nbFiles,
- const char* dictionary,
- size_t blockSize, int clevel,
- unsigned nbDictMax, unsigned nbBlocks,
+int bench(const char **fileNameTable, unsigned nbFiles, const char *dictionary,
+ size_t blockSize, int clevel, unsigned nbDictMax, unsigned nbBlocks,
unsigned nbRounds, int benchCompression,
- ZSTD_dictContentType_e dictContentType, ZSTD_CCtx_params* cctxParams,
- const char* exeName)
+ ZSTD_dictContentType_e dictContentType, ZSTD_CCtx_params *cctxParams,
+ const char *exeName, metricAggregatePref_e metricAggregatePref)
{
int result = 0;
@@ -855,7 +889,9 @@ int bench(const char** fileNameTable, unsigned nbFiles,
buffer_collection_t resultCollection = createBufferCollection_fromSliceCollection(srcSlices);
CONTROL(resultCollection.buffer.ptr != NULL);
- result = benchMem(dstSlices, resultCollection.slices, ddictionaries, cdictionaries, nbRounds, benchCompression, exeName, cctxParams);
+ result = benchMem(dstSlices, resultCollection.slices, ddictionaries,
+ cdictionaries, nbRounds, benchCompression, exeName,
+ cctxParams, metricAggregatePref);
freeBufferCollection(resultCollection);
} else {
@@ -869,7 +905,9 @@ int bench(const char** fileNameTable, unsigned nbFiles,
buffer_collection_t resultCollection = createBufferCollection_fromSliceCollectionSizes(srcSlices);
CONTROL(resultCollection.buffer.ptr != NULL);
- result = benchMem(resultCollection.slices, dstSlices, ddictionaries, cdictionaries, nbRounds, benchCompression, exeName, NULL);
+ result = benchMem(resultCollection.slices, dstSlices, ddictionaries,
+ cdictionaries, nbRounds, benchCompression, exeName,
+ NULL, metricAggregatePref);
freeBufferCollection(resultCollection);
}
@@ -947,6 +985,7 @@ int usage(const char* exeName)
DISPLAY ("-# : use compression level # (default: %u) \n", CLEVEL_DEFAULT);
DISPLAY ("-D # : use # as a dictionary (default: create one) \n");
DISPLAY ("-i# : nb benchmark rounds (default: %u) \n", BENCH_TIME_DEFAULT_S);
+ DISPLAY ("-p# : print speed for all rounds 0=fastest 1=median (default: 0)");
DISPLAY ("--nbBlocks=#: use # blocks for bench (default: one per file) \n");
DISPLAY ("--nbDicts=# : create # dictionaries for bench (default: one per block) \n");
DISPLAY ("-h : help (this text) \n");
@@ -987,6 +1026,7 @@ int main (int argc, const char** argv)
ZSTD_dictContentType_e dictContentType = ZSTD_dct_auto;
ZSTD_dictAttachPref_e dictAttachPref = ZSTD_dictDefaultAttach;
ZSTD_paramSwitch_e prefetchCDictTables = ZSTD_ps_auto;
+ metricAggregatePref_e metricAggregatePref = fastest;
for (int argNb = 1; argNb < argc ; argNb++) {
const char* argument = argv[argNb];
@@ -996,6 +1036,7 @@ int main (int argc, const char** argv)
if (!strcmp(argument, "-r")) { recursiveMode = 1; continue; }
if (!strcmp(argument, "-D")) { argNb++; assert(argNb < argc); dictionary = argv[argNb]; continue; }
if (longCommandWArg(&argument, "-i")) { nbRounds = readU32FromChar(&argument); continue; }
+ if (longCommandWArg(&argument, "-p")) { metricAggregatePref = (int)readU32FromChar(&argument); continue;}
if (longCommandWArg(&argument, "--dictionary=")) { dictionary = argument; continue; }
if (longCommandWArg(&argument, "-B")) { blockSize = readU32FromChar(&argument); continue; }
if (longCommandWArg(&argument, "--blockSize=")) { blockSize = readU32FromChar(&argument); continue; }
@@ -1030,7 +1071,11 @@ int main (int argc, const char** argv)
ZSTD_CCtxParams_setParameter(cctxParams, ZSTD_c_forceAttachDict, dictAttachPref);
ZSTD_CCtxParams_setParameter(cctxParams, ZSTD_c_prefetchCDictTables, prefetchCDictTables);
- int result = bench(filenameTable->fileNames, (unsigned)filenameTable->tableSize, dictionary, blockSize, cLevel, nbDicts, nbBlocks, nbRounds, benchCompression, dictContentType, cctxParams, exeName);
+ int result =
+ bench(filenameTable->fileNames, (unsigned)filenameTable->tableSize,
+ dictionary, blockSize, cLevel, nbDicts, nbBlocks, nbRounds,
+ benchCompression, dictContentType, cctxParams, exeName,
+ metricAggregatePref);
UTIL_freeFileNamesTable(filenameTable);
free(nameTable);
From 6255f994d35219b60d3df21535c72675d5d6a98f Mon Sep 17 00:00:00 2001
From: Han Zhu
Date: Wed, 20 Jul 2022 16:01:32 -0700
Subject: [PATCH 183/472] [largeNbDicts] Second try at fixing decompression
segfault to always create compressInstructions
Summary:
Freeing an uninitialized pointer is undefined behavior. This caused a segfault
when compiling the benchmark with Clang -O3 and benching decompression.
V2: always create compressInstructions but check if cctxParams is NULL before
setting CCtx params to avoid segfault.
Test Plan:
make and run
---
contrib/largeNbDicts/largeNbDicts.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/contrib/largeNbDicts/largeNbDicts.c b/contrib/largeNbDicts/largeNbDicts.c
index 72b4b41e9c2..cc489000ff0 100644
--- a/contrib/largeNbDicts/largeNbDicts.c
+++ b/contrib/largeNbDicts/largeNbDicts.c
@@ -582,7 +582,8 @@ compressInstructions createCompressInstructions(cdict_collection_t dictionaries,
compressInstructions ci;
ci.cctx = ZSTD_createCCtx();
CONTROL(ci.cctx != NULL);
- ZSTD_CCtx_setParametersUsingCCtxParams(ci.cctx, cctxParams);
+ if (cctxParams)
+ ZSTD_CCtx_setParametersUsingCCtxParams(ci.cctx, cctxParams);
ci.nbDicts = dictionaries.nbCDict;
ci.dictNb = 0;
ci.dictionaries = dictionaries;
@@ -697,9 +698,8 @@ static int benchMem(slice_collection_t dstBlocks, slice_collection_t srcBlocks,
BMK_createTimedFnState(total_time_ms, ms_per_round);
decompressInstructions di = createDecompressInstructions(ddictionaries);
- compressInstructions ci;
- if (benchCompression)
- ci = createCompressInstructions(cdictionaries, cctxParams);
+ compressInstructions ci =
+ createCompressInstructions(cdictionaries, cctxParams);
void* payload = benchCompression ? (void*)&ci : (void*)&di;
BMK_benchParams_t const bp = {
.benchFn = benchCompression ? compress : decompress,
From 85d633042dfbd0c5cc114063af69a7c19fe82e8b Mon Sep 17 00:00:00 2001
From: Mathew R Gordon <72643694+mgord9518@users.noreply.github.com>
Date: Fri, 29 Jul 2022 11:17:31 -0600
Subject: [PATCH 184/472] Add transparency and optimize logo (#3218)
Make the front page look better in dark GH themes
---
doc/images/zstd_logo86.png | Bin 5963 -> 13069 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
diff --git a/doc/images/zstd_logo86.png b/doc/images/zstd_logo86.png
index 216f228061c460c4e4725785486ebcff38804fbc..8abefe21b1e172b9683392057421984f64c8c9a4 100644
GIT binary patch
literal 13069
zcmeHucQ~Bi*7oS4Bw7&NDA9%)z4sb52qJ>PVAL^2??jF05+s7?q9#g02!as3mk^@&
zE;`>xekJF7&wJkQT-W#hcU*gB_Fn5=_g;JLwf5tghe#bQ6=DJ!0ssI&tfs1{i~iI@
zzqIkO(C?X^*9OoZcqlbZCA>vEI#MQ<80M1<0DxA{Ti*z!3-w?^Af2t@4lpK^7Xrov
z^MqRi0G<;i&+k7q5ph?#gb>`oXjGD*tF-sRWoS5*vl?J!yH{A$MIjK}{{e4uQ4lcS
zb9r{?cG;St`vAo`YT(WJQl>DU0Z%*a^6b`@$M(v(f502RdMbavsoc{8<#{&0<+YF!
zU%!jfgY7xYjPRznXM>>=C*GD9C(7Dpqs|{6a`-c7ung>-yJcOtF+a`_X&%LCvoUVi
zHwbvyESd4SY%8F-(JbGr@LYUd=B$paKK*@ct0zy&l1JtCt-uv$l(47nF@4ESQ)4f9
z&g|q|>K^8XEH~wi8`uw|S`VXdXvsTm*@tF)neN&<@fVlrzFYq|
z$@$~iyiF(r5dNMylwSGW_Ya?MQYvWdw0yCQew?#XWi9mMU6;h0qOU{0rAp^;6bGO5u!ktDNW1Bo
zxmePSeY6nv>{xs6j6BFVDv{#Ri#h8gNb1c5h0h4*2E9+G4qZEY{3%MnH#d!6$7jlw
zk~J!y4-FM$#e?!+xONEDOzv?Q=yv2L8Dslr8*2)U>e@S2jrJGkK79bM5*qcUm2$_T
zbLFf5G2+7*^89+K
zMr=`xj$Zdu&v9Id@SPg*j?aL-Sreb$bnC#8jTryq>I^7Z0v0S?LC0@ApH>0c;v2sr
zv6}im6v8DUwbLWCSviu*nN^)QT9jXG31%7m3X2(2TUND+oeW{QzEiEdB_gfrFdE|(
z?pp?s>7{kJK085cIPNpl;H(C(7J9t(I_Fb<`>Q(cJTcw+H7CDg*4N(qb4LjtG*tb~
zwzJ=S#_?_9o~db?8u|6MC4ElKnO0%JJKem;8R+r$N
zTsR+Dp5P+v@Zsf@vdH&i8~nCWe=p}!@;`(Vuw!
zWytd5p1{PevP85=+I0BI{n4-QOC)qn0>`g^g`C85li!J&5ezt5+An0%ZxaiTD$%I2
zsT_3_WBwtp^LEm;`<^wuEzybpG)}o$5dzUItxnIvZEVo^9!xb1{>qtND`tYEul=a^
zLZoueiSbkm@v6jb4R_Y4ji~ZAHLCiaPH~3Xmt*AP>~N{K8`ZPKJ35$iP6s)!-4><4
zNLA%k*wtphNRviKjeMa9W^o!6lDdYFEqgPgXn<;UgdrWN7*W2_-
z@8Ea+W{s}~8La%vj2D80wBVKaGT4lBNG`xm3kwPnN_y
z-7V~1AZkm$CyV(rgUdj;`D4gUj^}sSc=&wp2cMTQbZ~J7b=p-lWvYKvekaRPj(qRp
z{s`z0WXja|NM_gToLFZOC;Ab8L{Mg4&LqXPiLR4cD;7v>@>{pby9=|}9~7AMpV**C
zz7tupTZi)Ij30`(m+B!K8mudT^>m?`RReD7bTeJTk3>p0=%L?DmF!p{{W4xpUIHbL
z-_RLIe6eBU6eLrKVmNzcAMrs4?#ckur$ktTPdKM4s
zCehXjIVt~GHo1?KT(@PNnuuM@*O!yoRr47~aul)lS2@2a&|}7W_N$FgYt{lR1gWv_
zC9tBBi9As`gu?*K5MsRDpjG(pruI$hM16v^=Iq!T@eS>?vQY$jf>>W7=7SNNBJGb^
z5y{oIh=jnT=Rx?};%9m{S~2iG>BL(BcvL@O(LqALO9~ERF2^JV4f_Zem6Ci-GyvIx
zPR2(vR|TXL4SSwrUnP=
zx{sz7g!eb$S-H8?7t25HPQTV_9m4@Ohc2htFe?%0eE1@_1rgA*g8T4)`vTCF5ABef
z-sq7oObkk_SuJ!l+0tkUdm3ICDk86zW0wYKG`)4h^<*=_&gPCzgSQ*LR>Aul`nbfy
zru6e8+RkQdqyRHdMgyjK`Y-uJItY8M6r2m820Jk5*7`AjJ2wf8X4#YTYZ3u{%ttxD
zV5X|9uz{Q^Ks)9k21^2Hh>QRqGPBAwdRrtxwW+k-)Au!CTbXTGuPI>Lw~+UNX6sgv
zoqFz;*Q=x57T($?aucgNN=I-+Q=fzb5D?#}m>M7HsQgU8
zyD{LoZpa;zz1d3M6U7Mau-mm71R-DKHT$jWRrC!fDkw-*FSwx|lh;WPUI27YN_x0V%|Hjt5rvj6I{+?whg6BUBo(-YIlr
zUid!?{=qV)`jqD`Ru%)RQ&RaS0i9?k0U!nY^~7c(eaAu+UIKEnLL6BZTiUA%ZFZEs
z;W=uMXfL(fiVsXdh_}mcW)IXAsxNh-`2UDu%waan-_5Lu502%gA
za(lw$k(*JmFdf4IJQj9?ZVU*!ph~FS!Rvb-Wr0+1;v)0
zr+xv2eKL;LDSscgd`n?|#?Kt-_yF`MpE;^2V?pofe
z5^n_DNg{0S$BHj70=r`K#8b^ceesz+l-&j-Yue2a_2#joriHo|9yG5YFy`!#fDOzByf%72ftjUv#>cZD`EH0AaTCuv0&d&
z3htY{qa6n^Nd>V|3$30brgg$@O$o;j#-VS3Qqc`TL)?qO&2*;E#)(JcsMv4JJypnX
zTz4|V9zWIYQ?(3H^gIFLuAD;^BC8fcdfJ*(Oz>yvb5seF#vJV^hO<6jn=7;d*hLU_
zF?TDeiL8I4W(Kh97TH#+*>0CJ$$wy}M``Fs?LR0hg<@eXo;*E&b&tO-GNt5|lV;^a
zri4XKIO+r+=(wUU5C{6=p2xBX?0C0RYaQ~o`w)nX;jD%
zVjXp`);u14sW)uBI>CBi-bEAnNFEoXzh9ZfEGdC>yS~y(HWadjVZ~T-;9h=X_yz&7
z;l1t!
zn(cU9&7;jaSwnY4Y?oF&X?0B+W)O4orGZu%XG(X-wH}V(HN85zUaVV4iWwVNit5;P
z?ru?dqV;Yu_Jo)NGm^Eh>V1$#^M##jitl{Y-Ir3RQmM-*mkXIHLs|QAp;Yp
z-Lp9RCj{GTYHOZtm5=WazhXBUuCi7%9(*&yuvzS06;%gxa}JL4uJX6Sc4SSGV;26*
zvUAixr<(~{v7zC#58_UGFx5OmBb}C@?9s@6JNKx|ec)+F$6!m=MBDe*_|;>INWJ>_
zaMe_~u9q*m5J9+=g26{X&51jStrvv-`rHTA+R4u0sr?cI?A@(}_f$wum>$p`ci-$(
zP^;o7njxjl%MC&RY;rh}!RsPN?3>ZTbPw0ezx7WdhRE)BR@u45Qb(2o5qSqyC!1oA
z5f2MoM&-%9U+SN$#Qj
zYJ#fI1`hSOX%j2V7N;vr-`P{&U<-@}N6y)4=_wh^;$tSWn6iwu+h?W_>U5Oj&cbg`
z06QKy(QN}fv>eFA#_*B%2E(3as{jKBYlu`aZSe-ojT)X-e}2}~C;Aps5EwrIp2sq-
zcFodHvUQHj`s4tfDXNk@<1w&FxQ)GwyEbuF%sOZ?AIi|7adSw38*c&ClJEJTkO(2zs#0n*ylgf9cm!*UzlhsU-{(!z5<~S@$TFwt;`gRdN~er^*UUgj4EpKGSek
ziw$0%bZ_QSdu=@4@^4#tu#ZSJB)VR)3W#xihS4RrN`lU2c=D
z7(C0)a^NUYgx!sS^NHe<`lc4{Vh$*7KVe_A3k;*6{qe;At4SAILBt9irrXx>GAElO
zwUMseP}D`zp1(Zu9brziK~YQ!qI2hn&20EZSqoR(<5BO%%BLxaROOLv`+@*RO-kDr
zoW3ux5{*9&p1HT5t4S7SO38TDVa2vd|mb6j9#GR64jb;NeA
zqokraISqsZ`=CsG!M2PIvqU6|!C#*9gQ~ji48CiG+JuoVy)rwU~f)bk`%gUSl9Zj8Fm+{;p*}iy
z%A>GIXBKT0_2;*B#Ve_z&fbMk7ruE@8JNFn5mjAP@7QASIMZAzDB(~!;u`UtNIS6O
z=Ll=Afm`+8>{@`!r8eJq5Qhv`ZJc<8T31wzh
z^-D>M>k!;%EP;sUa`B1C4JtdoYf$Wo`Oh;{!7XGYHb8LSewP7-;BEf;p6li57y;Nx
z`riCUEQ9qwj&x+=guekQ@BnqmHGE@P;6}aE{mkIL3uNSJon`=PMTe!M`&
z_Ij4y+!%1({j*#q0j7vbfPEREamR<)g0&g2qO=JdDAoGPT|+l+aZp)F-^|)6o;$_L
zt`~Sawp%&U&g+9h=KTerx(j_Vq_SS}i|1arWnQ(1bZt1H8|b}-0`Q1>yD(x&9+%pS
z2Ix2dII?6;Gl-CV;Cl<)sUDr8bxhF8xKBw%%(9Sd&`FP!ZQs@uWOZ#x@@7YE^Q6f0
zun||x5#+=pHzzi5H8co6^3SXkE)`3$mB9!qyFK0OAGI#`u-F`#?Ps$Y69u_DdkVyC
zV+c=IuDutk_KM@6&=o;tGZ&;63uUXT`v}Cr;=ML!ypEhXhgQrW9(w%L5h-L0z<>_i4HWgx(cST+wQa!_TF(LpR88|4
zQNNsLzJ$iyD!(pDeImsP@3R^vU`WhlX3_b8R!TAWI@|O3#j^U-ib$_=`1x
zf_v00wb&?M3enP|5hY!aEl(1glrmHp740VS#g~KQPPqHQAJXHB;~>QQPTMzf_Xyq3Jn0}J}F1z!-|(l&SUYGtBa_K@Eo*8{uW
z_O8)y#!F!t;nyW7T#YsZztZ8G8WB~BieEz2xy`{;<+D|eB}yly!;(cVJV4JhR`WKA9^#SQYKaKKLGKb_Ye?Y~
z;7hhSOQnGuF7Y-ulc8*%Pl=;bs;ezltzM?{=%)4HPW-iKMLGdXe@c6U^v~nkTJR@4
z#Q|ZHG7MAG=QD?MJbCGD@~1?)k%WP@aT&a;)EIqkl>%n*IS19N
zr;h+okmPdGNDB>#!h7NmPgocqGTb2wg|u-1rEh2iYtL=6%J7s7|QLsB+g?1rsj_yi>nCJgI(OrmY&F7iK7?l_SU=D%Gq
z(de{V=;>Fn5N?_T&HOy?r>1PgM=3M~RCij1s0jm>cStnau(I!;P)w_guePgiM}UvM
zzS9WsDXY<4lI2Z0k|y6X?hcwQ5Iv&5rp#?Q%)}h1uy|jzhhX##>8^~|MphD}m{sbZM2J1}yW|n8=
zElM{}cy*2|eUzVuZV@@`n%;?D&R6jsxr?O~6G^|}=zTqjK~+|HLwi&;SZQu$I$Ew7
zqw~38nwO19wsB??1hw;3JE)!V>D&$4hRZel3wXsD@Lu)-W3FUJwQjojATKw`LT1HlMBQ3s
zRPPu`EOCk=)0=bi2u@VlV{b2u945bWpCeAjj#r}cCPSUq+X9y
z9sAIO?MZkXKB@HVvj@rb@49|l7;dg1d)Nfmf^id2A77pn4nzuOj5tlxl$Z&Aqznjq
z+K5Bje*YjQ31^l_s7meQyf6=axi0O1Vv&PnM^=$uO9?%FAAy4O&QPS{z#GU@%In^j
z_1WaJRoh|`JHjUM`dshmFNX67$1x~gV_UiUaqSxQ19xRtgwk6gCf+VJ(*e%Q)LEhmTIp3Nv(GWPJwQz#{_66;Mf70BIMYp<
zfQ>la`3WdGlDtS
zRL$9nX;$y$y)lsB)V!sOs_rSXc1$IL`pRaV&M9!V{C3#x=Ki3p$bNsBdD*1vBpN_4nKR;s%n(lfM3C3e7Jo_>tG%Su%lu5VY=v_i3O
z)7@Sf7kIBzmUT=e3!@1l44)dD_m21qQM9{u(^O6dLnF_!)`RC6N`8!_n99BBP^*N|
z;$-&$phdvRF%b$_^1eMA+`~N&s9n;K;B^`=pxH7%gKQu7rH)uvia%@{pWypK8OMjE
zqn*dnJEo*O6XXoKw~EbhSB1e5S90oDeizfU>T>2o_-W0JsIwBnyBT#pTNhL=hI|TV
z*Kf@O02q(p3JN-E3JOd*y4uQW8cOJgYA^iarBpx3(zTjBr~=+3m589|(n;ruh!VBC
z8KuKKL{K80Vse6`Ns~Z9rP5S2GxOPExT*ENA)cWe$r-=}h15mGlU+WeFmCZgOvZW~
zGI(qO0_wZ;-&)e?2z9Em&C0XaabcU0Dq^2gD?e+|@YAA5+@>=-+}=6hZF~En_gu_7
z`9%jHLsqgA^$GZm>E*XdRL^M()F6ps)^!#V!mE1L?l!(4(t{IzE-B!Uqqo-rwaDsg
zL(;x|l|`mas&eyQwr@ZiEOd|3rpg4??Xyc2hMf4qKMGflYXm-Jk`IjLy2J5FKHA_3
z1v%$Ltj|$6-A7g4R1-EZk|z`MX2KlaWythY+HuD<5l#v`aWjWgOKkj2%qMA%0gPYW
z=dkf5u?&qn%?hyuGIBpjiJa~>0Jss6gbJ_0V4?a
zi{IA^9lD$VatdG16C)7#(UcrMQ*%>O8};WtdE?VJw6KvWz_Q$Z0N}#tvw=HF0k`Tt0-4}eQb0o?O|T|H0cHzV
z^+v+8Ub?kaza5pK~AnLR}{ZE6k)EGNH_uocXndB;)Gf_yP>3jK(wCe
z4|25DQppo7{9}Tv@lSeJlog*EdLW2i4;sM74+e{Z!2BS7F}~mJ(OON-zpR~H|A->+f^A
zxx%hQKmB&FvgSiO^>gOmZB*1Wb^fxs3Zo6&5%JUF3jI6M%JMH9!VT&06Juq`2XlZq
zq8)KXGeiCXfAw_yFN=Rz&z0uCID(|2Oa}&3}QTz5D0ne}?y`>p!hxp~-pSXdXc!elY<-3u_SAN(=%L6c)4qiHZn7K-N$JE2yZbwWToB@;3@?
zBph8hP>0{Wx}vf|Q;9-FAtFLnLLf1ikO-Pe#PZ4;D-gs=R2bb6;)e)8e^Om-o4A~g
zniP$a?gz2H6S3!da3yFdG#e{@}1R-Eyev!XH
z1~8;6x+bnTAz)s9$WM)xrMNPh5sJ=DxFggC#)ojS`Kh?tMR6UtC(OY}5zX%O%M>Ck
z`pcic)%Sl_gN1&nuQpX29SZceK(7iB?blCMbR@(TkT5998L98=>>vfaS{KumLqF@6
zN%D`%6xVRJ{Au_T470i_?mx;~9%{q)b12F8ufYF>Nzc~V!|DHx=Wpm=EOJPchcnV%
z8>ww!2eU-|Yo31w{)kOfTC8U%w_fnlN+LJ+96;6JjvI$NVWph%dU4LS$V`;0EkpZmFOwwS)#?4`@+7a$H%(3P-vc0db^eK~UFEHw8v@|~havwlFn=b>
ze}ntY{*R>mpUi)U{jyeYMtGr1-WH|p;q))3|0lp-3>t7tba^@dE7yOA{0hr&7df=g
zzhvl(C;D2<_t(YvkF>a|od3n2Kho`gaRxN?esCE&L@%tA0uT0{
z|7`0Ufc4Ek+TRki@Xxlk4*1np*Z%IH`{=f^`bS${`MZx6F0Wi(UhZ66Y+qdLo}cfW
zpU<70&7PeNpPUSxoU|Puw;mt293QtG9km`Ey*oUrIXtR9JSzQhSn}hr;NVC8!H=AS
zA8GpsDfMTd&gsY2f4NFvbAlu
zwGH3ew%yvc-rRb)v1z=qd4FTmXk*i0V^e#5Lu-9QaeYI6eM4@2Lt$-QW^G+)bxmM(
zjc0X@Yjurdb&YmqmEhMx(CfRKoUl#J3HUHyB8CZ-m4j?OL~z5#*3p%IbMPvR1iQeM1#m0MU;Qd(C2=H2_c
z`i75fpF6vHdi(kZhDXLHzb`JWtZi;>@9Z5M9v`2ao}FKGzudG$Pl#cmuA*3=gD;!_
z0APuzDaz@4PE3wqy=7G=!I?4nTJ4B#`x9+dj!uihquiM&=%#76>*hshFLl)AZ4amIH?clmC=@q9(
zE&+loB?_XWVTnCtkK959+3_D$r+bc)yiC)4@i1cc0>4z6Af7HCHA>oVa;J*n?o<|q
z0t5OF$aCl93Z2qcyXh{9gaqewR?&U7q0giV6`*#Mbk&)AyAHmkCDK6xfi0U{=X
z8KcExLFqW3RF4cMafa{zI8=$!1uMrdkWxDn4L&y`CVN$}9lnt-b^GISr~?
z?}nkvdT>Y>J5Uy;Y`7`$>Bosj@*|{X6NsNa_+|0YL0fr!uTvJzI9N$4YzKRZ@>-L&(&G!5bp1L#}5`BR0I~q!a9D;
z&XrAi+ioBOGRjJf-Jkh_iRM1ld`I|S-
z?Ks&U4v*|6yNY>%wgp2IViaD#jwsB12^@5P7k|KMn1f+_*ip%34tiqLZuG1}P(PU2
zz6eB5RuDTpu$xC1y}>YT7T_;>&gpDu4VseE>a$@Le~>OO-W0!RNg%J{{S|N)3pEq
delta 5771
zcmZvfbx_n{x5t<6knToM5SH!`mTu`#azP}PrIGlN(j_Tf0)imn(kva)UE+e2bf*Yh
z-?(?)JNKS{o-^nB{me7-%$YOiiMJo-OQHp^K_E~@>CGw#gkRyPsHm%^sK}!04zqW3
zhJrw<(P`;82KuIS14sV+?Yd-9o(ix~PjuBUg}75)%BsQPWQr_NQ89z7xN%sNk91W$
zNg-XeAVDe!1p(W_Eha4_)vLxz5z1T>p8KP|)uAJY|H=oseAThi&VQ972TFaF0H~&7
z^MI20M#v&>`~+=n)-$hzg0Y0m*mLZ(S5`J0cToTqiEaI_uDxp7-XdgmmheZeDgCGT{B5uBi4k
zSMEl~>&Jt_5;=CXI!Dl_xTsJ~JvxWY2S0`fsi|oWA*bykwV$zfJrjDwGW9Jpn<0W!
zk9l!FSfjz-+*6!!-X{x
z%^ysM+mz~D6LR4NEDGfeCr0J{bbdY)N;{@WM3R%c&RH4DX(u=HPON|=;$ADPL5w~5N8D)UodB^e-eu@r0WkyG
zfINXX(O8(qD&01gPbWJIR@f>f4Hk2!IOcQTDEvuQLsfdg;1L$5D6D1$SzS^J>^_AP
zT|tg;4qc|NOix3G3e;aS8<5=L=Z20c8W+fJqs@>IDZ&c*!RY!{oF?R=;lH|g7chv!
z*;hH;1ar`Sbw?aOZUWG&Lr8i?kNvLc0&vp1zpM&ive<=(Y_n|P5}YU~B#Cq2Jz{rC
zvMFKs^qekPw1l8JLN)o(h7fjSp=~CON1RYUGkNq+8$qxW_+zHJCSxh>baZpX&On?E
z()N=H(P!E@yy5EsU=!Lh4O60VU-r+J$E?1@vILglD-m*Wa*-X;%)J0x65bM~Qz$=c0t*KP=@4Ed
zt^=MJo?2z@em8$!r>8pRWx9gP#^(o5g_d`(;}*$(2)s+9CXbx)PDM0rYS%63Xh
z@WW>fZw`JAfW?DdkAAl>S@{=Rj;ysz54R(
z@{1XQnX(xjcTWidi3SE8*D8xNi~41)=J`{IABG>v&BxQ`)6LU?P1s-}Hz_3{WgCS!
zz7s_Sh4zzWeH#4|9g<_?ETsi+m;h%p{pUh#+D*#A(W*9=xCMcUBS)*0oElpy`n=RZ@_Pz
zHATFI#=zAANS9C{>klPa`=UD{5n2bDH>HN9Vk1^U{z4&(uJy>~h2{j02oI+%lu%iw
zf2KpWSmu>Em^YLcpVy4{)%bKiwHR!#t=ezp*#Yk~FNuk4U6Co438e|bv>hRe*fKLV
zU1_I#I4>QmbF)29W>axiIx=Cxa`5|*8w|b{K8B1yz8A)&PHlVR
z)i1;t#S+ZpzYF=u_~iLl-6lmwtHcagC(Z+T>3KHeDS6E8Ic-M%2!D1|5ULL)BP*P*
zny;Q;hHOE8LlPm2gS>+(|190r-iO?q-yPM8ELwQW_zQQkT6xJkuyBH?46#~&ux!!L{ECjjIq
z?peEqY&?34Ro7M3>{L<%X4cAomnTl5CZUs4nSKJ5w$*(VefT!!>&I|CI09}P8$fkn
zBHXm#3ZGnS)rd>q;BBrmaB*Ck5*psffTuGi4)m)GFvmO$;r*O(>nB^lo5d@(;6Kzp
zsL+<)+LYKpsgRiBr%dZE;EFgy;
zw7>Ye9zNfH)YrCF$(hc%$!V$4{LvVw`~7LHELuw~`>p7A7^)ttQK@P3(5SJ>3mOA$
zhGKjVZ4`kyxbma!g9hpt0plT;;j2TAgVg<0BTU1uhf^yE=={8T&}Qz_@ru$n@w@S}
z%syw{E6eN1GWeG*)cbbJAgVtBK>iAHi>@c@^seb{@iw11N^;_YU@zmh8Di=E5)3B-
z=cAXkSMCMz#YLnrv5RXkJqJ-wBsKG)%vxXxVzLcxNnZSEgIoM5Ao2Tj{@U`0i9mV|
zp5xVa65bWPn{uAgH>!I!v^7Z;6#G@A0dZu|`6)s4S3^pN=^yXouVXtELPHLs`JB1W
zq^00|SIZ`?<$)Ukr#B%9P{u^QUW=w5%{BGEY*zb*GXnTuZl7*T`Ydjq?(k?EYrllP
zbZNE+s+zx7-)?xd0()Hk`~F!l@#WVCUi&V4*(*MmrQb)-8rxtmTSk$A2Vz_~2{)Tl?Zi*Hy!jRKLPNUXbbo_`dc!Ww)$l{PXxr%c2g#_%GKn4`wH$8|L4y
zI*$^$rnnwM7z7-K@L67WtEqw7WN<+uRG_!~AdY57m^9_Y*Zh38<9y#GIh_a@5aJL7
zX~0;`Cy}28N^qWkc(+v|661zb6Ccm}@aTh@=-jj!#f-I$oR((WG$v>TyHo6>xA*!m
zIvQ3!puz;2$P+mDR@OG^1uELmlbiO7<{!a%AOmnPr!Y$QTey)R5IVM_fr+PymZqdF
z%!SX|4rT-8^L26iyXt~K(!P>^Qx~YGHH)u{b4mmwH@*m;Fdvvr7N3PBrIk?${qJ$l
z$brf7H&U#bh5nT!G4lccaQr)@p}vmxlDdw*P-jz!qYKp4!x82xD*zS&OY{G~(*KK^
z{0Ay3{=d-wkp3G5SXiVz9qsKUb=)1HuAbJO|GZL~|Nqqf4M_95+u2J(pdPmFj&A=n
z|3mm!^#w#(7GIkGf8IhCe-P&j-rrAvx*FuUfiK!Yu5lEjAVbEI*DHagL#0!D&a?=s)hT)S%_ZQeGD#i82v~%{)Xo*GN1NB$S
zGE#wuIn7f3V$3-s`?<|!e~#wI#Znp`AShS&P$pjH`3ntu&0@UakNH7y=5Qe6Urp&uM1}>d70>iu0SNnJck-uU6U?{R4-o
zU=iY9comjG=byX60^PFv>OY1>d->}yT+j5!qqx@k?WbSE_ahYF`BIm|Up)rWupig7
z*EyzKeO=;T8$2w%&+1%Uo1yJ_=Xa|%pZ@$~@J$)Scdo??6*(1YSe(2zX09A}JjNhA
zcr%j|+N?3{Z%oR;^!+f7o6XTHZi5pmtxxvkR$G7v`XOU-G7eB%HMA?vHr%UE5^w-D<&P
z9zn=WT|j?pZ*rA_%99u~u(xv$!xA`ie0XBHH7Kxh)x;7fSe8{DN0jur$VMMu8hMZ<
z0XkmMFvSvz4KW+#kV8MhFS0=7H0N8D(`e3S-&~J&kw%~?C=OcuQvlTPMQBO+XT
zBBWCZMx=W0A!6+jHFzZ$^)KwfmZ@DKcmCv$`hIYzjbyQmU_PE4Rm6Yf&u-1mx8{m;J@faP{30W22)q@A#9_@+rrzTAs;u
zgYm@BZ78&ifD0_5HQD~i6sk{%Sb|lvPAn79nEJMf=ZX5o&ilbEwC&nEy%)I@Ltphd
zyryC($IjBWhbs+0otW>oR)vE_b~}Bk_XAa+-9*0kr*9-_7#p>%+^c8+pV0orG&6#|
z+9#M>{#kzvD~-QjiOb=b^u$O0<>IIr2%5^V?=L~XXhZefiW_tSV^xT{7kOK3+wZya
z6mx660xv6jZO3P)aFsa~S35b1nLRB#=X3#VjaFgB;P6X8TrHo6-Ry`2R7uhp~23_8=Tbw6wntoM5f
zM6rU$f8WB6ei1ge{*EEnF6z{5`q4pwIfDdX(NqQKhmIn9Fv~z&aUOZUt7eD7kG3cdiGBICFWxL#n>c-!}^>u+f(dY@uG_S&c(
z#AaV`IHqIy9Xt!6e>AY$6EB>-z^UX}yFJc*0-A>>l2nU}ZU@n33?26L-82Me?V>-v
z1n+Gn$9`L7dPLg=2W6yj*K}hPJVzQ*51=25EP=w}Zj}T~QP0df!U!Lnbob#-5#k!MbD8;6hPS;7hftL;
zMsJ9I_U_!2wwK>gK_$VDj47%Sje2ZUL2qKVYkcbYkc`I1mVPrbYIF}|1K>?9zJ${H
z5DS9`uCpibN_k?MWXKa!Hj-;EQJ|BH+1S4$@@XZf{^eH1EE;2rCZ1>*=``8+*zlK)
ztYaX<6UCdHZnbO$t5b&cbHmDg|CY_R$%*7|bY3+EyA_|q2mA!HeD(^_|2oJXa(J(z
zIBGTM*vF*Is>gz}1XTC3fu+2T)EN<1I%3%zg89c030*<(j_Ne_qorRt&sf54>b^hJjOKahBAhW9$4Hu-p@GCuBa8-8a&W^rcEG6!MHPM2|X06AEo@9
z_aU-pmlvc)M#2TL@wIRD3W8XJwK4|-;*q00i1QWPWEx_0HM!&*Kn=0~HI0X!mg`bV
zpR=@#rvk3#c96KXz>In5j_`V+_53QfB+5ykLsbX&><=`Ft!tx{R-KHzyO|?gx^m3f
z&Vr1@RV477_tksKMD-C!jKoV)j}~llOOo9AlpRu+N(kDwtZp<9myh~b_6}KvT>L8s
z%YLm241J;B%rSiZflk+5NN`)<4PIt%s2X*KQ%ZYIIPeS8e~LDGzk~}Q@7n0P{IR@v
z;?a}l-EKfZ&hd8~SCjgjW@V$ajh_Z)_NJ|-#E^N(h?Q)xnlrxsECwr3D>eY{1k=o*
zn6>x5&Q`A!>o)D(zdpwo3i)R2;MBn$KUy5Qy_o-s=hFxsP-Kst5_igrwv-)ZtYu+T
z={;6=IAS%mxD)e*f2=^Nj?m)5UzHZczx546H_!j>!tx8~!S~LlN)r-vW}sqsbYy1=
za|PpVG0&MB5gWxV#e^67sqWdDv+-|Yr=TrpW___tkUTSDM|G!_b&tIQobktyPYwf$
z1?A_T@I2@N{Z;c#K^4b0qyo5PDt=zY2Dq9++YIeNKO@2iR!0I2We)lAvUyk}s93qI
z1Vj2NTAH*hvZ|yD22+0oWmsV{;yF-pI#>xNAAOB*%N4B-nr%>-#j}MY7*2;bRj49s
zZ4ugCB^sAd-%a)iW5dx;>(Qi>n||_@7}XmX!9MjWhGq*_8GfiLE9GZ#_=NWw*!NQJE+oqmax1L!)}XiH
sF+_C_
Date: Sat, 30 Jul 2022 01:27:20 +0800
Subject: [PATCH 185/472] decomp: add prefetch for matched seq on aarch64
(#3164)
match is used for following sequence copy. It is
only updated when extDict is needed, which is a
low probability case. So it can be prefetched to
reduce cache miss.
The benchmarks on various Arm platforms showed
uplift from 1% ~ 14% with gcc-11/clang-14.
Signed-off-by: Jun He
Change-Id: If201af4799d2455d74c79f8387404439d7f684ae
---
lib/decompress/zstd_decompress_block.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/lib/decompress/zstd_decompress_block.c b/lib/decompress/zstd_decompress_block.c
index 6df4c3849e0..20211f72b2f 100644
--- a/lib/decompress/zstd_decompress_block.c
+++ b/lib/decompress/zstd_decompress_block.c
@@ -967,6 +967,11 @@ size_t ZSTD_execSequence(BYTE* op,
assert(op != NULL /* Precondition */);
assert(oend_w < oend /* No underflow */);
+
+#if defined(__aarch64__)
+ /* prefetch sequence starting from match that will be used for copy later */
+ PREFETCH_L1(match);
+#endif
/* Handle edge cases in a slow path:
* - Read beyond end of literals
* - Match end is within WILDCOPY_OVERLIMIT of oend
From ec5fdcde198b4653ef94bbdf4cbadd1656cbcbcb Mon Sep 17 00:00:00 2001
From: Jun He
Date: Sat, 30 Jul 2022 01:28:04 +0800
Subject: [PATCH 186/472] lib: add hint to generate more pipeline friendly code
(#3138)
With statistic data of test data files of silesia
the chance of position beyond highThreshold is very
low (~1.3%@L8 in most cases, all <2.5%), and is in
"lowprob area". Add the branch hint so compiler can
get better pipiline codegen.
With this change it is observed ~1% of mozilla and
xml, and slight (0.3%~0.8%) but consistent uplift on
other files on Arm N1.
Signed-off-by: Jun He
Change-Id: Id9ba1d5c767e975290b5c1bf0ecce906544f4ade
---
lib/decompress/zstd_decompress_block.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/decompress/zstd_decompress_block.c b/lib/decompress/zstd_decompress_block.c
index 20211f72b2f..6ebda486c55 100644
--- a/lib/decompress/zstd_decompress_block.c
+++ b/lib/decompress/zstd_decompress_block.c
@@ -543,7 +543,7 @@ void ZSTD_buildFSETable_body(ZSTD_seqSymbol* dt,
for (i=0; i highThreshold) position = (position + step) & tableMask; /* lowprob area */
+ while (UNLIKELY(position > highThreshold)) position = (position + step) & tableMask; /* lowprob area */
} }
assert(position == 0); /* position must reach all cells once, otherwise normalizedCounter is incorrect */
}
From e1873ad576cb478fff0e6e44ad99599cd5fd2846 Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Fri, 29 Jul 2022 11:10:47 -0700
Subject: [PATCH 187/472] Fix buffer underflow for null dir1
---
programs/util.c | 38 +++++++++++++++++++-------------------
1 file changed, 19 insertions(+), 19 deletions(-)
diff --git a/programs/util.c b/programs/util.c
index f53eb03fbec..b874344c4d1 100644
--- a/programs/util.c
+++ b/programs/util.c
@@ -870,30 +870,30 @@ static const char * trimPath(const char *pathname)
static char* mallocAndJoin2Dir(const char *dir1, const char *dir2)
{
- const size_t dir1Size = strlen(dir1);
- const size_t dir2Size = strlen(dir2);
- char *outDirBuffer, *buffer, trailingChar;
-
assert(dir1 != NULL && dir2 != NULL);
- outDirBuffer = (char *) malloc(dir1Size + dir2Size + 2);
- CONTROL(outDirBuffer != NULL);
+ { const size_t dir1Size = strlen(dir1);
+ const size_t dir2Size = strlen(dir2);
+ char *outDirBuffer, *buffer;
- memcpy(outDirBuffer, dir1, dir1Size);
- outDirBuffer[dir1Size] = '\0';
+ outDirBuffer = (char *) malloc(dir1Size + dir2Size + 2);
+ CONTROL(outDirBuffer != NULL);
- if (dir2[0] == '.')
- return outDirBuffer;
+ memcpy(outDirBuffer, dir1, dir1Size);
+ outDirBuffer[dir1Size] = '\0';
- buffer = outDirBuffer + dir1Size;
- trailingChar = *(buffer - 1);
- if (trailingChar != PATH_SEP) {
- *buffer = PATH_SEP;
- buffer++;
- }
- memcpy(buffer, dir2, dir2Size);
- buffer[dir2Size] = '\0';
+ if (dir2[0] == '.')
+ return outDirBuffer;
- return outDirBuffer;
+ buffer = outDirBuffer + dir1Size;
+ if (dir1Size > 0 && *(buffer - 1) != PATH_SEP) {
+ *buffer = PATH_SEP;
+ buffer++;
+ }
+ memcpy(buffer, dir2, dir2Size);
+ buffer[dir2Size] = '\0';
+
+ return outDirBuffer;
+ }
}
/* this function will return NULL if input srcFileName is not valid name for mirrored output path */
From b1bbb0eb4c91d8e5d170b0cf732daacd0252e459 Mon Sep 17 00:00:00 2001
From: Qiongsi Wu <274595+qiongsiwu@users.noreply.github.com>
Date: Fri, 29 Jul 2022 15:21:59 -0400
Subject: [PATCH 188/472] [AIX] Fix Compiler Flags and Bugs on AIX to Pass All
Tests (#3219)
* Fixing compiler warnings
* Replace the old -s flag with the -Wl,-s flag
* Fixing compiler warnings
* Fixing the linker strip flag and tests/code not working as expected on AIX
---
programs/Makefile | 2 +-
programs/dibio.c | 2 +-
programs/fileio.c | 2 +-
programs/util.c | 3 +++
tests/playTests.sh | 2 +-
5 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/programs/Makefile b/programs/Makefile
index 16763e49365..a5f5b45f2ed 100644
--- a/programs/Makefile
+++ b/programs/Makefile
@@ -242,7 +242,7 @@ zstd-pgo :
$(MAKE) zstd MOREFLAGS=-fprofile-use
## zstd-small: minimal target, supporting only zstd compression and decompression. no bench. no legacy. no other format.
-zstd-small: CFLAGS = -Os -s
+zstd-small: CFLAGS = -Os -Wl,-s
zstd-frugal zstd-small: $(ZSTDLIB_CORE_SRC) zstdcli.c util.c timefn.c fileio.c fileio_asyncio.c
$(CC) $(FLAGS) -DZSTD_NOBENCH -DZSTD_NODICT -DZSTD_NOTRACE -UZSTD_LEGACY_SUPPORT -DZSTD_LEGACY_SUPPORT=0 $^ -o $@$(EXT)
diff --git a/programs/dibio.c b/programs/dibio.c
index f6757dd3488..8643bc378f3 100644
--- a/programs/dibio.c
+++ b/programs/dibio.c
@@ -355,7 +355,7 @@ int DiB_trainFromFiles(const char* dictFileName, size_t maxDictSize,
}
/* Checks */
- if ((!sampleSizes) || (!srcBuffer) || (!dictBuffer))
+ if ((fs.nbSamples && !sampleSizes) || (!srcBuffer) || (!dictBuffer))
EXM_THROW(12, "not enough memory for DiB_trainFiles"); /* should not happen */
if (fs.oneSampleTooLarge) {
DISPLAYLEVEL(2, "! Warning : some sample(s) are very large \n");
diff --git a/programs/fileio.c b/programs/fileio.c
index b8b2818bcd8..a83b8064a9d 100644
--- a/programs/fileio.c
+++ b/programs/fileio.c
@@ -219,7 +219,7 @@ static void ABRThandler(int sig) {
}
#endif
-void FIO_addAbortHandler()
+void FIO_addAbortHandler(void)
{
#if BACKTRACE_ENABLE
signal(SIGABRT, ABRThandler);
diff --git a/programs/util.c b/programs/util.c
index f53eb03fbec..50db70fd447 100644
--- a/programs/util.c
+++ b/programs/util.c
@@ -1378,6 +1378,9 @@ int UTIL_countCores(int logical)
int UTIL_countCores(int logical)
{
+ /* suppress unused parameter warning */
+ (void)logical;
+
/* assume 1 */
return 1;
}
diff --git a/tests/playTests.sh b/tests/playTests.sh
index 50aef5df5a3..0e2b0fbb2bf 100755
--- a/tests/playTests.sh
+++ b/tests/playTests.sh
@@ -317,7 +317,7 @@ zstd -d -f tmp_corrupt.zst --no-check
zstd -d -f tmp_corrupt.zst --check --no-check # final flag overrides
zstd -d -f tmp.zst --no-check
-if [ "$isWindows" = false ]; then
+if [ "$isWindows" = false ] && [ "$UNAME" != "AIX" ]; then
if [ -n "$(which readelf)" ]; then
println "test: check if binary has executable stack (#2963)"
readelf -lW "$ZSTD_BIN" | grep 'GNU_STACK .* RW ' || die "zstd binary has executable stack!"
From 1e09cffd9b15b39379810a39ffae182b4a7e7b78 Mon Sep 17 00:00:00 2001
From: orbea
Date: Fri, 29 Jul 2022 12:22:10 -0700
Subject: [PATCH 189/472] zlibWrapper: Update for zlib 1.2.12 (#3217)
In zlib 1.2.12 the OF macro was changed to _Z_OF breaking any
project that used zlibWrapper. To fix this the OF has been
changed to _Z_OF everywhere and _Z_OF is defined as OF in the
case it is not yet defined for zlib 1.2.11 and older.
Fixes: https://github.com/facebook/zstd/issues/3216
---
zlibWrapper/examples/example.c | 26 ++++----
zlibWrapper/examples/example_original.c | 26 ++++----
zlibWrapper/examples/minigzip.c | 34 +++++-----
zlibWrapper/gzcompatibility.h | 14 ++---
zlibWrapper/gzguts.h | 18 +++---
zlibWrapper/gzlib.c | 4 +-
zlibWrapper/gzread.c | 18 +++---
zlibWrapper/gzwrite.c | 8 +--
zlibWrapper/zstd_zlibwrapper.c | 82 ++++++++++++-------------
zlibWrapper/zstd_zlibwrapper.h | 3 +
10 files changed, 118 insertions(+), 115 deletions(-)
diff --git a/zlibWrapper/examples/example.c b/zlibWrapper/examples/example.c
index 9000f7a3295..d7590e31237 100644
--- a/zlibWrapper/examples/example.c
+++ b/zlibWrapper/examples/example.c
@@ -56,26 +56,26 @@ z_const char hello[] = "hello, hello! I said hello, hello!";
const char dictionary[] = "hello, hello!";
uLong dictId; /* Adler32 value of the dictionary */
-void test_deflate OF((Byte *compr, uLong comprLen));
-void test_inflate OF((Byte *compr, uLong comprLen,
+void test_deflate _Z_OF((Byte *compr, uLong comprLen));
+void test_inflate _Z_OF((Byte *compr, uLong comprLen,
Byte *uncompr, uLong uncomprLen));
-void test_large_deflate OF((Byte *compr, uLong comprLen,
+void test_large_deflate _Z_OF((Byte *compr, uLong comprLen,
Byte *uncompr, uLong uncomprLen));
-void test_large_inflate OF((Byte *compr, uLong comprLen,
+void test_large_inflate _Z_OF((Byte *compr, uLong comprLen,
Byte *uncompr, uLong uncomprLen));
-void test_flush OF((Byte *compr, uLong *comprLen));
-void test_sync OF((Byte *compr, uLong comprLen,
+void test_flush _Z_OF((Byte *compr, uLong *comprLen));
+void test_sync _Z_OF((Byte *compr, uLong comprLen,
Byte *uncompr, uLong uncomprLen));
-void test_dict_deflate OF((Byte *compr, uLong comprLen));
-void test_dict_inflate OF((Byte *compr, uLong comprLen,
+void test_dict_deflate _Z_OF((Byte *compr, uLong comprLen));
+void test_dict_inflate _Z_OF((Byte *compr, uLong comprLen,
Byte *uncompr, uLong uncomprLen));
-int main OF((int argc, char *argv[]));
+int main _Z_OF((int argc, char *argv[]));
#ifdef Z_SOLO
-void *myalloc OF((void *, unsigned, unsigned));
-void myfree OF((void *, void *));
+void *myalloc _Z_OF((void *, unsigned, unsigned));
+void myfree _Z_OF((void *, void *));
void *myalloc(q, n, m)
void *q;
@@ -102,9 +102,9 @@ static free_func zfree = myfree;
static alloc_func zalloc = (alloc_func)0;
static free_func zfree = (free_func)0;
-void test_compress OF((Byte *compr, uLong comprLen,
+void test_compress _Z_OF((Byte *compr, uLong comprLen,
Byte *uncompr, uLong uncomprLen));
-void test_gzio OF((const char *fname,
+void test_gzio _Z_OF((const char *fname,
Byte *uncompr, uLong uncomprLen));
/* ===========================================================================
diff --git a/zlibWrapper/examples/example_original.c b/zlibWrapper/examples/example_original.c
index 649882bf0a7..5b4e4d1d8f6 100644
--- a/zlibWrapper/examples/example_original.c
+++ b/zlibWrapper/examples/example_original.c
@@ -51,26 +51,26 @@ z_const char hello[] = "hello, hello!";
const char dictionary[] = "hello";
uLong dictId; /* Adler32 value of the dictionary */
-void test_deflate OF((Byte *compr, uLong comprLen));
-void test_inflate OF((Byte *compr, uLong comprLen,
+void test_deflate _Z_OF((Byte *compr, uLong comprLen));
+void test_inflate _Z_OF((Byte *compr, uLong comprLen,
Byte *uncompr, uLong uncomprLen));
-void test_large_deflate OF((Byte *compr, uLong comprLen,
+void test_large_deflate _Z_OF((Byte *compr, uLong comprLen,
Byte *uncompr, uLong uncomprLen));
-void test_large_inflate OF((Byte *compr, uLong comprLen,
+void test_large_inflate _Z_OF((Byte *compr, uLong comprLen,
Byte *uncompr, uLong uncomprLen));
-void test_flush OF((Byte *compr, uLong *comprLen));
-void test_sync OF((Byte *compr, uLong comprLen,
+void test_flush _Z_OF((Byte *compr, uLong *comprLen));
+void test_sync _Z_OF((Byte *compr, uLong comprLen,
Byte *uncompr, uLong uncomprLen));
-void test_dict_deflate OF((Byte *compr, uLong comprLen));
-void test_dict_inflate OF((Byte *compr, uLong comprLen,
+void test_dict_deflate _Z_OF((Byte *compr, uLong comprLen));
+void test_dict_inflate _Z_OF((Byte *compr, uLong comprLen,
Byte *uncompr, uLong uncomprLen));
-int main OF((int argc, char *argv[]));
+int main _Z_OF((int argc, char *argv[]));
#ifdef Z_SOLO
-void *myalloc OF((void *, unsigned, unsigned));
-void myfree OF((void *, void *));
+void *myalloc _Z_OF((void *, unsigned, unsigned));
+void myfree _Z_OF((void *, void *));
void *myalloc(q, n, m)
void *q;
@@ -94,9 +94,9 @@ static free_func zfree = myfree;
static alloc_func zalloc = (alloc_func)0;
static free_func zfree = (free_func)0;
-void test_compress OF((Byte *compr, uLong comprLen,
+void test_compress _Z_OF((Byte *compr, uLong comprLen,
Byte *uncompr, uLong uncomprLen));
-void test_gzio OF((const char *fname,
+void test_gzio _Z_OF((const char *fname,
Byte *uncompr, uLong uncomprLen));
/* ===========================================================================
diff --git a/zlibWrapper/examples/minigzip.c b/zlibWrapper/examples/minigzip.c
index f67be09564f..fc9d263cab9 100644
--- a/zlibWrapper/examples/minigzip.c
+++ b/zlibWrapper/examples/minigzip.c
@@ -64,7 +64,7 @@
#if !defined(Z_HAVE_UNISTD_H) && !defined(_LARGEFILE64_SOURCE)
#ifndef WIN32 /* unlink already in stdio.h for WIN32 */
- extern int unlink OF((const char *));
+ extern int unlink _Z_OF((const char *));
#endif
#endif
@@ -154,8 +154,8 @@ static void pwinerror (s)
# include /* for unlink() */
#endif
-void *myalloc OF((void *, unsigned, unsigned));
-void myfree OF((void *, void *));
+void *myalloc _Z_OF((void *, unsigned, unsigned));
+void myfree _Z_OF((void *, void *));
void *myalloc(q, n, m)
void *q;
@@ -180,9 +180,9 @@ typedef struct gzFile_s {
z_stream strm;
} *gzFile;
-gzFile gzopen OF((const char *, const char *));
-gzFile gzdopen OF((int, const char *));
-gzFile gz_open OF((const char *, int, const char *));
+gzFile gzopen _Z_OF((const char *, const char *));
+gzFile gzdopen _Z_OF((int, const char *));
+gzFile gz_open _Z_OF((const char *, int, const char *));
gzFile gzopen(path, mode)
const char *path;
@@ -236,7 +236,7 @@ gzFile gz_open(path, fd, mode)
return gz;
}
-int gzwrite OF((gzFile, const void *, unsigned));
+int gzwrite _Z_OF((gzFile, const void *, unsigned));
int gzwrite(gz, buf, len)
gzFile gz;
@@ -260,7 +260,7 @@ int gzwrite(gz, buf, len)
return len;
}
-int gzread OF((gzFile, void *, unsigned));
+int gzread _Z_OF((gzFile, void *, unsigned));
int gzread(gz, buf, len)
gzFile gz;
@@ -297,7 +297,7 @@ int gzread(gz, buf, len)
return len - strm->avail_out;
}
-int gzclose OF((gzFile));
+int gzclose _Z_OF((gzFile));
int gzclose(gz)
gzFile gz;
@@ -326,7 +326,7 @@ int gzclose(gz)
return Z_OK;
}
-const char *gzerror OF((gzFile, int *));
+const char *gzerror _Z_OF((gzFile, int *));
const char *gzerror(gz, err)
gzFile gz;
@@ -340,15 +340,15 @@ const char *gzerror(gz, err)
char *prog;
-void error OF((const char *msg));
-void gz_compress OF((FILE *in, gzFile out));
+void error _Z_OF((const char *msg));
+void gz_compress _Z_OF((FILE *in, gzFile out));
#ifdef USE_MMAP
-int gz_compress_mmap OF((FILE *in, gzFile out));
+int gz_compress_mmap _Z_OF((FILE *in, gzFile out));
#endif
-void gz_uncompress OF((gzFile in, FILE *out));
-void file_compress OF((char *file, char *mode));
-void file_uncompress OF((char *file));
-int main OF((int argc, char *argv[]));
+void gz_uncompress _Z_OF((gzFile in, FILE *out));
+void file_compress _Z_OF((char *file, char *mode));
+void file_uncompress _Z_OF((char *file));
+int main _Z_OF((int argc, char *argv[]));
/* ===========================================================================
* Display error message and exit
diff --git a/zlibWrapper/gzcompatibility.h b/zlibWrapper/gzcompatibility.h
index c1aa2b87c12..3a46127b860 100644
--- a/zlibWrapper/gzcompatibility.h
+++ b/zlibWrapper/gzcompatibility.h
@@ -11,10 +11,10 @@
#if ZLIB_VERNUM <= 0x1240
-ZEXTERN int ZEXPORT gzclose_r OF((gzFile file));
-ZEXTERN int ZEXPORT gzclose_w OF((gzFile file));
-ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size));
-ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file));
+ZEXTERN int ZEXPORT gzclose_r _Z_OF((gzFile file));
+ZEXTERN int ZEXPORT gzclose_w _Z_OF((gzFile file));
+ZEXTERN int ZEXPORT gzbuffer _Z_OF((gzFile file, unsigned size));
+ZEXTERN z_off_t ZEXPORT gzoffset _Z_OF((gzFile file));
#if !defined(_WIN32) && defined(Z_LARGE64)
# define z_off64_t off64_t
@@ -40,7 +40,7 @@ struct gzFile_s {
#if ZLIB_VERNUM <= 0x1270
#if defined(_WIN32) && !defined(Z_SOLO)
# include /* for wchar_t */
-ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path,
+ZEXTERN gzFile ZEXPORT gzopen_w _Z_OF((const wchar_t *path,
const char *mode));
#endif
#endif
@@ -61,8 +61,8 @@ ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path,
# endif
# undef z_longlong
#endif
-ZEXTERN z_size_t ZEXPORT gzfread OF((voidp buf, z_size_t size, z_size_t nitems,
+ZEXTERN z_size_t ZEXPORT gzfread _Z_OF((voidp buf, z_size_t size, z_size_t nitems,
gzFile file));
-ZEXTERN z_size_t ZEXPORT gzfwrite OF((voidpc buf, z_size_t size,
+ZEXTERN z_size_t ZEXPORT gzfwrite _Z_OF((voidpc buf, z_size_t size,
z_size_t nitems, gzFile file));
#endif
diff --git a/zlibWrapper/gzguts.h b/zlibWrapper/gzguts.h
index b639b4be85f..83db17c1970 100644
--- a/zlibWrapper/gzguts.h
+++ b/zlibWrapper/gzguts.h
@@ -126,8 +126,8 @@
/* gz* functions always use library allocation functions */
#ifndef STDC
- extern voidp malloc OF((uInt size));
- extern void free OF((voidpf ptr));
+ extern voidp malloc _Z_OF((uInt size));
+ extern void free _Z_OF((voidpf ptr));
#endif
/* get errno and strerror definition */
@@ -145,10 +145,10 @@
/* provide prototypes for these when building zlib without LFS */
#if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0
- ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *));
- ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int));
- ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile));
- ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile));
+ ZEXTERN gzFile ZEXPORT gzopen64 _Z_OF((const char *, const char *));
+ ZEXTERN z_off64_t ZEXPORT gzseek64 _Z_OF((gzFile, z_off64_t, int));
+ ZEXTERN z_off64_t ZEXPORT gztell64 _Z_OF((gzFile));
+ ZEXTERN z_off64_t ZEXPORT gzoffset64 _Z_OF((gzFile));
#endif
/* default memLevel */
@@ -213,9 +213,9 @@ typedef union {
} gz_statep;
/* shared functions */
-void ZLIB_INTERNAL gz_error OF((gz_statep, int, const char *));
+void ZLIB_INTERNAL gz_error _Z_OF((gz_statep, int, const char *));
#if defined UNDER_CE
-char ZLIB_INTERNAL *gz_strwinerror OF((DWORD error));
+char ZLIB_INTERNAL *gz_strwinerror _Z_OF((DWORD error));
#endif
/* GT_OFF(x), where x is an unsigned value, is true if x > maximum z_off64_t
@@ -224,6 +224,6 @@ char ZLIB_INTERNAL *gz_strwinerror OF((DWORD error));
#ifdef INT_MAX
# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > INT_MAX)
#else
-unsigned ZLIB_INTERNAL gz_intmax OF((void));
+unsigned ZLIB_INTERNAL gz_intmax _Z_OF((void));
# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax())
#endif
diff --git a/zlibWrapper/gzlib.c b/zlibWrapper/gzlib.c
index b1fb98517e4..e2d2a31af2f 100644
--- a/zlibWrapper/gzlib.c
+++ b/zlibWrapper/gzlib.c
@@ -19,8 +19,8 @@
#endif
/* Local functions */
-local void gz_reset OF((gz_statep));
-local gzFile gz_open OF((const void *, int, const char *));
+local void gz_reset _Z_OF((gz_statep));
+local gzFile gz_open _Z_OF((const void *, int, const char *));
#if defined UNDER_CE
diff --git a/zlibWrapper/gzread.c b/zlibWrapper/gzread.c
index 359d1788913..18955de5fe8 100644
--- a/zlibWrapper/gzread.c
+++ b/zlibWrapper/gzread.c
@@ -17,13 +17,13 @@
/* Local functions */
-local int gz_load OF((gz_statep, unsigned char *, unsigned, unsigned *));
-local int gz_avail OF((gz_statep));
-local int gz_look OF((gz_statep));
-local int gz_decomp OF((gz_statep));
-local int gz_fetch OF((gz_statep));
-local int gz_skip OF((gz_statep, z_off64_t));
-local z_size_t gz_read OF((gz_statep, voidp, z_size_t));
+local int gz_load _Z_OF((gz_statep, unsigned char *, unsigned, unsigned *));
+local int gz_avail _Z_OF((gz_statep));
+local int gz_look _Z_OF((gz_statep));
+local int gz_decomp _Z_OF((gz_statep));
+local int gz_fetch _Z_OF((gz_statep));
+local int gz_skip _Z_OF((gz_statep, z_off64_t));
+local z_size_t gz_read _Z_OF((gz_statep, voidp, z_size_t));
/* Use read() to load a buffer -- return -1 on error, otherwise 0. Read from
state.state->fd, and update state.state->eof, state.state->err, and state.state->msg as appropriate.
@@ -464,8 +464,8 @@ z_size_t ZEXPORT gzfread(buf, size, nitems, file)
#endif
#if ZLIB_VERNUM <= 0x1250
-ZEXTERN int ZEXPORT gzgetc OF((gzFile file));
-ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file));
+ZEXTERN int ZEXPORT gzgetc _Z_OF((gzFile file));
+ZEXTERN int ZEXPORT gzgetc_ _Z_OF((gzFile file));
#endif
int ZEXPORT gzgetc(file)
diff --git a/zlibWrapper/gzwrite.c b/zlibWrapper/gzwrite.c
index 422ff17db97..277388f7fd7 100644
--- a/zlibWrapper/gzwrite.c
+++ b/zlibWrapper/gzwrite.c
@@ -11,10 +11,10 @@
#include "gzguts.h"
/* Local functions */
-local int gz_init OF((gz_statep));
-local int gz_comp OF((gz_statep, int));
-local int gz_zero OF((gz_statep, z_off64_t));
-local z_size_t gz_write OF((gz_statep, voidpc, z_size_t));
+local int gz_init _Z_OF((gz_statep));
+local int gz_comp _Z_OF((gz_statep, int));
+local int gz_zero _Z_OF((gz_statep, z_off64_t));
+local z_size_t gz_write _Z_OF((gz_statep, voidpc, z_size_t));
/* Initialize state for writing a gzip file. Mark initialization by setting
state.state->size to non-zero. Return -1 on a memory allocation failure, or 0 on
diff --git a/zlibWrapper/zstd_zlibwrapper.c b/zlibWrapper/zstd_zlibwrapper.c
index adb231f0606..386c0f0acd4 100644
--- a/zlibWrapper/zstd_zlibwrapper.c
+++ b/zlibWrapper/zstd_zlibwrapper.c
@@ -101,7 +101,7 @@ ZWRAP_decompress_type ZWRAP_getDecompressionType(void) { return g_ZWRAPdecompres
const char * zstdVersion(void) { return ZSTD_VERSION_STRING; }
-ZEXTERN const char * ZEXPORT z_zlibVersion OF((void)) { return zlibVersion(); }
+ZEXTERN const char * ZEXPORT z_zlibVersion _Z_OF((void)) { return zlibVersion(); }
static void* ZWRAP_allocFunction(void* opaque, size_t size)
{
@@ -260,7 +260,7 @@ static struct internal_state* convert_into_sis(void* ptr)
return (struct internal_state*) ptr;
}
-ZEXTERN int ZEXPORT z_deflateInit_ OF((z_streamp strm, int level,
+ZEXTERN int ZEXPORT z_deflateInit_ _Z_OF((z_streamp strm, int level,
const char *version, int stream_size))
{
ZWRAP_CCtx* zwc;
@@ -287,7 +287,7 @@ ZEXTERN int ZEXPORT z_deflateInit_ OF((z_streamp strm, int level,
}
-ZEXTERN int ZEXPORT z_deflateInit2_ OF((z_streamp strm, int level, int method,
+ZEXTERN int ZEXPORT z_deflateInit2_ _Z_OF((z_streamp strm, int level, int method,
int windowBits, int memLevel,
int strategy, const char *version,
int stream_size))
@@ -319,7 +319,7 @@ int ZWRAP_deflateReset_keepDict(z_streamp strm)
}
-ZEXTERN int ZEXPORT z_deflateReset OF((z_streamp strm))
+ZEXTERN int ZEXPORT z_deflateReset _Z_OF((z_streamp strm))
{
LOG_WRAPPERC("- deflateReset\n");
if (!g_ZWRAP_useZSTDcompression)
@@ -334,7 +334,7 @@ ZEXTERN int ZEXPORT z_deflateReset OF((z_streamp strm))
}
-ZEXTERN int ZEXPORT z_deflateSetDictionary OF((z_streamp strm,
+ZEXTERN int ZEXPORT z_deflateSetDictionary _Z_OF((z_streamp strm,
const Bytef *dictionary,
uInt dictLength))
{
@@ -359,7 +359,7 @@ ZEXTERN int ZEXPORT z_deflateSetDictionary OF((z_streamp strm,
}
-ZEXTERN int ZEXPORT z_deflate OF((z_streamp strm, int flush))
+ZEXTERN int ZEXPORT z_deflate _Z_OF((z_streamp strm, int flush))
{
ZWRAP_CCtx* zwc;
@@ -465,7 +465,7 @@ ZEXTERN int ZEXPORT z_deflate OF((z_streamp strm, int flush))
}
-ZEXTERN int ZEXPORT z_deflateEnd OF((z_streamp strm))
+ZEXTERN int ZEXPORT z_deflateEnd _Z_OF((z_streamp strm))
{
if (!g_ZWRAP_useZSTDcompression) {
LOG_WRAPPERC("- deflateEnd\n");
@@ -483,7 +483,7 @@ ZEXTERN int ZEXPORT z_deflateEnd OF((z_streamp strm))
}
-ZEXTERN uLong ZEXPORT z_deflateBound OF((z_streamp strm,
+ZEXTERN uLong ZEXPORT z_deflateBound _Z_OF((z_streamp strm,
uLong sourceLen))
{
if (!g_ZWRAP_useZSTDcompression)
@@ -493,7 +493,7 @@ ZEXTERN uLong ZEXPORT z_deflateBound OF((z_streamp strm,
}
-ZEXTERN int ZEXPORT z_deflateParams OF((z_streamp strm,
+ZEXTERN int ZEXPORT z_deflateParams _Z_OF((z_streamp strm,
int level,
int strategy))
{
@@ -594,7 +594,7 @@ static int ZWRAPD_finishWithErrorMsg(z_streamp strm, char* message)
}
-ZEXTERN int ZEXPORT z_inflateInit_ OF((z_streamp strm,
+ZEXTERN int ZEXPORT z_inflateInit_ _Z_OF((z_streamp strm,
const char* version, int stream_size))
{
if (g_ZWRAPdecompressionType == ZWRAP_FORCE_ZLIB) {
@@ -623,7 +623,7 @@ ZEXTERN int ZEXPORT z_inflateInit_ OF((z_streamp strm,
}
-ZEXTERN int ZEXPORT z_inflateInit2_ OF((z_streamp strm, int windowBits,
+ZEXTERN int ZEXPORT z_inflateInit2_ _Z_OF((z_streamp strm, int windowBits,
const char *version, int stream_size))
{
if (g_ZWRAPdecompressionType == ZWRAP_FORCE_ZLIB) {
@@ -660,7 +660,7 @@ int ZWRAP_inflateReset_keepDict(z_streamp strm)
}
-ZEXTERN int ZEXPORT z_inflateReset OF((z_streamp strm))
+ZEXTERN int ZEXPORT z_inflateReset _Z_OF((z_streamp strm))
{
LOG_WRAPPERD("- inflateReset\n");
if (g_ZWRAPdecompressionType == ZWRAP_FORCE_ZLIB || !strm->reserved)
@@ -678,7 +678,7 @@ ZEXTERN int ZEXPORT z_inflateReset OF((z_streamp strm))
#if ZLIB_VERNUM >= 0x1240
-ZEXTERN int ZEXPORT z_inflateReset2 OF((z_streamp strm,
+ZEXTERN int ZEXPORT z_inflateReset2 _Z_OF((z_streamp strm,
int windowBits))
{
if (g_ZWRAPdecompressionType == ZWRAP_FORCE_ZLIB || !strm->reserved)
@@ -696,7 +696,7 @@ ZEXTERN int ZEXPORT z_inflateReset2 OF((z_streamp strm,
#endif
-ZEXTERN int ZEXPORT z_inflateSetDictionary OF((z_streamp strm,
+ZEXTERN int ZEXPORT z_inflateSetDictionary _Z_OF((z_streamp strm,
const Bytef *dictionary,
uInt dictLength))
{
@@ -730,7 +730,7 @@ ZEXTERN int ZEXPORT z_inflateSetDictionary OF((z_streamp strm,
}
-ZEXTERN int ZEXPORT z_inflate OF((z_streamp strm, int flush))
+ZEXTERN int ZEXPORT z_inflate _Z_OF((z_streamp strm, int flush))
{
ZWRAP_DCtx* zwd;
@@ -926,7 +926,7 @@ ZEXTERN int ZEXPORT z_inflate OF((z_streamp strm, int flush))
}
-ZEXTERN int ZEXPORT z_inflateEnd OF((z_streamp strm))
+ZEXTERN int ZEXPORT z_inflateEnd _Z_OF((z_streamp strm))
{
if (g_ZWRAPdecompressionType == ZWRAP_FORCE_ZLIB || !strm->reserved)
return inflateEnd(strm);
@@ -943,7 +943,7 @@ ZEXTERN int ZEXPORT z_inflateEnd OF((z_streamp strm))
}
-ZEXTERN int ZEXPORT z_inflateSync OF((z_streamp strm))
+ZEXTERN int ZEXPORT z_inflateSync _Z_OF((z_streamp strm))
{
if (g_ZWRAPdecompressionType == ZWRAP_FORCE_ZLIB || !strm->reserved) {
return inflateSync(strm);
@@ -955,7 +955,7 @@ ZEXTERN int ZEXPORT z_inflateSync OF((z_streamp strm))
/* Advanced compression functions */
-ZEXTERN int ZEXPORT z_deflateCopy OF((z_streamp dest,
+ZEXTERN int ZEXPORT z_deflateCopy _Z_OF((z_streamp dest,
z_streamp source))
{
if (!g_ZWRAP_useZSTDcompression)
@@ -964,7 +964,7 @@ ZEXTERN int ZEXPORT z_deflateCopy OF((z_streamp dest,
}
-ZEXTERN int ZEXPORT z_deflateTune OF((z_streamp strm,
+ZEXTERN int ZEXPORT z_deflateTune _Z_OF((z_streamp strm,
int good_length,
int max_lazy,
int nice_length,
@@ -977,7 +977,7 @@ ZEXTERN int ZEXPORT z_deflateTune OF((z_streamp strm,
#if ZLIB_VERNUM >= 0x1260
-ZEXTERN int ZEXPORT z_deflatePending OF((z_streamp strm,
+ZEXTERN int ZEXPORT z_deflatePending _Z_OF((z_streamp strm,
unsigned *pending,
int *bits))
{
@@ -988,7 +988,7 @@ ZEXTERN int ZEXPORT z_deflatePending OF((z_streamp strm,
#endif
-ZEXTERN int ZEXPORT z_deflatePrime OF((z_streamp strm,
+ZEXTERN int ZEXPORT z_deflatePrime _Z_OF((z_streamp strm,
int bits,
int value))
{
@@ -998,7 +998,7 @@ ZEXTERN int ZEXPORT z_deflatePrime OF((z_streamp strm,
}
-ZEXTERN int ZEXPORT z_deflateSetHeader OF((z_streamp strm,
+ZEXTERN int ZEXPORT z_deflateSetHeader _Z_OF((z_streamp strm,
gz_headerp head))
{
if (!g_ZWRAP_useZSTDcompression)
@@ -1011,7 +1011,7 @@ ZEXTERN int ZEXPORT z_deflateSetHeader OF((z_streamp strm,
/* Advanced decompression functions */
#if ZLIB_VERNUM >= 0x1280
-ZEXTERN int ZEXPORT z_inflateGetDictionary OF((z_streamp strm,
+ZEXTERN int ZEXPORT z_inflateGetDictionary _Z_OF((z_streamp strm,
Bytef *dictionary,
uInt *dictLength))
{
@@ -1022,7 +1022,7 @@ ZEXTERN int ZEXPORT z_inflateGetDictionary OF((z_streamp strm,
#endif
-ZEXTERN int ZEXPORT z_inflateCopy OF((z_streamp dest,
+ZEXTERN int ZEXPORT z_inflateCopy _Z_OF((z_streamp dest,
z_streamp source))
{
if (g_ZWRAPdecompressionType == ZWRAP_FORCE_ZLIB || !source->reserved)
@@ -1032,7 +1032,7 @@ ZEXTERN int ZEXPORT z_inflateCopy OF((z_streamp dest,
#if ZLIB_VERNUM >= 0x1240
-ZEXTERN long ZEXPORT z_inflateMark OF((z_streamp strm))
+ZEXTERN long ZEXPORT z_inflateMark _Z_OF((z_streamp strm))
{
if (g_ZWRAPdecompressionType == ZWRAP_FORCE_ZLIB || !strm->reserved)
return inflateMark(strm);
@@ -1041,7 +1041,7 @@ ZEXTERN long ZEXPORT z_inflateMark OF((z_streamp strm))
#endif
-ZEXTERN int ZEXPORT z_inflatePrime OF((z_streamp strm,
+ZEXTERN int ZEXPORT z_inflatePrime _Z_OF((z_streamp strm,
int bits,
int value))
{
@@ -1051,7 +1051,7 @@ ZEXTERN int ZEXPORT z_inflatePrime OF((z_streamp strm,
}
-ZEXTERN int ZEXPORT z_inflateGetHeader OF((z_streamp strm,
+ZEXTERN int ZEXPORT z_inflateGetHeader _Z_OF((z_streamp strm,
gz_headerp head))
{
if (g_ZWRAPdecompressionType == ZWRAP_FORCE_ZLIB || !strm->reserved)
@@ -1060,7 +1060,7 @@ ZEXTERN int ZEXPORT z_inflateGetHeader OF((z_streamp strm,
}
-ZEXTERN int ZEXPORT z_inflateBackInit_ OF((z_streamp strm, int windowBits,
+ZEXTERN int ZEXPORT z_inflateBackInit_ _Z_OF((z_streamp strm, int windowBits,
unsigned char FAR *window,
const char *version,
int stream_size))
@@ -1071,7 +1071,7 @@ ZEXTERN int ZEXPORT z_inflateBackInit_ OF((z_streamp strm, int windowBits,
}
-ZEXTERN int ZEXPORT z_inflateBack OF((z_streamp strm,
+ZEXTERN int ZEXPORT z_inflateBack _Z_OF((z_streamp strm,
in_func in, void FAR *in_desc,
out_func out, void FAR *out_desc))
{
@@ -1081,7 +1081,7 @@ ZEXTERN int ZEXPORT z_inflateBack OF((z_streamp strm,
}
-ZEXTERN int ZEXPORT z_inflateBackEnd OF((z_streamp strm))
+ZEXTERN int ZEXPORT z_inflateBackEnd _Z_OF((z_streamp strm))
{
if (g_ZWRAPdecompressionType == ZWRAP_FORCE_ZLIB || !strm->reserved)
return inflateBackEnd(strm);
@@ -1089,14 +1089,14 @@ ZEXTERN int ZEXPORT z_inflateBackEnd OF((z_streamp strm))
}
-ZEXTERN uLong ZEXPORT z_zlibCompileFlags OF((void)) { return zlibCompileFlags(); }
+ZEXTERN uLong ZEXPORT z_zlibCompileFlags _Z_OF((void)) { return zlibCompileFlags(); }
/* === utility functions === */
#ifndef Z_SOLO
-ZEXTERN int ZEXPORT z_compress OF((Bytef *dest, uLongf *destLen,
+ZEXTERN int ZEXPORT z_compress _Z_OF((Bytef *dest, uLongf *destLen,
const Bytef *source, uLong sourceLen))
{
if (!g_ZWRAP_useZSTDcompression)
@@ -1115,7 +1115,7 @@ ZEXTERN int ZEXPORT z_compress OF((Bytef *dest, uLongf *destLen,
}
-ZEXTERN int ZEXPORT z_compress2 OF((Bytef *dest, uLongf *destLen,
+ZEXTERN int ZEXPORT z_compress2 _Z_OF((Bytef *dest, uLongf *destLen,
const Bytef *source, uLong sourceLen,
int level))
{
@@ -1131,7 +1131,7 @@ ZEXTERN int ZEXPORT z_compress2 OF((Bytef *dest, uLongf *destLen,
}
-ZEXTERN uLong ZEXPORT z_compressBound OF((uLong sourceLen))
+ZEXTERN uLong ZEXPORT z_compressBound _Z_OF((uLong sourceLen))
{
if (!g_ZWRAP_useZSTDcompression)
return compressBound(sourceLen);
@@ -1140,7 +1140,7 @@ ZEXTERN uLong ZEXPORT z_compressBound OF((uLong sourceLen))
}
-ZEXTERN int ZEXPORT z_uncompress OF((Bytef *dest, uLongf *destLen,
+ZEXTERN int ZEXPORT z_uncompress _Z_OF((Bytef *dest, uLongf *destLen,
const Bytef *source, uLong sourceLen))
{
if (!ZSTD_isFrame(source, sourceLen))
@@ -1159,24 +1159,24 @@ ZEXTERN int ZEXPORT z_uncompress OF((Bytef *dest, uLongf *destLen,
/* checksum functions */
-ZEXTERN uLong ZEXPORT z_adler32 OF((uLong adler, const Bytef *buf, uInt len))
+ZEXTERN uLong ZEXPORT z_adler32 _Z_OF((uLong adler, const Bytef *buf, uInt len))
{
return adler32(adler, buf, len);
}
-ZEXTERN uLong ZEXPORT z_crc32 OF((uLong crc, const Bytef *buf, uInt len))
+ZEXTERN uLong ZEXPORT z_crc32 _Z_OF((uLong crc, const Bytef *buf, uInt len))
{
return crc32(crc, buf, len);
}
#if ZLIB_VERNUM >= 0x12B0
-ZEXTERN uLong ZEXPORT z_adler32_z OF((uLong adler, const Bytef *buf, z_size_t len))
+ZEXTERN uLong ZEXPORT z_adler32_z _Z_OF((uLong adler, const Bytef *buf, z_size_t len))
{
return adler32_z(adler, buf, len);
}
-ZEXTERN uLong ZEXPORT z_crc32_z OF((uLong crc, const Bytef *buf, z_size_t len))
+ZEXTERN uLong ZEXPORT z_crc32_z _Z_OF((uLong crc, const Bytef *buf, z_size_t len))
{
return crc32_z(crc, buf, len);
}
@@ -1184,14 +1184,14 @@ ZEXTERN uLong ZEXPORT z_crc32_z OF((uLong crc, const Bytef *buf, z_size_t len))
#if ZLIB_VERNUM >= 0x1270
-ZEXTERN const z_crc_t FAR * ZEXPORT z_get_crc_table OF((void))
+ZEXTERN const z_crc_t FAR * ZEXPORT z_get_crc_table _Z_OF((void))
{
return get_crc_table();
}
#endif
/* Error function */
-ZEXTERN const char * ZEXPORT z_zError OF((int err))
+ZEXTERN const char * ZEXPORT z_zError _Z_OF((int err))
{
/* Just use zlib Error function */
return zError(err);
diff --git a/zlibWrapper/zstd_zlibwrapper.h b/zlibWrapper/zstd_zlibwrapper.h
index 042ab9f84fd..c39cf6379d8 100644
--- a/zlibWrapper/zstd_zlibwrapper.h
+++ b/zlibWrapper/zstd_zlibwrapper.h
@@ -25,6 +25,9 @@ extern "C" {
#define z_const
#endif
+#if !defined(_Z_OF)
+ #define _Z_OF OF
+#endif
/* returns a string with version of zstd library */
const char * zstdVersion(void);
From 2b9fde932b53502ca6fb59d9a9d7ca781ffeae55 Mon Sep 17 00:00:00 2001
From: Chris Burgess <9002722+cgbur@users.noreply.github.com>
Date: Fri, 29 Jul 2022 15:22:46 -0400
Subject: [PATCH 190/472] Fix small file passthrough (#3215)
---
programs/fileio.c | 5 ++++-
tests/playTests.sh | 10 ++++++++++
2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/programs/fileio.c b/programs/fileio.c
index a83b8064a9d..7a479069a07 100644
--- a/programs/fileio.c
+++ b/programs/fileio.c
@@ -2346,7 +2346,10 @@ static int FIO_decompressFrames(FIO_ctx_t* const fCtx,
break; /* no more input */
}
readSomething = 1; /* there is at least 1 byte in srcFile */
- if (ress.readCtx->srcBufferLoaded < toRead) {
+ if (ress.readCtx->srcBufferLoaded < toRead) { /* not enough input to check magic number */
+ if ((prefs->overwrite) && !strcmp (dstFileName, stdoutmark)) { /* pass-through mode */
+ return FIO_passThrough(&ress);
+ }
DISPLAYLEVEL(1, "zstd: %s: unknown header \n", srcFileName);
return 1;
}
diff --git a/tests/playTests.sh b/tests/playTests.sh
index 0e2b0fbb2bf..7c111e7a3e6 100755
--- a/tests/playTests.sh
+++ b/tests/playTests.sh
@@ -808,6 +808,16 @@ println "Hello world 1!" | zstd -df
println "Hello world 2!" | zstd -dcf
println "Hello world 3!" > tmp1
zstd -dcf tmp1
+println "" | zstd -df > tmp1
+println "" > tmp2
+$DIFF -q tmp1 tmp2
+println "1" | zstd -df > tmp1
+println "1" > tmp2
+$DIFF -q tmp1 tmp2
+println "12" | zstd -df > tmp1
+println "12" > tmp2
+$DIFF -q tmp1 tmp2
+rm -rf tmp*
println "\n===> frame concatenation "
From d4a5bc4efc40e090bf55a1a9221b6ab26b17b302 Mon Sep 17 00:00:00 2001
From: Tom Wang
Date: Fri, 29 Jul 2022 12:51:58 -0700
Subject: [PATCH 191/472] Add warning when multi-thread decompression is
requested (#3208)
When user pass in argument for both decompression and multi-thread, print a warning message
to indicate that multi-threaded decompression is not supported.
* Add warning when multi-thread decompression is requested
* add test case for multi-threaded decoding warning
Expectation is for -d -T0 we will not throw any warning,
and see warning for any other -d -T(>1) inputs
---
programs/zstdcli.c | 3 +++
tests/cli-tests/compression/multi-threaded.sh | 4 ++++
tests/cli-tests/compression/multi-threaded.sh.stderr.exact | 1 +
3 files changed, 8 insertions(+)
create mode 100644 tests/cli-tests/compression/multi-threaded.sh.stderr.exact
diff --git a/programs/zstdcli.c b/programs/zstdcli.c
index fbacb908a92..372b7d7a306 100644
--- a/programs/zstdcli.c
+++ b/programs/zstdcli.c
@@ -1226,6 +1226,9 @@ int main(int argCount, const char* argv[])
DISPLAYLEVEL(3, WELCOME_MESSAGE);
#ifdef ZSTD_MULTITHREAD
+ if ((operation==zom_decompress) && (!singleThread) && (nbWorkers > 1)) {
+ DISPLAYLEVEL(2, "Warning : decompression does not support multi-threading\n");
+ }
if ((nbWorkers==0) && (!singleThread)) {
/* automatically set # workers based on # of reported cpus */
if (defaultLogicalCores) {
diff --git a/tests/cli-tests/compression/multi-threaded.sh b/tests/cli-tests/compression/multi-threaded.sh
index e3961330a0a..bd01448e2e8 100755
--- a/tests/cli-tests/compression/multi-threaded.sh
+++ b/tests/cli-tests/compression/multi-threaded.sh
@@ -9,3 +9,7 @@ zstd --rsyncable -f file ; zstd -t file.zst
zstd -T0 -f file ; zstd -t file.zst
zstd -T0 --auto-threads=logical -f file ; zstd -t file.zst
zstd -T0 --auto-threads=physical -f file; zstd -t file.zst
+
+# multi-thread decompression warning test
+zstd -T0 -f file ; zstd -t file.zst; zstd -T0 -d file.zst -o file3
+zstd -T0 -f file ; zstd -t file.zst; zstd -T2 -d file.zst -o file4
diff --git a/tests/cli-tests/compression/multi-threaded.sh.stderr.exact b/tests/cli-tests/compression/multi-threaded.sh.stderr.exact
new file mode 100644
index 00000000000..54d47d8bd70
--- /dev/null
+++ b/tests/cli-tests/compression/multi-threaded.sh.stderr.exact
@@ -0,0 +1 @@
+Warning : decompression does not support multi-threading
From f9f27de91c89d826c6a39c3ef44fb1b02f9a43aa Mon Sep 17 00:00:00 2001
From: Elliot Gorokhovsky
Date: Fri, 29 Jul 2022 14:44:22 -0700
Subject: [PATCH 192/472] Disallow empty output directory
---
programs/zstdcli.c | 18 ++++++++++++++++--
tests/cli-tests/basic/output_dir.sh | 7 +++++++
.../cli-tests/basic/output_dir.sh.stderr.exact | 2 ++
.../cli-tests/basic/output_dir.sh.stdout.exact | 2 ++
4 files changed, 27 insertions(+), 2 deletions(-)
create mode 100755 tests/cli-tests/basic/output_dir.sh
create mode 100644 tests/cli-tests/basic/output_dir.sh.stderr.exact
create mode 100644 tests/cli-tests/basic/output_dir.sh.stdout.exact
diff --git a/programs/zstdcli.c b/programs/zstdcli.c
index fbacb908a92..1143ac3fe8c 100644
--- a/programs/zstdcli.c
+++ b/programs/zstdcli.c
@@ -1011,7 +1011,14 @@ int main(int argCount, const char* argv[])
if (longCommandWArg(&argument, "--stream-size=")) { streamSrcSize = readSizeTFromChar(&argument); continue; }
if (longCommandWArg(&argument, "--target-compressed-block-size=")) { targetCBlockSize = readSizeTFromChar(&argument); continue; }
if (longCommandWArg(&argument, "--size-hint=")) { srcSizeHint = readSizeTFromChar(&argument); continue; }
- if (longCommandWArg(&argument, "--output-dir-flat")) { NEXT_FIELD(outDirName); continue; }
+ if (longCommandWArg(&argument, "--output-dir-flat")) {
+ NEXT_FIELD(outDirName);
+ if (strlen(outDirName) == 0) {
+ DISPLAY("error: output dir cannot be empty string (did you mean to pass '.' instead?)\n");
+ CLEAN_RETURN(1);
+ }
+ continue;
+ }
if (longCommandWArg(&argument, "--auto-threads")) {
const char* threadDefault = NULL;
NEXT_FIELD(threadDefault);
@@ -1020,7 +1027,14 @@ int main(int argCount, const char* argv[])
continue;
}
#ifdef UTIL_HAS_MIRRORFILELIST
- if (longCommandWArg(&argument, "--output-dir-mirror")) { NEXT_FIELD(outMirroredDirName); continue; }
+ if (longCommandWArg(&argument, "--output-dir-mirror")) {
+ NEXT_FIELD(outMirroredDirName);
+ if (strlen(outMirroredDirName) == 0) {
+ DISPLAY("error: output dir cannot be empty string (did you mean to pass '.' instead?)\n");
+ CLEAN_RETURN(1);
+ }
+ continue;
+ }
#endif
#ifndef ZSTD_NOTRACE
if (longCommandWArg(&argument, "--trace")) { char const* traceFile; NEXT_FIELD(traceFile); TRACE_enable(traceFile); continue; }
diff --git a/tests/cli-tests/basic/output_dir.sh b/tests/cli-tests/basic/output_dir.sh
new file mode 100755
index 00000000000..a8819d29260
--- /dev/null
+++ b/tests/cli-tests/basic/output_dir.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+println "+ zstd -r * --output-dir-mirror=\"\""
+zstd -r * --output-dir-mirror="" && die "Should not allow empty output dir!"
+println "+ zstd -r * --output-dir-flat=\"\""
+zstd -r * --output-dir-flat="" && die "Should not allow empty output dir!"
+exit 0
diff --git a/tests/cli-tests/basic/output_dir.sh.stderr.exact b/tests/cli-tests/basic/output_dir.sh.stderr.exact
new file mode 100644
index 00000000000..e12b50427cf
--- /dev/null
+++ b/tests/cli-tests/basic/output_dir.sh.stderr.exact
@@ -0,0 +1,2 @@
+error: output dir cannot be empty string (did you mean to pass '.' instead?)
+error: output dir cannot be empty string (did you mean to pass '.' instead?)
diff --git a/tests/cli-tests/basic/output_dir.sh.stdout.exact b/tests/cli-tests/basic/output_dir.sh.stdout.exact
new file mode 100644
index 00000000000..1e478cd7531
--- /dev/null
+++ b/tests/cli-tests/basic/output_dir.sh.stdout.exact
@@ -0,0 +1,2 @@
++ zstd -r * --output-dir-mirror=""
++ zstd -r * --output-dir-flat=""
From ae4670466c5db56493f356c1a81e8cbefef3271e Mon Sep 17 00:00:00 2001
From: Yonatan Komornik <11005061+yoniko@users.noreply.github.com>
Date: Fri, 29 Jul 2022 16:13:07 -0700
Subject: [PATCH 193/472] stdin multiple file fixes (#3222)
* Fixes for https://github.com/facebook/zstd/issues/3206 - bugs when handling stdin as part of multiple files.
* new line at end of multiple-files.sh
---
programs/fileio.c | 10 +++++++++
programs/util.c | 10 +++++++++
programs/util.h | 5 +++++
programs/zstdcli.c | 8 +++----
tests/cli-tests/compression/multiple-files.sh | 21 +++++++++++++++++++
.../multiple-files.sh.stdout.exact | 12 +++++++++++
6 files changed, 62 insertions(+), 4 deletions(-)
create mode 100755 tests/cli-tests/compression/multiple-files.sh
create mode 100644 tests/cli-tests/compression/multiple-files.sh.stdout.exact
diff --git a/programs/fileio.c b/programs/fileio.c
index 7a479069a07..27daeca9330 100644
--- a/programs/fileio.c
+++ b/programs/fileio.c
@@ -1796,6 +1796,11 @@ FIO_determineCompressedName(const char* srcFileName, const char* outDirName, con
char* outDirFilename = NULL;
size_t sfnSize = strlen(srcFileName);
size_t const srcSuffixLen = strlen(suffix);
+
+ if(!strcmp(srcFileName, stdinmark)) {
+ return stdoutmark;
+ }
+
if (outDirName) {
outDirFilename = FIO_createFilename_fromOutDir(srcFileName, outDirName, srcSuffixLen);
sfnSize = strlen(outDirFilename);
@@ -2579,6 +2584,11 @@ FIO_determineDstName(const char* srcFileName, const char* outDirName)
size_t srcSuffixLen;
const char* const srcSuffix = strrchr(srcFileName, '.');
+
+ if(!strcmp(srcFileName, stdinmark)) {
+ return stdoutmark;
+ }
+
if (srcSuffix == NULL) {
DISPLAYLEVEL(1,
"zstd: %s: unknown suffix (%s expected). "
diff --git a/programs/util.c b/programs/util.c
index 50db70fd447..c925adabc9e 100644
--- a/programs/util.c
+++ b/programs/util.c
@@ -509,6 +509,16 @@ FileNamesTable* UTIL_allocateFileNamesTable(size_t tableSize)
return fnt;
}
+int UTIL_searchFileNamesTable(FileNamesTable* table, char const* name) {
+ size_t i;
+ for(i=0 ;i < table->tableSize; i++) {
+ if(!strcmp(table->fileNames[i], name)) {
+ return (int)i;
+ }
+ }
+ return -1;
+}
+
void UTIL_refFilename(FileNamesTable* fnt, const char* filename)
{
assert(fnt->tableSize < fnt->tableCapacity);
diff --git a/programs/util.h b/programs/util.h
index add165d57ce..faf8c9f11cb 100644
--- a/programs/util.h
+++ b/programs/util.h
@@ -269,6 +269,11 @@ UTIL_createFNT_fromROTable(const char** filenames, size_t nbFilenames);
*/
FileNamesTable* UTIL_allocateFileNamesTable(size_t tableSize);
+/*! UTIL_searchFileNamesTable() :
+ * Searched through entries in FileNamesTable for a specific name.
+ * @return : index of entry if found or -1 if not found
+ */
+int UTIL_searchFileNamesTable(FileNamesTable* table, char const* name);
/*! UTIL_refFilename() :
* Add a reference to read-only name into @fnt table.
diff --git a/programs/zstdcli.c b/programs/zstdcli.c
index 372b7d7a306..71b0dbefee0 100644
--- a/programs/zstdcli.c
+++ b/programs/zstdcli.c
@@ -1391,19 +1391,19 @@ int main(int argCount, const char* argv[])
UTIL_refFilename(filenames, stdinmark);
}
- if (!strcmp(filenames->fileNames[0], stdinmark) && !outFileName)
+ if (filenames->tableSize == 1 && !strcmp(filenames->fileNames[0], stdinmark) && !outFileName)
outFileName = stdoutmark; /* when input is stdin, default output is stdout */
/* Check if input/output defined as console; trigger an error in this case */
if (!forceStdin
- && !strcmp(filenames->fileNames[0], stdinmark)
+ && (UTIL_searchFileNamesTable(filenames, stdinmark) != -1)
&& IS_CONSOLE(stdin) ) {
DISPLAYLEVEL(1, "stdin is a console, aborting\n");
CLEAN_RETURN(1);
}
- if ( outFileName && !strcmp(outFileName, stdoutmark)
+ if ( (!outFileName || !strcmp(outFileName, stdoutmark))
&& IS_CONSOLE(stdout)
- && !strcmp(filenames->fileNames[0], stdinmark)
+ && (UTIL_searchFileNamesTable(filenames, stdinmark) != -1)
&& !forceStdout
&& operation!=zom_decompress ) {
DISPLAYLEVEL(1, "stdout is a console, aborting\n");
diff --git a/tests/cli-tests/compression/multiple-files.sh b/tests/cli-tests/compression/multiple-files.sh
new file mode 100755
index 00000000000..aeb74cf25eb
--- /dev/null
+++ b/tests/cli-tests/compression/multiple-files.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+set -e
+
+# setup
+echo "file1" > file1
+echo "file2" > file2
+
+echo "Test zstd ./file1 - file2"
+rm -f ./file*.zst
+echo "stdin" | zstd ./file1 - ./file2 | zstd -d
+cat file1.zst | zstd -d
+cat file2.zst | zstd -d
+
+echo "Test zstd -d ./file1.zst - file2.zst"
+rm ./file1 ./file2
+echo "stdin" | zstd - | zstd -d ./file1.zst - file2.zst
+cat file1
+cat file2
+
+echo "zstd -d ./file1.zst - file2.zst -c"
+echo "stdin" | zstd | zstd -d ./file1.zst - file2.zst -c
diff --git a/tests/cli-tests/compression/multiple-files.sh.stdout.exact b/tests/cli-tests/compression/multiple-files.sh.stdout.exact
new file mode 100644
index 00000000000..aad61d6359a
--- /dev/null
+++ b/tests/cli-tests/compression/multiple-files.sh.stdout.exact
@@ -0,0 +1,12 @@
+Test zstd ./file1 - file2
+stdin
+file1
+file2
+Test zstd -d ./file1.zst - file2.zst
+stdin
+file1
+file2
+zstd -d ./file1.zst - file2.zst -c
+file1
+stdin
+file2
From 1b445c1c2e89982ad7be6b7fa13a9a1bdc658cb2 Mon Sep 17 00:00:00 2001
From: Qiongsi Wu <274595+qiongsiwu@users.noreply.github.com>
Date: Mon, 1 Aug 2022 13:41:24 -0400
Subject: [PATCH 194/472] Fix hash4Ptr for big endian (#3227)
---
lib/compress/zstd_compress_internal.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h
index 4771b15ba6c..baa726f7dff 100644
--- a/lib/compress/zstd_compress_internal.h
+++ b/lib/compress/zstd_compress_internal.h
@@ -759,7 +759,7 @@ MEM_STATIC size_t ZSTD_hash3Ptr(const void* ptr, U32 h) { return ZSTD_hash3(MEM_
static const U32 prime4bytes = 2654435761U;
static U32 ZSTD_hash4(U32 u, U32 h) { assert(h <= 32); return (u * prime4bytes) >> (32-h) ; }
-static size_t ZSTD_hash4Ptr(const void* ptr, U32 h) { return ZSTD_hash4(MEM_read32(ptr), h); }
+static size_t ZSTD_hash4Ptr(const void* ptr, U32 h) { return ZSTD_hash4(MEM_readLE32(ptr), h); }
static const U64 prime5bytes = 889523592379ULL;
static size_t ZSTD_hash5(U64 u, U32 h) { assert(h <= 64); return (size_t)(((u << (64-40)) * prime5bytes) >> (64-h)) ; }
From 0f4fd28a64880bdd1c14847983d5a7561950d8d5 Mon Sep 17 00:00:00 2001
From: Nick Terrell
Date: Mon, 1 Aug 2022 11:52:14 -0700
Subject: [PATCH 195/472] Deprecate ZSTD_getDecompressedSize() (#3225)
Fixes #3158.
Mark ZSTD_getDecompressedSize() as deprecated and replaced by ZSTD_getFrameContentSize().
---
doc/zstd_manual.html | 31 +++++++++++++++--------
lib/zstd.h | 59 +++++++++++++++++++++++++++-----------------
2 files changed, 57 insertions(+), 33 deletions(-)
diff --git a/doc/zstd_manual.html b/doc/zstd_manual.html
index ff9eaca3635..cca287b729f 100644
--- a/doc/zstd_manual.html
+++ b/doc/zstd_manual.html
@@ -121,7 +121,9 @@ zstd 1.5.3 Manual
note 6 : This function replaces ZSTD_getDecompressedSize()
-unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize);
+ZSTDLIB_API
+ZSTD_DEPRECATED("Replaced by ZSTD_getFrameContentSize")
+unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize);
NOTE: This function is now obsolete, in favor of ZSTD_getFrameContentSize().
Both functions work the same way, but ZSTD_getDecompressedSize() blends
"empty", "unknown" and "error" results to the same return value (0),
@@ -1379,7 +1381,8 @@
Streaming decompression functions
This function never fails (wide contract)
-ZSTD_DEPRECATED("use ZSTD_compress2")
+ZSTDLIB_STATIC_API
+ZSTD_DEPRECATED("use ZSTD_compress2")
size_t ZSTD_compress_advanced(ZSTD_CCtx* cctx,
void* dst, size_t dstCapacity,
const void* src, size_t srcSize,
@@ -1390,7 +1393,8 @@ Streaming decompression functions
This prototype will generate compilation warnings.
-ZSTD_DEPRECATED("use ZSTD_compress2 with ZSTD_CCtx_loadDictionary")
+ZSTDLIB_STATIC_API
+ZSTD_DEPRECATED("use ZSTD_compress2 with ZSTD_CCtx_loadDictionary")
size_t ZSTD_compress_usingCDict_advanced(ZSTD_CCtx* cctx,
void* dst, size_t dstCapacity,
const void* src, size_t srcSize,
@@ -1551,7 +1555,8 @@ Streaming decompression functions
-ZSTD_DEPRECATED("use ZSTD_DCtx_setParameter() instead")
+ZSTDLIB_STATIC_API
+ZSTD_DEPRECATED("use ZSTD_DCtx_setParameter() instead")
size_t ZSTD_DCtx_setFormat(ZSTD_DCtx* dctx, ZSTD_format_e format);
This function is REDUNDANT. Prefer ZSTD_DCtx_setParameter().
Instruct the decoder context about what kind of data to decode next.
@@ -1577,7 +1582,8 @@
Streaming decompression functions
Advanced Streaming compression functions
-ZSTD_DEPRECATED("use ZSTD_CCtx_reset, see zstd.h for detailed instructions")
+ZSTDLIB_STATIC_API
+ZSTD_DEPRECATED("use ZSTD_CCtx_reset, see zstd.h for detailed instructions")
size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs,
int compressionLevel,
unsigned long long pledgedSrcSize);
@@ -1594,7 +1600,8 @@ Advanced Streaming compression functions
-ZSTD_DEPRECATED("use ZSTD_CCtx_reset, see zstd.h for detailed instructions")
+ZSTDLIB_STATIC_API
+ZSTD_DEPRECATED("use ZSTD_CCtx_reset, see zstd.h for detailed instructions")
size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs,
const void* dict, size_t dictSize,
int compressionLevel);
@@ -1611,7 +1618,8 @@ Advanced Streaming compression functions
-ZSTD_DEPRECATED("use ZSTD_CCtx_reset, see zstd.h for detailed instructions")
+ZSTDLIB_STATIC_API
+ZSTD_DEPRECATED("use ZSTD_CCtx_reset, see zstd.h for detailed instructions")
size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs,
const void* dict, size_t dictSize,
ZSTD_parameters params,
@@ -1632,7 +1640,8 @@ Advanced Streaming compression functions
-ZSTD_DEPRECATED("use ZSTD_CCtx_reset and ZSTD_CCtx_refCDict, see zstd.h for detailed instructions")
+ZSTDLIB_STATIC_API
+ZSTD_DEPRECATED("use ZSTD_CCtx_reset and ZSTD_CCtx_refCDict, see zstd.h for detailed instructions")
size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict);
This function is DEPRECATED, and equivalent to:
ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);
@@ -1643,7 +1652,8 @@
Advanced Streaming compression functions
-ZSTD_DEPRECATED("use ZSTD_CCtx_reset and ZSTD_CCtx_refCDict, see zstd.h for detailed instructions")
+ZSTDLIB_STATIC_API
+ZSTD_DEPRECATED("use ZSTD_CCtx_reset and ZSTD_CCtx_refCDict, see zstd.h for detailed instructions")
size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs,
const ZSTD_CDict* cdict,
ZSTD_frameParameters fParams,
@@ -1664,7 +1674,8 @@ Advanced Streaming compression functions
-ZSTD_DEPRECATED("use ZSTD_CCtx_reset, see zstd.h for detailed instructions")
+ZSTDLIB_STATIC_API
+ZSTD_DEPRECATED("use ZSTD_CCtx_reset, see zstd.h for detailed instructions")
size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pledgedSrcSize);
This function is DEPRECATED, and is equivalent to:
ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);
diff --git a/lib/zstd.h b/lib/zstd.h
index 4c2eee69609..07586c24cc4 100644
--- a/lib/zstd.h
+++ b/lib/zstd.h
@@ -37,6 +37,28 @@ extern "C" {
# define ZSTDLIB_API ZSTDLIB_VISIBLE
#endif
+/* Deprecation warnings :
+ * Should these warnings be a problem, it is generally possible to disable them,
+ * typically with -Wno-deprecated-declarations for gcc or _CRT_SECURE_NO_WARNINGS in Visual.
+ * Otherwise, it's also possible to define ZSTD_DISABLE_DEPRECATE_WARNINGS.
+ */
+#ifdef ZSTD_DISABLE_DEPRECATE_WARNINGS
+# define ZSTD_DEPRECATED(message) /* disable deprecation warnings */
+#else
+# if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */
+# define ZSTD_DEPRECATED(message) [[deprecated(message)]]
+# elif (defined(GNUC) && (GNUC > 4 || (GNUC == 4 && GNUC_MINOR >= 5))) || defined(__clang__)
+# define ZSTD_DEPRECATED(message) __attribute__((deprecated(message)))
+# elif defined(__GNUC__) && (__GNUC__ >= 3)
+# define ZSTD_DEPRECATED(message) __attribute__((deprecated))
+# elif defined(_MSC_VER)
+# define ZSTD_DEPRECATED(message) __declspec(deprecated(message))
+# else
+# pragma message("WARNING: You need to implement ZSTD_DEPRECATED for this compiler")
+# define ZSTD_DEPRECATED(message)
+# endif
+#endif /* ZSTD_DISABLE_DEPRECATE_WARNINGS */
+
/*******************************************************************************
Introduction
@@ -165,7 +187,9 @@ ZSTDLIB_API unsigned long long ZSTD_getFrameContentSize(const void *src, size_t
* "empty", "unknown" and "error" results to the same return value (0),
* while ZSTD_getFrameContentSize() gives them separate return values.
* @return : decompressed size of `src` frame content _if known and not empty_, 0 otherwise. */
-ZSTDLIB_API unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize);
+ZSTDLIB_API
+ZSTD_DEPRECATED("Replaced by ZSTD_getFrameContentSize")
+unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize);
/*! ZSTD_findFrameCompressedSize() : Requires v1.4.0+
* `src` should point to the start of a ZSTD frame or skippable frame.
@@ -1088,28 +1112,6 @@ ZSTDLIB_API size_t ZSTD_sizeof_DDict(const ZSTD_DDict* ddict);
# endif
#endif
-/* Deprecation warnings :
- * Should these warnings be a problem, it is generally possible to disable them,
- * typically with -Wno-deprecated-declarations for gcc or _CRT_SECURE_NO_WARNINGS in Visual.
- * Otherwise, it's also possible to define ZSTD_DISABLE_DEPRECATE_WARNINGS.
- */
-#ifdef ZSTD_DISABLE_DEPRECATE_WARNINGS
-# define ZSTD_DEPRECATED(message) ZSTDLIB_STATIC_API /* disable deprecation warnings */
-#else
-# if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */
-# define ZSTD_DEPRECATED(message) [[deprecated(message)]] ZSTDLIB_STATIC_API
-# elif (defined(GNUC) && (GNUC > 4 || (GNUC == 4 && GNUC_MINOR >= 5))) || defined(__clang__)
-# define ZSTD_DEPRECATED(message) ZSTDLIB_STATIC_API __attribute__((deprecated(message)))
-# elif defined(__GNUC__) && (__GNUC__ >= 3)
-# define ZSTD_DEPRECATED(message) ZSTDLIB_STATIC_API __attribute__((deprecated))
-# elif defined(_MSC_VER)
-# define ZSTD_DEPRECATED(message) ZSTDLIB_STATIC_API __declspec(deprecated(message))
-# else
-# pragma message("WARNING: You need to implement ZSTD_DEPRECATED for this compiler")
-# define ZSTD_DEPRECATED(message) ZSTDLIB_STATIC_API
-# endif
-#endif /* ZSTD_DISABLE_DEPRECATE_WARNINGS */
-
/****************************************************************************************
* experimental API (static linking only)
****************************************************************************************
@@ -1681,6 +1683,7 @@ ZSTDLIB_STATIC_API ZSTD_compressionParameters ZSTD_adjustCParams(ZSTD_compressio
* Note : this function is now DEPRECATED.
* It can be replaced by ZSTD_compress2(), in combination with ZSTD_CCtx_setParameter() and other parameter setters.
* This prototype will generate compilation warnings. */
+ZSTDLIB_STATIC_API
ZSTD_DEPRECATED("use ZSTD_compress2")
size_t ZSTD_compress_advanced(ZSTD_CCtx* cctx,
void* dst, size_t dstCapacity,
@@ -1692,6 +1695,7 @@ size_t ZSTD_compress_advanced(ZSTD_CCtx* cctx,
* Note : this function is now DEPRECATED.
* It can be replaced by ZSTD_compress2(), in combination with ZSTD_CCtx_loadDictionary() and other parameter setters.
* This prototype will generate compilation warnings. */
+ZSTDLIB_STATIC_API
ZSTD_DEPRECATED("use ZSTD_compress2 with ZSTD_CCtx_loadDictionary")
size_t ZSTD_compress_usingCDict_advanced(ZSTD_CCtx* cctx,
void* dst, size_t dstCapacity,
@@ -2195,6 +2199,7 @@ ZSTDLIB_STATIC_API size_t ZSTD_DCtx_getParameter(ZSTD_DCtx* dctx, ZSTD_dParamete
* This instruction is mandatory to decode data without a fully-formed header,
* such ZSTD_f_zstd1_magicless for example.
* @return : 0, or an error code (which can be tested using ZSTD_isError()). */
+ZSTDLIB_STATIC_API
ZSTD_DEPRECATED("use ZSTD_DCtx_setParameter() instead")
size_t ZSTD_DCtx_setFormat(ZSTD_DCtx* dctx, ZSTD_format_e format);
@@ -2231,6 +2236,7 @@ ZSTDLIB_STATIC_API size_t ZSTD_decompressStream_simpleArgs (
* "0" also disables frame content size field. It may be enabled in the future.
* This prototype will generate compilation warnings.
*/
+ZSTDLIB_STATIC_API
ZSTD_DEPRECATED("use ZSTD_CCtx_reset, see zstd.h for detailed instructions")
size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs,
int compressionLevel,
@@ -2248,6 +2254,7 @@ size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs,
* it begins with ZSTD_MAGIC_DICTIONARY, else as raw content) and ZSTD_dlm_byCopy.
* This prototype will generate compilation warnings.
*/
+ZSTDLIB_STATIC_API
ZSTD_DEPRECATED("use ZSTD_CCtx_reset, see zstd.h for detailed instructions")
size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs,
const void* dict, size_t dictSize,
@@ -2268,6 +2275,7 @@ size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs,
* If srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN.
* This prototype will generate compilation warnings.
*/
+ZSTDLIB_STATIC_API
ZSTD_DEPRECATED("use ZSTD_CCtx_reset, see zstd.h for detailed instructions")
size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs,
const void* dict, size_t dictSize,
@@ -2282,6 +2290,7 @@ size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs,
* note : cdict will just be referenced, and must outlive compression session
* This prototype will generate compilation warnings.
*/
+ZSTDLIB_STATIC_API
ZSTD_DEPRECATED("use ZSTD_CCtx_reset and ZSTD_CCtx_refCDict, see zstd.h for detailed instructions")
size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict);
@@ -2300,6 +2309,7 @@ size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict);
* value ZSTD_CONTENTSIZE_UNKNOWN.
* This prototype will generate compilation warnings.
*/
+ZSTDLIB_STATIC_API
ZSTD_DEPRECATED("use ZSTD_CCtx_reset and ZSTD_CCtx_refCDict, see zstd.h for detailed instructions")
size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs,
const ZSTD_CDict* cdict,
@@ -2324,6 +2334,7 @@ size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs,
* @return : 0, or an error code (which can be tested using ZSTD_isError())
* This prototype will generate compilation warnings.
*/
+ZSTDLIB_STATIC_API
ZSTD_DEPRECATED("use ZSTD_CCtx_reset, see zstd.h for detailed instructions")
size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pledgedSrcSize);
@@ -2444,8 +2455,10 @@ ZSTDLIB_STATIC_API size_t ZSTD_compressContinue(ZSTD_CCtx* cctx, void* dst, size
ZSTDLIB_STATIC_API size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
/* The ZSTD_compressBegin_advanced() and ZSTD_compressBegin_usingCDict_advanced() are now DEPRECATED and will generate a compiler warning */
+ZSTDLIB_STATIC_API
ZSTD_DEPRECATED("use advanced API to access custom parameters")
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_STATIC_API
ZSTD_DEPRECATED("use advanced API to access custom parameters")
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 */
/**
From c450f9f952d22033919c71a6279b55adeb343e8e Mon Sep 17 00:00:00 2001
From: Miles HU
Date: Wed, 13 Jul 2022 11:00:05 -0700
Subject: [PATCH 196/472] [T124890272] Mark 2 Obsolete Functions(ZSTD_copy*Ctx)
Deprecated in Zstd
The discussion for this task is here: facebook/zstd#3128.
This task can probably be scoped to the first part: marking these functions deprecated.
We'll later look at removal when we roll out v1.6.0.
---
lib/zstd.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lib/zstd.h b/lib/zstd.h
index 4c2eee69609..ad8eec416ac 100644
--- a/lib/zstd.h
+++ b/lib/zstd.h
@@ -2413,7 +2413,6 @@ ZSTDLIB_STATIC_API size_t ZSTD_resetDStream(ZSTD_DStream* zds);
Start by initializing a context.
Use ZSTD_compressBegin(), or ZSTD_compressBegin_usingDict() for dictionary compression.
- It's also possible to duplicate a reference context which has already been initialized, using ZSTD_copyCCtx()
Then, consume your input using ZSTD_compressContinue().
There are some important considerations to keep in mind when using this advanced function :
@@ -2438,6 +2437,7 @@ ZSTDLIB_STATIC_API size_t ZSTD_resetDStream(ZSTD_DStream* zds);
ZSTDLIB_STATIC_API size_t ZSTD_compressBegin(ZSTD_CCtx* cctx, int compressionLevel);
ZSTDLIB_STATIC_API size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel);
ZSTDLIB_STATIC_API size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict); /**< note: fails if cdict==NULL */
+ZSTD_DEPRECATED("This function will likely be removed in the next minor release. It is misleading and has very limited utility.")
ZSTDLIB_STATIC_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_STATIC_API size_t ZSTD_compressContinue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
@@ -2553,6 +2553,7 @@ ZSTDLIB_STATIC_API size_t ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx* dctx);
ZSTDLIB_STATIC_API size_t ZSTD_decompressContinue(ZSTD_DCtx* dctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
/* misc */
+ZSTD_DEPRECATED("This function will likely be removed in the next minor release. It is misleading and has very limited utility.")
ZSTDLIB_STATIC_API void ZSTD_copyDCtx(ZSTD_DCtx* dctx, const ZSTD_DCtx* preparedDCtx);
typedef enum { ZSTDnit_frameHeader, ZSTDnit_blockHeader, ZSTDnit_block, ZSTDnit_lastBlock, ZSTDnit_checksum, ZSTDnit_skippableFrame } ZSTD_nextInputType_e;
ZSTDLIB_STATIC_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx);
@@ -2575,7 +2576,6 @@ ZSTDLIB_STATIC_API ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx* dctx);
- It is necessary to init context before starting
+ compression : any ZSTD_compressBegin*() variant, including with dictionary
+ decompression : any ZSTD_decompressBegin*() variant, including with dictionary
- + copyCCtx() and copyDCtx() can be used too
- Block size is limited, it must be <= ZSTD_getBlockSize() <= ZSTD_BLOCKSIZE_MAX == 128 KB
+ If input is larger than a block size, it's necessary to split input data into multiple blocks
+ For inputs larger than a single block, consider using regular ZSTD_compress() instead.
From a70ca2bd7dbc74d3c9db173e3682532e18565246 Mon Sep 17 00:00:00 2001
From: Nick Terrell
Date: Wed, 3 Aug 2022 11:28:39 -0700
Subject: [PATCH 197/472] Fix off-by-one error in superblock mode (#3221)
Fixes #3212.
Long literal and match lengths had an off-by-one error in ZSTD_getSequenceLength.
Fix the off-by-one error, and add a golden compression test that catches the bug.
Also run all the golden tests in the cli-tests framework.
---
lib/common/zstd_internal.h | 4 ++--
lib/decompress/zstd_decompress_block.c | 2 +-
tests/cli-tests/compression/golden.sh | 12 ++++++++++++
tests/cli-tests/decompression/golden.sh | 7 +++++++
tests/cli-tests/dictionaries/golden.sh | 9 +++++++++
tests/cli-tests/run.py | 1 +
.../large-literal-and-match-lengths | Bin 0 -> 199998 bytes
7 files changed, 32 insertions(+), 3 deletions(-)
create mode 100755 tests/cli-tests/compression/golden.sh
create mode 100755 tests/cli-tests/decompression/golden.sh
create mode 100755 tests/cli-tests/dictionaries/golden.sh
create mode 100644 tests/golden-compression/large-literal-and-match-lengths
diff --git a/lib/common/zstd_internal.h b/lib/common/zstd_internal.h
index 038232453f8..e8922670283 100644
--- a/lib/common/zstd_internal.h
+++ b/lib/common/zstd_internal.h
@@ -324,10 +324,10 @@ MEM_STATIC ZSTD_sequenceLength ZSTD_getSequenceLength(seqStore_t const* seqStore
seqLen.matchLength = seq->mlBase + MINMATCH;
if (seqStore->longLengthPos == (U32)(seq - seqStore->sequencesStart)) {
if (seqStore->longLengthType == ZSTD_llt_literalLength) {
- seqLen.litLength += 0xFFFF;
+ seqLen.litLength += 0x10000;
}
if (seqStore->longLengthType == ZSTD_llt_matchLength) {
- seqLen.matchLength += 0xFFFF;
+ seqLen.matchLength += 0x10000;
}
}
return seqLen;
diff --git a/lib/decompress/zstd_decompress_block.c b/lib/decompress/zstd_decompress_block.c
index 6ebda486c55..e1ff21582c5 100644
--- a/lib/decompress/zstd_decompress_block.c
+++ b/lib/decompress/zstd_decompress_block.c
@@ -1578,7 +1578,7 @@ ZSTD_decompressSequences_body(ZSTD_DCtx* dctx,
const BYTE* const prefixStart = (const BYTE*)(dctx->prefixStart);
const BYTE* const vBase = (const BYTE*)(dctx->virtualStart);
const BYTE* const dictEnd = (const BYTE*)(dctx->dictEnd);
- DEBUGLOG(5, "ZSTD_decompressSequences_body");
+ DEBUGLOG(5, "ZSTD_decompressSequences_body: nbSeq = %d", nbSeq);
(void)frame;
/* Regen sequences */
diff --git a/tests/cli-tests/compression/golden.sh b/tests/cli-tests/compression/golden.sh
new file mode 100755
index 00000000000..85dd3fdb8af
--- /dev/null
+++ b/tests/cli-tests/compression/golden.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+set -e
+
+GOLDEN_DIR="$ZSTD_REPO_DIR/tests/golden-compression/"
+cp -r "$GOLDEN_DIR" golden/
+
+zstd -rf golden/ --output-dir-mirror golden-compressed/
+zstd -r -t golden-compressed/
+
+zstd --target-compressed-block-size=1024 -rf golden/ --output-dir-mirror golden-compressed/
+zstd -r -t golden-compressed/
diff --git a/tests/cli-tests/decompression/golden.sh b/tests/cli-tests/decompression/golden.sh
new file mode 100755
index 00000000000..36919e69285
--- /dev/null
+++ b/tests/cli-tests/decompression/golden.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+set -e
+
+GOLDEN_DIR="$ZSTD_REPO_DIR/tests/golden-decompression/"
+
+zstd -r -t "$GOLDEN_DIR"
diff --git a/tests/cli-tests/dictionaries/golden.sh b/tests/cli-tests/dictionaries/golden.sh
new file mode 100755
index 00000000000..85da2ee538c
--- /dev/null
+++ b/tests/cli-tests/dictionaries/golden.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+set -e
+
+GOLDEN_COMP_DIR="$ZSTD_REPO_DIR/tests/golden-compression/"
+GOLDEN_DICT_DIR="$ZSTD_REPO_DIR/tests/golden-dictionaries/"
+
+zstd -D "$GOLDEN_DICT_DIR/http-dict-missing-symbols" "$GOLDEN_COMP_DIR/http" -o http.zst
+zstd -D "$GOLDEN_DICT_DIR/http-dict-missing-symbols" -t http.zst
diff --git a/tests/cli-tests/run.py b/tests/cli-tests/run.py
index b91aa984145..d726fba7f7d 100755
--- a/tests/cli-tests/run.py
+++ b/tests/cli-tests/run.py
@@ -699,6 +699,7 @@ def setup_zstd_symlink_dir(zstd_symlink_dir: str, zstd: str) -> None:
if args.exec_prefix is not None:
env["EXEC_PREFIX"] = args.exec_prefix
env["ZSTD_SYMLINK_DIR"] = zstd_symlink_dir
+ env["ZSTD_REPO_DIR"] = os.path.abspath(REPO_DIR)
env["DATAGEN_BIN"] = os.path.abspath(args.datagen)
env["ZSTDGREP_BIN"] = os.path.abspath(args.zstdgrep)
env["ZSTDLESS_BIN"] = os.path.abspath(args.zstdless)
diff --git a/tests/golden-compression/large-literal-and-match-lengths b/tests/golden-compression/large-literal-and-match-lengths
new file mode 100644
index 0000000000000000000000000000000000000000..fb63c32c50f44563ccb9dc3544216cab833ad6c8
GIT binary patch
literal 199998
zcmV(rK<>Y!`4pKCN*rK&oW#Ftnh@o5k3It2HaNU?7wVH)A4?fMPqhGL$-?v#tK&yNMiW*sa>lim+Qn*G9-aIQ_+YlA_eJi3zD|d}12#ZOp_ttS6|fBaGJt
z@$5_Lf4TYP3?d-Xr|}Wj-W@lEK2#(|J4ffP1330+0}kSw!`x7!KNd0jOSfz4qxZf+
zm5+Uttaw@rA7ynHaIyFBJkI{;`JxX+2&ldubu}%6=c_3
zGsVwtGEL}&;`_24hva;SJ3Ogce|+wKU$gFT!N|sA7pW8~
z!#mV&8AfybdQQ$xio`)GWexb1ebUH-e1mz7KJ%|Fc-e-h(ug~~9!;sYf=X_O2x{g(
zhThts60NHxwCdRWicB(g4Q31G?bp-<1Wm|y!?pQjgAB8XMUM`7*!J}-B5ZC|G}%6s
z4$hlEhqJ;GiG_7vHaO?FxEbs~6?)YiA2xbP$#XymOX>@Tf?yEzMcR9o{el}nw-B#V
z&`_2TGU3@rY*iD9_?g+TsCf
zkuGP($BO*Llykr%P6e7$vP#a7&0;mW-ju)5tY^Lo^V_~=>N$-9W)(jN2F^2+cK?W5
z3o%6)$#8_H5h;|9YutW3f&fRQx9*B`{*q}M#`9GzyTjao6D51U>NX^4u@cur{RhGMMB_WIwZlWx-EW{X7C^gcmE=h$}N;0_cv?D+sOE=ariZ{4*VGunI`2$qXqNVbU^W@a
z0Ju%rd&r-sO6S>G%F?-B5P1WL*`f2YHN+Q74NF26)X5R%A||IiN|O-7T46~=
zA!1?!uq
zqYU+0lWZ6!(C-Z*IN|(|vw>
zTiECKiL=Ua3oi>2($ko&41X>i^FvO~l}ivdcufhk9Gkt!)RjY}a{|hYHFVYA|gx|CNWn!2M;fHWRAL
zW~MmxJ#^BQvp-oxAm0;^p{{st%-MB=`qh3+?mGH-FF
z<^2VI2nqaesV+QNHZ^f>vcbW`g_*PM6-=V=v0||Qr*LdjMYaws
zDCN`6OGD!#dlzJWrBpxbmGp{!i1%2W&vT>hlkJml-qLnxes$MzjA0*+IEG=ibY)z1
zRK%U89EwQYBq{tA3Ygfp13SU$;Z8tkqCneMh@I2=tR1R*Nd3uj9W#ZQZp>+AUo4`=
zy)}qT(%RoKVOVxt2OP%c9v-nI!bhdH_{|y%Amfa6U&pP*zuF^Nz}>@od8zZFh|~Tz
zQgMt;mjEc+Uw7iv4K9@1XFG3u16rAwYaKXYg6hPY06bJXKN_)yP!ayjjNS
zfsSQR$)<7>^}uu9isq=Ip_2(t`TbjAOnYrG$b1Kc(Y%=4?896kx(Y
zQL#&6`!;#q;rQ#~S;yDU
z7SAnG{ey8Gfn88g6;)3^zW}PBB^tF~j9`r848l|io*7Rt(Q(-SNk|K&FpD|%;J!pl
zeOkHEdZdRr@8w7xyd&i(^#)%&+<$6aqE1(Tn6;5hl*5bN*TvxPKCCd~cW)7d)(d{L
zqSPfbC!6RY^i3^%@e?u9saN7sTu0`UviFsO!l}92x~B>F4CgtClbwnd>SzE+;R*Nc
z#)JW>Ae;HAdx*$yX*7N!J;9wON}9moEfCM(|6W-M_iNDDB^*24qr$58-YVeU1A<*I!fl*uI$-Lk{_N<4}R(Dr&;<{7a>5
zOli_t8~Z7xK8RsGk}Af!Z#W^#c8M)z4Hcq$kDwtX=RTxf^HxJ-nTyTGYaHTS63U-@
zyVE9wpCiUQFK=*prQz0Zu>Jm1ivssZ$-x|36&>pjv|tMDiTxhtgk7Hrf5GqLs>Y_%
zalPKd96BF@PKcKMz-c9KseePZ97GtT9h($Du#e^9nZ|a0KGhw+2iikVe6i?==#Uwxe+$rPVidGf$d%
zMprV(24<#QU7*bG8RF)iEwQ=?C2;|qTbpwmV^R|OBM)?goZMzfBQ~QZJ&MwqLhen%
zTEmSL7nVl|4-S)76;aYH*)F-~mE3t}g}u#=wjOTDEjmC_k7GFdh^}_{^iLwAIYhU|34LOz`|d%5W+hhyWddX+?BHgJ||yBV@!Ib
z%fl#NzW)gGTb#;c>a)d05Z$%N?%pudghWxdEZi!eRmMAI7?V5BHTA~BdYvXXrz`|i
zHm&gCar2XM|Am~mPUzU7>ynK(9^`Q)d+Q(hytZUNyS-+~k1ml^&}Qqg`>$?3N~Lt#
z!!=%xwSmsQrXajy%F!uf|5
z7#=QzQAJWyfUa3aTj1==ixHKdvc<^&wC_~5wjUg_Yv%*0Srs0d
zbF;1FTynLD`cf?)Dag**ZDJpkI}C+a0$DH}WMT=HFNyp1p2OBkA4dj_K-;<%(QJT+
zbha_ajh1}aC-D38_Z_;V^ygu$@-|~jkd;J@efl_~IIcGZjVf^V)a^D`DuN<3zil!P
zs6Ldgb&3Yxuuh-|19d;j|15HbFvFL6{HyTlu9AadOeaoaQL{|Z2{Q>p&3uU
zya#Z7{|@OauwK8UrV?5uLRm5Z43cl|dbZ@)T7gd7KSquy*2~{$&a-x%b1*~zVzXVa
z0ch_bul@ilhL^AJV0lqL^OezR%9a$Af3d<6m2*dl72TVl#f^Z62s?V8Rdl7G83*Ta
z#pHoiTCxbg1eyW|_q@7KK8g*wE-Ovf2^Z^{+tf|Y7>_y8`;NL?5!@7k?jGnza^xN+
z)Z_=3_$1N?+{`b(Jboi@d9qhh(fF&O(?e!Xj7*Ca(h{f-zVA5GAl*ya7i|!`Ce+$w
zs%rKuWPK_N5->B=xe5vEDu*tpfgc;ALPO`K4`P?tiU~K%|ih*q89LSvv$ph&g!4
zlEGaoaE<3T3s=J{p|e69`&em+`73KL32d-M1~arT(H4dt#CYck9((-a7|S&vJuK}%
z%2~Yj({Nka%9z{3atWpY;WW)pn6rHB@Nj$HH<)yGjskiWyh|;YwB_DXj;~K!D3GqU
z@9-bsiGlqN7a1y?U+3GdLbDUYyH5)iF=o8K-|<3h)A$4om@mEw5Pj^H0j>t7)!I`d
zXNm#sqeyuUm#{F=B5te{Uo*T`rvQrwE7%x0*6&!&ZR?gAWGTwWED~
z{{5F9Km*d{6FeqB?q6;H>CZcyRGmCeRVDbUHZen|4ozZC2EB$zFRDNTzF!|>VY1vr
zsAIZU)VMA18|GC3uAdIEBga*Hx5m`O%uKkjWhQd^(I4gb|G#E)|1yMB(*=ytnG7Js
zQK$z8C)j;GBSH0pWWbcxi+Yudgdc2^W5Gjm3x|A=MA2m`0pwg$|B9A&VbMrFOgRU5
z-d~~n?oGpq`Bo`pg+Z;|BW&|a;79(vfINx@up;++5Iy{+@R-r<4c3mh+@{#>C1=St
znAQ?4_53HizV14gr95mXhs~KnJpx`eYwc%HYir?3@v~P;XO(%SxO@*fNna4Al^KW<
ziVlu~pbOArbLStQ{Cks^KbwyH{~VkC6RvqYeRl`oFXq
zCQDC3tEsL=pbYml)(?=@nOOmhQbM;B^f4^-#=gg(2RYoe0%F3UeyD5FXm~uoFDdPi
zp2cAbj~k}+Khv58-D5=5P_fdlttHSOpvNr-!7ZGr@DuE-RmN5~rPXE@l|2ufosa~*
zFhmTG<^qQY`_XBXVWU`PU{C`09P44UPwm_TAg8zQD`L8K~!%6AY~prLjAGdmo@n(uYAJbbY*M(*EZD(PSGWF~3Kyd<(B#R+QX<{N`A2ulk9
zqP|OeGn@5|G~g
z+{JuKGRqcq#2XSxK6#ZlQ^N=?1IQfL{pG1qP*pt
zxZP8n?mB=5Tjz(lD0S|ma{XR@H~rr)HmzL2dR+(l$&Wm~+$l3&<9iwmBcC3WA5@z>
z9{=IaY>hAe)hK@#CY{PHHSA(S!v)?SA7TKP+
zINTEr&qmaIWW=a@<5)P;2?^hDL0%XKo7hb0iq9_^2KJk#ykHv-d1L%K3^7MA$L*ps
z*3}OV*H^*&ip04;uR!(N)
z#XWc;YDF-`gaQy){q^)sNl
zs3s?#T#eXfWt9FEH8ju17jqU6LSF4FYLpX;mZe*eAaampvMc#!gsLqp}4y
zqO`CW5*{v276%j~#l{|)Dz-|$IR?{yCMAN4QB!1STLa>4+e!k*%Pc^_<
zcqJ|&E@<#}w;G&?Z-;FO-?BKi6V~$QPJ(n1nSq`P)F|7jTZ+LXo!HIAQnblfvfbKr
zAf*6yoX5TfP|S*cbIbaF*&yJv3U9tozM!PS){=t`nT2-+;ae5HcMIR;tXx2D7Ydqr
zGDlvTJHQ)1iG1*7c8^^UI-}33c9C{b9_9OjLQtAO1j#5%jai~lr#?FuG(RzQa3Q;h
zYU+TI9&42lm0qsmNlR&C`GLa3O=zi4UF`HzCkLvYgOD+h)z3$z7~;R-GofFO_u`N|
zwjkqpV5&-Flw%LGQ;@f?U>o!3FD$DTc)P+zw1eq&NkX*&KiKX7z@%=$P`~G{5-$Yh
z%^^a4+ajR*ZPEfdJCnSR&UZvzW67vg4%6W1b0f9g2tEOvNI)9_q;~Rf*Po|{RGNdx
zuG*<)bK@m_6{75pG+jk*p0U(VP@G1)aI-ifm^eA!s{y8YC;FNIlLGEV7*mP}N-{5t
za21$0T3ioAysN~Fg`JAgB@b0f4{_>uH1LdK&qC9-sP?@1-wa2QISS_ajmFa~bat*9
zND46?j22qLUbHq@fof+l6n`GSDX=NW+At-I(e9K^%RP{_8-2XlG?0y!2
z)?m_Yca|5qK6A`af@ZaP>Bw0D>ROUj%;yuTb|wt@4Ppb6TXcGqqOpkvY07RN8DR0hs7pFavb4DS}6yI$2+IV5}e)z0#ioi(V-$+bZt&Bzj{g3U9J~yB{Zvj
z91YoajWRuzrU17=FAy4pH
zWE8r<1(L!cK)h
zf~w0zF21_s+pdDSmJHL(sZ>&?mXCWg28W?@KWfZ
zw!EUZ@ZNfR%6jrQA00~_nUOpPW128E*wn&cut$La*bP%Vg3*Wpfc4sh#x-sqvs2Kt
z`A91Gx<-M&N>>2yo9b6@M|vshA~C}BjDVF=^i;aVtXUtj3l7^St_43rTOw*L@qcvT
z{|eBx@xFS1s{9^$9#I`1ErW&=yCNW3MCW98O`FN$ycY*P2Ks?-MosGpwj?pSzu_Hf
zAY>2Vor<(1l`)}cJI&strV&m_)x14rw5!x1MBFfkhsGU^&$_KnAi#fvfx+YZQ8o^9
z-(fri{;qE$`Df!|MT<0lFjr-LqFT*&$Vhz@x5!C)LQ-TI)sypts5YmrK4?SX$1dn3
zL8g!M$ggq}h*Nl1(dYyT$-YDQUiDkx_8-X8WXtp6>{{Vdt6Fxc~TD
zaAuldt;Kiwa!RFRqhz`w!aNqoWk>*D@uw-})D#@UWB%tlnWvYJc!L_}(g*qtOwl79
zj43zTH+Q+S81ISDM*Cz0KeI@ssE+|`RTmTo32M#ap~y!>Lm4Y1-xj|UDL^TKG~{T)
zo9(|_=h?hxrxPKJjI5s3zbs>jFi8D53fFbt_qhan9vtn@Uy3xh51Lq#2@?m42UrlL
zJa5|sI4j&>YY5h~06&f=?ORMGZz8Sv8n~H?lmB`>oZO&6EJZeyXUU~Yl%H!`beNHI&Wk@4Iq)tLxF|k@)9VB~VwG^*tL{3I(kBaB5V>3Emhx9}4l1`1HX*c$&UpmpENV
zwaAo7bs-7@xpfKgYlX3ssXymW+(hk8=n=cBR&NmSQV7af3ReT2t!WU;xZ
z0jLtlEdcsPzi)RLcV9^v@xeag0C29QzDq)lW^l2*hEbKx3>+iR`^HV{Ci}v+^&adg
zGL6mpm4muOog?a?-7om4F;qXv(|S*z~KGD)#PYWXNmgGIXh<=
zObxvI+dcV_Q*Xl-O|;n86Z3+xSxsq$*(@k(cl`>$Vr^#(ik*t=$D64}*O+!V7wYC(Xh#7z!|5D^2RQX%&RoJCp-|Cb{>ysiqmo>WFy%
zZ>`-4uA$9_BiH3q2z0uN2&AFshaJhMlSXu~eVXknUr(|WzfdEEti$+pd
zURbM)v@{Q%b~!$>z;~Nk{rWqbr5ic)ecphRAZ
zT_6S081=Kk)WX!Tg&)@brz7eTMi!CDC+n?S72>=g9ZhzQ(u=+LfPJoabOSE^@Ss$7&$ku1@tZ
zFVOI-+`w%iT7u^FvGm{^^xIW7H-E!|tjOx^aS$UK1}Y3p=4w0j-idxJO9kjdx6QTS
zp63`EDw~uGGg()(sgXWk@~CT=X?;%E^mEK4kfFvgzeX8#T5k4Kjj^tcs{@5=4jy+Y
z4!7TsEI|o?d~7W2w?N@0ptdzLAI!+-2{62G5Hz}x!v8EE)fPDl)tFDS8bFcDdvAuG
zw`TC(K-L#T{oR~eH8I*sm8$%GsFaoX^#BoZ;r2tL4Ur{-93VN_UpIhj1DlB1dWE#f
zXJJIQ|9Lq{#}wopP0;bYlZ$IlG4DnH%|6Xf_ntoB#za4urS!GOgYZ)Qy!WnKCIvx;
zRY*2NQv5kh;JI=u@UIIy@DMV*ep%^9MB-~UA542rijx2arP>y67
zJnD?bR(F3sn5Gf9E0-No7s9hPHMS`P{=SMUJLYnQ;zLRYa5
zDqb!_22%eeko!6rLEM|@umF8G7LYQJshCc7Ur7m5I4*qgBa6^x9S6K0wlHBsnxSYY
zNNd(Bh{=-+wguV>E5^0x*hKmdl3pT_%ynENQ8C+61){kIB&FPJ&%GL3|EGQbYOD2(
zRiuCNSpjYdR7(cL-z+z2
z?5Sie{QBihs87tH^=8D(<}IK6vOI)K%Mgzpudop4eA=7`JspjiZPzr6Ztcp_p@g
z>h0q7Y(H0OC5JPsTeTrKBqmox4X{8_TeU&3iXX7|>Rc8dboXczo27yR$Ts1-$rI4~
zZ=XZ#X{@$3;T=Gyq
z#cMqGo|aTVGEvZF?iGEY1NV>^=9=7wgzv-n?fY*)Y~hifgou!5IhA7!>&CX8j7
z0B`tD!F>1@>oqVV*`=bjmQl%L{S@$k{R}7@m;#qx^;n3b)Toj7+hfgcOpnJYF%t}+
z_9C&9Ql0}oTg8GA>dRCc!nadrik_<+%^8sdmr>ILf4exstWi>TL@47Z{8X;IqmN{m
z2t=frNs8%1A!8^NZfy`xJ0PL*DW)n5z*qb}Ky8$Fz|VKMSFm|&&{0iu8`Nd?o%btw
z;-N+yftQj9S_h?2PC)OONygM6o-$-s^8`BK;$#{!^38Dba+<{eIWUOMl6G)W4uB@D
zDE8}|y4O5f@rU+w?<#JTE}i-${~ui@m;zui!0j6@4?x?3O1CguV8C>nsf0er`6R<40Tm~QzNBwh;l%)O8tad+W5!pD?_uB%iTcvD1a~0*;ev)@~-IH+$^3~f@1r*
zXj}(b0V;V?AQJ1G-y|C6AMpP5n2xJSy+f6HW*>(UC}w);
zO95_7PnfB>PK8Y(b(D{
z+=GowBgAwK$H*WE5}Yxn)CI}Opzw`tSthj>Aa>12F^M;~Gt2Ot={XhVW5b&P_>FQrv)&*#l3k2_c
zW$1y((2^jO@d
z*;#dXEvyXy`_5GXZ9nJ5Fbg8_yh^Vc?G-o)NcAdGq9VG+d2mZ&daWE^Xn&wOrx&?f
zwGu*AJ3_+kW&pTW!CyK7Jw5(P=#ux+Q}_qSrtO(j>pkp|OPQOL9tV((L|2G2h?wSh
zms;+-67FJma*S)R@sDo5l*{gKM_p*kq`uix!ngsBi5T1~x*knzXxirb1k^xqhgQv9Q;%^1H>Qrh&e
zgy`hwL3+*E9$d7|g^}w=r+|Eonus)0z6oN?I1K(V<59!I5lnoQ-^|v?pQOyK?uIMA
z95}9`wngg_jrB=vunX~#c`TG&LgdW;hUdW*)=G^>x?r6FzI2VuR4my6jut(c!?E1i
zKZbJyquJidVxyNu%>m)E)IMexj4N6$Z|L$rbjywUcxOZwvAYyX^S8?+!Rt-(y!+TO
zz&7~qYd9qT9Q?X0dx>xr;=CFFzd5FyAuJ6~eE$_nq)o$yY48hz=PGs+V-GvB%m1|}6gO5PUN6LT1&pl%=
z7RB&+R6|?i3rDwv9!^_ZET#TTvAL~cUGMp6!=n9kWE3$*{P!No`6yLUcw}q8+3PCZ
z@O!IPM$!&0AUt!VHspzZLpEIf+X8iG_V_iuThkp!w-Nq8j4=*NIz-72VQqmOxcA=N
z05xG3YW3Qc1{3ncZ`_IN_EWZk2EFep_xy`%6W7caVVZ7YOdr`}!je1}2iWjxLgWS`
z#;Yhqu;C4B^Cuf<Q~;!5$Zn
z%wC6^D}PooNjnuIeB|-f$Oqzsld+f{ji3RvBj?G9hxxE8iekg^pmd=i`m4qQ^G`I1Bu##b}9aIRR1&8
z#q>|b5iY-R=gU>WIp#-1%Ed1hj~`&9;i8EcI$PtRnZlcFStxa??_Bg8a2jj5JN*Ls
z@;w*@A0$p;hcC^F4_#>i1AeH-gxMgHrqu7%#fM5>s08-WF=!nFT+x&vGt0;z
z{;XVb4iBzw@j{FmvyY!_;8qw6^G5^IIK3T4&SZKg+KY&=hV^lYi-aeKPgV$8jUV29
zW{4V!*vjHIXJ1Ow`q16Svon{P8oh?N`Z}vwvwBueuh$W4b+6`zQ;#M8yo~xY_$pUx;e&m+2rY?j#s7LakSxpERYJO{p-xuJXGUw
zf%inmX-SVchd&oa5&k(*^Gl7~qJf6dvkdpl*p&0o(C!X_)u{pz+P-Kh{0XE8hcgF)}@tYRPI&Csgw{yjXS*uy%k=0y0a
z;4QE287UEaBFgfGJF~qtKjb@N7V*-5X&!z|Au-?#GR>i3YD`0(nM#qzp-|4
zVSdzy-B$k{F#-_hjSk`yiC(x>uOId}wROrfac!_cOtxz&MvfdVi0j!CwWU7Y?-016
zU&4{*6J@iTO+5Kh7uZ1vjtUZG92GjDLpVEll!*xQ5pxAs(C%cbgH(n#mT)1ZR(w3p
ziv)R3tkfu;P?_zaA@`MaP{_pM`R~orI}8lkASpT_UT1y;b)XmW-qEx?kyxFE$ik|N
zqSmoD2VRFQ!D8b(zebsg#KS`!tJOx9c8yWl=I{uu$YYPM1p9C@<=E&H!I_OyT{Bw{
zFwMizbfV_wj${Mp%XO_H7@WxXP#39+)9BX2H^+%9^8PCMVl4h+Xo}?kqbU{|UqCB4
zB|wnUTaKaRRP*j$P#MHB7kx7<@L{>qpcIrD1dL<5r`VsabBnxPXrPdlLbC`O@&bm&
z1x!)F_cpKYYq8n3ML;K$8~%5%8@NR`2ID%BI%R5DSEAsRL}!(KMoW{t-lB-rzqInS
zrhrR*k=peC4eb&*Rj7`!h5))p>=X_hFsAL#!=#q&CA7&y&Tx(~sQPeUsdLJJg5@`R
zm_7AqvWCY%BFvB#NaOZg7
z*|#!S(t1Z3aH}!zLQ^6!8CCOg`sMbw`xQ`mB!a*C-3;GMk7_^x%`xXLL}1#7*%3
z3jxdSE<<7aX
zXiNvV1{hFMa=|Z93gppNHT4@;j`)J(H?aIgn(OCC{mTV={&m(gX_q^(x09C_E(2tY
zlt$@%>JbJWB!JHsLRt0v9$9WacPkteKd{2f@5q6
zapt*H
znGMdZ+j?&Rt|f}PQJoak$B=*fdguEZ5x;|H`4#~f&+TgZmmBdenz|V=9g9CE2sWAJ
zGUjQEW;j4WiN%dJM3D9|%2t$x$Z*S|BLbQjh6ZQLL^>Xx#k6XRQKj~M4*wtkxJ43L
zWkw_Gi@iW|)SDx`ff6yhtk+9+P
zUR4t}%0cb3E6fR-7$lRSwqpTlq-ne0elk9OmX5i@csldZoe>}oCEj?ktFINw&kK(^
z-Q^=dqotpo5ui5FQD-j6ktCvqF0z$npdty$nmigaOdJ|zWOu+YlOWqqb-qAAFy_m<
zAMkE9uNTIo=He}`e8-)?x|BEJD-z_I<;C|*M72nW7rvIeXxEAvj_eCVJ<*#~0@M~K
zmK7j*o85UYc8Coc=Mrg9S1JWHCO$p_i&Wd987dvuaTj+IQ++~|?O<35bKn|cwzvaT
znxCVsUQa)}vO|d!|-BU3N8kJ4ZUmyr71<10N#Rp
z7Sy1}M8^!yYj9<2lQYZ6!+=ej3_?%ybzP53h8nuEJlJ{zjU(!L&n*sRCR?(`kW=q5
z5b_-B-P*+yttg)?oEG4bo|m5Ei&q9Z$18r@$>`t-7Ic!Uf_gn7O=$Xmg3!kTgq9y*
z?+^Imn!fsSz!c5R_fBBTIFFwuFv@9tx*uhb(X_Slz~iQ#QcS>TJ(GB
zQu^mg_{L+HR6{z_QoJQ;w+iR`Updqv&@z?v*wlnc{AhGcP;5}Jdqp`9
zSOsfxMPfB6f->Hu1z)Fd)YiXG3f%QDK7-jXo}#QASMb#xo*`qCv^5x&`al?OCru~S
z;tFg%C(^&esuq~~P3|A$N-z*-#?Wn|{?>%M&5rk54^)Xr0pARikKljCG8#@$cAs6C
z1yyAkMQXXLabnqZX=w8KOVZIO9kDAf2iE!WHKt%^L4rnbUyoA*9b>)1Mp;<`=pq=D
zM6`+0>jsEVA`dO7v8|*sOlUf@D*XreBT+XWhe-r2brP;b(EPPcPj~rnML1~k)ecDA
zeseJ0ZFO)b4z!SI1wD=8kwzV!m4UvuqFZrj+{@xBDY#WRoCJw&2TBj=FCKj+61jVF
zrVOeflUgrz9eSKalq86zZ_$Jj^oyDdWUXWrSM%H51^9HQeqzg;YExOeCa>X6B;>2G
zRvZ?)Za^F&=woQu6EQ*=D;VG`he8Y4w4mtn6R-PpUHUN~)p-Ufz^~->=ccZ`2{suU
zFYPw*ddd^sLJTR=%fk$LUW%nniIjCxqw;qc0(?@ryC@^wsXhB{Vzw7+O^9+z@Ak>Q
zk%^`A?g*LC{D`K^_tyySTi*%5i|S9X{*!5E`0}gWxJR_Jw?h`@MT6quN8B?uwsI{8
zZFVAo%>=uusI{U==7%5?lDEctahO$~VzI&b^J{Rsv6%3UgXdP;<+TolSNDp-%dPGu
z_onio3$5NrF8_=Zuqq$Te{$nVTJUH{d1y2IKE5>cacQ;)(KKW=z+nYFOtbEqZ}Zc>
z0d;$2NhLzWZRE(e@J4r+!^8|&lIo+vrIS};iGtWm4{R;;cw?icJ;YsPQUmY@FYRe|
zHEflu79$d9*f($tT)LtdhaE_ac-ZE&ES$90~STSeyfgP;l=gvv|
z4LA$A9{v}0CL-)72>MqGuxF!1J=P~pPP0hWam&Y|Y!h_?9>RD|p(oXy*_9UQCEZC~
zdbS9^SPGqnIUqR;^?lPFdt}s?kYH68kbQ{Z-Ch-0O=Pf|d}?XqoV%(V9)sq70p@
zEj40#^$D^NdoOMVZJ+mWbh}#1#qUXx-K^DS#COY;Aw9^Fz;k_A+EA`eDf1FG{&oyl
zX!-@o>CBBaGo(MPZh)h%`*9!uU4#CIT*?RD&0RF0rUV#Etg$)2z_GXd;B-S$V;kM8
z+q62XB+>r&?Muw0RBfGI<(;n~*LJf##!0M`J8C-FEo*5}AUtgN*RHI4#R@)Fv*TjtR6g4?7(!w8G}6iNa!T9kI3vG9qT{CN
zycJoySW+3-t5NC));~&l5!OVwGk7hHwh~3I_}*o?-96A#FNcRN6eQ&L`20~HIc`Cz
z(xEm4CFc7ek9v+&+ZXbSta!yl-zwS<0;S%(U+7gKuDUHvFh>0Ju~siV!6eDff^0qu
zi0q$^X?4nCUh_QW3^ek83+VegV`P%k8o{`!2_HbH7Zi~j
zG$;`@{cfC+Xbr;Ny~*t0n!~SM|2tQg0$|C|<-^dD<3jbrtn;JYMh&o%V2z3L+A099
z0Q%B|!Il&`k**=@KCvZg(WXjWEh9u4g!HEcSpS^joSQi4sGp<~XjNC$yLIw)fTIl(
z`}9fd<_V2FjG&=rDU$ecmFJ7L8_T_v*C_BsY8D+ZUA9_3iw_?R)Qugd?~VtK8=$i<
zD4TYd7^z5ta1erpAYk_ICZ%Qs&aFTgL`M%DC5EZ*86D2@^U8270=U<0tiZ84E4W$u
z@C=|;NvOMyu(4N|C|^pgS8Y?~NC2v&Yf=|zIF(it
zgA7^P@g(i59eL~u7}yxn6vq8!Sfrk}wAG*e7~*D#RIKo|c2bpKpLGne`MmggVwghn
zPzVG)y(PAaeie-w)?U*~m>fl=diQr%&TRqt9aT=8zz4q#4vHfY<}>!&ihb)Ruz54D)3KT
zU}o2IYOXyJT-!C}xu1m>R{Ry^+MYrHa%)ZjXyr7IN<8tu@}WGC7}lWfKNgB~n1Rv<
zYsGvwhdM~nBPtAyT1qwym<(4sHXim2Rz#@_za^uKBc8^*Ip9r^r$h0cciQ2%TO(q$
ziGeaa&=M|@hD;~7LMyHTm@sK&HWT+-D^F}W)6&MDualUG$gJ$fbLh}zYFjFsnehp;?
z`2a&h$}=G_Seq$`rShfVsv*U9DeH$)0$xTv7!hk^eTPjRBMk>kr$@
zg%%ON+xyR4rf`iW
zP5a+?gzJ_GSJ1?f65?HrrggqvaE++16>>r`8GvnU1tm&0w$$WAKt
z_#dw0{KQ{#I}B8x|8HGD5G=U-C>%zDi_%Ks9^z0^ID
z2MjCKGxz-WkggKD(;fe8wRQ27>Xy9*yqbTXzw}7dh!>3xtNp12$_O(R#g8H>z
zGCL(%nN_hQHmii0-}dne$dBp~3GhQ#Jyj+E4gASwa3ooxXR8@asnxuz*^E2G_L!tJ
zWagXuw1Sf5)A`T|z&eC|QZ##`>4k`slo@gKzruLp)WU3y7(W~M*`zQ2aU?g*l`>P$8+=({5lbT}X!ox>YtVtc%H
z_d1$ys6ZV0wd(kg>y)dF)jSE)vgh6$1#~3}w;etUBor}#(yMvFJ++~%xBKKpQTLmo
z7z-N3Jum#(s;a(s8?#GLoJX^06@r#0W_RFF#+=qW)#c33RR*)o+>e)hPTHX)05Q}4
z2IhWYiYJAtWwOt)DJG{#mbH!_FX&!JQyulLl*jGswzv=SfYm-eoO}B27|Il!#OCVD
z>1dLTiZX#i6;e(Sskb7>69%cy@GH%>ga;0(9h5uG-Td=iGQkPIXjg{tfBNVnAK6j3
zY6w`&KcO65-}MxN#E%ua%*ThA5WQ*xglHd_omQ??!6Z*UxAvGwIZTZv
zt(YTkWaEof_X@hs&T7&DdrSC)XZMHoh{A%qs%3EmY`t6&Q3OHw{GXSf_Z>xI5};t?
z{t1)=YFi=FUS?98x*hJNmQ{v`15*PiFumXDl1aYS38_a^ieMmxKtUmW^F5!Z1Zc{Y
zNRj`42zj7|*f=U~fFZ|^3$ltawYs!=Dxu3wm$)+)jFn2_Y|03Zp@t-k)fGI)+TBAk
zAcx3Kq?4VkYV?*8a={;Qqoo);;ps24Uq=YXQf9B^aXJznEx2%pb$(od75u}nMEZdh
zV1~AjUOp5$=?rI1l!5KX2nGiMsPU)d`58RbkobH`k`J7!h-JWVX8C7I!K1z_u<4W&l{6=VF<8#-k;lJS-9nmAi$$c(bU?=A#|=N2c4aD1u&F
zIseg532>JVM|c6k2bOYNt%a+r#_g_m*p{7x1SaT3HLVAutQ|oLcMX}ww`_&_2
z?Pq1MlqZLufViqo*fK7>1{#bzeUX$mbZiHL`L|?f=~1B1x+QifNA!lS>Q%}SMI3?+
zvw}=Wh&dt&!c7BsDpdA-Br*(opiG`D;4##6Yk}JmrsLF#2io0sWzgpBKlp%T2uifb
z{D$sYPF-v(22;U)f}WRkE&H}0fBAq*2~jhVC>HHX$semHGX#I%V@kK{q1Q~5Jm2ZC
zG^QxGd~nz1G?8Da6hfiW9<~@vP16FMs44ko&dcy8O+o94;5dn93sugQ40Z0imu_`P
zQUJnA0gN$}Sn3)Pn>=>xl<;gmzp<_MeLiAEJqZN0zxcdl{*JO_EmV>R%*wc1)Nrlo
zO53{0*Ju26v4W=4B5C!GsAJiU<_Lbb|7KA;i?llW>)@F!lr4XeVuz|;2V}ZJ^LiI@
zu)_jm3*Q7-an^dIBCd$SNO!k6MOKQnjL}N?PcXR^0SUW(@P5^TOf5c8p4^*PX}RdC
zfEw#lyR0BlsvzNyNWwqcaEP<`+>5bVI{=LGsw4yJ?{J0Hj5e@h;K(InggPMvNPw?=
zbx(t3Dbz{ex07Edos
zt@(Yb&87E=%XdmHWNoA2yAI{EIu-D@s!ikTi##Fq>Vhlm-`#+rF69)i%%AA@Vb
z-=Tui>Sz;s0wPr(fX~l^!3Vh*abQ4ZkZ*2l_zlt0g*;if+kuPDHZ-M01T{BQgvaZD
zSVeJAl0SGPuOi}yiJqoGK<^IU7=3+|I<4Wt)v`NhQ6pjMqejAvnk@AetAEvas_O6i
zNCBT&gP22p=7{JU>EZxKZy-+RQII{kGlf;k`SEinMnievJvu{YQ;HqVIrSFIk1)oR
zN-Z#*6!%%|wgFgE!tb}lv5HO{Tl1|XmhcLFt-e2I3Wl)ueK*d0FUjYJnbG;IsQ>P*
zWe9>hpswPhK})W-^z|h*2%a1d-6*oL2mJdjy7`w+M3NN$R4#r26PBXmpMN|(acl^!pZt{z
z_VmftW~7XqR{BS6;)jF1h}36F8%fM{HT^}6kY2ciqQ&Q=lT$wlzgYoi3sO3Atca+Jr!_%b~XWF<<
zp{m7$yJa5VCAcLh;A2dBupMF8+^D^?8-agj>=KIh1HAZZQl<71?c@!WpwlE^@{#D7
zz%;>;y3X_#=ziJx4*Rvo0VqY&Tcy~R^Xw^X4$A1IKH75Q$ho8X5X`cr|3Pv9?+)JU
zE@!Laed!^Bdb1sNC^BB5eQ8t$Ma*QV-h&LsE^F%mzZLw+yDcdjj<8(>hl$QK}VqM`SHQ0>7Vy-64~6
z!WT(DS7q*Co=-Bc7s8lPKg7aL+FQIj)Cj^w!>gq6>7z_Uu(y+d^5JW&+JE{ge1Qxp
z0@i!1k4;bSl0Zg}Q}?Z~7P
zs;Yocd73^*DiTmuUWnWTZZU(oA^iDuXEA@X=&_mEZ)qZx)2FoB>4z7SW>3mQ
zx^=8Cf)sskfx2ko*@2WlU1}Z^dd{IXjaMwmG>-AuD%&(KXE1p)>7$2*xqk!+$O=NZ
zmOD@?R48?68S9~9RIem58jj2<4X3GlijYZsvh0Zlj^%Fso?dXi9^9;#F9TE7LguLt
z$S>5UDzqIwm?X>+1AEc1wqd>>VQFWkXp+Qem?~|Qs`GuQ$kjL!8qE>s&+R={V=`t0
zLh&ZSXAE@Hk}J*oR0B?Mmtu0nir+1-8sykwb(jxk8SW*8gjyITH>2@Mg5OdRX9aZP
ztCO+)p&N%p(&iXdU&XOm<;>8wT!?60#vd=@?k=<#T6dHsw-k7H-ssXA&bex-$2zKV
zWC?MY!k?P({*V*HnT-Q-+Z@xcoq?s`3hciDM8Q~Hm$bw_28NWAL;ihMOgRW{-n`K8
z-v8HbmYbK>X0p*Q=bY@q36#CEt0Y8;6E-U}N_?(7r94#QE?fh5=3ecj*DI~e)Q(iz
z2$wz&GfM^p&5`=o2`2%yL_{M&nJz%St8j@KtN
z$M*)S;>dy2iKBsQ#uU{ANo*^a<8IZ01m-{>L7+z&
zt7|WUtCUnw!taFRJX3{)f+vMGVwL-&U$EJ^CJJCPCXX><@;xi_edXpRtq3UpT)blw&?RYVTVgk$<#B9MUIjLP)Qk`bSo9!
zj=q;aY-pbqdA`OsQzS|>wu~{g_Is5O5BdPwogh6E6gwv3t*qmAwXNcwQAd8fmgX4W
zD_RaS(>s&fUYfraLFFpQZ^jJB{e7fzlw4%^yZa0IOj;A@L|G4Ys@J)~gyb}9?kaE{
z6by_&Atfb@KN&g(LinOo!Dq>CS;mlS(<}NbD>1g*Oc&@U;V`EF>8s5MgK7k=0|fw{
z4pwU_ZP~zjfU#I{kNYsF5u45*Uoq*igx}!E3Z)-jN)as(1wB!~l3l8Z8NFc|4&Bgd
z3vcuYX6*X194%K_OaHmaP_VWljB7Y+LaSv72Y1^|Uu@|zD)49Eoull5SD{Oi8*@TL
znLn$?_fszN>!LKk`@Cq1$YGH8JHUAoIC1oc&|da+0g{Mhf&uRFhom2i8#P#au^C2*
z$x2pFK7$O!O^AvzU{+Sf(hQQedM+Wn*PI(=86!>=?#T!c82>(n7&}X~^I%Z$bb`9F
zynPOYDiD39pS&8T8hG1G>%$&-_g;ia#U3FYza}ZG%0|(~ReiV+ckuV637pY%EO)eb+xenEIq-
zg^YOa3=Z70A0AHI2RcT#f4@82xPS5K$17)GA&8RQ6KTel#Y^IWXjyR3Ag{jW!EZcG
zi}sk@Ez}7&P0xa8k>PvEr4LypN+CvhSiD4T#`V#}S5XwamnF!d0nrD3KW2K}>qVU^
z{WV|CbZaM+s6{ee6w1$$1RN`JYn>BR*sz{BrR3;a0SvYG>$AQGd<~7L9Vz&M=Mj;v
zFz{lGKX5ff>UHY`&hw?eheB|Un@n-Z{o^)?t-<9A%R5_Ga*QNo!v(o>a8gX}|`GV5kRI~D4n(^MTALH4piXzwv^;jYk|ZSJ}r3A-h(r$vvM
zhi<0_l!fM~O>V}Lx3Ox~l4fZhkCk0xJN*=*%s~d>k`-x7f6ba52GQR_`RLiMZgcRO
zrA~;=+e*G?Qq9h4NLTSi`DO%|y?d6UZ^0Pia>~NgRLq6ja0)ZL)UdmyMcPJo0?nhK
zxEPFD0HCMrjmow;^fW$E!XIT3b16c>a`_U#T8T@G6jgF69*6xltW8&v7M~GBZqRH8
z-~jqfCl2cfNm$m`_YJpo)TwUkO6)Xa#7RVZcV#Bx_zUZUw`w;kcVSE0HEQJ&`luv#
z1H-}p3mv9kVRl@sDUM!`w7M1AOJIiU3I#g!Kxa@>cd%7H<~bIP1u$doQsWLrh&0@4e
z1DkwXh?hz~j+$P{JPkT@`I%7iFQJ_hT<=Do9&jscgJ;8e+r?-OP63kec7w3IUzY;I
zKF3m;ANyvydzsvZqBGQMi$h!zwG0DH=!j&=vDi=n1{+IDP**9z`ZY1!w58%9V$;n;
zpjt^H4rXQ|iYFP;3OOIow><6$q5pYT3LLOy1<~Hba4b!9kIa;|AjN
zd4B@X8_j->!ZhoZ;p~8U_1PhJ)Y~Tu+Qx${;cSHnCZZv9az
zo4Ybs2~jweiynLT)c@#vqFw$BtJdf?NHk$Nn_FpPKaXw3IVz@iEx78XB~AYUWNaR_
zOE7)Xj(hi(iQgr?I86bQ7f`F6UKRvwvIm!6e>*R+OzLrwhZj40hdwFC>uCnYA$dd$
ztQL4}yjWv0t^LW+TjR8++zzUag5-6>$A>EEZVllrCIzBejcO)23+^d9sqFzG5|OG#
zRB`5D4t0ORAH}k2i8TX-sR?Jmjnq45P$b}R$Se1Haon3D6R|5@D^|_2>ze_FQmZi)
z#~k@45AZ`R98LNE3MNm`2gszE6mFrT<5wc7M36U(1diSD_Mg_P)oPb-!!eMn>|AI!q=3p`ixYCXA&a~VSz
zf2^uX$!3viq-=C&};`hSAcVblgLCBVi>RBg!^Iv&X#8N|{np)69@
z-5fHHOL>!**r;P$M4#R+qvxh8JyTgSo6e-Uy`lcLY1p+*G;vAQx+;EAm+r+$>!Jn#
zn-WC4G6Mf?x%VqxXV5K6u^twDL{?QBYU8Q)&&%`xRk&?%nM7FklwsB~Wsr4b;YI7^
zF8s5`cH+5uj&Huf%5j+6ih8CKLTOeV6c{jRn4-Frp|02JYrLgbTI`2Hy
zP75vim%B;6_*F30Db&+@c5!1CjUFw3wE&CkpVM#Gr%8=N?2py@%PKh
z1_;b`4DO7{;0oT4oMKf4OVHKGhJ#%-(6QAwNB4(OXftyKa0tE?zD0l^cXnAq+}`BE
z8*RSAx&iRH-qignRBBc=?W1NC32gcK7wF!b*@g$TbGfbr`WJc|DW|s_NQP{H5~eq%
zRIJqjORJcECY{5AR0PE$KAWa0X=o}!W&lUL;*yM(R!YAc>5Gg%H!LoKSdTLG#Kt{+
zKzu;zb+{cgRJMKl)$k=qmR4`wh*@fbSZJh21pbpIVEC|evS?$fv11%AM$d1n0+p%A
zX>YFW^p_8hmX7ZkDX3=dgxuK6@Bk8SH%#2IX#-k_DpFAj!LQ(<*U)9+Yi8XGe`0Nn
z=+A!(F3aU?_|B?vYzXIOxtEOP_TQ;|ylG5@UytB}u`IrssbQ9!`W(g82o~(O$ccOFut8JPlDatiIftLg9vJp
z8UhZc#s-gh?GYCCsxD9cp3?EKkU+^8l({8HKNA+(^j3rqlSxL>G34f1R{75Y+388z
zGP7QSd7k+oHv|*6`e_b_kN(<|#tLTr81$Wa>$-6~2+BaeEl6o5CDdq{nM2Jc3ACn(
zbRgzm!08$HJ56mp3}DaaU%OpGBUJ#rHbZ=5hb{p-jEXy@+8uKhh?bDAbm7{f7RCK*
z+$c_5!?;4-PGH>gZt3vGPm?96&mMtn!4t-_;d!ud*(8%q(iMsN#tGL}y4AHYc_~6(
zwP2E5osc9~ra*)hSsCz74J0}Si8?g&9cHcK9tzQKB{-34zCY5Zqza1Dm_K2A>7{=F
zSn_fJ*f~Li3bQbASxv8Sx)xdx`(QG1!q5hfcc!f7=RU3wQFtpu9toAA>`6bUBd9iN
zJn7f&(KBOcY`sShU|Crn>Hhv{0&Ne(1Ocah$=mcmDwgHNElAZlA1r=!oB3^`@-AluJA0N|TBH
z&+{}`{j?-`z*aQ`e*|X-r$bR*LIW>op_Fht7eH@%4v7(=L-~?xJ9d=vT2A!pWquWM
zYIV{rDMG2g&ida(s_I=lvedIQ|Nq=+$(Y3U;sf=!!!k#pI+MLjbc;ruj4a8e{90
zXGLR<^!ZZ?QfflphR5hYmy{|=_{jn${dzUtCeeXf~A-!3MpC#?#x)SHA
zZZy|NsE!KV$ghBi`YenI)V-r8ymH(1ztC22he?UAl-R-;9%6k^nF7ts3M-|F@C2!-
z;5%y!i`bi0E-^Vzqjrx8NhlNJK!aoIbKX8pD>39LpWz{DuN3!srL
zQ@DWI$T?L|%g`T>0_ZZ&6-8TQo~2_~tc85JPlG%OhO&sdNm4i&`xMOl3WRG|L)4Ys
z8m~)R{T$iIMjwaTrQ_H|%iVmj1Sia9g1i39qPGkb{#nxSzNODzQ1!yOiaTeSXfjQ8
zS(wqQH|*O&bR?PSZ@=*VD3f$AzHpF6!lDC|YGsMLmRTw7JCW@^obOlEOu>89P5Z;DQ*|-bX7jBVhgukDm4*^HJYEJI>|=t_nBIiSiaaFS?+(6n!I0jPssm8
zib$zc!q(+|ToOVb~B;d`zs&G^ET{bM_^djo_^W~Zsf
z&aUxncpv|W-$H*aTr$a{+NMzcYS7Q)b8S&kgqTelMGpZL&1kkVH{xUSlBTI+gdUep
zEO)?mxQOxspd<>xd!kLE>iH*?tsc-TgW`DEOKx>%lJLKpZp5LFOrza`*G~Hk->Z6I
z|E=cr*i&8)R%MP<)#yT^0AF>`C78X^hAD$$RdDGk1~~yX=g;p9CrzyHJ8MABCC!?F
zKsot*`4~)-1`(8CzLyQ`eu9Wb$LC`~Xm-2GXBo*O|1G6i4k#N6|Fqz_%tQC~yNqAu
z3OSB`=Dp#@QiTfF?uT0!u66jkUPgze&2cJQ(7V7>YjZ_^BPMNpy^=!{M78z_0yaKQC&8DX_h_vhXJ;+Jgt??x2!2u;8{z2&?Iaj5v+JrAbsnY
zqh)v@Q!%Y#tb_y2!|C~+G_;LnLK^q)E=)~-*HyHxxC?S;(KS)#DstPC?`&~Jk-NbX
z(Qg|IB8;T7)oxo{xBhc!<;t38LS0(0R
z$=mjRNCJoAj55b=Jp)I0z>Y)&>Fyl;*++|8S~vIAMA$yJ-xSyZjzSR%@Fh<&a8!(F
zb+b4=Qku|4S>O=%qxFmx8mE$t-k((G=QN_WlLTtBtoINa%LNkA4LtZK$w?QDMB%c3
zAeU4h*E9G2S%Uxp`wCVx-j!Yw8F!OCg<%HlUA3_u?yD*(^_D}30ZdF7@~UuxPX!wh
z+S=)v!c9oy-YXbVE@Pzg;1iPDG~*(=L%tM^#EjDI7$gd$zQ{z6+vosY`D(+Ka~~l8
zAqmy%yPBXPQM|pQ!zRS`PMvx#3kOuANK3E-V`BV6!4%#U8`Vt-jtMN7>)>iGKbp$l8CU^ROcf_R}()mB;Y9EUw}o@%opl3temRe1~=(w^H!swp#^V!UTkOpBgE_*y
zXG3WrJ~EN~K0M|P2~b$fc2Qe==zta#T2Z*|Jua~;Sx+64x-q0qociJ-Y!pWX9(Tgl
zbf!51lUCvrTo7vyt~%zK4;Lg$KdgW{R=NIr(ilH%ANeC%iVmo`I>tf}eML3U
z*a|aM{emi4W7XgID)=8w!m|5!=|m?>xO#WkET_g2C>~6QHyp54>a%hHl(LEOBUlah
z;WN@~1{2bfVJCO8ro`lq)X0Jwd`V_hg!*2D}4ELmaO;1uOx##X?cX8wxiSq^E-j(Bg*Pm8(s!;xeN7T
zTE!}alaESbEkN5WTSn|-ij@N5LytGdW!ud%>RYGwW4W4_iXaERPzmPk=552@QKp=I
zP45|ZJzk(B#6Uju>}alSjSGo(Oi|Y}|2kmA)P@245uNCnsML=HoyJcf>dPGR!G+*<
zcx>VInAkh+B35#&c=%+dWh$=}OX-l7T1Dt=`6tW|AeAV0_rQ-?yqwm6gDKl(hNfOC
z{}cjg%TjnV_z^v)5F6Uf{UkirFsEwVvgJ5DQaMT>Jn0^h2eoefN95W
zKn>T^7|T=~Dr7(cS2JfmLc6HCtR+GK0bs^B_Q{dZrmBv&;i=-lDik?J&+vC+ACusx
z_Q|gN+_Uv$0IuT)FEeGy@cR7t(FhfqB5AqDQJ;>YvQxXxM~P$I5aky?#$};X(ck<0
ze5R8ys^>cC%-cu-+RT3igX%6|qFU#~Gcq#g=Y#E>W4rQEHH^vWPBBlnB(7Az9;t=nCgA8n
z4M2PfWM-a|ad|#5+^sqF@SOf%LksbdM3Zv>1JZ`{DW2am#-!8#PHn=nx#0tc^y7
zBId1F-sR(;%Ch+LFQx$yzOHo#vTZ;JcRXqZehWsE)HTPnEHq`gydPqASS;M*1exR-
zh5)Chc%4+k67HrAT`#3wS~xD6IHp2*KkkXEbQ+AVO=igv$4Or;Qv?6qG&``t6Q;@w
zYuT~Oe@O(ZSo&8sAQ*TXZS^X$@Z;Tkp1^M)VF=>J@zj&?8`FpBncB`_zT-erfEej+
z+Zww-|1O{XrDj}no6-FPtj|TtliM((*LBbyG@D7y?dQW5vw|`LP2Ku`fr%Ih$^b00
zAb%5xaEw2AzQfT)al4G>Lu`otUejH1xg~M@*>CS5MO}8x-&cpYz4SeSRV;iy5BR2q
z(w+rnRp*dCCA97F!Nq!52|LfKT;?s>(M16RAoN1s*v**Lw*c}+8yeh|T;r=71C^$)
z3KN)+#jZE|FK03yw0x8`QGjRxv4eDNN8C;SoY-ZVcKZmlw@{}mF~^qIittELd_%_f
z*lKSpylcsrcP=|yB;5j?XOVN8ZsMMA*ENv{MPwJNWp>{aOHO!;*XARE{IYiU*qsf<$qoaAXg^uXc
z`5dW=OPSMPy}l(XA!Q~$7xJ&crzA!>NpFFUHM)rWVD_n4g?^X(X+ewla{Mj1q@)K8$ag!I|=j`gY^y2Z~{vkre`@UPfa8OQH
zoi0K>K6ngLKd`aRcD?g=0&PovBH?wFQ!cXPGNj_%9`p7FZvGuuCR6K?4*nXuJ~ew7
zq7bROg_DMDBp-_6Pug|`x-H^#8QDLX1G@I(^?deZj+T{|{{m+g(Yq_`aLzeyovyc&
zAt=i}0a=;*65l&mRRR>ddsB)mL`QB!|D)=E*B}Qy1_HG=yTm?P^Tdh^6xFqe3jfk=
zaLm2mG?^rHg>Su
zSZXk6`Z8Unt<v|NJOOF!))D7Rnq}34<&T_
zMMrAWpd}~Djr$1tS4ucN7tH8T#W?lI8@Rj`Hm#;j8y(*GLW?jcb|GrM&@)FIQis)6
z`H^{x7xrkm=N=zEtd;tC#8?z{WOgTkv;Oc%mh5UQK+=^XVAa?NQ#ppc4?dx@pD+(3ovQJO=?4be5_dszBTJ3CM)$71xJCZ27sk^l5
zy<`r9{Sk7Hh7CLP5ct@!X1J^%z!)e|97S9bQH0T@{^!hpJUry}s`wYeG+pT*eDB`2p|ErMI%CVJ4~
zbXNOMC^AAqhV5jxYwclJew^?Ty@HciN#q$rXtqb6LQ0L8=uYPhen*KI;Bm|e79brU
zS~=ASdrWpC3;cSc+2C8G^HtPKRe#jllA}7dBzM(t?Mgwxv7xVUQpEA*9f
z6|9xlRpS<_2&UcQ6Z^~)%uYJOOzX+UcGox?c(db*9_3ZW9WyZLbXB36MJI+cPtZyj
z(titm^;#wSpw9(A&fn*Tj)qH+pcA%1bXyWX=pZ%tA&g!LMQ6t;ze4S}bn&yEP={ZU
zeNJ(=q$|tYgg_?XT(~x>6??5gy`P6?kak8t8Q_==&m=5-6P*edOVq_Kh@3KuD?U^8
z0`#sen{^pTc3$->Ogw6PjASk7eu+5;nM4PPM3F)t=jRL{hJl9y1p?mhx42jZ0gHG%
zK^Uff{V7|>R$9F(+H~&t-rQnu(;)u-jyH`CY;`NaVWsF2$E&0D>`o3;7og`PZN**|
zqyc$+hLj3rW;sOz>wu4Xm5e2KF@)qA5lajQpL2E*f7Fq?zIJJiMltcZeh99A0yG;>
z7N*f%x|l=YOtTNNQKIZ(gQy+Gb6i>emEBVk_pMR*0p)sLRO2dt8wurdSi=?(w)4FV
zMoE}+z>4$nE%J;$Fanjr0g!948nKywNF?+Vo~qnfOu5>H3}?TzmXxIQc6SPQ72Br~
z=z9`UuPn_)%N@jB!lehB2SB%l5=;E|O1W0*r>b!#klDfI`_sl1l#Lf!a;xA?`@gms
zeX5OHUdhr-#ktiV{`bv{gr*5t?RU42{VvOSW30(WkH3yM14s=}Z&2BA0*n)J*koH_
zm4&Gf>810zW11hINn|-ZD(;6RN3oPeZ7H^0^2Xo4gl$ra7fU}BQo?)AbW)IKYsuU-
zLrOO~@yQBuX;iH_2>Y*qZe=M(5=DpxS{USC4_AbKcmxl1R8Yv{k#Xpr)tXXZjQ!6&
z9t#J%Kh*8Fnd0Rcf4?J4^5*H
zP^%e&c8%tcB{UiOW9%`Tau(ySZlj*=U^K&xDRw^1iB%>}^bhFXU{R`=S$t?pTw}em
zH&a(rFWvtY-|4my?7@0&QA`ffz$DLrqMTYEy?T8|T*B}u8XzLBCsy+>SoHZ?#9X6p
zc%%I2U8R-0(S|R66W$p9a7*cMT7Ukgz
z;d_%u=Ob2tR7ShVJ51Q>g`OTmf*M9=VyR~TN~s^D%@p#1TfoVzf{Dj=zLO+k0(lo4
z9hQEs0l7foizP{Ym&cQ6YhP#;2ymEI9a)aTn;R_^=ynMApr7en5;nqRQ};7gUir~y
zAbN-m_Ob_S|87*RVaF$+
z`A`!;R~UNfz3I6~sc!`(CP^+ji!pI#h<#u9bJ0x;&L%vZ;V^n@7CN1zY>cj^8JtE|
zyDH{fds6;KkF;YxC9VCFMKfmgDVzPxWw?yhzb$3OxUBHXZW)Q@XY>rM*`^QkATcaU
z%_YwQl`zirV*nG=jEfM#(*-@Aim?!no-M5aG$T`W52MFI`-aT$jS%%?tTp1AOfnW2
zQfA&GcKk)lDWp>p2@)~EkT8hnt^z~OD0go}7Dt^LO$!l~NKmCdN%O;ZuKaEiQGXZO
z&@!q8`XaifdZG|t%}TYbnCzo+;QV_e|I&T5>}Ra+44p=n9}NYaV?Z6!RKt+SH`1Q1
zY1W1_FU$DCEC#*H29k;=q{!^3Fp0qbCYuRNksHZ21QJzYky9ohDiD+lVP$c*}LOx(f@0
zTeLSkq+8#jgZaSS+7mdDRC9BN{;LCGWnKJs1j*sCzCefFd
z>u6e8*;rTjd^`x&4b5gcoxw5Uz`VXT%qs0Au_!fB&_yNV$YZLR2yc~d6B`G0bQm)b!Yq&quRWb{Sz~zR$t2o@8ps@=nyH)|S
za*bP!1HtvWtL;Y^GFoFqS&|}{Z)0pJDvy9MWAU2h(gaicjS69dIwL&zJ;-PEaVb-}
zev+F;JR$-9fZ^3HpRIHJ$N;vjQKMMY3liPHf&X<2mH3VWTptgY#ty$0*HwutRX~PQ
zLZ^gCJYmk$L8-+HaJ!hoP1T$TR5U9yx`dvV%@iBZ0;K)GzggfE3F+9q3=;%t7V!GK
z!0_winw!W+pk;0C%gagN!YYX}AmdczZ8$R}b;?G{O|Q(UT+T8e3P|xQVtPEuYwXDI
z*dH{N$Mgv%xGXz{8OlT_*XnN+YqngY%4N?ko_4JHX&y
z)G4Iu1v1N2_T0wZtP5e6-_eTCjZmJKflsaTzkxjO^+*}YQub8^Bg_>6g=AA`{ADSa
ze}ghXyD5j}P~#a+_CQiB?5yuALQYzJD)ICQVnDFJaszAV+{O00DqQ2xtUiRakjZ8m
z*nlf>PY5o)t~-L9BzM2uupd?-;8n%#u;!4pW)O#c9QcMr&5nVo2tu2t68w}7op&dE
zYOV+#S($PCAr~**d$33Jca(h=-{tFb-pD4BCVYr3-KJuRQSI?`UI)ijRBbN}b@KMG
z3=<=^67Os;f}JOdj*Pr1>Eti9c~g%h7Dhmy-g`a2sFFN(F
zL1swBih`fhJ(|CYhW)~D?OE%>@0k!?s0C5F-`*c85Q7t{QVidM4`>Z&0%{N2#4|~N
zU9m^A;O)MMEFz*!*g3%>uvB<>07g&O(`{+3}
z-9S;d^)Io+O$7~8(@X=384`AgokMp)Bu@-{YrX0u+BN=6cUkQeCwriul9P4{d;Q@R
z=wy3_!6_uwsm-a$`25})x6DcJ+eO>iYzI2X_^&p{x2xaQ!c8p_hwu0u>FHsvN3f=$
zN=PH*Kq#EmzjYw#sU?#?;9k@*y#~2>gLs6_5sERgg?pFp{-JV7Sy_F=f&011HWH*^mvQfE?(=mj$`~(q}g6OHJK~a8yO)PzOLbVA)P`
zbn=?$WsCIGqYYd*H=&Z|vxTo|yO^jaakd$UVH3}uSxI5w4U~Xvn7HiL3mBcI3f7T|
zymZ^U;%)woWLeIC!`)LPpzvf+4%y|<4)4jN6Lg)d$+{;RR;Qb)1=d^YS&?|(>=VmY
zSS)G-)<4zY3QLD39$RHaZTfMLL4Da$r7DWJ6+UmS$A4_R(Gbm
z>r~kc5ztF7BYUEampHqU1%xFss4R~@up^^a*)QAk>s&VujyvJ<)xviz4zy!#VYyq+
zI9R~SC-KD|tBW|5oF`63s&7SDMr^?+W8CZHOL~P)y8-^hUxNVDg%Z@cl9YAc@4>nq
ztcd^$JKFh9Kt*XzjxhwK>mUOqyPfguLohBMyM&3=@)
zgb&@E0ZnAd`ZXw-Z?}Xp6OM0nvw(Hn6WXIUGp|U?sDsa=bM@Y$Fzo@rV^?6zZ>hwF
zBeK6cwr~IF$Oh8jiE~l27P(+tPI`rDkIgH>^uz(CA5bP&I$i5;n{yVgS`ELbI1Iep
zsTeZ0A#2EVC?ulf6z&7JJE@q#JYKZP`DWGHE6^^HVh@tC_wz{$uA*n<)yW
zMRj(MyI{YFvUltp!8c8r0B9p+(}iX-l*^A2o&{6aG?i)e4U|IZ)iOZ%O;>D$f9SmU
zdhExMxNwUFqVnaGf%q=Yh&A?`8)9YPjwhUQW{}zS0<3CzR@Vc$a{mqi_|o5jZLQ9%
z?m%Rj8P7X!LCXHK+{^SqWn+IdIjI4qC4(uGOm5sA+mTK;Yl>ut67!H9`?m5yD59%g
z^7NOK!vN=yNb1KXtaAytumsjNX%kfEjIn&a6Wg$KyA-H+(8L=21BpLiPb)F@z%cfv{p$K0`$xyq<;W;W?O6$
zt4LQJf66Qs*5-=m?PxBW<%{xU|LH#GPns(N{(&UlS@RJx@(-}y^tJvCy^K5bcB8f$
z8-n^}?7-VnHJbI+IHt=W4Zu2kNjjGZ`_L=84OuYkS^NtojN&$bb=j?$AaY+eUVkn>
zAi7wc1P6!}^Eby`xJ$cfw&+)oY#nx|a#{KJ?MurA4FvZORohO)Q;U
z!F(%o9+8}i{*Q;e-DRbkP$5l#9IXrPw?IsRB3{xbf&z&X=?s~0$fQhd*KWLoQbG0J1J&z{5ceY
zI=j9gy$xJ(j}a`|EpQNhm{TBShgU+
z^GJK8u9%%{e!P`c8;3aTeD|70bb(cbC#kEUiNz_|T+II?sXZtjzZNvT9SnM!fU_)TH^VXv|rB
zp4m96kk1?ko3`w&awf#Xl>CNctNBa^|Ja5qKt+Zy6<^iXgqBG8+)Oxl?2SOGPj`p>
zqf5R>N~EJ=Lw_{c3cC~5Gu$D@Phhbp)F#EOxQ+@Q)YQaj
zz_%rI&=cIn@_im+g~_A;a>NXK#fU388G8&WLHf|Wy`7OD%Ht1kL9%De#)e9(ElcQt
z0{4+GWB9Fz7DldoDEuZd5{L1~yN|SlZ?Ssy8_Z+a*~KK-YmZ1!`#sDUsJV1n=d`y$
zr9V73xlr}sOSY|7-h*yKX;KGN;nqLVUcDgMOMVn$Q%Gnkb?z`mpT@nrp;->lyjTks>J9ODGc(UNv{f>?tFLEGtLQ#--Mj_j-A}mYw4D1gaPmv
zL-Z1q2&fAxZri)`6q{c_>&VCHwq`jRyi)3t#tXWmgPN}%rUFWCblbUpX4BjMW0Dp7
z9YWO1MWd!X3XaA};G?0nCQ+d}GIunihwS007*e)Y3;dA}vjbGv4Orq|s`W_Fl?g64
zJgs(jQY%93d#lGt>+ZcdFGb=eS7W3g?wMB=>zO;&1u(Gt-wTY1RQnD?&)$ed%!qg>
z@|Jn3VGg~?Ln3K~KWq*YM2gLD%__sOHi2jcog)18@zA89hz^&!UcNo8)ZSQ1(A@yQEW7
zFNA=lu7ee3y_}a#()o~WOni#9J{|m#u2&t(S8y#LvTkiEDi7VG+A>r>pbYHgMLy!y
zp;h#^z7Ha8f6Mr6>6uSt)4UMiR!-O%I+?ShD#owHG0oYK0cVq;#%P8Sho&?8MY)Oh
z5su2aHC?K%d=4xO4o_@;Fn*DeS|Gc2Mg%i7tm$Lq{GVc8!yNO2dx1I6cIQZzVW6GT
zE?U2w?o_PjLg^ZrNieFJ?&S`?W-X9&GJG-u+R?gBm|2@Y%~U?r-L}}29)NNwEcMYl
zj+#lg@*^m12EjL87ts)FpUh%X0RQ^9L)|AKCn>wB1Tc|B9epfScWt|}qnw^7ve!H@
zbFuAvf$-j0>9qh>?E@CLq1q^J?}1bWY80H!I6vl+y=&7q>9*)wnohIWs)t=y(yQli
z#st~<&Q`+JHg329_@Ju{0YE$DM4Hj8bk?+G?cAFJH)FBg3FVJq;fvYeWgr7$
zY&~d6DT}+3b(}K%?A&mm7Zi`Ic;iY^yi2@)lmRg>J~B8wW)@9lpA7XZeyU|iqvclY9oG?-oC^#1O_3IjtrZxxTO>Ma_Xu~0)^BK*4O`zCLXUtA@M
z)Og7F&ILxD2>8Rcq_r?ux)`MC)Icj7^zv
zcu}EFIO(?Ixm8S2i!?CwV1lUg#drNO0hM(+Kq@F_>#aQF=~SN$6n?F&2;W^-hRC1b
zc%NHTV?+P$MO!>MT|Hd5s?Iql7sW$wy#Q4?uPUzO*#6e!2hz<$zc`s7
z35?}_2*`T$9Y+}z6A+*DPUs$v!QiI2$!A0}Z
zR0c%zL{IDAe=vAxL~t(G)12>DPG^y1LRdW_sc7${@~e~)e}(hJF$H;FXqIG?hGAh$
zkomlwI^3f>3+o}W_r7pc1;%9mailVM4teoxm2%t$v?@b^2lqBZGv0u`;}%3LhcK32
zj062I+D#_h-?KdV@T$ZmGU$B>MMPxuHiE?v3L){`TR#xBLh
zBGTFYD_t2h!hOF<3sk5S`0QL7E5B!EC(^~c%-Z0%F$-fEmf>?S<^>vQM{aw)CvK5t
zEewPxc(_jUCPcF)FW9BN-j~$4?kn9L_Sw|8HM37*g3ST9%46b{T$^1Im|Q~mzI}$x
zHQ8J+N+ed|5%Bd{sf?-M#o^1Gifre?K`8FhYhp7Yt8%luE=F1yyKGtZeoip*
zN0$8|j4hp)=*4x-qiro(gERckFGrm-uL+<0XW3x#iQR*-cF-TegRdRyFuh>StI>rR
zzFrqArAe%(N?BXg{L<;&v!GodbvroRjaR`jTt8F{ksm6ZE){(VX&DCgKs&dHsywyt
zby+Sf!k)?KIsdCq>tNdbI6&rCh!QVKX)(_BE-Y}hGkcMuldtW1LMA`ETbeI9c?k#f
z4nh^h8%9ierlf;Cqy9}?`H7m9I=n%|PgY0voAZf3@ceszx0l+)y^s{%X2Iz1Os2q8
zJIX=LH_osx*|ILd^|B_9u3F3k!^GAHj69;P(NFl*pLt5ZLxvB&Gr>$=FQb<6J
z@t_uelOr@IYd}FE=q1|CuZfklX^Tf?`gJDEYwW~h5^cj1Xrkc#9@$@PUWJ!fU+|t4
z*w{P49^zbnMqNzZr&UI~uow)HxDGY6CZT=PQYqxUGGHker-K5Q!(*nmjz35fA}4C;
zcBoR*4?M~~Ba2y$L`Vdf$@+%4(Pm`zmUtYC
zG6_X!DZksHXzOFLL?-$!_BqU(ySH
z8~gV_G!)4z2kW0bnEnfDq8ky6=A`EUb
z_1vZUo$EgLi7$9z-^^)k~d|qRTwA8uH}xOy_~`kRu2S(BlMvGV-eMyyoZ@7baFp
zcVL49>lh&~fG3ijSsanA!NAGy&|XgW2bMCE5zQ4==XZg9DPd9e6y`gzd&Hmv*Lx*&0(-?HKyrP!Em+=R?&zZ>alebFUp
zra*Cn+*@`4pEMV4&4(T{IOZ23LECSw!JA;6{7mdT(4GnV=0`C83*LG{
zpv;m>`DhkE7}4!iv*7DfiNu1|ed?Bp$%>Gdcb^%aHZN7cWN7-q+@@kL-5jJMohi#)
zow5%5R9r|l(Z3BlCE7Hwx9AZoslqtsmoXWu57t@Eh@|s2joX7h+3-c$N4E2;w|%GP
zksA4;1d5!7URtt1UBsp)sxL4CMj=@Q+uoI3^bvFlK`<~pj;0qtmHXIxC;L#78Wlo7
zl*(N04EB~cej~F=(nV+MbCav2WLG3ZzX+Y*vzz_Y6dA5Wx@XiEo`=_oZ~i4$@;bh+
zL=qjsm(*~WgE$D>uJa5O?x*dTuRk~1X^)NdE06Q6F7tTz
zjywL|d6-TBFd1ClK;?}E`ASu|)E@nx!JP1I0{_k`2kzE8VX`ol*gvC$Iv-yoxQ0;E
zIiQ1^$k++5YqPnLoEODE_)PO)0yB1Q{~a+65>H@%wieVk!a&anmrYEl6gXUFs*HaN
z{k&8oT6ck83-0Y^5tX&CyhW&KFrN#
zq{*$|$|l|7tn$G6X-%7i&8w*2IFkm$CR7LhU!hfChg>|q&<<)8HC}Mdl?nE>2E)u7
z6;X5BKs!6XV)WKc-?-{ALWI0RI7RLr0*nuNwM}O82S)?KypW3G6ha(ThJuj&8{Xop
zJW($E69mpBvrvQIsPzvP@Au~?0Pp14p7=*RBjS}gg$LD3Wh`Rid=0@4ax|YL*D?*JdslZ9Y4X*%kb2Rc7wp%y
zSe};mGtTJ1?2#-;j?zQT2aHhH_EjnTD+YGs)yx4{G8p~~nfM5gbW&*jqb=4#u|g!$
z9=J{-Flwt+eayCl05N>|!ts`pHlwM3xboPv{gsSuPiwbc)=a^E&UNKI$h2QOnsH)F
z;5nhoV_-k>Hjh}x9|HgS?Ar(q@cm2f)oockNfL<6rc`z*n$_p+#n6RKoc5tajh=$PJ{w;lo2C^oC
zx196+qwUp5A9l%0%2axPfXr$h%+zjQUWgz%b`XTYt^m#8Fu*_y#UD^TEPFaEa_5B_c1F6mfiLR+(Y_neV?dTiYdPySxD)+du
z2rqb~G1C3>z=G|N`KFkVdQ!y1e$5>r1UA$W>@$v@(=G@K0wxuAw^Rzz6)Ipi;8?YUy4o!hFsb+nMr6#X*R+#N8FJFJj2cTL$9jp
zGsmQYOv)~)<)#U#WtD!o{1EcZWZ?R8-0PLQB=k?YU1ET2cBUx=7yILIJKFIo*Zun7Q=lHw5~+`Rfhw)t-^OQ_bEhCylGHV*%GRyCczc{s(;=5k;p${b
z-nu6$L~Uq-lt8LIyxNo5LDY>)lH48lXZaB=)}OYez8*R8Zx}=puEAobt)K?B*0i7k
zH>9B1Xps&&q6wy(|7!X>(d+>ay7(_t6N@?ZvW2o<3&u{AHKC-gn08D|^VAVIj3v>C
zb1e|han%&>?UY$Ne7>RO6M>uKCt$nimYmrdvp4*F$e&v-?4`46IB4zw%3b@c7~VRc
zd0Exhg@cB9XO#o_R$6n4ghNxJ@T}1(JkLLHtaGLRB2rxkzx$r;=_HzxOKk
zbj!#U-k%|h8=cefUaM75`J-Nb4Q>m&!8vZLC;o0Gx&ee^4`ZK)`pqDGfgvmmtB4u)
zNr@&?B9zTkR@}F3ommE4R~fQbiz45ifs7DnT}o9i&gzM!aH&7M;#L%C2=qvPgk7Kt
z?k*>$q;tk@ja1~L4;CxnwvA7+7K^zt?H5oF;pG7yLckAd*>L_j
z+CK-rU&8ZIs#NOT^3a|SmlHOUE*wA2RZtQPT4UfQzh
zY(N<*64zTVKzBxZ^}=nXJL&iDYnCY1=DyyDS_JFg5r!(jWc?qPX+LR^g>U3;Sm7Q4
zALQQqd``LGW4C>}Ar8dMBQkOH(Qa6P!l<7~HcOfJPl=KgN$JGAsi&Rno3S?OMo_tq
z`@Z1~-D+kk;^_2+A41qU9=V;k`~MUVmAXZHKBS}yt^TO3QOs1{8hADW%`x2EM}RU0
zBcxd4`pe=NEFdw0(u!&waT~R8fs)2H#mzY`8{!8B?mD;mXmwdT%*KGMDUs_cyjpj$C=;${ngg+fJ{43V
z>IUPLaNn1&otihM6lI<1r2;K2l@gsm0zH6xKRW6bD>iJw%*mZiBAT}>TK0_i2$BES
zfgmH6vb-Wd_U~jzxm0rxL}5CEf$Mkp{zB*{&Hh)cng{EUlDrh^;vC@N@5=Az8l#|i^(T03ouiY
z>2mo+-c(TlyM$}KZSNC~{wgu)56l0BSJHd#nf>LL>gQ<<9`Y{<81SHzePMnas;eAP
z&tlS;JDaEqi89UtA>89VI$DTuhNrR!38cUR<{rWYu6t#i#Q|MWyHJ9uY%6WPuSD;j
z%&%9Cv*KjjUkSz40#E~Gx*r;dw8FJlQWy9h*V~*l6&?syfu?AXux!I|phlmW(PgQ=v*+ng(QD?I7mk3M10`8uH45~U!d+(3
zxqyhz4`Ry8dB#7R@;&p!0^)|5L8<)o$-UD8-hAzEf*95~V3ht+MW_I+*V$GfeHDy&
z*8Dw9W9Wq;$BH3$Jt$}br?D(v|2>(5}vyVd?d+qKW35n*@76hfx*
z*u}FjgNOMan7_MZ?7)T!Y)~>bhV%+AsrZ{5vszHZq<-p^jzY
z4Ny^WFqsS}d)dSL&i0u)_KGU+>}b|x;Q7M3th!j=~xz;8iIU;&DU(JpUQ&!2i6OySIM2R8DCy!=&EY
ziY+-|01AjFC7rS>8JK5YYXn>0GNr7FRN_JEqhYjQ9Tt+2d3
z!dF$bUkS55CnplI%BBja6Vqre1cgPIY6qfz(dqX>Bt+>jv$GXag{LJQo?P*I8WvJP
z;JFDyu;a27xJ2QxFT?=nIX}m@$(MP<-P&q#ySZq
zb{+Ll#C%ZDr$f)c#!+yHQA-m#f$TZ~qpb*Ck^R=2)4j*C&
zwun}mnm{iK;C0%a52VWHC!w?y8c7}TvF@Bx2Io;q#%zxItjhiHJ=%L1Pii_Q{|LXE
zBK6^~+*QsUy$QC@v?zaTkfS#9Rpl!FvEN^Uk%HJ?8PZ?D)&&Dnqysv^;v7Vg_hQSAg5RsZ&gQmBQ
zfe@TEQ;gRVBcUaHvC#rsmz-3F5uUkaUbelWnx}dVd+Y9kzIDe3L#3;knN~<5n90X`gF>VsSQrRW3Lzr2X
zks{Ip395-oC;{u?_au3x382*{G|Vqo0=m@^9<;T6K_yvy8_KD^jI~lWEbfkdv4+&P
z%s}Z0q15j_3mL^rV`VenybTgDW)6iY^4|~Wmg5vHPv!x#j5mCVj68I7HiGT?<=;@tmh$&i=4b)3&ssdb4_BKOQt?#L$+VDn#K+Mg(M5b?sI~aZ-qr$LKHuCL^sA`=yO#HXO8nu*52RwS-BcGm&R3R
z$;6P5rdg7R@_d<8D|9Uwx{g6S5Y&b$)l9LPQUR?3Z4SC306$~{Eh|B;=&zwww4AH4Yc(^GlNF*M
zH;IU?pQ_O0jhpV7^!{~zb>!j^0HH)`XS7NU4BVg!4JTC15jo|Hs}R|KXS7Whx*V=L
zK!hc}n_UZD(rG&5MX+S$^;FfPPDnTX&~rk^{IF|Zq6SJUlo7}&V7;&x9(xw@hPSp6
zv;k--QNmUxSRI<`nKOFAoajs7yW}4&=_-@ZICK9OP-8&o?7^l`~cJ{XIYoSe#;
zqtL9FGL6H)@7GiTwKgEKYjWT1P2M}~W%3WxoE46z4qn7^1Er@iqHp>-BBXIgbe5Tn
zK_&IH;jq)$d_ta_NwseqF0w~xw0SQegAUw#I$oAt~VkJzuwR_p0!5=_AQ
z!gm{Z1b2T{4-r<3@LJa&??cnP7mylJ>N_v~k5BFWs(Im-g?$7%vYX28y)qxzh~CR0
zfvw<&S7-(1?|0P3hJ8lP6(h8E%HNJ&_=6BucHY!a>k20{4B`*}fN;Kpkdss7F|+L>
zs<0*lWNoD14MAIOC3E85Hbrj9dPj$sI$X$2yY->r7v%o&Yy~>@7c4{)d^;X3Q<$}R
zsmUAt=2|S(a$>^|aOT8A7Q7B>WSdHBuB5?!M_x!vi`Fukth4zVs+rmWEz{egt!JAP
z6Q;Q2wTtr;9OqGA2TVPt-(KZR#(SK-gix6=HYsF|?wUw}Zl}))DwF+&&@SyMsMB#b
zWmwk$ls!=tOZ!S4jJ;4At^L##_&^JKOvOnph~KIIAhw;Dy(^I5)#@YYoN*Mtr5!Ew
z+W7^lFNl~S(W7&A;$~IK)#FydFNM@>ZHw{VQ)V^!X6R9XI`DvP19&fu(gGwfGaob(
zKoMf0B2kZQa@FxyEBSB6_;X3kjt2s|4SbY4scz{5N6r}p^)#h9Pf1MrJQKF1BN0xl
z?l%~dOSWdp443^xA~8;fk6dJM5e=YA)i35+bkj7ry!CEzQa8C5;5Uz|@I#?0KuUBE
zEB;<)%u$OMnu0GtR$|2=q@Cqq>NY-a_&-X(xO$N?+10fplMVc)Z{|HnpV8T&fzT&9ko)
z1YN`F-ag;d--~y{(WCJ`XZXLa!E#ogDOKtr8GvKeA+EXn#}*TdEfG&+<+|ua1U=>z
z$tL-$we^PX|NWs`L3{(Y=xs9{(02nQ6p>*FanpBUezGuiUgc!=LYrv_ABGm-G1w)y
zS*Ww!Lv{ox$P`k73G{Er*bzY*lXWNm{$nI^lS3?(M(Ys8>yNSC)Cgw~BC8d`uJl@w
zp_qwnBTD=v?xE<}F-zJ@s0@Othv)zz0o+gUrZhbJlxvK*+a`F8#;9FWXGOV%