From 49cf880513c124e1116f80f4ca0c860ffe937151 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Mon, 16 Apr 2018 15:37:27 -0700 Subject: [PATCH 01/32] Approximate FSE encoding costs for selection Estimate the cost for using FSE modes `set_basic`, `set_compressed`, and `set_repeat`, and select the one with the lowest cost. * The cost of `set_basic` is computed using the cross-entropy cost function `ZSTD_crossEntropyCost()`, using the normalized default count and the count. * The cost of `set_repeat` is computed using `FSE_bitCost()`. We check the previous table to see if it is able to represent the distribution. * The cost of `set_compressed` is computed with the entropy cost function `ZSTD_entropyCost()`, together with the cost of writing the normalized count `ZSTD_NCountCost()`. --- lib/common/fse.h | 10 +- lib/compress/zstd_compress.c | 239 ++++++++++++++++++++++---- lib/compress/zstd_compress_internal.h | 6 +- 3 files changed, 217 insertions(+), 38 deletions(-) diff --git a/lib/common/fse.h b/lib/common/fse.h index 5a234444..3d11a75e 100644 --- a/lib/common/fse.h +++ b/lib/common/fse.h @@ -402,6 +402,7 @@ typedef struct { const void* stateTable; const void* symbolTT; unsigned stateLog; + unsigned maxSymbolValue; } FSE_CState_t; static void FSE_initCState(FSE_CState_t* CStatePtr, const FSE_CTable* ct); @@ -538,11 +539,13 @@ MEM_STATIC void FSE_initCState(FSE_CState_t* statePtr, const FSE_CTable* ct) { const void* ptr = ct; const U16* u16ptr = (const U16*) ptr; - const U32 tableLog = MEM_read16(ptr); + const U32 tableLog = MEM_read16(u16ptr); + const U32 maxSymbolValue = MEM_read16(u16ptr + 1); statePtr->value = (ptrdiff_t)1<stateTable = u16ptr+2; statePtr->symbolTT = ((const U32*)ct + 1 + (tableLog ? (1<<(tableLog-1)) : 1)); statePtr->stateLog = tableLog; + statePtr->maxSymbolValue = maxSymbolValue; } @@ -581,12 +584,13 @@ MEM_STATIC U32 FSE_getMaxNbBits(const void* symbolTTPtr, U32 symbolValue) return (symbolTT[symbolValue].deltaNbBits + ((1<<16)-1)) >> 16; } -/* FSE_bitCost_b256() : +/* FSE_bitCost() : * Approximate symbol cost, * provide fractional value, using fixed-point format (accuracyLog fractional bits) * note: assume symbolValue is valid */ -MEM_STATIC U32 FSE_bitCost(const FSE_symbolCompressionTransform* symbolTT, U32 tableLog, U32 symbolValue, U32 accuracyLog) +MEM_STATIC U32 FSE_bitCost(const void* symbolTTPtr, U32 tableLog, U32 symbolValue, U32 accuracyLog) { + const FSE_symbolCompressionTransform* symbolTT = (const FSE_symbolCompressionTransform*) symbolTTPtr; U32 const minNbBits = symbolTT[symbolValue].deltaNbBits >> 16; U32 const threshold = (minNbBits+1) << 16; assert(tableLog < 16); diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index d8420a8a..114845b1 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1561,6 +1561,129 @@ void ZSTD_seqToCodes(const seqStore_t* seqStorePtr) mlCodeTable[seqStorePtr->longLengthPos] = MaxML; } + +/** + * -log2(x / 256) lookup table for x in [0, 256). + * If x == 0: Return 0 + * Else: Return floor(-log2(x / 256) * 256) + */ +static unsigned const kInverseProbabiltyLog256[256] = { + 0, 2048, 1792, 1642, 1536, 1453, 1386, 1329, 1280, 1236, 1197, 1162, + 1130, 1100, 1073, 1047, 1024, 1001, 980, 960, 941, 923, 906, 889, + 874, 859, 844, 830, 817, 804, 791, 779, 768, 756, 745, 734, + 724, 714, 704, 694, 685, 676, 667, 658, 650, 642, 633, 626, + 618, 610, 603, 595, 588, 581, 574, 567, 561, 554, 548, 542, + 535, 529, 523, 517, 512, 506, 500, 495, 489, 484, 478, 473, + 468, 463, 458, 453, 448, 443, 438, 434, 429, 424, 420, 415, + 411, 407, 402, 398, 394, 390, 386, 382, 377, 373, 370, 366, + 362, 358, 354, 350, 347, 343, 339, 336, 332, 329, 325, 322, + 318, 315, 311, 308, 305, 302, 298, 295, 292, 289, 286, 282, + 279, 276, 273, 270, 267, 264, 261, 258, 256, 253, 250, 247, + 244, 241, 239, 236, 233, 230, 228, 225, 222, 220, 217, 215, + 212, 209, 207, 204, 202, 199, 197, 194, 192, 190, 187, 185, + 182, 180, 178, 175, 173, 171, 168, 166, 164, 162, 159, 157, + 155, 153, 151, 149, 146, 144, 142, 140, 138, 136, 134, 132, + 130, 128, 126, 123, 121, 119, 117, 115, 114, 112, 110, 108, + 106, 104, 102, 100, 98, 96, 94, 93, 91, 89, 87, 85, + 83, 82, 80, 78, 76, 74, 73, 71, 69, 67, 66, 64, + 62, 61, 59, 57, 55, 54, 52, 50, 49, 47, 46, 44, + 42, 41, 39, 37, 36, 34, 33, 31, 30, 28, 26, 25, + 23, 22, 20, 19, 17, 16, 14, 13, 11, 10, 8, 7, + 5, 4, 2, 1, +}; + + +/** + * Returns the cost in bits of encoding the distribution described by count + * using the entropy bound. + */ +static size_t ZSTD_entropyCost(unsigned const* count, unsigned const max, size_t const total) +{ + unsigned cost = 0; + unsigned s; + for (s = 0; s <= max; ++s) { + unsigned norm = (unsigned)((256 * count[s]) / total); + if (count[s] != 0 && norm == 0) + norm = 1; + assert(count[s] < total); + cost += count[s] * kInverseProbabiltyLog256[norm]; + } + return cost >> 8; +} + + +/** + * Returns the cost in bits of encoding the distribution in count using the + * table described by norm. The max symbol support by norm is assumed >= max. + * norm must be valid for every symbol with non-zero probability in count. + */ +static size_t ZSTD_crossEntropyCost(short const* norm, unsigned accuracyLog, + unsigned const* count, unsigned const max) +{ + unsigned const shift = 8 - accuracyLog; + size_t cost = 0; + unsigned s; + assert(accuracyLog <= 8); + for (s = 0; s <= max; ++s) { + unsigned const normAcc = norm[s] != -1 ? norm[s] : 1; + unsigned const norm256 = normAcc << shift; + assert(norm256 > 0); + assert(norm256 < 256); + cost += count[s] * kInverseProbabiltyLog256[norm256]; + } + return cost >> 8; +} + + +/** + * Returns the cost in bits of encoding the distribution in count using ctable. + * Returns an error if ctable cannot represent all the symbols in count. + */ +static size_t ZSTD_fseBitCost( + FSE_CTable const* ctable, + unsigned const* count, + unsigned const max) +{ + unsigned const kAccuracyLog = 8; + size_t cost = 0; + unsigned s; + FSE_CState_t cstate; + FSE_initCState(&cstate, ctable); + if (cstate.maxSymbolValue < max) { + DEBUGLOG(5, "Repeat FSE_CTable has maxSymbolValue %u < %u", + cstate.maxSymbolValue, max); + return ERROR(GENERIC); + } + for (s = 0; s <= max; ++s) { + unsigned const tableLog = cstate.stateLog; + unsigned const badCost = (tableLog + 1) << kAccuracyLog; + unsigned const bitCost = FSE_bitCost(cstate.symbolTT, tableLog, s, kAccuracyLog); + if (count[s] == 0) + continue; + if (bitCost >= badCost) { + DEBUGLOG(5, "Repeat FSE_CTable has Prob[%u] == 0", s); + return ERROR(GENERIC); + } + cost += count[s] * bitCost; + } + return cost >> kAccuracyLog; +} + +/** + * Returns the cost in bytes of encoding the normalized count header. + * Returns an error if any of the helper functions return an error. + */ +static size_t ZSTD_NCountCost(unsigned const* count, unsigned const max, + size_t const nbSeq, unsigned const FSELog) +{ + BYTE wksp[FSE_NCOUNTBOUND]; + S16 norm[MaxSeq + 1]; + const U32 tableLog = FSE_optimalTableLog(FSELog, nbSeq, max); + CHECK_F(FSE_normalizeCount(norm, tableLog, count, nbSeq, max)); + return FSE_writeNCount(wksp, sizeof(wksp), norm, max, tableLog); +} + + typedef enum { ZSTD_defaultDisallowed = 0, ZSTD_defaultAllowed = 1 @@ -1568,37 +1691,73 @@ typedef enum { MEM_STATIC symbolEncodingType_e ZSTD_selectEncodingType( - FSE_repeat* repeatMode, size_t const mostFrequent, size_t nbSeq, - U32 defaultNormLog, ZSTD_defaultPolicy_e const isDefaultAllowed) + FSE_repeat* repeatMode, unsigned const* count, unsigned const max, + size_t const mostFrequent, size_t nbSeq, unsigned const FSELog, + FSE_CTable const* prevCTable, + short const* defaultNorm, U32 defaultNormLog, + ZSTD_defaultPolicy_e const isDefaultAllowed, + ZSTD_strategy const strategy) { #define MIN_SEQ_FOR_DYNAMIC_FSE 64 #define MAX_SEQ_FOR_STATIC_FSE 1000 ZSTD_STATIC_ASSERT(ZSTD_defaultDisallowed == 0 && ZSTD_defaultAllowed != 0); - if ((mostFrequent == nbSeq) && (!isDefaultAllowed || nbSeq > 2)) { + if (mostFrequent == nbSeq) { + *repeatMode = FSE_repeat_none; + if (isDefaultAllowed && nbSeq <= 2) { + /* Prefer set_basic over set_rle when there are 2 or less symbols, + * since RLE uses 1 byte, but set_basic uses 5-6 bits per symbol. + * If basic encoding isn't possible, always choose RLE. + */ + DEBUGLOG(5, "Selected set_basic"); + return set_basic; + } DEBUGLOG(5, "Selected set_rle"); - /* Prefer set_basic over set_rle when there are 2 or less symbols, - * since RLE uses 1 byte, but set_basic uses 5-6 bits per symbol. - * If basic encoding isn't possible, always choose RLE. - */ - *repeatMode = FSE_repeat_check; return set_rle; } - if ( isDefaultAllowed - && (*repeatMode == FSE_repeat_valid) && (nbSeq < MAX_SEQ_FOR_STATIC_FSE)) { - DEBUGLOG(5, "Selected set_repeat"); - return set_repeat; - } - if ( isDefaultAllowed - && ((nbSeq < MIN_SEQ_FOR_DYNAMIC_FSE) || (mostFrequent < (nbSeq >> (defaultNormLog-1)))) ) { - DEBUGLOG(5, "Selected set_basic"); - /* The format allows default tables to be repeated, but it isn't useful. - * When using simple heuristics to select encoding type, we don't want - * to confuse these tables with dictionaries. When running more careful - * analysis, we don't need to waste time checking both repeating tables - * and default tables. - */ - *repeatMode = FSE_repeat_none; - return set_basic; + if (strategy < ZSTD_lazy) { + if (isDefaultAllowed) { + if ((*repeatMode == FSE_repeat_valid) && (nbSeq < MAX_SEQ_FOR_STATIC_FSE)) { + DEBUGLOG(5, "Selected set_repeat"); + return set_repeat; + } + if ((nbSeq < MIN_SEQ_FOR_DYNAMIC_FSE) || (mostFrequent < (nbSeq >> (defaultNormLog-1)))) { + DEBUGLOG(5, "Selected set_basic"); + /* The format allows default tables to be repeated, but it isn't useful. + * When using simple heuristics to select encoding type, we don't want + * to confuse these tables with dictionaries. When running more careful + * analysis, we don't need to waste time checking both repeating tables + * and default tables. + */ + *repeatMode = FSE_repeat_none; + return set_basic; + } + } + } else { + size_t const basicCost = isDefaultAllowed ? ZSTD_crossEntropyCost(defaultNorm, defaultNormLog, count, max) : ERROR(GENERIC); + size_t const repeatCost = *repeatMode != FSE_repeat_none ? ZSTD_fseBitCost(prevCTable, count, max) : ERROR(GENERIC); + size_t const NCountCost = ZSTD_NCountCost(count, max, nbSeq, FSELog); + size_t const compressedCost = (NCountCost << 3) + ZSTD_entropyCost(count, max, nbSeq); + + if (isDefaultAllowed) { + assert(!ZSTD_isError(basicCost)); + assert(!(*repeatMode == FSE_repeat_valid && ZSTD_isError(repeatCost))); + } + assert(!ZSTD_isError(NCountCost)); + assert(compressedCost < ERROR(maxCode)); + DEBUGLOG(5, "Estimated bit costs: basic=%u\trepeat=%u\tcompressed=%u", + (U32)basicCost, (U32)repeatCost, (U32)compressedCost); + if (basicCost <= repeatCost && basicCost <= compressedCost) { + DEBUGLOG(5, "Selected set_basic"); + assert(isDefaultAllowed); + *repeatMode = FSE_repeat_none; + return set_basic; + } + if (repeatCost <= compressedCost) { + DEBUGLOG(5, "Selected set_repeat"); + assert(!ZSTD_isError(repeatCost)); + return set_repeat; + } + assert(compressedCost < basicCost && compressedCost < repeatCost); } DEBUGLOG(5, "Selected set_compressed"); *repeatMode = FSE_repeat_check; @@ -1803,6 +1962,7 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, const int bmi2) { const int longOffsets = cctxParams->cParams.windowLog > STREAM_ACCUMULATOR_MIN; + ZSTD_strategy const strategy = cctxParams->cParams.strategy; U32 count[MaxSeq+1]; FSE_CTable* CTable_LitLength = nextEntropy->litlengthCTable; FSE_CTable* CTable_OffsetBits = nextEntropy->offcodeCTable; @@ -1844,13 +2004,20 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, else op[0]=0xFF, MEM_writeLE16(op+1, (U16)(nbSeq - LONGNBSEQ)), op+=3; if (nbSeq==0) { - memcpy(nextEntropy->litlengthCTable, prevEntropy->litlengthCTable, sizeof(prevEntropy->litlengthCTable)); - nextEntropy->litlength_repeatMode = prevEntropy->litlength_repeatMode; - memcpy(nextEntropy->offcodeCTable, prevEntropy->offcodeCTable, sizeof(prevEntropy->offcodeCTable)); - nextEntropy->offcode_repeatMode = prevEntropy->offcode_repeatMode; - memcpy(nextEntropy->matchlengthCTable, prevEntropy->matchlengthCTable, sizeof(prevEntropy->matchlengthCTable)); - nextEntropy->matchlength_repeatMode = prevEntropy->matchlength_repeatMode; - return op - ostart; + /* Check that all the Huffman data is first */ + ZSTD_STATIC_ASSERT(offsetof(ZSTD_entropyCTables_t, hufCTable) == 0); + ZSTD_STATIC_ASSERT( + offsetof(ZSTD_entropyCTables_t, hufCTable_repeatMode) == + sizeof(prevEntropy->hufCTable)); + ZSTD_STATIC_ASSERT( + offsetof(ZSTD_entropyCTables_t, offcodeCTable) == + sizeof(prevEntropy->hufCTable) + sizeof(prevEntropy->hufCTable_repeatMode)); + /* Copy starting at the first FSE element */ + memcpy( + nextEntropy->offcodeCTable, + prevEntropy->offcodeCTable, + sizeof(*prevEntropy) - offsetof(ZSTD_entropyCTables_t, offcodeCTable)); + return op - ostart; } /* seqHead : flags for FSE encoding type */ @@ -1863,7 +2030,9 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, size_t const mostFrequent = FSE_countFast_wksp(count, &max, llCodeTable, nbSeq, workspace); DEBUGLOG(5, "Building LL table"); nextEntropy->litlength_repeatMode = prevEntropy->litlength_repeatMode; - LLtype = ZSTD_selectEncodingType(&nextEntropy->litlength_repeatMode, mostFrequent, nbSeq, LL_defaultNormLog, ZSTD_defaultAllowed); + LLtype = ZSTD_selectEncodingType(&nextEntropy->litlength_repeatMode, count, max, mostFrequent, nbSeq, LLFSELog, prevEntropy->litlengthCTable, LL_defaultNorm, LL_defaultNormLog, ZSTD_defaultAllowed, strategy); + assert(set_basic < set_compressed && set_rle < set_compressed); + assert(!(LLtype < set_compressed && nextEntropy->litlength_repeatMode != FSE_repeat_none)); /* We don't copy tables */ { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_LitLength, LLFSELog, (symbolEncodingType_e)LLtype, count, max, llCodeTable, nbSeq, LL_defaultNorm, LL_defaultNormLog, MaxLL, prevEntropy->litlengthCTable, sizeof(prevEntropy->litlengthCTable), @@ -1878,7 +2047,8 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, ZSTD_defaultPolicy_e const defaultPolicy = (max <= DefaultMaxOff) ? ZSTD_defaultAllowed : ZSTD_defaultDisallowed; DEBUGLOG(5, "Building OF table"); nextEntropy->offcode_repeatMode = prevEntropy->offcode_repeatMode; - Offtype = ZSTD_selectEncodingType(&nextEntropy->offcode_repeatMode, mostFrequent, nbSeq, OF_defaultNormLog, defaultPolicy); + Offtype = ZSTD_selectEncodingType(&nextEntropy->offcode_repeatMode, count, max, mostFrequent, nbSeq, OffFSELog, prevEntropy->offcodeCTable, OF_defaultNorm, OF_defaultNormLog, defaultPolicy, strategy); + assert(!(Offtype < set_compressed && nextEntropy->offcode_repeatMode != FSE_repeat_none)); /* We don't copy tables */ { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_OffsetBits, OffFSELog, (symbolEncodingType_e)Offtype, count, max, ofCodeTable, nbSeq, OF_defaultNorm, OF_defaultNormLog, DefaultMaxOff, prevEntropy->offcodeCTable, sizeof(prevEntropy->offcodeCTable), @@ -1891,7 +2061,8 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, size_t const mostFrequent = FSE_countFast_wksp(count, &max, mlCodeTable, nbSeq, workspace); DEBUGLOG(5, "Building ML table"); nextEntropy->matchlength_repeatMode = prevEntropy->matchlength_repeatMode; - MLtype = ZSTD_selectEncodingType(&nextEntropy->matchlength_repeatMode, mostFrequent, nbSeq, ML_defaultNormLog, ZSTD_defaultAllowed); + MLtype = ZSTD_selectEncodingType(&nextEntropy->matchlength_repeatMode, count, max, mostFrequent, nbSeq, MLFSELog, prevEntropy->matchlengthCTable, ML_defaultNorm, ML_defaultNormLog, ZSTD_defaultAllowed, strategy); + assert(!(MLtype < set_compressed && nextEntropy->matchlength_repeatMode != FSE_repeat_none)); /* We don't copy tables */ { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_MatchLength, MLFSELog, (symbolEncodingType_e)MLtype, count, max, mlCodeTable, nbSeq, ML_defaultNorm, ML_defaultNormLog, MaxML, prevEntropy->matchlengthCTable, sizeof(prevEntropy->matchlengthCTable), diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 0f1830a5..6c4e8bdc 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -53,11 +53,15 @@ typedef struct ZSTD_prefixDict_s { } ZSTD_prefixDict; typedef struct { + /* Huffman data + * Must be before the FSE data. + */ U32 hufCTable[HUF_CTABLE_SIZE_U32(255)]; + HUF_repeat hufCTable_repeatMode; + /* FSE data */ FSE_CTable offcodeCTable[FSE_CTABLE_SIZE_U32(OffFSELog, MaxOff)]; FSE_CTable matchlengthCTable[FSE_CTABLE_SIZE_U32(MLFSELog, MaxML)]; FSE_CTable litlengthCTable[FSE_CTABLE_SIZE_U32(LLFSELog, MaxLL)]; - HUF_repeat hufCTable_repeatMode; FSE_repeat offcode_repeatMode; FSE_repeat matchlength_repeatMode; FSE_repeat litlength_repeatMode; From e3959d5eba0cdccc7b97aa27e1f9ddc088b99cf9 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Tue, 22 May 2018 16:06:33 -0700 Subject: [PATCH 02/32] Fixes --- lib/common/fse.h | 5 +- lib/compress/zstd_compress.c | 109 ++++++++++++-------------- lib/compress/zstd_compress_internal.h | 16 ++-- lib/compress/zstd_opt.c | 22 +++--- 4 files changed, 74 insertions(+), 78 deletions(-) diff --git a/lib/common/fse.h b/lib/common/fse.h index 3d11a75e..e88a5ef5 100644 --- a/lib/common/fse.h +++ b/lib/common/fse.h @@ -402,7 +402,6 @@ typedef struct { const void* stateTable; const void* symbolTT; unsigned stateLog; - unsigned maxSymbolValue; } FSE_CState_t; static void FSE_initCState(FSE_CState_t* CStatePtr, const FSE_CTable* ct); @@ -539,13 +538,11 @@ MEM_STATIC void FSE_initCState(FSE_CState_t* statePtr, const FSE_CTable* ct) { const void* ptr = ct; const U16* u16ptr = (const U16*) ptr; - const U32 tableLog = MEM_read16(u16ptr); - const U32 maxSymbolValue = MEM_read16(u16ptr + 1); + const U32 tableLog = MEM_read16(ptr); statePtr->value = (ptrdiff_t)1<stateTable = u16ptr+2; statePtr->symbolTT = ((const U32*)ct + 1 + (tableLog ? (1<<(tableLog-1)) : 1)); statePtr->stateLog = tableLog; - statePtr->maxSymbolValue = maxSymbolValue; } diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 114845b1..22c704f1 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -946,10 +946,10 @@ static void ZSTD_reset_compressedBlockState(ZSTD_compressedBlockState_t* bs) int i; for (i = 0; i < ZSTD_REP_NUM; ++i) bs->rep[i] = repStartValue[i]; - bs->entropy.hufCTable_repeatMode = HUF_repeat_none; - bs->entropy.offcode_repeatMode = FSE_repeat_none; - bs->entropy.matchlength_repeatMode = FSE_repeat_none; - bs->entropy.litlength_repeatMode = FSE_repeat_none; + bs->entropy.huf.repeatMode = HUF_repeat_none; + bs->entropy.fse.offcode_repeatMode = FSE_repeat_none; + bs->entropy.fse.matchlength_repeatMode = FSE_repeat_none; + bs->entropy.fse.litlength_repeatMode = FSE_repeat_none; } /*! ZSTD_invalidateMatchState() @@ -1455,8 +1455,8 @@ static size_t ZSTD_compressRleLiteralsBlock (void* dst, size_t dstCapacity, cons static size_t ZSTD_minGain(size_t srcSize) { return (srcSize >> 6) + 2; } -static size_t ZSTD_compressLiterals (ZSTD_entropyCTables_t const* prevEntropy, - ZSTD_entropyCTables_t* nextEntropy, +static size_t ZSTD_compressLiterals (ZSTD_hufCTables_t const* prevHuf, + ZSTD_hufCTables_t* nextHuf, ZSTD_strategy strategy, int disableLiteralCompression, void* dst, size_t dstCapacity, const void* src, size_t srcSize, @@ -1473,27 +1473,25 @@ static size_t ZSTD_compressLiterals (ZSTD_entropyCTables_t const* prevEntropy, disableLiteralCompression); /* Prepare nextEntropy assuming reusing the existing table */ - nextEntropy->hufCTable_repeatMode = prevEntropy->hufCTable_repeatMode; - memcpy(nextEntropy->hufCTable, prevEntropy->hufCTable, - sizeof(prevEntropy->hufCTable)); + memcpy(nextHuf, prevHuf, sizeof(*prevHuf)); if (disableLiteralCompression) return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); /* small ? don't even attempt compression (speed opt) */ # define COMPRESS_LITERALS_SIZE_MIN 63 - { size_t const minLitSize = (prevEntropy->hufCTable_repeatMode == HUF_repeat_valid) ? 6 : COMPRESS_LITERALS_SIZE_MIN; + { size_t const minLitSize = (prevHuf->repeatMode == HUF_repeat_valid) ? 6 : COMPRESS_LITERALS_SIZE_MIN; if (srcSize <= minLitSize) return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); } if (dstCapacity < lhSize+1) return ERROR(dstSize_tooSmall); /* not enough space for compression */ - { HUF_repeat repeat = prevEntropy->hufCTable_repeatMode; + { HUF_repeat repeat = prevHuf->repeatMode; int const preferRepeat = strategy < ZSTD_lazy ? srcSize <= 1024 : 0; if (repeat == HUF_repeat_valid && lhSize == 3) singleStream = 1; cLitSize = singleStream ? HUF_compress1X_repeat(ostart+lhSize, dstCapacity-lhSize, src, srcSize, 255, 11, - workspace, HUF_WORKSPACE_SIZE, (HUF_CElt*)nextEntropy->hufCTable, &repeat, preferRepeat, bmi2) + workspace, HUF_WORKSPACE_SIZE, (HUF_CElt*)nextHuf->CTable, &repeat, preferRepeat, bmi2) : HUF_compress4X_repeat(ostart+lhSize, dstCapacity-lhSize, src, srcSize, 255, 11, - workspace, HUF_WORKSPACE_SIZE, (HUF_CElt*)nextEntropy->hufCTable, &repeat, preferRepeat, bmi2); + workspace, HUF_WORKSPACE_SIZE, (HUF_CElt*)nextHuf->CTable, &repeat, preferRepeat, bmi2); if (repeat != HUF_repeat_none) { /* reused the existing table */ hType = set_repeat; @@ -1501,17 +1499,17 @@ static size_t ZSTD_compressLiterals (ZSTD_entropyCTables_t const* prevEntropy, } if ((cLitSize==0) | (cLitSize >= srcSize - minGain) | ERR_isError(cLitSize)) { - memcpy(nextEntropy->hufCTable, prevEntropy->hufCTable, sizeof(prevEntropy->hufCTable)); + memcpy(nextHuf, prevHuf, sizeof(*prevHuf)); return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize); } if (cLitSize==1) { - memcpy(nextEntropy->hufCTable, prevEntropy->hufCTable, sizeof(prevEntropy->hufCTable)); + memcpy(nextHuf, prevHuf, sizeof(*prevHuf)); return ZSTD_compressRleLiteralsBlock(dst, dstCapacity, src, srcSize); } if (hType == set_compressed) { /* using a newly constructed table */ - nextEntropy->hufCTable_repeatMode = HUF_repeat_check; + nextHuf->repeatMode = HUF_repeat_check; } /* Build header */ @@ -1635,6 +1633,14 @@ static size_t ZSTD_crossEntropyCost(short const* norm, unsigned accuracyLog, } +static unsigned ZSTD_getFSEMaxSymbolValue(FSE_CTable const* ctable) { + void const* ptr = ctable; + U16 const* u16ptr = (U16 const*)ptr; + U32 const maxSymbolValue = MEM_read16(u16ptr + 1); + return maxSymbolValue; +} + + /** * Returns the cost in bits of encoding the distribution in count using ctable. * Returns an error if ctable cannot represent all the symbols in count. @@ -1649,9 +1655,9 @@ static size_t ZSTD_fseBitCost( unsigned s; FSE_CState_t cstate; FSE_initCState(&cstate, ctable); - if (cstate.maxSymbolValue < max) { + if (ZSTD_getFSEMaxSymbolValue(ctable) < max) { DEBUGLOG(5, "Repeat FSE_CTable has maxSymbolValue %u < %u", - cstate.maxSymbolValue, max); + ZSTD_getFSEMaxSymbolValue(ctable), max); return ERROR(GENERIC); } for (s = 0; s <= max; ++s) { @@ -1964,9 +1970,9 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, const int longOffsets = cctxParams->cParams.windowLog > STREAM_ACCUMULATOR_MIN; ZSTD_strategy const strategy = cctxParams->cParams.strategy; U32 count[MaxSeq+1]; - FSE_CTable* CTable_LitLength = nextEntropy->litlengthCTable; - FSE_CTable* CTable_OffsetBits = nextEntropy->offcodeCTable; - FSE_CTable* CTable_MatchLength = nextEntropy->matchlengthCTable; + FSE_CTable* CTable_LitLength = nextEntropy->fse.litlengthCTable; + FSE_CTable* CTable_OffsetBits = nextEntropy->fse.offcodeCTable; + FSE_CTable* CTable_MatchLength = nextEntropy->fse.matchlengthCTable; U32 LLtype, Offtype, MLtype; /* compressed, raw or rle */ const seqDef* const sequences = seqStorePtr->sequencesStart; const BYTE* const ofCodeTable = seqStorePtr->ofCode; @@ -1984,7 +1990,7 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, { const BYTE* const literals = seqStorePtr->litStart; size_t const litSize = seqStorePtr->lit - literals; size_t const cSize = ZSTD_compressLiterals( - prevEntropy, nextEntropy, + &prevEntropy->huf, &nextEntropy->huf, cctxParams->cParams.strategy, cctxParams->disableLiteralCompression, op, dstCapacity, literals, litSize, @@ -2004,19 +2010,8 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, else op[0]=0xFF, MEM_writeLE16(op+1, (U16)(nbSeq - LONGNBSEQ)), op+=3; if (nbSeq==0) { - /* Check that all the Huffman data is first */ - ZSTD_STATIC_ASSERT(offsetof(ZSTD_entropyCTables_t, hufCTable) == 0); - ZSTD_STATIC_ASSERT( - offsetof(ZSTD_entropyCTables_t, hufCTable_repeatMode) == - sizeof(prevEntropy->hufCTable)); - ZSTD_STATIC_ASSERT( - offsetof(ZSTD_entropyCTables_t, offcodeCTable) == - sizeof(prevEntropy->hufCTable) + sizeof(prevEntropy->hufCTable_repeatMode)); - /* Copy starting at the first FSE element */ - memcpy( - nextEntropy->offcodeCTable, - prevEntropy->offcodeCTable, - sizeof(*prevEntropy) - offsetof(ZSTD_entropyCTables_t, offcodeCTable)); + /* Copy the old tables over as if we repeated them */ + memcpy(&nextEntropy->fse, &prevEntropy->fse, sizeof(prevEntropy->fse)); return op - ostart; } @@ -2029,13 +2024,13 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, { U32 max = MaxLL; size_t const mostFrequent = FSE_countFast_wksp(count, &max, llCodeTable, nbSeq, workspace); DEBUGLOG(5, "Building LL table"); - nextEntropy->litlength_repeatMode = prevEntropy->litlength_repeatMode; - LLtype = ZSTD_selectEncodingType(&nextEntropy->litlength_repeatMode, count, max, mostFrequent, nbSeq, LLFSELog, prevEntropy->litlengthCTable, LL_defaultNorm, LL_defaultNormLog, ZSTD_defaultAllowed, strategy); + nextEntropy->fse.litlength_repeatMode = prevEntropy->fse.litlength_repeatMode; + LLtype = ZSTD_selectEncodingType(&nextEntropy->fse.litlength_repeatMode, count, max, mostFrequent, nbSeq, LLFSELog, prevEntropy->fse.litlengthCTable, LL_defaultNorm, LL_defaultNormLog, ZSTD_defaultAllowed, strategy); assert(set_basic < set_compressed && set_rle < set_compressed); - assert(!(LLtype < set_compressed && nextEntropy->litlength_repeatMode != FSE_repeat_none)); /* We don't copy tables */ + assert(!(LLtype < set_compressed && nextEntropy->fse.litlength_repeatMode != FSE_repeat_none)); /* We don't copy tables */ { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_LitLength, LLFSELog, (symbolEncodingType_e)LLtype, count, max, llCodeTable, nbSeq, LL_defaultNorm, LL_defaultNormLog, MaxLL, - prevEntropy->litlengthCTable, sizeof(prevEntropy->litlengthCTable), + prevEntropy->fse.litlengthCTable, sizeof(prevEntropy->fse.litlengthCTable), workspace, HUF_WORKSPACE_SIZE); if (ZSTD_isError(countSize)) return countSize; op += countSize; @@ -2046,12 +2041,12 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, /* We can only use the basic table if max <= DefaultMaxOff, otherwise the offsets are too large */ ZSTD_defaultPolicy_e const defaultPolicy = (max <= DefaultMaxOff) ? ZSTD_defaultAllowed : ZSTD_defaultDisallowed; DEBUGLOG(5, "Building OF table"); - nextEntropy->offcode_repeatMode = prevEntropy->offcode_repeatMode; - Offtype = ZSTD_selectEncodingType(&nextEntropy->offcode_repeatMode, count, max, mostFrequent, nbSeq, OffFSELog, prevEntropy->offcodeCTable, OF_defaultNorm, OF_defaultNormLog, defaultPolicy, strategy); - assert(!(Offtype < set_compressed && nextEntropy->offcode_repeatMode != FSE_repeat_none)); /* We don't copy tables */ + nextEntropy->fse.offcode_repeatMode = prevEntropy->fse.offcode_repeatMode; + Offtype = ZSTD_selectEncodingType(&nextEntropy->fse.offcode_repeatMode, count, max, mostFrequent, nbSeq, OffFSELog, prevEntropy->fse.offcodeCTable, OF_defaultNorm, OF_defaultNormLog, defaultPolicy, strategy); + assert(!(Offtype < set_compressed && nextEntropy->fse.offcode_repeatMode != FSE_repeat_none)); /* We don't copy tables */ { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_OffsetBits, OffFSELog, (symbolEncodingType_e)Offtype, count, max, ofCodeTable, nbSeq, OF_defaultNorm, OF_defaultNormLog, DefaultMaxOff, - prevEntropy->offcodeCTable, sizeof(prevEntropy->offcodeCTable), + prevEntropy->fse.offcodeCTable, sizeof(prevEntropy->fse.offcodeCTable), workspace, HUF_WORKSPACE_SIZE); if (ZSTD_isError(countSize)) return countSize; op += countSize; @@ -2060,12 +2055,12 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, { U32 max = MaxML; size_t const mostFrequent = FSE_countFast_wksp(count, &max, mlCodeTable, nbSeq, workspace); DEBUGLOG(5, "Building ML table"); - nextEntropy->matchlength_repeatMode = prevEntropy->matchlength_repeatMode; - MLtype = ZSTD_selectEncodingType(&nextEntropy->matchlength_repeatMode, count, max, mostFrequent, nbSeq, MLFSELog, prevEntropy->matchlengthCTable, ML_defaultNorm, ML_defaultNormLog, ZSTD_defaultAllowed, strategy); - assert(!(MLtype < set_compressed && nextEntropy->matchlength_repeatMode != FSE_repeat_none)); /* We don't copy tables */ + nextEntropy->fse.matchlength_repeatMode = prevEntropy->fse.matchlength_repeatMode; + MLtype = ZSTD_selectEncodingType(&nextEntropy->fse.matchlength_repeatMode, count, max, mostFrequent, nbSeq, MLFSELog, prevEntropy->fse.matchlengthCTable, ML_defaultNorm, ML_defaultNormLog, ZSTD_defaultAllowed, strategy); + assert(!(MLtype < set_compressed && nextEntropy->fse.matchlength_repeatMode != FSE_repeat_none)); /* We don't copy tables */ { size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_MatchLength, MLFSELog, (symbolEncodingType_e)MLtype, count, max, mlCodeTable, nbSeq, ML_defaultNorm, ML_defaultNormLog, MaxML, - prevEntropy->matchlengthCTable, sizeof(prevEntropy->matchlengthCTable), + prevEntropy->fse.matchlengthCTable, sizeof(prevEntropy->fse.matchlengthCTable), workspace, HUF_WORKSPACE_SIZE); if (ZSTD_isError(countSize)) return countSize; op += countSize; @@ -2113,8 +2108,8 @@ MEM_STATIC size_t ZSTD_compressSequences(seqStore_t* seqStorePtr, * block. After the first block, the offcode table might not have large * enough codes to represent the offsets in the data. */ - if (nextEntropy->offcode_repeatMode == FSE_repeat_valid) - nextEntropy->offcode_repeatMode = FSE_repeat_check; + if (nextEntropy->fse.offcode_repeatMode == FSE_repeat_valid) + nextEntropy->fse.offcode_repeatMode = FSE_repeat_check; return cSize; } @@ -2555,7 +2550,7 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_compressedBlockState_t* bs, dictPtr += 4; { unsigned maxSymbolValue = 255; - size_t const hufHeaderSize = HUF_readCTable((HUF_CElt*)bs->entropy.hufCTable, &maxSymbolValue, dictPtr, dictEnd-dictPtr); + size_t const hufHeaderSize = HUF_readCTable((HUF_CElt*)bs->entropy.huf.CTable, &maxSymbolValue, dictPtr, dictEnd-dictPtr); if (HUF_isError(hufHeaderSize)) return ERROR(dictionary_corrupted); if (maxSymbolValue < 255) return ERROR(dictionary_corrupted); dictPtr += hufHeaderSize; @@ -2567,7 +2562,7 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_compressedBlockState_t* bs, if (offcodeLog > OffFSELog) return ERROR(dictionary_corrupted); /* Defer checking offcodeMaxValue because we need to know the size of the dictionary content */ /* fill all offset symbols to avoid garbage at end of table */ - CHECK_E( FSE_buildCTable_wksp(bs->entropy.offcodeCTable, offcodeNCount, MaxOff, offcodeLog, workspace, HUF_WORKSPACE_SIZE), + CHECK_E( FSE_buildCTable_wksp(bs->entropy.fse.offcodeCTable, offcodeNCount, MaxOff, offcodeLog, workspace, HUF_WORKSPACE_SIZE), dictionary_corrupted); dictPtr += offcodeHeaderSize; } @@ -2579,7 +2574,7 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_compressedBlockState_t* bs, if (matchlengthLog > MLFSELog) return ERROR(dictionary_corrupted); /* Every match length code must have non-zero probability */ CHECK_F( ZSTD_checkDictNCount(matchlengthNCount, matchlengthMaxValue, MaxML)); - CHECK_E( FSE_buildCTable_wksp(bs->entropy.matchlengthCTable, matchlengthNCount, matchlengthMaxValue, matchlengthLog, workspace, HUF_WORKSPACE_SIZE), + CHECK_E( FSE_buildCTable_wksp(bs->entropy.fse.matchlengthCTable, matchlengthNCount, matchlengthMaxValue, matchlengthLog, workspace, HUF_WORKSPACE_SIZE), dictionary_corrupted); dictPtr += matchlengthHeaderSize; } @@ -2591,7 +2586,7 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_compressedBlockState_t* bs, if (litlengthLog > LLFSELog) return ERROR(dictionary_corrupted); /* Every literal length code must have non-zero probability */ CHECK_F( ZSTD_checkDictNCount(litlengthNCount, litlengthMaxValue, MaxLL)); - CHECK_E( FSE_buildCTable_wksp(bs->entropy.litlengthCTable, litlengthNCount, litlengthMaxValue, litlengthLog, workspace, HUF_WORKSPACE_SIZE), + CHECK_E( FSE_buildCTable_wksp(bs->entropy.fse.litlengthCTable, litlengthNCount, litlengthMaxValue, litlengthLog, workspace, HUF_WORKSPACE_SIZE), dictionary_corrupted); dictPtr += litlengthHeaderSize; } @@ -2617,10 +2612,10 @@ static size_t ZSTD_loadZstdDictionary(ZSTD_compressedBlockState_t* bs, if (bs->rep[u] > dictContentSize) return ERROR(dictionary_corrupted); } } - bs->entropy.hufCTable_repeatMode = HUF_repeat_valid; - bs->entropy.offcode_repeatMode = FSE_repeat_valid; - bs->entropy.matchlength_repeatMode = FSE_repeat_valid; - bs->entropy.litlength_repeatMode = FSE_repeat_valid; + bs->entropy.huf.repeatMode = HUF_repeat_valid; + bs->entropy.fse.offcode_repeatMode = FSE_repeat_valid; + bs->entropy.fse.matchlength_repeatMode = FSE_repeat_valid; + bs->entropy.fse.litlength_repeatMode = FSE_repeat_valid; CHECK_F(ZSTD_loadDictionaryContent(ms, params, dictPtr, dictContentSize, dtlm)); return dictID; } diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 6c4e8bdc..937234c3 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -53,18 +53,22 @@ typedef struct ZSTD_prefixDict_s { } ZSTD_prefixDict; typedef struct { - /* Huffman data - * Must be before the FSE data. - */ - U32 hufCTable[HUF_CTABLE_SIZE_U32(255)]; - HUF_repeat hufCTable_repeatMode; - /* FSE data */ + U32 CTable[HUF_CTABLE_SIZE_U32(255)]; + HUF_repeat repeatMode; +} ZSTD_hufCTables_t; + +typedef struct { FSE_CTable offcodeCTable[FSE_CTABLE_SIZE_U32(OffFSELog, MaxOff)]; FSE_CTable matchlengthCTable[FSE_CTABLE_SIZE_U32(MLFSELog, MaxML)]; FSE_CTable litlengthCTable[FSE_CTABLE_SIZE_U32(LLFSELog, MaxLL)]; FSE_repeat offcode_repeatMode; FSE_repeat matchlength_repeatMode; FSE_repeat litlength_repeatMode; +} ZSTD_fseCTables_t; + +typedef struct { + ZSTD_hufCTables_t huf; + ZSTD_fseCTables_t fse; } ZSTD_entropyCTables_t; typedef struct { diff --git a/lib/compress/zstd_opt.c b/lib/compress/zstd_opt.c index 3a48187c..521fbbf3 100644 --- a/lib/compress/zstd_opt.c +++ b/lib/compress/zstd_opt.c @@ -39,7 +39,7 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, optPtr->priceType = zop_predef; assert(optPtr->symbolCosts != NULL); - if (optPtr->symbolCosts->hufCTable_repeatMode == HUF_repeat_valid) { /* huffman table presumed generated by dictionary */ + if (optPtr->symbolCosts->huf.repeatMode == HUF_repeat_valid) { /* huffman table presumed generated by dictionary */ if (srcSize <= 8192) /* heuristic */ optPtr->priceType = zop_static; else { @@ -52,7 +52,7 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, { unsigned lit; for (lit=0; lit<=MaxLit; lit++) { U32 const scaleLog = 11; /* scale to 2K */ - U32 const bitCost = HUF_getNbBits(optPtr->symbolCosts->hufCTable, lit); + U32 const bitCost = HUF_getNbBits(optPtr->symbolCosts->huf.CTable, lit); assert(bitCost <= scaleLog); optPtr->litFreq[lit] = bitCost ? 1 << (scaleLog-bitCost) : 1 /*minimum to calculate cost*/; optPtr->litSum += optPtr->litFreq[lit]; @@ -60,7 +60,7 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, { unsigned ll; FSE_CState_t llstate; - FSE_initCState(&llstate, optPtr->symbolCosts->litlengthCTable); + FSE_initCState(&llstate, optPtr->symbolCosts->fse.litlengthCTable); optPtr->litLengthSum = 0; for (ll=0; ll<=MaxLL; ll++) { U32 const scaleLog = 10; /* scale to 1K */ @@ -72,7 +72,7 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, { unsigned ml; FSE_CState_t mlstate; - FSE_initCState(&mlstate, optPtr->symbolCosts->matchlengthCTable); + FSE_initCState(&mlstate, optPtr->symbolCosts->fse.matchlengthCTable); optPtr->matchLengthSum = 0; for (ml=0; ml<=MaxML; ml++) { U32 const scaleLog = 10; @@ -84,7 +84,7 @@ static void ZSTD_rescaleFreqs(optState_t* const optPtr, { unsigned of; FSE_CState_t ofstate; - FSE_initCState(&ofstate, optPtr->symbolCosts->offcodeCTable); + FSE_initCState(&ofstate, optPtr->symbolCosts->fse.offcodeCTable); optPtr->offCodeSum = 0; for (of=0; of<=MaxOff; of++) { U32 const scaleLog = 10; @@ -180,9 +180,9 @@ static U32 ZSTD_rawLiteralsCost(const BYTE* const literals, U32 const litLength, if (optPtr->priceType == zop_static) { U32 u, cost; assert(optPtr->symbolCosts != NULL); - assert(optPtr->symbolCosts->hufCTable_repeatMode == HUF_repeat_valid); + assert(optPtr->symbolCosts->huf.repeatMode == HUF_repeat_valid); for (u=0, cost=0; u < litLength; u++) - cost += HUF_getNbBits(optPtr->symbolCosts->hufCTable, literals[u]); + cost += HUF_getNbBits(optPtr->symbolCosts->huf.CTable, literals[u]); return cost * BITCOST_MULTIPLIER; } @@ -202,7 +202,7 @@ static U32 ZSTD_litLengthPrice(U32 const litLength, const optState_t* const optP if (optPtr->priceType == zop_static) { U32 const llCode = ZSTD_LLcode(litLength); FSE_CState_t cstate; - FSE_initCState(&cstate, optPtr->symbolCosts->litlengthCTable); + FSE_initCState(&cstate, optPtr->symbolCosts->fse.litlengthCTable); { U32 const price = LL_bits[llCode]*BITCOST_MULTIPLIER + BITCOST_SYMBOL(cstate.symbolTT, cstate.stateLog, llCode); DEBUGLOG(8, "ZSTD_litLengthPrice: ll=%u, bitCost=%.2f", litLength, (double)price / BITCOST_MULTIPLIER); return price; @@ -234,7 +234,7 @@ static int ZSTD_litLengthContribution(U32 const litLength, const optState_t* con if (optPtr->priceType == zop_static) { U32 const llCode = ZSTD_LLcode(litLength); FSE_CState_t cstate; - FSE_initCState(&cstate, optPtr->symbolCosts->litlengthCTable); + FSE_initCState(&cstate, optPtr->symbolCosts->fse.litlengthCTable); return (int)(LL_bits[llCode] * BITCOST_MULTIPLIER) + BITCOST_SYMBOL(cstate.symbolTT, cstate.stateLog, llCode) - BITCOST_SYMBOL(cstate.symbolTT, cstate.stateLog, 0); @@ -284,8 +284,8 @@ ZSTD_getMatchPrice(U32 const offset, U32 const matchLength, if (optPtr->priceType == zop_static) { U32 const mlCode = ZSTD_MLcode(mlBase); FSE_CState_t mlstate, offstate; - FSE_initCState(&mlstate, optPtr->symbolCosts->matchlengthCTable); - FSE_initCState(&offstate, optPtr->symbolCosts->offcodeCTable); + FSE_initCState(&mlstate, optPtr->symbolCosts->fse.matchlengthCTable); + FSE_initCState(&offstate, optPtr->symbolCosts->fse.offcodeCTable); return BITCOST_SYMBOL(offstate.symbolTT, offstate.stateLog, offCode) + offCode*BITCOST_MULTIPLIER + BITCOST_SYMBOL(mlstate.symbolTT, mlstate.stateLog, mlCode) + ML_bits[mlCode]*BITCOST_MULTIPLIER; } From 73f4c890cd07dbc6e0f24f7399456a060a5a7105 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Tue, 22 May 2018 16:12:33 -0700 Subject: [PATCH 03/32] Clarify what happens when Number_of_Sequences == 0 --- doc/zstd_compression_format.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/zstd_compression_format.md b/doc/zstd_compression_format.md index 66819d13..d9547544 100644 --- a/doc/zstd_compression_format.md +++ b/doc/zstd_compression_format.md @@ -603,6 +603,7 @@ Let's call its first byte `byte0`. - `if (byte0 == 0)` : there are no sequences. The sequence section stops there. Decompressed content is defined entirely as Literals Section content. + The FSE tables used in `Repeat_Mode` aren't updated. - `if (byte0 < 128)` : `Number_of_Sequences = byte0` . Uses 1 byte. - `if (byte0 < 255)` : `Number_of_Sequences = ((byte0-128) << 8) + byte1` . Uses 2 bytes. - `if (byte0 == 255)`: `Number_of_Sequences = byte1 + (byte2<<8) + 0x7F00` . Uses 3 bytes. @@ -631,7 +632,7 @@ They follow the same enumeration : No distribution table will be present. - `RLE_Mode` : The table description consists of a single byte. This code will be repeated for all sequences. -- `Repeat_Mode` : The table used in the previous `Compressed_Block` will be used again, +- `Repeat_Mode` : The table used in the previous `Compressed_Block` with `Number_of_Sequences > 0` will be used again, or if this is the first block, table in the dictionary will be used No distribution table will be present. Note that this includes `RLE_mode`, so if `Repeat_Mode` follows `RLE_Mode`, the same symbol will be repeated. From a97e9a627adf18b0efc9f2c4aeeac25b2a2af730 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Wed, 23 May 2018 12:16:00 -0700 Subject: [PATCH 04/32] [zstd] Fix decompression edge case This edge case is only possible with the new optimal encoding selector, since before zstd would always choose `set_basic` for small numbers of sequences. Fix `FSE_readNCount()` to support buffers < 4 bytes. Credit to OSS-Fuzz --- lib/common/entropy_common.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/common/entropy_common.c b/lib/common/entropy_common.c index 344c3236..a8d0b146 100644 --- a/lib/common/entropy_common.c +++ b/lib/common/entropy_common.c @@ -72,7 +72,14 @@ size_t FSE_readNCount (short* normalizedCounter, unsigned* maxSVPtr, unsigned* t unsigned charnum = 0; int previous0 = 0; - if (hbSize < 4) return ERROR(srcSize_wrong); + if (hbSize < 4) { + /* This function only works when hbSize >= 4 */ + char buffer[4]; + memset(buffer, 0, sizeof(buffer)); + memcpy(buffer, headerBuffer, hbSize); + return FSE_readNCount(normalizedCounter, maxSVPtr, tableLogPtr, buffer, sizeof(buffer)); + } + bitStream = MEM_readLE32(ip); nbBits = (bitStream & 0xF) + FSE_MIN_TABLELOG; /* extract tableLog */ if (nbBits > FSE_TABLELOG_ABSOLUTE_MAX) return ERROR(tableLog_tooLarge); @@ -105,6 +112,7 @@ size_t FSE_readNCount (short* normalizedCounter, unsigned* maxSVPtr, unsigned* t if (n0 > *maxSVPtr) return ERROR(maxSymbolValue_tooSmall); while (charnum < n0) normalizedCounter[charnum++] = 0; if ((ip <= iend-7) || (ip + (bitCount>>3) <= iend-4)) { + assert((bitCount >> 3) <= 3); /* For first condition to work */ ip += bitCount>>3; bitCount &= 7; bitStream = MEM_readLE32(ip) >> bitCount; From c92dd11940f68c71d3b627de2612537b7e2ae92a Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Wed, 23 May 2018 14:47:20 -0700 Subject: [PATCH 05/32] Error if reported size is too large in edge case --- lib/common/entropy_common.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/common/entropy_common.c b/lib/common/entropy_common.c index a8d0b146..2edb6e9b 100644 --- a/lib/common/entropy_common.c +++ b/lib/common/entropy_common.c @@ -77,8 +77,13 @@ size_t FSE_readNCount (short* normalizedCounter, unsigned* maxSVPtr, unsigned* t char buffer[4]; memset(buffer, 0, sizeof(buffer)); memcpy(buffer, headerBuffer, hbSize); - return FSE_readNCount(normalizedCounter, maxSVPtr, tableLogPtr, buffer, sizeof(buffer)); + size_t const countSize = FSE_readNCount(normalizedCounter, maxSVPtr, tableLogPtr, + buffer, sizeof(buffer)); + if (FSE_isError(countSize)) return countSize; + if (countSize > hbSize) return ERROR(corruption_detected); + return countSize; } + assert(hbSize >= 4); bitStream = MEM_readLE32(ip); nbBits = (bitStream & 0xF) + FSE_MIN_TABLELOG; /* extract tableLog */ From d18a4057796a43ce4f3d1c928d612e91e297b061 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Fri, 27 Apr 2018 18:46:59 -0400 Subject: [PATCH 06/32] Refer to the Dictionary Match State In-Place (Sometimes) --- lib/compress/zstd_compress.c | 68 +++++++++++++++++---------- lib/compress/zstd_compress_internal.h | 6 ++- 2 files changed, 48 insertions(+), 26 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 22c704f1..18091b09 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -963,6 +963,7 @@ static void ZSTD_invalidateMatchState(ZSTD_matchState_t* ms) ms->nextToUpdate = ms->window.dictLimit + 1; ms->loadedDictEnd = 0; ms->opt.litLengthSum = 0; /* force reset of btopt stats */ + ms->dictMatchState = NULL; } /*! ZSTD_continueCCtx() : @@ -1203,42 +1204,61 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, U64 pledgedSrcSize, ZSTD_buffered_policy_e zbuff) { + /* We have a choice between copying the dictionary context into the working + * context, or referencing the dictionary context from the working context + * in-place. We decide here which strategy to use. */ + /* TODO: pick reasonable cut-off size, handle ZSTD_CONTENTSIZE_UNKNOWN */ + int attachDict = pledgedSrcSize < 64 KB + && cdict->cParams.strategy == ZSTD_fast + && ZSTD_equivalentCParams(cctx->appliedParams.cParams, + cdict->cParams); + { unsigned const windowLog = params.cParams.windowLog; assert(windowLog != 0); /* Copy only compression parameters related to tables. */ params.cParams = cdict->cParams; params.cParams.windowLog = windowLog; - ZSTD_resetCCtx_internal(cctx, params, pledgedSrcSize, ZSTDcrp_noMemset, zbuff); + ZSTD_resetCCtx_internal(cctx, params, pledgedSrcSize, + attachDict ? ZSTDcrp_continue : ZSTDcrp_noMemset, + zbuff); assert(cctx->appliedParams.cParams.strategy == cdict->cParams.strategy); assert(cctx->appliedParams.cParams.hashLog == cdict->cParams.hashLog); assert(cctx->appliedParams.cParams.chainLog == cdict->cParams.chainLog); } - /* copy tables */ - { size_t const chainSize = (cdict->cParams.strategy == ZSTD_fast) ? 0 : ((size_t)1 << cdict->cParams.chainLog); - size_t const hSize = (size_t)1 << cdict->cParams.hashLog; - size_t const tableSpace = (chainSize + hSize) * sizeof(U32); - assert((U32*)cctx->blockState.matchState.chainTable == (U32*)cctx->blockState.matchState.hashTable + hSize); /* chainTable must follow hashTable */ - assert((U32*)cctx->blockState.matchState.hashTable3 == (U32*)cctx->blockState.matchState.chainTable + chainSize); - assert((U32*)cdict->matchState.chainTable == (U32*)cdict->matchState.hashTable + hSize); /* chainTable must follow hashTable */ - assert((U32*)cdict->matchState.hashTable3 == (U32*)cdict->matchState.chainTable + chainSize); - memcpy(cctx->blockState.matchState.hashTable, cdict->matchState.hashTable, tableSpace); /* presumes all tables follow each other */ - } - /* Zero the hashTable3, since the cdict never fills it */ - { size_t const h3Size = (size_t)1 << cctx->blockState.matchState.hashLog3; - assert(cdict->matchState.hashLog3 == 0); - memset(cctx->blockState.matchState.hashTable3, 0, h3Size * sizeof(U32)); + if (attachDict) { + DEBUGLOG(4, "attaching dictionary into context"); + cctx->blockState.matchState.dictMatchState = &cdict->matchState; + } else { + DEBUGLOG(4, "copying dictionary into context"); + /* copy tables */ + { size_t const chainSize = (cdict->cParams.strategy == ZSTD_fast) ? 0 : ((size_t)1 << cdict->cParams.chainLog); + size_t const hSize = (size_t)1 << cdict->cParams.hashLog; + size_t const tableSpace = (chainSize + hSize) * sizeof(U32); + assert((U32*)cctx->blockState.matchState.chainTable == (U32*)cctx->blockState.matchState.hashTable + hSize); /* chainTable must follow hashTable */ + assert((U32*)cctx->blockState.matchState.hashTable3 == (U32*)cctx->blockState.matchState.chainTable + chainSize); + assert((U32*)cdict->matchState.chainTable == (U32*)cdict->matchState.hashTable + hSize); /* chainTable must follow hashTable */ + assert((U32*)cdict->matchState.hashTable3 == (U32*)cdict->matchState.chainTable + chainSize); + memcpy(cctx->blockState.matchState.hashTable, cdict->matchState.hashTable, tableSpace); /* presumes all tables follow each other */ + } + + /* Zero the hashTable3, since the cdict never fills it */ + { size_t const h3Size = (size_t)1 << cctx->blockState.matchState.hashLog3; + assert(cdict->matchState.hashLog3 == 0); + memset(cctx->blockState.matchState.hashTable3, 0, h3Size * sizeof(U32)); + } + + /* copy dictionary offsets */ + { + ZSTD_matchState_t const* srcMatchState = &cdict->matchState; + ZSTD_matchState_t* dstMatchState = &cctx->blockState.matchState; + dstMatchState->window = srcMatchState->window; + dstMatchState->nextToUpdate = srcMatchState->nextToUpdate; + dstMatchState->nextToUpdate3= srcMatchState->nextToUpdate3; + dstMatchState->loadedDictEnd= srcMatchState->loadedDictEnd; + } } - /* copy dictionary offsets */ - { - ZSTD_matchState_t const* srcMatchState = &cdict->matchState; - ZSTD_matchState_t* dstMatchState = &cctx->blockState.matchState; - dstMatchState->window = srcMatchState->window; - dstMatchState->nextToUpdate = srcMatchState->nextToUpdate; - dstMatchState->nextToUpdate3= srcMatchState->nextToUpdate3; - dstMatchState->loadedDictEnd= srcMatchState->loadedDictEnd; - } cctx->dictID = cdict->dictID; /* copy block state */ diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 937234c3..9209fb02 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -122,7 +122,8 @@ typedef struct { U32 lowLimit; /* below that point, no more data */ } ZSTD_window_t; -typedef struct { +typedef struct ZSTD_matchState_t ZSTD_matchState_t; +struct ZSTD_matchState_t { ZSTD_window_t window; /* State for window round buffer management */ U32 loadedDictEnd; /* index of end of dictionary */ U32 nextToUpdate; /* index from which to continue table update */ @@ -132,7 +133,8 @@ typedef struct { U32* hashTable3; U32* chainTable; optState_t opt; /* optimal parser state */ -} ZSTD_matchState_t; + const ZSTD_matchState_t *dictMatchState; +}; typedef struct { ZSTD_compressedBlockState_t* prevCBlock; From 8d24ff03534daba8b8fd2169c1dc6873548dda04 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Sat, 28 Apr 2018 00:42:37 -0400 Subject: [PATCH 07/32] Preliminary Support in ZSTD_compressBlock_fast_generic() for Ext Dict Ctx --- lib/compress/zstd_compress_internal.h | 2 + lib/compress/zstd_fast.c | 92 +++++++++++++++++++++------ 2 files changed, 75 insertions(+), 19 deletions(-) diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 9209fb02..05685e55 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -250,6 +250,8 @@ struct ZSTD_CCtx_s { typedef enum { ZSTD_dtlm_fast, ZSTD_dtlm_full } ZSTD_dictTableLoadMethod_e; +typedef enum { ZSTD_noDictMatchState, ZSTD_hasDictMatchState } ZSTD_hasDictMatchState_e; + typedef size_t (*ZSTD_blockCompressor) ( ZSTD_matchState_t* bs, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index 22b84d1c..df4423fc 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -45,7 +45,8 @@ FORCE_INLINE_TEMPLATE size_t ZSTD_compressBlock_fast_generic( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize, - U32 const hlog, U32 const stepSize, U32 const mls) + U32 const hlog, U32 const stepSize, U32 const mls, + ZSTD_hasDictMatchState_e const hasDict) { U32* const hashTable = ms->hashTable; const BYTE* const base = ms->window.base; @@ -59,6 +60,19 @@ size_t ZSTD_compressBlock_fast_generic( U32 offset_1=rep[0], offset_2=rep[1]; U32 offsetSaved = 0; + const ZSTD_matchState_t* const dms = ms->dictMatchState; + const U32* const dictHashTable = hasDict == ZSTD_hasDictMatchState ? + dms->hashTable : NULL; + const U32 dictLowestIndex = hasDict == ZSTD_hasDictMatchState ? + dms->window.dictLimit : 0; + const BYTE* const dictBase = hasDict == ZSTD_hasDictMatchState ? + dms->window.base : NULL; + const BYTE* const dictLowest = hasDict == ZSTD_hasDictMatchState ? + dictBase + dictLowestIndex : NULL; + const BYTE* const dictEnd = hasDict == ZSTD_hasDictMatchState ? + dms->window.nextSrc : NULL; + const U32 dictIndexDelta = lowestIndex - (dictEnd - dictBase); + /* init */ ip += (ip==lowest); { U32 const maxRep = (U32)(ip-lowest); @@ -75,19 +89,41 @@ size_t ZSTD_compressBlock_fast_generic( const BYTE* match = base + matchIndex; hashTable[h] = current; /* update hash table */ - if ((offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1))) { + if ((hasDict != ZSTD_hasDictMatchState || current >= lowestIndex + offset_1) + && (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, ip-anchor, anchor, 0, mLength-MINMATCH); } else { if ( (matchIndex <= lowestIndex) || (MEM_read32(match) != MEM_read32(ip)) ) { - assert(stepSize >= 1); - ip += ((ip-anchor) >> kSearchStrength) + stepSize; - continue; - } - mLength = ZSTD_count(ip+4, match+4, iend) + 4; - { U32 const offset = (U32)(ip-match); + if (hasDict == ZSTD_hasDictMatchState) { + U32 const dictMatchIndex = dictHashTable[h]; + const BYTE* dictMatch = dictBase + dictMatchIndex; + if (dictMatchIndex <= dictLowestIndex || + MEM_read32(dictMatch) != MEM_read32(ip)) { + assert(stepSize >= 1); + ip += ((ip-anchor) >> kSearchStrength) + stepSize; + continue; + } + + mLength = ZSTD_count_2segments(ip+4, dictMatch+4, iend, dictEnd, istart) + 4; + { U32 const offset = (U32)(current-dictMatchIndex-dictIndexDelta); + DEBUGLOG(6, "ip %p (%u) dictMatch %p (%u) idxDelta %u", ip, current, dictMatch, dictMatchIndex, dictIndexDelta); + while (((ip>anchor) & (dictMatch>dictLowest)) && (ip[-1] == dictMatch[-1])) { ip--; dictMatch--; mLength++; } /* catch up */ + offset_2 = offset_1; + offset_1 = offset; + ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); + } + + } else { + assert(stepSize >= 1); + ip += ((ip-anchor) >> kSearchStrength) + stepSize; + continue; + } + } else { + U32 const offset = (U32)(ip-match); + mLength = ZSTD_count(ip+4, match+4, iend) + 4; while (((ip>anchor) & (match>lowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ offset_2 = offset_1; offset_1 = offset; @@ -104,6 +140,7 @@ size_t ZSTD_compressBlock_fast_generic( hashTable[ZSTD_hashPtr(ip-2, hlog, mls)] = (U32)(ip-2-base); /* check immediate repcode */ while ( (ip <= ilimit) + && (hasDict != ZSTD_hasDictMatchState || ip - offset_2 >= istart) && ( (offset_2>0) & (MEM_read32(ip) == MEM_read32(ip - offset_2)) )) { /* store sequence */ @@ -132,17 +169,34 @@ size_t ZSTD_compressBlock_fast( U32 const hlog = cParams->hashLog; U32 const mls = cParams->searchLength; U32 const stepSize = cParams->targetLength; - switch(mls) - { - default: /* includes case 3 */ - case 4 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 4); - case 5 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 5); - case 6 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 6); - case 7 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 7); + if (ms->dictMatchState != NULL) { + ZSTD_hasDictMatchState_e const hdms = ZSTD_hasDictMatchState; + switch(mls) + { + default: /* includes case 3 */ + case 4 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 4, hdms); + case 5 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 5, hdms); + case 6 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 6, hdms); + case 7 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 7, hdms); + } + } else { + ZSTD_hasDictMatchState_e const hdms = ZSTD_noDictMatchState; + switch(mls) + { + default: /* includes case 3 */ + case 4 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 4, hdms); + case 5 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 5, hdms); + case 6 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 6, hdms); + case 7 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 7, hdms); + } } } From 70a537d1d7113a0604c0c22fe44fa1ae07824723 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Tue, 1 May 2018 16:21:18 -0400 Subject: [PATCH 08/32] Initial Repcode Check Support for Ext Dict Ctx --- lib/compress/zstd_fast.c | 42 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index df4423fc..7ea86511 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -87,10 +87,20 @@ size_t ZSTD_compressBlock_fast_generic( U32 const current = (U32)(ip-base); U32 const matchIndex = hashTable[h]; const BYTE* match = base + matchIndex; + const int repIndex = current + 1 - offset_1; + const BYTE* repBase = hasDict == ZSTD_hasDictMatchState && repIndex < lowestIndex ? dictBase - dictIndexDelta : base; + const BYTE* repMatch = repBase + repIndex; hashTable[h] = current; /* update hash table */ - if ((hasDict != ZSTD_hasDictMatchState || current >= lowestIndex + offset_1) - && (offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1))) { + if (hasDict == ZSTD_hasDictMatchState + && (((U32)((lowestIndex-1) - (U32)repIndex) >= 3) /* intentional underflow */) + && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { + const BYTE* repMatchEnd = repIndex < lowestIndex ? dictEnd : iend; + mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, istart) + 4; + ip++; + ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); + } else if (hasDict == ZSTD_noDictMatchState + && (offset_1 > 0) & (MEM_read32(repMatch) == MEM_read32(ip+1))) { mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4; ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); @@ -109,7 +119,6 @@ size_t ZSTD_compressBlock_fast_generic( mLength = ZSTD_count_2segments(ip+4, dictMatch+4, iend, dictEnd, istart) + 4; { U32 const offset = (U32)(current-dictMatchIndex-dictIndexDelta); - DEBUGLOG(6, "ip %p (%u) dictMatch %p (%u) idxDelta %u", ip, current, dictMatch, dictMatchIndex, dictIndexDelta); while (((ip>anchor) & (dictMatch>dictLowest)) && (ip[-1] == dictMatch[-1])) { ip--; dictMatch--; mLength++; } /* catch up */ offset_2 = offset_1; offset_1 = offset; @@ -139,6 +148,31 @@ size_t ZSTD_compressBlock_fast_generic( hashTable[ZSTD_hashPtr(base+current+2, hlog, mls)] = current+2; /* here because current+2 could be > iend-8 */ hashTable[ZSTD_hashPtr(ip-2, hlog, mls)] = (U32)(ip-2-base); /* check immediate repcode */ + + if (hasDict == ZSTD_hasDictMatchState) { + while (ip <= ilimit) { + U32 const current2 = (U32)(ip-base); + int const repIndex2 = current2 - offset_2; + const BYTE* repMatch2 = hasDict == ZSTD_hasDictMatchState + && repIndex2 < lowestIndex ? + dictBase - dictIndexDelta + repIndex2 : + base + repIndex2; + if ( (((U32)((lowestIndex-1) - (U32)repIndex2) >= 3)) /* intentional overflow */ + && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { + const BYTE* const repEnd2 = repIndex2 < lowestIndex ? dictEnd : iend; + size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, istart) + 4; + U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ + ZSTD_storeSeq(seqStore, 0, anchor, 0, repLength2-MINMATCH); + hashTable[ZSTD_hashPtr(ip, hlog, mls)] = current2; + ip += repLength2; + anchor = ip; + continue; + } + break; + } + } + + if (hasDict == ZSTD_noDictMatchState) { while ( (ip <= ilimit) && (hasDict != ZSTD_hasDictMatchState || ip - offset_2 >= istart) && ( (offset_2>0) @@ -151,7 +185,7 @@ size_t ZSTD_compressBlock_fast_generic( ip += rLength; anchor = ip; continue; /* faster when present ... (?) */ - } } } + } } } } /* save reps for next block */ rep[0] = offset_1 ? offset_1 : offsetSaved; From 6929964d65778ce6f0b851f955b3bb68f1a3490d Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 2 May 2018 15:12:18 -0400 Subject: [PATCH 09/32] Add bounds check in repcode tests --- lib/compress/zstd_fast.c | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index 7ea86511..fb86199e 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -60,18 +60,33 @@ size_t ZSTD_compressBlock_fast_generic( U32 offset_1=rep[0], offset_2=rep[1]; U32 offsetSaved = 0; + /* This is all complicated by the fact that we need to handle positions + * specified in 3 different ways: by direct pointers, by indices relative + * to the working context base, and by indices relative to the dict context + * base. + * + * Hence the unfortunate collision of "lowestDictIndex", which is the lowest + * index in the dict's index space, and "dictLowestIndex", which is the same + * position in the working context's index space. + */ + const ZSTD_matchState_t* const dms = ms->dictMatchState; const U32* const dictHashTable = hasDict == ZSTD_hasDictMatchState ? dms->hashTable : NULL; - const U32 dictLowestIndex = hasDict == ZSTD_hasDictMatchState ? + const U32 lowestDictIndex = hasDict == ZSTD_hasDictMatchState ? dms->window.dictLimit : 0; const BYTE* const dictBase = hasDict == ZSTD_hasDictMatchState ? dms->window.base : NULL; const BYTE* const dictLowest = hasDict == ZSTD_hasDictMatchState ? - dictBase + dictLowestIndex : NULL; + dictBase + lowestDictIndex : NULL; const BYTE* const dictEnd = hasDict == ZSTD_hasDictMatchState ? dms->window.nextSrc : NULL; - const U32 dictIndexDelta = lowestIndex - (dictEnd - dictBase); + const U32 dictIndexDelta = hasDict == ZSTD_hasDictMatchState ? + lowestIndex - (dictEnd - dictBase) : + 0; + ptrdiff_t dictLowestIndex = hasDict == ZSTD_hasDictMatchState ? + lowestDictIndex + dictIndexDelta : + lowestIndex; /* init */ ip += (ip==lowest); @@ -87,15 +102,15 @@ size_t ZSTD_compressBlock_fast_generic( U32 const current = (U32)(ip-base); U32 const matchIndex = hashTable[h]; const BYTE* match = base + matchIndex; - const int repIndex = current + 1 - offset_1; - const BYTE* repBase = hasDict == ZSTD_hasDictMatchState && repIndex < lowestIndex ? dictBase - dictIndexDelta : base; + const ptrdiff_t repIndex = current + 1 - offset_1; + const BYTE* repBase = hasDict == ZSTD_hasDictMatchState && repIndex < (ptrdiff_t)lowestIndex ? dictBase - dictIndexDelta : base; const BYTE* repMatch = repBase + repIndex; hashTable[h] = current; /* update hash table */ if (hasDict == ZSTD_hasDictMatchState - && (((U32)((lowestIndex-1) - (U32)repIndex) >= 3) /* intentional underflow */) + && (((U32)((lowestIndex-1) - repIndex) >= 3) & (repIndex > dictLowestIndex) /* intentional underflow */) && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { - const BYTE* repMatchEnd = repIndex < lowestIndex ? dictEnd : iend; + const BYTE* repMatchEnd = repIndex < (ptrdiff_t)lowestIndex ? dictEnd : iend; mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, istart) + 4; ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); @@ -110,7 +125,7 @@ size_t ZSTD_compressBlock_fast_generic( if (hasDict == ZSTD_hasDictMatchState) { U32 const dictMatchIndex = dictHashTable[h]; const BYTE* dictMatch = dictBase + dictMatchIndex; - if (dictMatchIndex <= dictLowestIndex || + if (dictMatchIndex <= lowestDictIndex || MEM_read32(dictMatch) != MEM_read32(ip)) { assert(stepSize >= 1); ip += ((ip-anchor) >> kSearchStrength) + stepSize; @@ -152,14 +167,14 @@ size_t ZSTD_compressBlock_fast_generic( if (hasDict == ZSTD_hasDictMatchState) { while (ip <= ilimit) { U32 const current2 = (U32)(ip-base); - int const repIndex2 = current2 - offset_2; + ptrdiff_t const repIndex2 = current2 - offset_2; const BYTE* repMatch2 = hasDict == ZSTD_hasDictMatchState - && repIndex2 < lowestIndex ? + && repIndex2 < (ptrdiff_t)lowestIndex ? dictBase - dictIndexDelta + repIndex2 : base + repIndex2; - if ( (((U32)((lowestIndex-1) - (U32)repIndex2) >= 3)) /* intentional overflow */ + if ( (((U32)((lowestIndex-1) - (U32)repIndex2) >= 3) & (repIndex2 > dictLowestIndex)) /* intentional overflow */ && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { - const BYTE* const repEnd2 = repIndex2 < lowestIndex ? dictEnd : iend; + const BYTE* const repEnd2 = repIndex2 < (ptrdiff_t)lowestIndex ? dictEnd : iend; size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, istart) + 4; U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ ZSTD_storeSeq(seqStore, 0, anchor, 0, repLength2-MINMATCH); From 265c2869d1e955ae7a026f9e4130e4b981cb2835 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 2 May 2018 17:10:51 -0400 Subject: [PATCH 10/32] Split Wrapper Functions to Cause Inlining --- lib/compress/zstd_compress.c | 15 ++++--- lib/compress/zstd_compress_internal.h | 14 +++++- lib/compress/zstd_fast.c | 62 +++++++++++++++------------ lib/compress/zstd_fast.h | 3 ++ lib/compress/zstd_ldm.c | 3 +- 5 files changed, 62 insertions(+), 35 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 18091b09..c58909fb 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2137,9 +2137,9 @@ MEM_STATIC size_t ZSTD_compressSequences(seqStore_t* seqStorePtr, /* ZSTD_selectBlockCompressor() : * Not static, but internal use only (used by long distance matcher) * assumption : strat is a valid strategy */ -ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, int extDict) +ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, int extDict, ZSTD_hasDictMatchState_e hdms) { - static const ZSTD_blockCompressor blockCompressor[2][(unsigned)ZSTD_btultra+1] = { + static const ZSTD_blockCompressor blockCompressor[3][(unsigned)ZSTD_btultra+1] = { { ZSTD_compressBlock_fast /* default for 0 */, ZSTD_compressBlock_fast, ZSTD_compressBlock_doubleFast, ZSTD_compressBlock_greedy, ZSTD_compressBlock_lazy, ZSTD_compressBlock_lazy2, ZSTD_compressBlock_btlazy2, @@ -2147,13 +2147,16 @@ ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, int extDict { ZSTD_compressBlock_fast_extDict /* default for 0 */, ZSTD_compressBlock_fast_extDict, ZSTD_compressBlock_doubleFast_extDict, ZSTD_compressBlock_greedy_extDict, ZSTD_compressBlock_lazy_extDict,ZSTD_compressBlock_lazy2_extDict, ZSTD_compressBlock_btlazy2_extDict, - ZSTD_compressBlock_btopt_extDict, ZSTD_compressBlock_btultra_extDict } + ZSTD_compressBlock_btopt_extDict, ZSTD_compressBlock_btultra_extDict }, + { ZSTD_compressBlock_fast_extDictMatchState /* default for 0 */, + ZSTD_compressBlock_fast_extDictMatchState, + NULL, NULL, NULL, NULL, NULL, NULL, NULL } }; ZSTD_STATIC_ASSERT((unsigned)ZSTD_fast == 1); assert((U32)strat >= (U32)ZSTD_fast); assert((U32)strat <= (U32)ZSTD_btultra); - return blockCompressor[extDict!=0][(U32)strat]; + return blockCompressor[hdms == ZSTD_hasDictMatchState ? 2 : (extDict!=0)][(U32)strat]; } static void ZSTD_storeLastLiterals(seqStore_t* seqStorePtr, @@ -2196,6 +2199,8 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, /* select and store sequences */ { U32 const extDict = ZSTD_window_hasExtDict(ms->window); + ZSTD_hasDictMatchState_e const hdms = + ZSTD_matchState_hasDictMatchState(ms); size_t lastLLSize; { int i; for (i = 0; i < ZSTD_REP_NUM; ++i) @@ -2229,7 +2234,7 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, src, srcSize, extDict); assert(ldmSeqStore.pos == ldmSeqStore.size); } else { /* not long range mode */ - ZSTD_blockCompressor const blockCompressor = ZSTD_selectBlockCompressor(zc->appliedParams.cParams.strategy, extDict); + ZSTD_blockCompressor const blockCompressor = ZSTD_selectBlockCompressor(zc->appliedParams.cParams.strategy, extDict, hdms); lastLLSize = blockCompressor(ms, &zc->seqStore, zc->blockState.nextCBlock->rep, &zc->appliedParams.cParams, 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 05685e55..f3a4347b 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -252,10 +252,20 @@ typedef enum { ZSTD_dtlm_fast, ZSTD_dtlm_full } ZSTD_dictTableLoadMethod_e; typedef enum { ZSTD_noDictMatchState, ZSTD_hasDictMatchState } ZSTD_hasDictMatchState_e; +/** + * ZSTD_matchState_hasDictMatchState(): + * Does what the label says. + */ +MEM_STATIC ZSTD_hasDictMatchState_e ZSTD_matchState_hasDictMatchState(const ZSTD_matchState_t *ms) +{ + return ms->dictMatchState != NULL ? ZSTD_hasDictMatchState : ZSTD_noDictMatchState; +} + + typedef size_t (*ZSTD_blockCompressor) ( ZSTD_matchState_t* bs, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); -ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, int extDict); +ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, int extDict, ZSTD_hasDictMatchState_e hdms); MEM_STATIC U32 ZSTD_LLcode(U32 litLength) @@ -512,6 +522,8 @@ MEM_STATIC U32 ZSTD_window_hasExtDict(ZSTD_window_t const window) return window.lowLimit < window.dictLimit; } + + /** * ZSTD_window_needOverflowCorrection(): * Returns non-zero if the indices are getting too large and need overflow diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index fb86199e..5f152488 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -218,34 +218,40 @@ size_t ZSTD_compressBlock_fast( U32 const hlog = cParams->hashLog; U32 const mls = cParams->searchLength; U32 const stepSize = cParams->targetLength; - if (ms->dictMatchState != NULL) { - ZSTD_hasDictMatchState_e const hdms = ZSTD_hasDictMatchState; - switch(mls) - { - default: /* includes case 3 */ - case 4 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 4, hdms); - case 5 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 5, hdms); - case 6 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 6, hdms); - case 7 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 7, hdms); - } - } else { - ZSTD_hasDictMatchState_e const hdms = ZSTD_noDictMatchState; - switch(mls) - { - default: /* includes case 3 */ - case 4 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 4, hdms); - case 5 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 5, hdms); - case 6 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 6, hdms); - case 7 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 7, hdms); - } + assert(ms->dictMatchState == NULL); + switch(mls) + { + default: /* includes case 3 */ + case 4 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 4, ZSTD_noDictMatchState); + case 5 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 5, ZSTD_noDictMatchState); + case 6 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 6, ZSTD_noDictMatchState); + case 7 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 7, ZSTD_noDictMatchState); + } +} + +size_t ZSTD_compressBlock_fast_extDictMatchState( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize) +{ + U32 const hlog = cParams->hashLog; + U32 const mls = cParams->searchLength; + U32 const stepSize = cParams->targetLength; + assert(ms->dictMatchState != NULL); + switch(mls) + { + default: /* includes case 3 */ + case 4 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 4, ZSTD_hasDictMatchState); + case 5 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 5, ZSTD_hasDictMatchState); + case 6 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 6, ZSTD_hasDictMatchState); + case 7 : + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 7, ZSTD_hasDictMatchState); } } diff --git a/lib/compress/zstd_fast.h b/lib/compress/zstd_fast.h index 746849fc..804d36f2 100644 --- a/lib/compress/zstd_fast.h +++ b/lib/compress/zstd_fast.h @@ -24,6 +24,9 @@ void ZSTD_fillHashTable(ZSTD_matchState_t* ms, size_t ZSTD_compressBlock_fast( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); +size_t ZSTD_compressBlock_fast_extDictMatchState( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); size_t ZSTD_compressBlock_fast_extDict( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); diff --git a/lib/compress/zstd_ldm.c b/lib/compress/zstd_ldm.c index 9d825e69..b58c2f1c 100644 --- a/lib/compress/zstd_ldm.c +++ b/lib/compress/zstd_ldm.c @@ -596,7 +596,8 @@ size_t ZSTD_ldm_blockCompress(rawSeqStore_t* rawSeqStore, { unsigned const minMatch = cParams->searchLength; ZSTD_blockCompressor const blockCompressor = - ZSTD_selectBlockCompressor(cParams->strategy, extDict); + ZSTD_selectBlockCompressor(cParams->strategy, extDict, + ZSTD_matchState_hasDictMatchState(ms)); BYTE const* const base = ms->window.base; /* Input bounds */ BYTE const* const istart = (BYTE const*)src; From b67196f30d093c0be0fab4e090c9c748779ab27a Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 2 May 2018 17:34:34 -0400 Subject: [PATCH 11/32] Coalesce hasDictMatchState and extDict Checks into One Enum and Rename Stuff --- lib/compress/zstd_compress.c | 20 +++++------ lib/compress/zstd_compress_internal.h | 23 ++++++------ lib/compress/zstd_fast.c | 52 ++++++++++++++------------- lib/compress/zstd_fast.h | 2 +- lib/compress/zstd_ldm.c | 7 ++-- lib/compress/zstd_ldm.h | 3 +- 6 files changed, 52 insertions(+), 55 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index c58909fb..6bbd09c0 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2137,7 +2137,7 @@ MEM_STATIC size_t ZSTD_compressSequences(seqStore_t* seqStorePtr, /* ZSTD_selectBlockCompressor() : * Not static, but internal use only (used by long distance matcher) * assumption : strat is a valid strategy */ -ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, int extDict, ZSTD_hasDictMatchState_e hdms) +ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, ZSTD_dictMode_e dictMode) { static const ZSTD_blockCompressor blockCompressor[3][(unsigned)ZSTD_btultra+1] = { { ZSTD_compressBlock_fast /* default for 0 */, @@ -2148,15 +2148,15 @@ ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, int extDict ZSTD_compressBlock_fast_extDict, ZSTD_compressBlock_doubleFast_extDict, ZSTD_compressBlock_greedy_extDict, ZSTD_compressBlock_lazy_extDict,ZSTD_compressBlock_lazy2_extDict, ZSTD_compressBlock_btlazy2_extDict, ZSTD_compressBlock_btopt_extDict, ZSTD_compressBlock_btultra_extDict }, - { ZSTD_compressBlock_fast_extDictMatchState /* default for 0 */, - ZSTD_compressBlock_fast_extDictMatchState, - NULL, NULL, NULL, NULL, NULL, NULL, NULL } + { ZSTD_compressBlock_fast_dictMatchState /* default for 0 */, + ZSTD_compressBlock_fast_dictMatchState, + NULL, NULL, NULL, NULL, NULL, NULL, NULL /* unimplemented as of yet */ } }; ZSTD_STATIC_ASSERT((unsigned)ZSTD_fast == 1); assert((U32)strat >= (U32)ZSTD_fast); assert((U32)strat <= (U32)ZSTD_btultra); - return blockCompressor[hdms == ZSTD_hasDictMatchState ? 2 : (extDict!=0)][(U32)strat]; + return blockCompressor[(int)dictMode][(U32)strat]; } static void ZSTD_storeLastLiterals(seqStore_t* seqStorePtr, @@ -2198,9 +2198,7 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, } /* select and store sequences */ - { U32 const extDict = ZSTD_window_hasExtDict(ms->window); - ZSTD_hasDictMatchState_e const hdms = - ZSTD_matchState_hasDictMatchState(ms); + { ZSTD_dictMode_e const dictMode = ZSTD_matchState_dictMode(ms); size_t lastLLSize; { int i; for (i = 0; i < ZSTD_REP_NUM; ++i) @@ -2214,7 +2212,7 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, ms, &zc->seqStore, zc->blockState.nextCBlock->rep, &zc->appliedParams.cParams, - src, srcSize, extDict); + src, srcSize); assert(zc->externSeqStore.pos <= zc->externSeqStore.size); } else if (zc->appliedParams.ldmParams.enableLdm) { rawSeqStore_t ldmSeqStore = {NULL, 0, 0, 0}; @@ -2231,10 +2229,10 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, ms, &zc->seqStore, zc->blockState.nextCBlock->rep, &zc->appliedParams.cParams, - src, srcSize, extDict); + src, srcSize); assert(ldmSeqStore.pos == ldmSeqStore.size); } else { /* not long range mode */ - ZSTD_blockCompressor const blockCompressor = ZSTD_selectBlockCompressor(zc->appliedParams.cParams.strategy, extDict, hdms); + ZSTD_blockCompressor const blockCompressor = ZSTD_selectBlockCompressor(zc->appliedParams.cParams.strategy, dictMode); lastLLSize = blockCompressor(ms, &zc->seqStore, zc->blockState.nextCBlock->rep, &zc->appliedParams.cParams, 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 f3a4347b..32bbe08b 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -250,22 +250,13 @@ struct ZSTD_CCtx_s { typedef enum { ZSTD_dtlm_fast, ZSTD_dtlm_full } ZSTD_dictTableLoadMethod_e; -typedef enum { ZSTD_noDictMatchState, ZSTD_hasDictMatchState } ZSTD_hasDictMatchState_e; - -/** - * ZSTD_matchState_hasDictMatchState(): - * Does what the label says. - */ -MEM_STATIC ZSTD_hasDictMatchState_e ZSTD_matchState_hasDictMatchState(const ZSTD_matchState_t *ms) -{ - return ms->dictMatchState != NULL ? ZSTD_hasDictMatchState : ZSTD_noDictMatchState; -} +typedef enum { ZSTD_noDict = 0, ZSTD_extDict = 1, ZSTD_dictMatchState = 2 } ZSTD_dictMode_e; typedef size_t (*ZSTD_blockCompressor) ( ZSTD_matchState_t* bs, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); -ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, int extDict, ZSTD_hasDictMatchState_e hdms); +ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, ZSTD_dictMode_e hdms); MEM_STATIC U32 ZSTD_LLcode(U32 litLength) @@ -522,7 +513,15 @@ MEM_STATIC U32 ZSTD_window_hasExtDict(ZSTD_window_t const window) return window.lowLimit < window.dictLimit; } - +/** + * ZSTD_matchState_dictMode(): + * Does what the label says. + */ +MEM_STATIC ZSTD_dictMode_e ZSTD_matchState_dictMode(const ZSTD_matchState_t *ms) +{ + return ms->dictMatchState != NULL ? ZSTD_dictMatchState : + ZSTD_window_hasExtDict(ms->window) ? ZSTD_extDict : ZSTD_noDict; +} /** * ZSTD_window_needOverflowCorrection(): diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index 5f152488..8f3d33e6 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -46,7 +46,7 @@ size_t ZSTD_compressBlock_fast_generic( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize, U32 const hlog, U32 const stepSize, U32 const mls, - ZSTD_hasDictMatchState_e const hasDict) + ZSTD_dictMode_e const hasDict) { U32* const hashTable = ms->hashTable; const BYTE* const base = ms->window.base; @@ -71,23 +71,25 @@ size_t ZSTD_compressBlock_fast_generic( */ const ZSTD_matchState_t* const dms = ms->dictMatchState; - const U32* const dictHashTable = hasDict == ZSTD_hasDictMatchState ? + const U32* const dictHashTable = hasDict == ZSTD_dictMatchState ? dms->hashTable : NULL; - const U32 lowestDictIndex = hasDict == ZSTD_hasDictMatchState ? + const U32 lowestDictIndex = hasDict == ZSTD_dictMatchState ? dms->window.dictLimit : 0; - const BYTE* const dictBase = hasDict == ZSTD_hasDictMatchState ? + const BYTE* const dictBase = hasDict == ZSTD_dictMatchState ? dms->window.base : NULL; - const BYTE* const dictLowest = hasDict == ZSTD_hasDictMatchState ? + const BYTE* const dictLowest = hasDict == ZSTD_dictMatchState ? dictBase + lowestDictIndex : NULL; - const BYTE* const dictEnd = hasDict == ZSTD_hasDictMatchState ? + const BYTE* const dictEnd = hasDict == ZSTD_dictMatchState ? dms->window.nextSrc : NULL; - const U32 dictIndexDelta = hasDict == ZSTD_hasDictMatchState ? + const U32 dictIndexDelta = hasDict == ZSTD_dictMatchState ? lowestIndex - (dictEnd - dictBase) : 0; - ptrdiff_t dictLowestIndex = hasDict == ZSTD_hasDictMatchState ? + ptrdiff_t dictLowestIndex = hasDict == ZSTD_dictMatchState ? lowestDictIndex + dictIndexDelta : lowestIndex; + assert(hasDict == ZSTD_noDict || hasDict == ZSTD_dictMatchState); + /* init */ ip += (ip==lowest); { U32 const maxRep = (U32)(ip-lowest); @@ -103,18 +105,18 @@ size_t ZSTD_compressBlock_fast_generic( U32 const matchIndex = hashTable[h]; const BYTE* match = base + matchIndex; const ptrdiff_t repIndex = current + 1 - offset_1; - const BYTE* repBase = hasDict == ZSTD_hasDictMatchState && repIndex < (ptrdiff_t)lowestIndex ? dictBase - dictIndexDelta : base; + const BYTE* repBase = hasDict == ZSTD_dictMatchState && repIndex < (ptrdiff_t)lowestIndex ? dictBase - dictIndexDelta : base; const BYTE* repMatch = repBase + repIndex; hashTable[h] = current; /* update hash table */ - if (hasDict == ZSTD_hasDictMatchState + if (hasDict == ZSTD_dictMatchState && (((U32)((lowestIndex-1) - repIndex) >= 3) & (repIndex > dictLowestIndex) /* intentional underflow */) && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { const BYTE* repMatchEnd = repIndex < (ptrdiff_t)lowestIndex ? dictEnd : iend; mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, istart) + 4; ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); - } else if (hasDict == ZSTD_noDictMatchState + } else if (hasDict == ZSTD_noDict && (offset_1 > 0) & (MEM_read32(repMatch) == MEM_read32(ip+1))) { mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4; ip++; @@ -122,7 +124,7 @@ size_t ZSTD_compressBlock_fast_generic( } else { if ( (matchIndex <= lowestIndex) || (MEM_read32(match) != MEM_read32(ip)) ) { - if (hasDict == ZSTD_hasDictMatchState) { + if (hasDict == ZSTD_dictMatchState) { U32 const dictMatchIndex = dictHashTable[h]; const BYTE* dictMatch = dictBase + dictMatchIndex; if (dictMatchIndex <= lowestDictIndex || @@ -164,11 +166,11 @@ size_t ZSTD_compressBlock_fast_generic( hashTable[ZSTD_hashPtr(ip-2, hlog, mls)] = (U32)(ip-2-base); /* check immediate repcode */ - if (hasDict == ZSTD_hasDictMatchState) { + if (hasDict == ZSTD_dictMatchState) { while (ip <= ilimit) { U32 const current2 = (U32)(ip-base); ptrdiff_t const repIndex2 = current2 - offset_2; - const BYTE* repMatch2 = hasDict == ZSTD_hasDictMatchState + const BYTE* repMatch2 = hasDict == ZSTD_dictMatchState && repIndex2 < (ptrdiff_t)lowestIndex ? dictBase - dictIndexDelta + repIndex2 : base + repIndex2; @@ -187,9 +189,9 @@ size_t ZSTD_compressBlock_fast_generic( } } - if (hasDict == ZSTD_noDictMatchState) { + if (hasDict == ZSTD_noDict) { while ( (ip <= ilimit) - && (hasDict != ZSTD_hasDictMatchState || ip - offset_2 >= istart) + && (hasDict != ZSTD_dictMatchState || ip - offset_2 >= istart) && ( (offset_2>0) & (MEM_read32(ip) == MEM_read32(ip - offset_2)) )) { /* store sequence */ @@ -223,17 +225,17 @@ size_t ZSTD_compressBlock_fast( { default: /* includes case 3 */ case 4 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 4, ZSTD_noDictMatchState); + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 4, ZSTD_noDict); case 5 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 5, ZSTD_noDictMatchState); + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 5, ZSTD_noDict); case 6 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 6, ZSTD_noDictMatchState); + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 6, ZSTD_noDict); case 7 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 7, ZSTD_noDictMatchState); + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 7, ZSTD_noDict); } } -size_t ZSTD_compressBlock_fast_extDictMatchState( +size_t ZSTD_compressBlock_fast_dictMatchState( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize) { @@ -245,13 +247,13 @@ size_t ZSTD_compressBlock_fast_extDictMatchState( { default: /* includes case 3 */ case 4 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 4, ZSTD_hasDictMatchState); + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 4, ZSTD_dictMatchState); case 5 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 5, ZSTD_hasDictMatchState); + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 5, ZSTD_dictMatchState); case 6 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 6, ZSTD_hasDictMatchState); + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 6, ZSTD_dictMatchState); case 7 : - return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 7, ZSTD_hasDictMatchState); + return ZSTD_compressBlock_fast_generic(ms, seqStore, rep, src, srcSize, hlog, stepSize, 7, ZSTD_dictMatchState); } } diff --git a/lib/compress/zstd_fast.h b/lib/compress/zstd_fast.h index 804d36f2..7e7435f8 100644 --- a/lib/compress/zstd_fast.h +++ b/lib/compress/zstd_fast.h @@ -24,7 +24,7 @@ void ZSTD_fillHashTable(ZSTD_matchState_t* ms, size_t ZSTD_compressBlock_fast( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); -size_t ZSTD_compressBlock_fast_extDictMatchState( +size_t ZSTD_compressBlock_fast_dictMatchState( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); size_t ZSTD_compressBlock_fast_extDict( diff --git a/lib/compress/zstd_ldm.c b/lib/compress/zstd_ldm.c index b58c2f1c..b0c5d065 100644 --- a/lib/compress/zstd_ldm.c +++ b/lib/compress/zstd_ldm.c @@ -591,13 +591,12 @@ static rawSeq maybeSplitSequence(rawSeqStore_t* rawSeqStore, size_t ZSTD_ldm_blockCompress(rawSeqStore_t* rawSeqStore, ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], - ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize, - int const extDict) + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize) { unsigned const minMatch = cParams->searchLength; ZSTD_blockCompressor const blockCompressor = - ZSTD_selectBlockCompressor(cParams->strategy, extDict, - ZSTD_matchState_hasDictMatchState(ms)); + ZSTD_selectBlockCompressor(cParams->strategy, + ZSTD_matchState_dictMode(ms)); BYTE const* const base = ms->window.base; /* Input bounds */ BYTE const* const istart = (BYTE const*)src; diff --git a/lib/compress/zstd_ldm.h b/lib/compress/zstd_ldm.h index 0c3789ff..96588adb 100644 --- a/lib/compress/zstd_ldm.h +++ b/lib/compress/zstd_ldm.h @@ -62,8 +62,7 @@ size_t ZSTD_ldm_generateSequences( size_t ZSTD_ldm_blockCompress(rawSeqStore_t* rawSeqStore, ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], ZSTD_compressionParameters const* cParams, - void const* src, size_t srcSize, - int const extDict); + void const* src, size_t srcSize); /** * ZSTD_ldm_skipSequences(): From c31ee3c7f827ebb1f3141ca4162e4721d3617752 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 2 May 2018 20:30:03 -0400 Subject: [PATCH 12/32] Fix Rep Code Initialization --- lib/compress/zstd_fast.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index 8f3d33e6..067efba5 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -91,8 +91,10 @@ size_t ZSTD_compressBlock_fast_generic( assert(hasDict == ZSTD_noDict || hasDict == ZSTD_dictMatchState); /* init */ - ip += (ip==lowest); - { U32 const maxRep = (U32)(ip-lowest); + ip += (hasDict == ZSTD_noDict && ip == lowest); + { U32 const maxRep = hasDict == ZSTD_dictMatchState ? + (U32)(ip - dictLowest) : + (U32)(ip - lowest); if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0; if (offset_1 > maxRep) offsetSaved = offset_1, offset_1 = 0; } From 66bc1ca64142a63f846035c4f5539d400113e56b Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 2 May 2018 22:28:29 -0400 Subject: [PATCH 13/32] Change Cut-Off to 8 KB --- 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 6bbd09c0..b877a7fb 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1208,7 +1208,7 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, * context, or referencing the dictionary context from the working context * in-place. We decide here which strategy to use. */ /* TODO: pick reasonable cut-off size, handle ZSTD_CONTENTSIZE_UNKNOWN */ - int attachDict = pledgedSrcSize < 64 KB + int attachDict = pledgedSrcSize <= 8 KB && cdict->cParams.strategy == ZSTD_fast && ZSTD_equivalentCParams(cctx->appliedParams.cParams, cdict->cParams); From ca26cecc7a48e317bee986c0e34dd8a9887eb2f5 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Fri, 4 May 2018 13:08:07 -0400 Subject: [PATCH 14/32] Rename and Reformat --- lib/compress/zstd_fast.c | 163 +++++++++++++++++++-------------------- 1 file changed, 80 insertions(+), 83 deletions(-) diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index 067efba5..d4eaeb6d 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -53,23 +53,13 @@ size_t ZSTD_compressBlock_fast_generic( const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart; const BYTE* anchor = istart; - const U32 lowestIndex = ms->window.dictLimit; - const BYTE* const lowest = base + lowestIndex; + const U32 localLowestIndex = ms->window.dictLimit; + const BYTE* const localLowest = base + localLowestIndex; 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; - /* This is all complicated by the fact that we need to handle positions - * specified in 3 different ways: by direct pointers, by indices relative - * to the working context base, and by indices relative to the dict context - * base. - * - * Hence the unfortunate collision of "lowestDictIndex", which is the lowest - * index in the dict's index space, and "dictLowestIndex", which is the same - * position in the working context's index space. - */ - const ZSTD_matchState_t* const dms = ms->dictMatchState; const U32* const dictHashTable = hasDict == ZSTD_dictMatchState ? dms->hashTable : NULL; @@ -82,19 +72,19 @@ size_t ZSTD_compressBlock_fast_generic( const BYTE* const dictEnd = hasDict == ZSTD_dictMatchState ? dms->window.nextSrc : NULL; const U32 dictIndexDelta = hasDict == ZSTD_dictMatchState ? - lowestIndex - (dictEnd - dictBase) : + localLowestIndex - (dictEnd - dictBase) : 0; - ptrdiff_t dictLowestIndex = hasDict == ZSTD_dictMatchState ? + ptrdiff_t dictLowestLocalIndex = hasDict == ZSTD_dictMatchState ? lowestDictIndex + dictIndexDelta : - lowestIndex; + localLowestIndex; assert(hasDict == ZSTD_noDict || hasDict == ZSTD_dictMatchState); /* init */ - ip += (hasDict == ZSTD_noDict && ip == lowest); + ip += (hasDict == ZSTD_noDict && ip == localLowest); { U32 const maxRep = hasDict == ZSTD_dictMatchState ? (U32)(ip - dictLowest) : - (U32)(ip - lowest); + (U32)(ip - localLowest); if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0; if (offset_1 > maxRep) offsetSaved = offset_1, offset_1 = 0; } @@ -106,57 +96,63 @@ size_t ZSTD_compressBlock_fast_generic( U32 const current = (U32)(ip-base); U32 const matchIndex = hashTable[h]; const BYTE* match = base + matchIndex; - const ptrdiff_t repIndex = current + 1 - offset_1; - const BYTE* repBase = hasDict == ZSTD_dictMatchState && repIndex < (ptrdiff_t)lowestIndex ? dictBase - dictIndexDelta : base; + const ptrdiff_t repIndex = (ptrdiff_t)current + 1 - offset_1; + const BYTE* repBase = (hasDict == ZSTD_dictMatchState + && repIndex < (ptrdiff_t)localLowestIndex) ? + dictBase - dictIndexDelta : base; const BYTE* repMatch = repBase + repIndex; hashTable[h] = current; /* update hash table */ if (hasDict == ZSTD_dictMatchState - && (((U32)((lowestIndex-1) - repIndex) >= 3) & (repIndex > dictLowestIndex) /* intentional underflow */) + && (((U32)((localLowestIndex-1) - repIndex) >= 3 /* intentional underflow */) + & (repIndex > dictLowestLocalIndex)) && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { - const BYTE* repMatchEnd = repIndex < (ptrdiff_t)lowestIndex ? dictEnd : iend; + const BYTE* repMatchEnd = repIndex < (ptrdiff_t)localLowestIndex ? dictEnd : iend; mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, istart) + 4; ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); - } else if (hasDict == ZSTD_noDict - && (offset_1 > 0) & (MEM_read32(repMatch) == MEM_read32(ip+1))) { + } else if ( hasDict == ZSTD_noDict + && (offset_1 > 0) & (MEM_read32(repMatch) == MEM_read32(ip+1))) { mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4; ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); - } else { - if ( (matchIndex <= lowestIndex) - || (MEM_read32(match) != MEM_read32(ip)) ) { - if (hasDict == ZSTD_dictMatchState) { - U32 const dictMatchIndex = dictHashTable[h]; - const BYTE* dictMatch = dictBase + dictMatchIndex; - if (dictMatchIndex <= lowestDictIndex || - MEM_read32(dictMatch) != MEM_read32(ip)) { - assert(stepSize >= 1); - ip += ((ip-anchor) >> kSearchStrength) + stepSize; - continue; - } - - mLength = ZSTD_count_2segments(ip+4, dictMatch+4, iend, dictEnd, istart) + 4; - { U32 const offset = (U32)(current-dictMatchIndex-dictIndexDelta); - while (((ip>anchor) & (dictMatch>dictLowest)) && (ip[-1] == dictMatch[-1])) { ip--; dictMatch--; mLength++; } /* catch up */ - offset_2 = offset_1; - offset_1 = offset; - ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); - } - - } else { + } else if ( (matchIndex <= localLowestIndex) + || (MEM_read32(match) != MEM_read32(ip)) ) { + if (hasDict == ZSTD_dictMatchState) { + U32 const dictMatchIndex = dictHashTable[h]; + const BYTE* dictMatch = dictBase + dictMatchIndex; + if (dictMatchIndex <= lowestDictIndex || + MEM_read32(dictMatch) != MEM_read32(ip)) { assert(stepSize >= 1); ip += ((ip-anchor) >> kSearchStrength) + stepSize; continue; + } else { + /* found a dict match */ + U32 const offset = (U32)(current-dictMatchIndex-dictIndexDelta); + mLength = ZSTD_count_2segments(ip+4, dictMatch+4, iend, dictEnd, istart) + 4; + while (((ip>anchor) & (dictMatch>dictLowest)) + && (ip[-1] == dictMatch[-1])) { + ip--; dictMatch--; mLength++; + } /* catch up */ + offset_2 = offset_1; + offset_1 = offset; + ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); } } else { - U32 const offset = (U32)(ip-match); - mLength = ZSTD_count(ip+4, match+4, iend) + 4; - while (((ip>anchor) & (match>lowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ - offset_2 = offset_1; - offset_1 = offset; - ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); - } } + 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>localLowest)) + && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ + offset_2 = offset_1; + offset_1 = offset; + ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); + } /* match found */ ip += mLength; @@ -169,41 +165,42 @@ size_t ZSTD_compressBlock_fast_generic( /* check immediate repcode */ if (hasDict == ZSTD_dictMatchState) { - while (ip <= ilimit) { - U32 const current2 = (U32)(ip-base); - ptrdiff_t const repIndex2 = current2 - offset_2; - const BYTE* repMatch2 = hasDict == ZSTD_dictMatchState - && repIndex2 < (ptrdiff_t)lowestIndex ? - dictBase - dictIndexDelta + repIndex2 : - base + repIndex2; - if ( (((U32)((lowestIndex-1) - (U32)repIndex2) >= 3) & (repIndex2 > dictLowestIndex)) /* intentional overflow */ - && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { - const BYTE* const repEnd2 = repIndex2 < (ptrdiff_t)lowestIndex ? dictEnd : iend; - size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, istart) + 4; - U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ - ZSTD_storeSeq(seqStore, 0, anchor, 0, repLength2-MINMATCH); - hashTable[ZSTD_hashPtr(ip, hlog, mls)] = current2; - ip += repLength2; - anchor = ip; - continue; + while (ip <= ilimit) { + U32 const current2 = (U32)(ip-base); + ptrdiff_t const repIndex2 = (ptrdiff_t)current2 - offset_2; + const BYTE* repMatch2 = hasDict == ZSTD_dictMatchState + && repIndex2 < (ptrdiff_t)localLowestIndex ? + dictBase - dictIndexDelta + repIndex2 : + base + repIndex2; + if ( (((U32)((localLowestIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */) + & (repIndex2 > dictLowestLocalIndex)) + && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { + const BYTE* const repEnd2 = repIndex2 < (ptrdiff_t)localLowestIndex ? dictEnd : iend; + size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, istart) + 4; + U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ + ZSTD_storeSeq(seqStore, 0, anchor, 0, repLength2-MINMATCH); + hashTable[ZSTD_hashPtr(ip, hlog, mls)] = current2; + ip += repLength2; + anchor = ip; + continue; + } + break; } - break; - } } if (hasDict == ZSTD_noDict) { - while ( (ip <= ilimit) - && (hasDict != ZSTD_dictMatchState || ip - offset_2 >= istart) - && ( (offset_2>0) - & (MEM_read32(ip) == MEM_read32(ip - offset_2)) )) { - /* store sequence */ - size_t const rLength = ZSTD_count(ip+4, ip+4-offset_2, iend) + 4; - { U32 const tmpOff = offset_2; offset_2 = offset_1; offset_1 = tmpOff; } /* swap offset_2 <=> offset_1 */ - hashTable[ZSTD_hashPtr(ip, hlog, mls)] = (U32)(ip-base); - ZSTD_storeSeq(seqStore, 0, anchor, 0, rLength-MINMATCH); - ip += rLength; - anchor = ip; - continue; /* faster when present ... (?) */ + while ( (ip <= ilimit) + && (ip - offset_2 >= istart) + && ( (offset_2>0) + & (MEM_read32(ip) == MEM_read32(ip - offset_2)) )) { + /* store sequence */ + size_t const rLength = ZSTD_count(ip+4, ip+4-offset_2, iend) + 4; + U32 const tmpOff = offset_2; offset_2 = offset_1; offset_1 = tmpOff; /* swap offset_2 <=> offset_1 */ + hashTable[ZSTD_hashPtr(ip, hlog, mls)] = (U32)(ip-base); + ZSTD_storeSeq(seqStore, 0, anchor, 0, rLength-MINMATCH); + ip += rLength; + anchor = ip; + continue; /* faster when present ... (?) */ } } } } /* save reps for next block */ From ae4fcf781613255b76dbf3da55f452bbc5537065 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 9 May 2018 13:14:20 -0400 Subject: [PATCH 15/32] Respond to PR Comments; Formatting/Style/Lint Fixes --- lib/compress/zstd_compress.c | 5 ++++- lib/compress/zstd_compress_internal.h | 10 +++++++--- lib/compress/zstd_fast.c | 2 +- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index b877a7fb..f0576f08 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2152,11 +2152,14 @@ ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, ZSTD_dictMo ZSTD_compressBlock_fast_dictMatchState, NULL, NULL, NULL, NULL, NULL, NULL, NULL /* unimplemented as of yet */ } }; + ZSTD_blockCompressor selectedCompressor; ZSTD_STATIC_ASSERT((unsigned)ZSTD_fast == 1); assert((U32)strat >= (U32)ZSTD_fast); assert((U32)strat <= (U32)ZSTD_btultra); - return blockCompressor[(int)dictMode][(U32)strat]; + selectedCompressor = blockCompressor[(int)dictMode][(U32)strat]; + assert(selectedCompressor != NULL); + return selectedCompressor; } static void ZSTD_storeLastLiterals(seqStore_t* seqStorePtr, diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 32bbe08b..913497e7 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -515,12 +515,16 @@ MEM_STATIC U32 ZSTD_window_hasExtDict(ZSTD_window_t const window) /** * ZSTD_matchState_dictMode(): - * Does what the label says. + * Inspects the provided matchState and figures out what dictMode should be + * passed to the compressor. */ MEM_STATIC ZSTD_dictMode_e ZSTD_matchState_dictMode(const ZSTD_matchState_t *ms) { - return ms->dictMatchState != NULL ? ZSTD_dictMatchState : - ZSTD_window_hasExtDict(ms->window) ? ZSTD_extDict : ZSTD_noDict; + return ms->dictMatchState != NULL ? + ZSTD_dictMatchState : + ZSTD_window_hasExtDict(ms->window) ? + ZSTD_extDict : + ZSTD_noDict; } /** diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index d4eaeb6d..60c88e57 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -72,7 +72,7 @@ size_t ZSTD_compressBlock_fast_generic( const BYTE* const dictEnd = hasDict == ZSTD_dictMatchState ? dms->window.nextSrc : NULL; const U32 dictIndexDelta = hasDict == ZSTD_dictMatchState ? - localLowestIndex - (dictEnd - dictBase) : + localLowestIndex - (U32)(dictEnd - dictBase) : 0; ptrdiff_t dictLowestLocalIndex = hasDict == ZSTD_dictMatchState ? lowestDictIndex + dictIndexDelta : From 191fc74a51aa20d46b1ec996a7e20dc3f1dbaf5e Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 9 May 2018 15:14:12 -0400 Subject: [PATCH 16/32] Rename 'hasDict' to 'dictMode' --- lib/compress/zstd_compress_internal.h | 2 +- lib/compress/zstd_fast.c | 36 +++++++++++++-------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 913497e7..80c03433 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -256,7 +256,7 @@ typedef enum { ZSTD_noDict = 0, ZSTD_extDict = 1, ZSTD_dictMatchState = 2 } ZSTD typedef size_t (*ZSTD_blockCompressor) ( ZSTD_matchState_t* bs, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); -ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, ZSTD_dictMode_e hdms); +ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, ZSTD_dictMode_e dictMode); MEM_STATIC U32 ZSTD_LLcode(U32 litLength) diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index 60c88e57..f211f142 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -46,7 +46,7 @@ size_t ZSTD_compressBlock_fast_generic( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], void const* src, size_t srcSize, U32 const hlog, U32 const stepSize, U32 const mls, - ZSTD_dictMode_e const hasDict) + ZSTD_dictMode_e const dictMode) { U32* const hashTable = ms->hashTable; const BYTE* const base = ms->window.base; @@ -61,28 +61,28 @@ size_t ZSTD_compressBlock_fast_generic( U32 offsetSaved = 0; const ZSTD_matchState_t* const dms = ms->dictMatchState; - const U32* const dictHashTable = hasDict == ZSTD_dictMatchState ? + const U32* const dictHashTable = dictMode == ZSTD_dictMatchState ? dms->hashTable : NULL; - const U32 lowestDictIndex = hasDict == ZSTD_dictMatchState ? + const U32 lowestDictIndex = dictMode == ZSTD_dictMatchState ? dms->window.dictLimit : 0; - const BYTE* const dictBase = hasDict == ZSTD_dictMatchState ? + const BYTE* const dictBase = dictMode == ZSTD_dictMatchState ? dms->window.base : NULL; - const BYTE* const dictLowest = hasDict == ZSTD_dictMatchState ? + const BYTE* const dictLowest = dictMode == ZSTD_dictMatchState ? dictBase + lowestDictIndex : NULL; - const BYTE* const dictEnd = hasDict == ZSTD_dictMatchState ? + const BYTE* const dictEnd = dictMode == ZSTD_dictMatchState ? dms->window.nextSrc : NULL; - const U32 dictIndexDelta = hasDict == ZSTD_dictMatchState ? + const U32 dictIndexDelta = dictMode == ZSTD_dictMatchState ? localLowestIndex - (U32)(dictEnd - dictBase) : 0; - ptrdiff_t dictLowestLocalIndex = hasDict == ZSTD_dictMatchState ? + ptrdiff_t dictLowestLocalIndex = dictMode == ZSTD_dictMatchState ? lowestDictIndex + dictIndexDelta : localLowestIndex; - assert(hasDict == ZSTD_noDict || hasDict == ZSTD_dictMatchState); + assert(dictMode == ZSTD_noDict || dictMode == ZSTD_dictMatchState); /* init */ - ip += (hasDict == ZSTD_noDict && ip == localLowest); - { U32 const maxRep = hasDict == ZSTD_dictMatchState ? + ip += (dictMode == ZSTD_noDict && ip == localLowest); + { U32 const maxRep = dictMode == ZSTD_dictMatchState ? (U32)(ip - dictLowest) : (U32)(ip - localLowest); if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0; @@ -97,13 +97,13 @@ size_t ZSTD_compressBlock_fast_generic( U32 const matchIndex = hashTable[h]; const BYTE* match = base + matchIndex; const ptrdiff_t repIndex = (ptrdiff_t)current + 1 - offset_1; - const BYTE* repBase = (hasDict == ZSTD_dictMatchState + const BYTE* repBase = (dictMode == ZSTD_dictMatchState && repIndex < (ptrdiff_t)localLowestIndex) ? dictBase - dictIndexDelta : base; const BYTE* repMatch = repBase + repIndex; hashTable[h] = current; /* update hash table */ - if (hasDict == ZSTD_dictMatchState + if (dictMode == ZSTD_dictMatchState && (((U32)((localLowestIndex-1) - repIndex) >= 3 /* intentional underflow */) & (repIndex > dictLowestLocalIndex)) && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { @@ -111,14 +111,14 @@ size_t ZSTD_compressBlock_fast_generic( mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, istart) + 4; ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); - } else if ( hasDict == ZSTD_noDict + } else if ( dictMode == ZSTD_noDict && (offset_1 > 0) & (MEM_read32(repMatch) == MEM_read32(ip+1))) { mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4; ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); } else if ( (matchIndex <= localLowestIndex) || (MEM_read32(match) != MEM_read32(ip)) ) { - if (hasDict == ZSTD_dictMatchState) { + if (dictMode == ZSTD_dictMatchState) { U32 const dictMatchIndex = dictHashTable[h]; const BYTE* dictMatch = dictBase + dictMatchIndex; if (dictMatchIndex <= lowestDictIndex || @@ -164,11 +164,11 @@ size_t ZSTD_compressBlock_fast_generic( hashTable[ZSTD_hashPtr(ip-2, hlog, mls)] = (U32)(ip-2-base); /* check immediate repcode */ - if (hasDict == ZSTD_dictMatchState) { + if (dictMode == ZSTD_dictMatchState) { while (ip <= ilimit) { U32 const current2 = (U32)(ip-base); ptrdiff_t const repIndex2 = (ptrdiff_t)current2 - offset_2; - const BYTE* repMatch2 = hasDict == ZSTD_dictMatchState + const BYTE* repMatch2 = dictMode == ZSTD_dictMatchState && repIndex2 < (ptrdiff_t)localLowestIndex ? dictBase - dictIndexDelta + repIndex2 : base + repIndex2; @@ -188,7 +188,7 @@ size_t ZSTD_compressBlock_fast_generic( } } - if (hasDict == ZSTD_noDict) { + if (dictMode == ZSTD_noDict) { while ( (ip <= ilimit) && (ip - offset_2 >= istart) && ( (offset_2>0) From 154eb0941990b97543081b03bf38c469d3a3c172 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 9 May 2018 18:40:23 -0400 Subject: [PATCH 17/32] Switch to Original Match Calc for noDict Repcode Check --- lib/compress/zstd_fast.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index f211f142..48a165d3 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -112,7 +112,7 @@ size_t ZSTD_compressBlock_fast_generic( ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); } else if ( dictMode == ZSTD_noDict - && (offset_1 > 0) & (MEM_read32(repMatch) == MEM_read32(ip+1))) { + && (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, ip-anchor, anchor, 0, mLength-MINMATCH); From d005e5daf4323b0c67eb34391ca51445a60372a5 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Thu, 10 May 2018 13:46:19 -0400 Subject: [PATCH 18/32] Whitespace Fix --- lib/compress/zstd_fast.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index 48a165d3..df2a0a05 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -130,7 +130,7 @@ size_t ZSTD_compressBlock_fast_generic( /* found a dict match */ U32 const offset = (U32)(current-dictMatchIndex-dictIndexDelta); mLength = ZSTD_count_2segments(ip+4, dictMatch+4, iend, dictEnd, istart) + 4; - while (((ip>anchor) & (dictMatch>dictLowest)) + while (((ip>anchor) & (dictMatch>dictLowest)) && (ip[-1] == dictMatch[-1])) { ip--; dictMatch--; mLength++; } /* catch up */ @@ -162,8 +162,8 @@ size_t ZSTD_compressBlock_fast_generic( /* Fill Table */ hashTable[ZSTD_hashPtr(base+current+2, hlog, mls)] = current+2; /* here because current+2 could be > iend-8 */ hashTable[ZSTD_hashPtr(ip-2, hlog, mls)] = (U32)(ip-2-base); - /* check immediate repcode */ + /* check immediate repcode */ if (dictMode == ZSTD_dictMatchState) { while (ip <= ilimit) { U32 const current2 = (U32)(ip-base); From 2d598e6fedd798086894f953fbc44b189bca746a Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Thu, 10 May 2018 17:17:10 -0400 Subject: [PATCH 19/32] Force Working Context Indices Greater than Dict Indices --- lib/compress/zstd_compress.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index f0576f08..e0588268 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1229,6 +1229,17 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, if (attachDict) { DEBUGLOG(4, "attaching dictionary into context"); cctx->blockState.matchState.dictMatchState = &cdict->matchState; + + /* prep working match state so dict matches never have negative indices + * when they are translated to the working context's index space. */ + if (cctx->blockState.matchState.window.dictLimit < + (U32)(cdict->matchState.window.nextSrc - cdict->matchState.window.base)) { + cctx->blockState.matchState.window.nextSrc = + cctx->blockState.matchState.window.base + + ( cdict->matchState.window.nextSrc + - cdict->matchState.window.base); + ZSTD_window_clear(&cctx->blockState.matchState.window); + } } else { DEBUGLOG(4, "copying dictionary into context"); /* copy tables */ From 1a7b34ef28d309f58ce4988071eb2b6bf4830599 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Thu, 10 May 2018 17:18:08 -0400 Subject: [PATCH 20/32] Use New Index Invariant to Simplify Conditionals --- lib/compress/zstd_fast.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index df2a0a05..dacb2637 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -74,16 +74,18 @@ size_t ZSTD_compressBlock_fast_generic( const U32 dictIndexDelta = dictMode == ZSTD_dictMatchState ? localLowestIndex - (U32)(dictEnd - dictBase) : 0; - ptrdiff_t dictLowestLocalIndex = dictMode == ZSTD_dictMatchState ? - lowestDictIndex + dictIndexDelta : - localLowestIndex; assert(dictMode == ZSTD_noDict || dictMode == ZSTD_dictMatchState); + /* otherwise, we would get index underflow when translating a dict index + * into a local index */ + assert(dictMode != ZSTD_dictMatchState + || localLowestIndex >= (U32)(dictEnd - dictBase)); + /* init */ ip += (dictMode == ZSTD_noDict && ip == localLowest); { U32 const maxRep = dictMode == ZSTD_dictMatchState ? - (U32)(ip - dictLowest) : + (U32)(ip - localLowest + dictEnd - dictLowest) : (U32)(ip - localLowest); if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0; if (offset_1 > maxRep) offsetSaved = offset_1, offset_1 = 0; @@ -96,23 +98,22 @@ size_t ZSTD_compressBlock_fast_generic( U32 const current = (U32)(ip-base); U32 const matchIndex = hashTable[h]; const BYTE* match = base + matchIndex; - const ptrdiff_t repIndex = (ptrdiff_t)current + 1 - offset_1; + const U32 repIndex = current + 1 - offset_1; const BYTE* repBase = (dictMode == ZSTD_dictMatchState - && repIndex < (ptrdiff_t)localLowestIndex) ? + && repIndex < localLowestIndex) ? dictBase - dictIndexDelta : base; const BYTE* repMatch = repBase + repIndex; hashTable[h] = current; /* update hash table */ if (dictMode == ZSTD_dictMatchState - && (((U32)((localLowestIndex-1) - repIndex) >= 3 /* intentional underflow */) - & (repIndex > dictLowestLocalIndex)) + && ((U32)((localLowestIndex-1) - repIndex) >= 3 /* intentional underflow */) && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { - const BYTE* repMatchEnd = repIndex < (ptrdiff_t)localLowestIndex ? dictEnd : iend; + const BYTE* repMatchEnd = repIndex < localLowestIndex ? dictEnd : iend; mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, istart) + 4; ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); } else if ( dictMode == ZSTD_noDict - && (offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1))) { + && ((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, ip-anchor, anchor, 0, mLength-MINMATCH); @@ -167,15 +168,14 @@ size_t ZSTD_compressBlock_fast_generic( if (dictMode == ZSTD_dictMatchState) { while (ip <= ilimit) { U32 const current2 = (U32)(ip-base); - ptrdiff_t const repIndex2 = (ptrdiff_t)current2 - offset_2; + U32 const repIndex2 = current2 - offset_2; const BYTE* repMatch2 = dictMode == ZSTD_dictMatchState - && repIndex2 < (ptrdiff_t)localLowestIndex ? + && repIndex2 < localLowestIndex ? dictBase - dictIndexDelta + repIndex2 : base + repIndex2; - if ( (((U32)((localLowestIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */) - & (repIndex2 > dictLowestLocalIndex)) + if ( ((U32)((localLowestIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */) && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { - const BYTE* const repEnd2 = repIndex2 < (ptrdiff_t)localLowestIndex ? dictEnd : iend; + const BYTE* const repEnd2 = repIndex2 < localLowestIndex ? dictEnd : iend; size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, istart) + 4; U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ ZSTD_storeSeq(seqStore, 0, anchor, 0, repLength2-MINMATCH); @@ -190,7 +190,6 @@ size_t ZSTD_compressBlock_fast_generic( if (dictMode == ZSTD_noDict) { while ( (ip <= ilimit) - && (ip - offset_2 >= istart) && ( (offset_2>0) & (MEM_read32(ip) == MEM_read32(ip - offset_2)) )) { /* store sequence */ From b05ae9b6086fea37b7c7edee1fc8296e01a1b521 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Tue, 15 May 2018 01:15:33 -0400 Subject: [PATCH 21/32] Refine ip Initialization to Avoid ARM Weirdness --- lib/compress/zstd_fast.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index dacb2637..5c6f0dc8 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -83,7 +83,7 @@ size_t ZSTD_compressBlock_fast_generic( || localLowestIndex >= (U32)(dictEnd - dictBase)); /* init */ - ip += (dictMode == ZSTD_noDict && ip == localLowest); + ip += (ip - localLowest + dictEnd - dictLowest == 0); { U32 const maxRep = dictMode == ZSTD_dictMatchState ? (U32)(ip - localLowest + dictEnd - dictLowest) : (U32)(ip - localLowest); From 3ba70cc759550c7ac4f3c02d0dfc3de113d594e9 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Tue, 15 May 2018 13:08:03 -0400 Subject: [PATCH 22/32] Clear the Dictionary When Sliding the Window --- lib/compress/zstd_compress.c | 4 +++- lib/compress/zstd_compress_internal.h | 13 ++++++++----- lib/compress/zstd_ldm.c | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index e0588268..9f488b9a 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1240,6 +1240,7 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, - cdict->matchState.window.base); ZSTD_window_clear(&cctx->blockState.matchState.window); } + cctx->blockState.matchState.loadedDictEnd = cctx->blockState.matchState.window.dictLimit; } else { DEBUGLOG(4, "copying dictionary into context"); /* copy tables */ @@ -2313,8 +2314,9 @@ static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx, if (ms->nextToUpdate < correction) ms->nextToUpdate = 0; else ms->nextToUpdate -= correction; ms->loadedDictEnd = 0; + ms->dictMatchState = NULL; } - ZSTD_window_enforceMaxDist(&ms->window, ip + blockSize, maxDist, &ms->loadedDictEnd); + ZSTD_window_enforceMaxDist(&ms->window, ip + blockSize, maxDist, &ms->loadedDictEnd, &ms->dictMatchState); if (ms->nextToUpdate < ms->window.lowLimit) ms->nextToUpdate = ms->window.lowLimit; { size_t cSize = ZSTD_compressBlock_internal(cctx, diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 80c03433..a61fc374 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -520,10 +520,10 @@ MEM_STATIC U32 ZSTD_window_hasExtDict(ZSTD_window_t const window) */ MEM_STATIC ZSTD_dictMode_e ZSTD_matchState_dictMode(const ZSTD_matchState_t *ms) { - return ms->dictMatchState != NULL ? - ZSTD_dictMatchState : - ZSTD_window_hasExtDict(ms->window) ? - ZSTD_extDict : + return ZSTD_window_hasExtDict(ms->window) ? + ZSTD_extDict : + ms->dictMatchState != NULL ? + ZSTD_dictMatchState : ZSTD_noDict; } @@ -605,7 +605,8 @@ MEM_STATIC U32 ZSTD_window_correctOverflow(ZSTD_window_t* window, U32 cycleLog, */ MEM_STATIC void ZSTD_window_enforceMaxDist(ZSTD_window_t* window, void const* srcEnd, U32 maxDist, - U32* loadedDictEndPtr) + U32* loadedDictEndPtr, + const ZSTD_matchState_t** dictMatchStatePtr) { U32 const current = (U32)((BYTE const*)srcEnd - window->base); U32 loadedDictEnd = loadedDictEndPtr != NULL ? *loadedDictEndPtr : 0; @@ -619,6 +620,8 @@ MEM_STATIC void ZSTD_window_enforceMaxDist(ZSTD_window_t* window, } if (loadedDictEndPtr) *loadedDictEndPtr = 0; + if (dictMatchStatePtr) + *dictMatchStatePtr = NULL; } } diff --git a/lib/compress/zstd_ldm.c b/lib/compress/zstd_ldm.c index b0c5d065..03d1f54c 100644 --- a/lib/compress/zstd_ldm.c +++ b/lib/compress/zstd_ldm.c @@ -508,7 +508,7 @@ size_t ZSTD_ldm_generateSequences( * * Try invalidation after the sequence generation and test the * the offset against maxDist directly. */ - ZSTD_window_enforceMaxDist(&ldmState->window, chunkEnd, maxDist, NULL); + ZSTD_window_enforceMaxDist(&ldmState->window, chunkEnd, maxDist, NULL, NULL); /* 3. Generate the sequences for the chunk, and get newLeftoverSize. */ newLeftoverSize = ZSTD_ldm_generateSequences_internal( ldmState, sequences, params, chunkStart, chunkSize); From 7e0402e738f2e3ff6d74e63283bc7c514b67b3a4 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Tue, 15 May 2018 13:13:19 -0400 Subject: [PATCH 23/32] Also Attach Dict When Source Size is Unknown --- lib/compress/zstd_compress.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 9f488b9a..4d4e171b 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1208,7 +1208,8 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, * context, or referencing the dictionary context from the working context * in-place. We decide here which strategy to use. */ /* TODO: pick reasonable cut-off size, handle ZSTD_CONTENTSIZE_UNKNOWN */ - int attachDict = pledgedSrcSize <= 8 KB + int attachDict = ( pledgedSrcSize <= 8 KB + || pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN ) && cdict->cParams.strategy == ZSTD_fast && ZSTD_equivalentCParams(cctx->appliedParams.cParams, cdict->cParams); From 95bdf20a872ab7eef689799e19f83fa1462a44ba Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Tue, 15 May 2018 13:16:50 -0400 Subject: [PATCH 24/32] Moar Renames --- lib/compress/zstd_fast.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index 5c6f0dc8..09b1a8ec 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -53,8 +53,8 @@ size_t ZSTD_compressBlock_fast_generic( const BYTE* const istart = (const BYTE*)src; const BYTE* ip = istart; const BYTE* anchor = istart; - const U32 localLowestIndex = ms->window.dictLimit; - const BYTE* const localLowest = base + localLowestIndex; + const U32 prefixLowestIndex = ms->window.dictLimit; + const BYTE* const prefixLowest = base + prefixLowestIndex; const BYTE* const iend = istart + srcSize; const BYTE* const ilimit = iend - HASH_READ_SIZE; U32 offset_1=rep[0], offset_2=rep[1]; @@ -63,16 +63,16 @@ size_t ZSTD_compressBlock_fast_generic( const ZSTD_matchState_t* const dms = ms->dictMatchState; const U32* const dictHashTable = dictMode == ZSTD_dictMatchState ? dms->hashTable : NULL; - const U32 lowestDictIndex = dictMode == ZSTD_dictMatchState ? + const U32 dictLowestIndex = dictMode == ZSTD_dictMatchState ? dms->window.dictLimit : 0; const BYTE* const dictBase = dictMode == ZSTD_dictMatchState ? dms->window.base : NULL; const BYTE* const dictLowest = dictMode == ZSTD_dictMatchState ? - dictBase + lowestDictIndex : NULL; + dictBase + dictLowestIndex : NULL; const BYTE* const dictEnd = dictMode == ZSTD_dictMatchState ? dms->window.nextSrc : NULL; const U32 dictIndexDelta = dictMode == ZSTD_dictMatchState ? - localLowestIndex - (U32)(dictEnd - dictBase) : + prefixLowestIndex - (U32)(dictEnd - dictBase) : 0; assert(dictMode == ZSTD_noDict || dictMode == ZSTD_dictMatchState); @@ -80,13 +80,13 @@ size_t ZSTD_compressBlock_fast_generic( /* otherwise, we would get index underflow when translating a dict index * into a local index */ assert(dictMode != ZSTD_dictMatchState - || localLowestIndex >= (U32)(dictEnd - dictBase)); + || prefixLowestIndex >= (U32)(dictEnd - dictBase)); /* init */ - ip += (ip - localLowest + dictEnd - dictLowest == 0); + ip += (ip - prefixLowest + dictEnd - dictLowest == 0); { U32 const maxRep = dictMode == ZSTD_dictMatchState ? - (U32)(ip - localLowest + dictEnd - dictLowest) : - (U32)(ip - localLowest); + (U32)(ip - prefixLowest + dictEnd - dictLowest) : + (U32)(ip - prefixLowest); if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0; if (offset_1 > maxRep) offsetSaved = offset_1, offset_1 = 0; } @@ -100,15 +100,15 @@ size_t ZSTD_compressBlock_fast_generic( const BYTE* match = base + matchIndex; const U32 repIndex = current + 1 - offset_1; const BYTE* repBase = (dictMode == ZSTD_dictMatchState - && repIndex < localLowestIndex) ? + && repIndex < prefixLowestIndex) ? dictBase - dictIndexDelta : base; const BYTE* repMatch = repBase + repIndex; hashTable[h] = current; /* update hash table */ if (dictMode == ZSTD_dictMatchState - && ((U32)((localLowestIndex-1) - repIndex) >= 3 /* intentional underflow */) + && ((U32)((prefixLowestIndex-1) - repIndex) >= 3 /* intentional underflow */) && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { - const BYTE* repMatchEnd = repIndex < localLowestIndex ? dictEnd : iend; + const BYTE* repMatchEnd = repIndex < prefixLowestIndex ? dictEnd : iend; mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, istart) + 4; ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); @@ -117,12 +117,12 @@ size_t ZSTD_compressBlock_fast_generic( mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4; ip++; ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); - } else if ( (matchIndex <= localLowestIndex) + } else if ( (matchIndex <= prefixLowestIndex) || (MEM_read32(match) != MEM_read32(ip)) ) { if (dictMode == ZSTD_dictMatchState) { U32 const dictMatchIndex = dictHashTable[h]; const BYTE* dictMatch = dictBase + dictMatchIndex; - if (dictMatchIndex <= lowestDictIndex || + if (dictMatchIndex <= dictLowestIndex || MEM_read32(dictMatch) != MEM_read32(ip)) { assert(stepSize >= 1); ip += ((ip-anchor) >> kSearchStrength) + stepSize; @@ -148,7 +148,7 @@ size_t ZSTD_compressBlock_fast_generic( /* found a regular match */ U32 const offset = (U32)(ip-match); mLength = ZSTD_count(ip+4, match+4, iend) + 4; - while (((ip>anchor) & (match>localLowest)) + while (((ip>anchor) & (match>prefixLowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ offset_2 = offset_1; offset_1 = offset; @@ -170,12 +170,12 @@ size_t ZSTD_compressBlock_fast_generic( U32 const current2 = (U32)(ip-base); U32 const repIndex2 = current2 - offset_2; const BYTE* repMatch2 = dictMode == ZSTD_dictMatchState - && repIndex2 < localLowestIndex ? + && repIndex2 < prefixLowestIndex ? dictBase - dictIndexDelta + repIndex2 : base + repIndex2; - if ( ((U32)((localLowestIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */) + if ( ((U32)((prefixLowestIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */) && (MEM_read32(repMatch2) == MEM_read32(ip)) ) { - const BYTE* const repEnd2 = repIndex2 < localLowestIndex ? dictEnd : iend; + const BYTE* const repEnd2 = repIndex2 < prefixLowestIndex ? dictEnd : iend; size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, istart) + 4; U32 tmpOffset = offset_2; offset_2 = offset_1; offset_1 = tmpOffset; /* swap offset_2 <=> offset_1 */ ZSTD_storeSeq(seqStore, 0, anchor, 0, repLength2-MINMATCH); From a44ab3b475882fc5447a949e8b21e68d9ed9be5e Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Tue, 15 May 2018 15:41:37 -0400 Subject: [PATCH 25/32] Remove Out-of-Date 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 4d4e171b..aa7fc1e8 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1207,7 +1207,6 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, /* We have a choice between copying the dictionary context into the working * context, or referencing the dictionary context from the working context * in-place. We decide here which strategy to use. */ - /* TODO: pick reasonable cut-off size, handle ZSTD_CONTENTSIZE_UNKNOWN */ int attachDict = ( pledgedSrcSize <= 8 KB || pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN ) && cdict->cParams.strategy == ZSTD_fast From 9c92223468acca6e5a7082e4e09b1f6870df7aa4 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Tue, 15 May 2018 15:45:37 -0400 Subject: [PATCH 26/32] Avoid Undefined Behavior in Match Ptr Calculation --- lib/compress/zstd_fast.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index 09b1a8ec..b21bc768 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -99,10 +99,10 @@ size_t ZSTD_compressBlock_fast_generic( U32 const matchIndex = hashTable[h]; const BYTE* match = base + matchIndex; const U32 repIndex = current + 1 - offset_1; - const BYTE* repBase = (dictMode == ZSTD_dictMatchState + const BYTE* repMatch = (dictMode == ZSTD_dictMatchState && repIndex < prefixLowestIndex) ? - dictBase - dictIndexDelta : base; - const BYTE* repMatch = repBase + repIndex; + dictBase + (repIndex - dictIndexDelta) : + base + repIndex; hashTable[h] = current; /* update hash table */ if (dictMode == ZSTD_dictMatchState From 582b7f85ed25bf828854f9507d1c75c4d74962bf Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Tue, 15 May 2018 17:23:16 -0400 Subject: [PATCH 27/32] Don't Attach Empty Dict Contents In weird corner cases, they produce unexpected results... --- lib/compress/zstd_compress.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index aa7fc1e8..b1d52b9a 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1227,20 +1227,25 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, } if (attachDict) { - DEBUGLOG(4, "attaching dictionary into context"); - cctx->blockState.matchState.dictMatchState = &cdict->matchState; + if (cdict->matchState.window.nextSrc - cdict->matchState.window.base == 0) { + /* don't even attach dictionaries with no contents */ + DEBUGLOG(4, "skipping attaching empty dictionary"); + } else { + DEBUGLOG(4, "attaching dictionary into context"); + cctx->blockState.matchState.dictMatchState = &cdict->matchState; - /* prep working match state so dict matches never have negative indices - * when they are translated to the working context's index space. */ - if (cctx->blockState.matchState.window.dictLimit < - (U32)(cdict->matchState.window.nextSrc - cdict->matchState.window.base)) { - cctx->blockState.matchState.window.nextSrc = - cctx->blockState.matchState.window.base + - ( cdict->matchState.window.nextSrc - - cdict->matchState.window.base); - ZSTD_window_clear(&cctx->blockState.matchState.window); + /* prep working match state so dict matches never have negative indices + * when they are translated to the working context's index space. */ + if (cctx->blockState.matchState.window.dictLimit < + (U32)(cdict->matchState.window.nextSrc - cdict->matchState.window.base)) { + cctx->blockState.matchState.window.nextSrc = + cctx->blockState.matchState.window.base + + ( cdict->matchState.window.nextSrc + - cdict->matchState.window.base); + ZSTD_window_clear(&cctx->blockState.matchState.window); + } + cctx->blockState.matchState.loadedDictEnd = cctx->blockState.matchState.window.dictLimit; } - cctx->blockState.matchState.loadedDictEnd = cctx->blockState.matchState.window.dictLimit; } else { DEBUGLOG(4, "copying dictionary into context"); /* copy tables */ From 7ef85e061877d96991a4fc419ad96146afc8f88b Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Mon, 21 May 2018 18:27:08 -0400 Subject: [PATCH 28/32] Fixes in re Comments --- lib/compress/zstd_compress.c | 24 ++++++++++++------------ lib/compress/zstd_fast.c | 17 +++++++++++------ 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index b1d52b9a..e49046fc 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1207,11 +1207,12 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, /* We have a choice between copying the dictionary context into the working * context, or referencing the dictionary context from the working context * in-place. We decide here which strategy to use. */ - int attachDict = ( pledgedSrcSize <= 8 KB - || pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN ) - && cdict->cParams.strategy == ZSTD_fast - && ZSTD_equivalentCParams(cctx->appliedParams.cParams, - cdict->cParams); + const int attachDict = ( pledgedSrcSize <= 8 KB + || pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN ) + && cdict->cParams.strategy == ZSTD_fast + && ZSTD_equivalentCParams(cctx->appliedParams.cParams, + cdict->cParams); + { unsigned const windowLog = params.cParams.windowLog; assert(windowLog != 0); @@ -1227,7 +1228,9 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, } if (attachDict) { - if (cdict->matchState.window.nextSrc - cdict->matchState.window.base == 0) { + const U32 cdictLen = (U32)( cdict->matchState.window.nextSrc + - cdict->matchState.window.base); + if (cdictLen == 0) { /* don't even attach dictionaries with no contents */ DEBUGLOG(4, "skipping attaching empty dictionary"); } else { @@ -1236,15 +1239,12 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, /* prep working match state so dict matches never have negative indices * when they are translated to the working context's index space. */ - if (cctx->blockState.matchState.window.dictLimit < - (U32)(cdict->matchState.window.nextSrc - cdict->matchState.window.base)) { + if (cctx->blockState.matchState.window.dictLimit < cdictLen) { cctx->blockState.matchState.window.nextSrc = - cctx->blockState.matchState.window.base + - ( cdict->matchState.window.nextSrc - - cdict->matchState.window.base); + cctx->blockState.matchState.window.base + cdictLen; ZSTD_window_clear(&cctx->blockState.matchState.window); } - cctx->blockState.matchState.loadedDictEnd = cctx->blockState.matchState.window.dictLimit; + cctx->blockState.matchState.loadedDictEnd = params.forceWindow ? 0 : cdictLen; } } else { DEBUGLOG(4, "copying dictionary into context"); diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index b21bc768..3bac2bdd 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -74,6 +74,7 @@ size_t ZSTD_compressBlock_fast_generic( const U32 dictIndexDelta = dictMode == ZSTD_dictMatchState ? prefixLowestIndex - (U32)(dictEnd - dictBase) : 0; + const U32 dictAndPrefixLength = (U32)(ip - prefixLowest + dictEnd - dictLowest); assert(dictMode == ZSTD_noDict || dictMode == ZSTD_dictMatchState); @@ -83,13 +84,18 @@ size_t ZSTD_compressBlock_fast_generic( || prefixLowestIndex >= (U32)(dictEnd - dictBase)); /* init */ - ip += (ip - prefixLowest + dictEnd - dictLowest == 0); - { U32 const maxRep = dictMode == ZSTD_dictMatchState ? - (U32)(ip - prefixLowest + dictEnd - dictLowest) : - (U32)(ip - prefixLowest); + ip += (dictAndPrefixLength == 0); + if (dictMode == ZSTD_noDict) { + U32 const maxRep = (U32)(ip - prefixLowest); if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0; if (offset_1 > maxRep) offsetSaved = offset_1, offset_1 = 0; } + if (dictMode == ZSTD_dictMatchState) { + /* 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) */ @@ -169,8 +175,7 @@ size_t ZSTD_compressBlock_fast_generic( while (ip <= ilimit) { U32 const current2 = (U32)(ip-base); U32 const repIndex2 = current2 - offset_2; - const BYTE* repMatch2 = dictMode == ZSTD_dictMatchState - && repIndex2 < prefixLowestIndex ? + const BYTE* repMatch2 = repIndex2 < prefixLowestIndex ? dictBase - dictIndexDelta + repIndex2 : base + repIndex2; if ( ((U32)((prefixLowestIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */) From 298d24fa573842c7cc0c3530869817bc9ebe36f8 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Mon, 21 May 2018 20:12:11 -0400 Subject: [PATCH 29/32] Make loadedDictEnd an Index, not the Dict Len --- lib/compress/zstd_compress.c | 4 +++- lib/compress/zstd_compress_internal.h | 6 ++++++ lib/compress/zstd_fast.c | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index e49046fc..105cea44 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1209,6 +1209,8 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, * in-place. We decide here which strategy to use. */ const int attachDict = ( pledgedSrcSize <= 8 KB || pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN ) + && !params.forceWindow /* dictMatchState isn't correctly + * handled in _enforceMaxDist */ && cdict->cParams.strategy == ZSTD_fast && ZSTD_equivalentCParams(cctx->appliedParams.cParams, cdict->cParams); @@ -1244,7 +1246,7 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, cctx->blockState.matchState.window.base + cdictLen; ZSTD_window_clear(&cctx->blockState.matchState.window); } - cctx->blockState.matchState.loadedDictEnd = params.forceWindow ? 0 : cdictLen; + cctx->blockState.matchState.loadedDictEnd = cctx->blockState.matchState.window.dictLimit; } } else { DEBUGLOG(4, "copying dictionary into context"); diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index a61fc374..a7666d5c 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -594,14 +594,20 @@ MEM_STATIC U32 ZSTD_window_correctOverflow(ZSTD_window_t* window, U32 cycleLog, * ZSTD_window_enforceMaxDist(): * Updates lowLimit so that: * (srcEnd - base) - lowLimit == maxDist + loadedDictEnd + * * This allows a simple check that index >= lowLimit to see if index is valid. * This must be called before a block compression call, with srcEnd as the block * source end. + * * If loadedDictEndPtr is not NULL, we set it to zero once we update lowLimit. * This is because dictionaries are allowed to be referenced as long as the last * byte of the dictionary is in the window, but once they are out of range, * they cannot be referenced. If loadedDictEndPtr is NULL, we use * loadedDictEnd == 0. + * + * In normal dict mode, the dict is between lowLimit and dictLimit. In + * dictMatchState mode, lowLimit and dictLimit are the same, and the dictionary + * is below them. forceWindow and dictMatchState are therefore incompatible. */ MEM_STATIC void ZSTD_window_enforceMaxDist(ZSTD_window_t* window, void const* srcEnd, U32 maxDist, diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index 3bac2bdd..bf962a17 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -72,7 +72,7 @@ size_t ZSTD_compressBlock_fast_generic( const BYTE* const dictEnd = dictMode == ZSTD_dictMatchState ? dms->window.nextSrc : NULL; const U32 dictIndexDelta = dictMode == ZSTD_dictMatchState ? - prefixLowestIndex - (U32)(dictEnd - dictBase) : + ms->loadedDictEnd - (U32)(dictEnd - dictBase) : 0; const U32 dictAndPrefixLength = (U32)(ip - prefixLowest + dictEnd - dictLowest); From d9c7e67125d95d751e934870fdf611fbe0995934 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 23 May 2018 16:00:17 -0400 Subject: [PATCH 30/32] Assert that Dict and Current Window are Adjacent in Index Space --- lib/compress/zstd_compress.c | 5 +++++ lib/compress/zstd_fast.c | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 105cea44..00f3e789 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -2210,6 +2210,11 @@ static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc, ZSTD_resetSeqStore(&(zc->seqStore)); ms->opt.symbolCosts = &zc->blockState.prevCBlock->entropy; /* required for optimal parser to read stats from dictionary */ + /* a gap between an attached dict and the current window is not safe, + * they must remain adjacent, and when that stops being the case, the dict + * must be unset */ + assert(ms->dictMatchState == NULL || ms->loadedDictEnd == ms->window.dictLimit); + /* limited update after a very long match */ { const BYTE* const base = ms->window.base; const BYTE* const istart = (const BYTE*)src; diff --git a/lib/compress/zstd_fast.c b/lib/compress/zstd_fast.c index bf962a17..3bac2bdd 100644 --- a/lib/compress/zstd_fast.c +++ b/lib/compress/zstd_fast.c @@ -72,7 +72,7 @@ size_t ZSTD_compressBlock_fast_generic( const BYTE* const dictEnd = dictMode == ZSTD_dictMatchState ? dms->window.nextSrc : NULL; const U32 dictIndexDelta = dictMode == ZSTD_dictMatchState ? - ms->loadedDictEnd - (U32)(dictEnd - dictBase) : + prefixLowestIndex - (U32)(dictEnd - dictBase) : 0; const U32 dictAndPrefixLength = (U32)(ip - prefixLowest + dictEnd - dictLowest); From f2d0924b87e4dab590cb53f98bb33a21c4375119 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Wed, 23 May 2018 14:58:58 -0700 Subject: [PATCH 31/32] Variable declarations --- lib/common/entropy_common.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/common/entropy_common.c b/lib/common/entropy_common.c index 2edb6e9b..33fd04bd 100644 --- a/lib/common/entropy_common.c +++ b/lib/common/entropy_common.c @@ -73,16 +73,16 @@ size_t FSE_readNCount (short* normalizedCounter, unsigned* maxSVPtr, unsigned* t int previous0 = 0; if (hbSize < 4) { - /* This function only works when hbSize >= 4 */ - char buffer[4]; - memset(buffer, 0, sizeof(buffer)); - memcpy(buffer, headerBuffer, hbSize); - size_t const countSize = FSE_readNCount(normalizedCounter, maxSVPtr, tableLogPtr, - buffer, sizeof(buffer)); - if (FSE_isError(countSize)) return countSize; - if (countSize > hbSize) return ERROR(corruption_detected); - return countSize; - } + /* This function only works when hbSize >= 4 */ + char buffer[4]; + memset(buffer, 0, sizeof(buffer)); + memcpy(buffer, headerBuffer, hbSize); + { size_t const countSize = FSE_readNCount(normalizedCounter, maxSVPtr, tableLogPtr, + buffer, sizeof(buffer)); + if (FSE_isError(countSize)) return countSize; + if (countSize > hbSize) return ERROR(corruption_detected); + return countSize; + } } assert(hbSize >= 4); bitStream = MEM_readLE32(ip); From 06b70179da2b8df06ab9a7d284e48f45f437b670 Mon Sep 17 00:00:00 2001 From: Nick Terrell Date: Wed, 23 May 2018 18:02:30 -0700 Subject: [PATCH 32/32] Work around bug in zstd decoder (#1147) Work around bug in zstd decoder Pull request #1144 exercised a new path in the zstd decoder that proved to be buggy. Avoid the extremely rare bug by emitting an uncompressed block. --- lib/compress/zstd_compress.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 22c704f1..01bb3036 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1983,6 +1983,7 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, BYTE* op = ostart; size_t const nbSeq = seqStorePtr->sequences - seqStorePtr->sequencesStart; BYTE* seqHead; + BYTE* lastNCount = NULL; ZSTD_STATIC_ASSERT(HUF_WORKSPACE_SIZE >= (1<fse.litlengthCTable, sizeof(prevEntropy->fse.litlengthCTable), workspace, HUF_WORKSPACE_SIZE); if (ZSTD_isError(countSize)) return countSize; + if (LLtype == set_compressed) + lastNCount = op; op += countSize; } } /* build CTable for Offsets */ @@ -2049,6 +2052,8 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, prevEntropy->fse.offcodeCTable, sizeof(prevEntropy->fse.offcodeCTable), workspace, HUF_WORKSPACE_SIZE); if (ZSTD_isError(countSize)) return countSize; + if (Offtype == set_compressed) + lastNCount = op; op += countSize; } } /* build CTable for MatchLengths */ @@ -2063,6 +2068,8 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, prevEntropy->fse.matchlengthCTable, sizeof(prevEntropy->fse.matchlengthCTable), workspace, HUF_WORKSPACE_SIZE); if (ZSTD_isError(countSize)) return countSize; + if (MLtype == set_compressed) + lastNCount = op; op += countSize; } } @@ -2077,6 +2084,21 @@ MEM_STATIC size_t ZSTD_compressSequences_internal(seqStore_t* seqStorePtr, longOffsets, bmi2); if (ZSTD_isError(bitstreamSize)) return bitstreamSize; op += bitstreamSize; + /* zstd versions <= 1.3.4 mistakenly report corruption when + * FSE_readNCount() recieves a buffer < 4 bytes. + * Fixed by https://github.com/facebook/zstd/pull/1146. + * This can happen when the last set_compressed table present is 2 + * bytes and the bitstream is only one byte. + * In this exceedingly rare case, we will simply emit an uncompressed + * block, since it isn't worth optimizing. + */ + if (lastNCount && (op - lastNCount) < 4) { + /* NCountSize >= 2 && bitstreamSize > 0 ==> lastCountSize == 3 */ + assert(op - lastNCount == 3); + DEBUGLOG(5, "Avoiding bug in zstd decoder in versions <= 1.3.4 by " + "emitting an uncompressed block."); + return 0; + } } return op - ostart; @@ -2092,6 +2114,7 @@ MEM_STATIC size_t ZSTD_compressSequences(seqStore_t* seqStorePtr, size_t const cSize = ZSTD_compressSequences_internal( seqStorePtr, prevEntropy, nextEntropy, cctxParams, dst, dstCapacity, workspace, bmi2); + if (cSize == 0) return 0; /* 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. */