From c10d1b4011c9a0c8d126635899c8416f050fb7b3 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Fri, 4 May 2018 15:59:27 -0400 Subject: [PATCH 01/17] Skeleton for In-Place Impl for ZSTD_dfast --- lib/compress/zstd_compress.c | 5 +++-- lib/compress/zstd_double_fast.c | 32 +++++++++++++++++++++++++++----- lib/compress/zstd_double_fast.h | 3 +++ 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 538319bcb..46a3fe541 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1211,7 +1211,7 @@ static size_t ZSTD_resetCCtx_usingCDict(ZSTD_CCtx* cctx, || pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN ) && !params.forceWindow /* dictMatchState isn't correctly * handled in _enforceMaxDist */ - && cdict->cParams.strategy == ZSTD_fast + && cdict->cParams.strategy <= ZSTD_dfast && ZSTD_equivalentCParams(cctx->appliedParams.cParams, cdict->cParams); @@ -2192,7 +2192,8 @@ ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, ZSTD_dictMo ZSTD_compressBlock_btopt_extDict, ZSTD_compressBlock_btultra_extDict }, { ZSTD_compressBlock_fast_dictMatchState /* default for 0 */, ZSTD_compressBlock_fast_dictMatchState, - NULL, NULL, NULL, NULL, NULL, NULL, NULL /* unimplemented as of yet */ } + ZSTD_compressBlock_doubleFast_dictMatchState, + NULL, NULL, NULL, NULL, NULL, NULL /* unimplemented as of yet */ } }; ZSTD_blockCompressor selectedCompressor; ZSTD_STATIC_ASSERT((unsigned)ZSTD_fast == 1); diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index 78aa0cbad..28c4f0248 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -52,7 +52,7 @@ FORCE_INLINE_TEMPLATE size_t ZSTD_compressBlock_doubleFast_generic( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize, - U32 const mls /* template */) + U32 const mls /* template */, ZSTD_dictMode_e const dictMode) { U32* const hashLong = ms->hashTable; const U32 hBitsL = cParams->hashLog; @@ -69,6 +69,8 @@ size_t ZSTD_compressBlock_doubleFast_generic( U32 offset_1=rep[0], offset_2=rep[1]; U32 offsetSaved = 0; + (void)dictMode; + /* init */ ip += (ip==lowest); { U32 const maxRep = (U32)(ip-lowest); @@ -170,13 +172,33 @@ size_t ZSTD_compressBlock_doubleFast( { default: /* includes case 3 */ case 4 : - return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, cParams, src, srcSize, 4); + return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, cParams, src, srcSize, 4, ZSTD_noDict); case 5 : - return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, cParams, src, srcSize, 5); + return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, cParams, src, srcSize, 5, ZSTD_noDict); case 6 : - return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, cParams, src, srcSize, 6); + return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, cParams, src, srcSize, 6, ZSTD_noDict); case 7 : - return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, cParams, src, srcSize, 7); + return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, cParams, src, srcSize, 7, ZSTD_noDict); + } +} + + +size_t ZSTD_compressBlock_doubleFast_dictMatchState( + ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], + ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize) +{ + const U32 mls = cParams->searchLength; + switch(mls) + { + default: /* includes case 3 */ + case 4 : + return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, cParams, src, srcSize, 4, ZSTD_dictMatchState); + case 5 : + return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, cParams, src, srcSize, 5, ZSTD_dictMatchState); + case 6 : + return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, cParams, src, srcSize, 6, ZSTD_dictMatchState); + case 7 : + return ZSTD_compressBlock_doubleFast_generic(ms, seqStore, rep, cParams, src, srcSize, 7, ZSTD_dictMatchState); } } diff --git a/lib/compress/zstd_double_fast.h b/lib/compress/zstd_double_fast.h index 3f5a24eb3..c475021d2 100644 --- a/lib/compress/zstd_double_fast.h +++ b/lib/compress/zstd_double_fast.h @@ -24,6 +24,9 @@ void ZSTD_fillDoubleHashTable(ZSTD_matchState_t* ms, size_t ZSTD_compressBlock_doubleFast( 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_doubleFast_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_doubleFast_extDict( ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM], ZSTD_compressionParameters const* cParams, void const* src, size_t srcSize); From aacbbf4f9a2f993cb281707a21a41d8453a24865 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 9 May 2018 15:22:36 -0400 Subject: [PATCH 02/17] Rename 'lowest' to 'localLowest' to Prepare to Introduce Dict Indices --- lib/compress/zstd_double_fast.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index 28c4f0248..d1b926de9 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -62,8 +62,8 @@ size_t ZSTD_compressBlock_doubleFast_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]; @@ -72,8 +72,8 @@ size_t ZSTD_compressBlock_doubleFast_generic( (void)dictMode; /* init */ - ip += (ip==lowest); - { U32 const maxRep = (U32)(ip-lowest); + ip += (ip==localLowest); + { U32 const maxRep = (U32)(ip-localLowest); if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0; if (offset_1 > maxRep) offsetSaved = offset_1, offset_1 = 0; } @@ -98,24 +98,24 @@ size_t ZSTD_compressBlock_doubleFast_generic( ZSTD_storeSeq(seqStore, ip-anchor, anchor, 0, mLength-MINMATCH); } else { U32 offset; - if ( (matchIndexL > lowestIndex) && (MEM_read64(matchLong) == MEM_read64(ip)) ) { + if ( (matchIndexL > localLowestIndex) && (MEM_read64(matchLong) == MEM_read64(ip)) ) { mLength = ZSTD_count(ip+8, matchLong+8, iend) + 8; offset = (U32)(ip-matchLong); - while (((ip>anchor) & (matchLong>lowest)) && (ip[-1] == matchLong[-1])) { ip--; matchLong--; mLength++; } /* catch up */ - } else if ( (matchIndexS > lowestIndex) && (MEM_read32(match) == MEM_read32(ip)) ) { + while (((ip>anchor) & (matchLong>localLowest)) && (ip[-1] == matchLong[-1])) { ip--; matchLong--; mLength++; } /* catch up */ + } else if ( (matchIndexS > localLowestIndex) && (MEM_read32(match) == MEM_read32(ip)) ) { size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8); U32 const matchIndexL3 = hashLong[hl3]; const BYTE* matchL3 = base + matchIndexL3; hashLong[hl3] = current + 1; - if ( (matchIndexL3 > lowestIndex) && (MEM_read64(matchL3) == MEM_read64(ip+1)) ) { + if ( (matchIndexL3 > localLowestIndex) && (MEM_read64(matchL3) == MEM_read64(ip+1)) ) { mLength = ZSTD_count(ip+9, matchL3+8, iend) + 8; ip++; offset = (U32)(ip-matchL3); - while (((ip>anchor) & (matchL3>lowest)) && (ip[-1] == matchL3[-1])) { ip--; matchL3--; mLength++; } /* catch up */ + while (((ip>anchor) & (matchL3>localLowest)) && (ip[-1] == matchL3[-1])) { ip--; matchL3--; mLength++; } /* catch up */ } else { mLength = ZSTD_count(ip+4, match+4, iend) + 4; offset = (U32)(ip-match); - while (((ip>anchor) & (match>lowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ + while (((ip>anchor) & (match>localLowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ } } else { ip += ((ip-anchor) >> kSearchStrength) + 1; From 7097a037492b9089fb43e88d50a6fc39ac5f3780 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 9 May 2018 15:28:47 -0400 Subject: [PATCH 03/17] Add Necessary Dict Variables --- lib/compress/zstd_double_fast.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index d1b926de9..8c11d8ebe 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -69,6 +69,26 @@ size_t ZSTD_compressBlock_doubleFast_generic( U32 offset_1=rep[0], offset_2=rep[1]; U32 offsetSaved = 0; + const ZSTD_matchState_t* const dms = ms->dictMatchState; + const U32* const dictHashLong = dictMode == ZSTD_dictMatchState ? + dms->hashTable : NULL; + const U32* const dictHashSmall = dictMode == ZSTD_dictMatchState ? + dms->hashTable : NULL; + const U32 lowestDictIndex = 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; + const BYTE* const dictEnd = dictMode == ZSTD_dictMatchState ? + dms->window.nextSrc : NULL; + const U32 dictIndexDelta = dictMode == ZSTD_dictMatchState ? + localLowestIndex - (U32)(dictEnd - dictBase) : + 0; + ptrdiff_t dictLowestLocalIndex = dictMode == ZSTD_dictMatchState ? + lowestDictIndex + dictIndexDelta : + localLowestIndex; + (void)dictMode; /* init */ From 8b241da4dfd69ab377753e984f8e50258487fdeb Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 9 May 2018 15:23:22 -0400 Subject: [PATCH 04/17] Properly Initialize Repcode Values --- lib/compress/zstd_double_fast.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index 8c11d8ebe..2bcd4b5d0 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -89,11 +89,13 @@ size_t ZSTD_compressBlock_doubleFast_generic( lowestDictIndex + dictIndexDelta : localLowestIndex; - (void)dictMode; + assert(dictMode == ZSTD_noDict || dictMode == ZSTD_dictMatchState); /* init */ - ip += (ip==localLowest); - { U32 const maxRep = (U32)(ip-localLowest); + 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; if (offset_1 > maxRep) offsetSaved = offset_1, offset_1 = 0; } From 7a25f7ef5b10c4965a1700eaa44e067154e7ad7f Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 9 May 2018 16:46:57 -0400 Subject: [PATCH 05/17] Existing Repcode Check Only Applies to noDict Case --- lib/compress/zstd_double_fast.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index 2bcd4b5d0..0b8cba13e 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -112,8 +112,8 @@ size_t ZSTD_compressBlock_doubleFast_generic( const BYTE* match = base + matchIndexS; hashLong[h2] = hashSmall[h] = current; /* update hash tables */ - assert(offset_1 <= current); /* supposed guaranteed by construction */ - if ((offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1))) { + if (dictMode == ZSTD_noDict && ((offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1)))) { + assert(offset_1 <= current); /* supposed guaranteed by construction */ /* favor repcode */ mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4; ip++; @@ -162,7 +162,8 @@ size_t ZSTD_compressBlock_doubleFast_generic( hashSmall[ZSTD_hashPtr(ip-2, hBitsS, mls)] = (U32)(ip-2-base); /* check immediate repcode */ - while ( (ip <= ilimit) + while ( dictMode == ZSTD_noDict + && (ip <= ilimit) && ( (offset_2>0) & (MEM_read32(ip) == MEM_read32(ip - offset_2)) )) { /* store sequence */ From 50c5b2bb9027ed3061dbb3b8d4ede12cac561c51 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 9 May 2018 18:31:51 -0400 Subject: [PATCH 06/17] Find Dict Hash Table Matches --- lib/compress/zstd_double_fast.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index 0b8cba13e..6bf5fb5eb 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -139,6 +139,36 @@ size_t ZSTD_compressBlock_doubleFast_generic( offset = (U32)(ip-match); while (((ip>anchor) & (match>localLowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ } + } else if (dictMode == ZSTD_dictMatchState) { + U32 const dictMatchIndexL = dictHashLong[h2]; + U32 const dictMatchIndexS = dictHashSmall[h]; + const BYTE* dictMatchL = dictBase + dictMatchIndexL; + const BYTE* dictMatchS = dictBase + dictMatchIndexS; + assert(dictMatchL < dictEnd); + assert(dictMatchS < dictEnd); + if (dictMatchL > dictLowest && MEM_read64(dictMatchL) == MEM_read64(ip)) { + mLength = ZSTD_count_2segments(ip+8, dictMatchL+8, iend, dictEnd, localLowest) + 8; + offset = (U32)(current - dictMatchIndexL - dictIndexDelta); + while (((ip>anchor) & (dictMatchL>dictLowest)) && (ip[-1] == dictMatchL[-1])) { ip--; dictMatchL--; mLength++; } /* catch up */ + } else if (dictMatchS > dictLowest && MEM_read32(dictMatchS) == MEM_read32(ip)) { + size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8); + U32 const dictMatchIndexL3 = dictHashLong[hl3]; + const BYTE* dictMatchL3 = dictBase + dictMatchIndexL3; + assert(dictMatchL3 < dictEnd); + if (dictMatchL3 > dictLowest && MEM_read64(dictMatchL3) == MEM_read64(ip)) { + mLength = ZSTD_count_2segments(ip+1+8, dictMatchL3+8, iend, dictEnd, localLowest) + 8; + ip++; + offset = (U32)(current + 1 - dictMatchIndexL3 - dictIndexDelta); + while (((ip>anchor) & (dictMatchL3>dictLowest)) && (ip[-1] == dictMatchL3[-1])) { ip--; dictMatchL3--; mLength++; } /* catch up */ + } else { + mLength = ZSTD_count_2segments(ip+4, dictMatchS+4, iend, dictEnd, istart) + 4; + offset = (U32)(current - dictMatchIndexS - dictIndexDelta); + while (((ip>anchor) & (dictMatchS>dictLowest)) && (ip[-1] == dictMatchS[-1])) { ip--; dictMatchS--; mLength++; } /* catch up */ + } + } else { + ip += ((ip-anchor) >> kSearchStrength) + 1; + continue; + } } else { ip += ((ip-anchor) >> kSearchStrength) + 1; continue; From 0998f10813efc40d42f7f4043183a81153032292 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 9 May 2018 18:37:24 -0400 Subject: [PATCH 07/17] Implement First Repcode Check --- lib/compress/zstd_double_fast.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index 6bf5fb5eb..beffe8661 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -110,9 +110,23 @@ size_t ZSTD_compressBlock_doubleFast_generic( U32 const matchIndexS = hashSmall[h]; const BYTE* matchLong = base + matchIndexL; const BYTE* match = base + matchIndexS; + const ptrdiff_t repIndex = (ptrdiff_t)current + 1 - offset_1; + const BYTE* repBase = (dictMode == ZSTD_dictMatchState + && repIndex < (ptrdiff_t)localLowestIndex) ? + dictBase - dictIndexDelta : base; + const BYTE* repMatch = repBase + repIndex; hashLong[h2] = hashSmall[h] = current; /* update hash tables */ - if (dictMode == ZSTD_noDict && ((offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1)))) { + if (dictMode == ZSTD_dictMatchState + && (((U32)((localLowestIndex-1) - repIndex) >= 3 /* intentional underflow */) + & (repIndex > dictLowestLocalIndex)) + && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { + 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 ( dictMode == ZSTD_noDict + && (offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1))) { assert(offset_1 <= current); /* supposed guaranteed by construction */ /* favor repcode */ mLength = ZSTD_count(ip+1+4, ip+1+4-offset_1, iend) + 4; From 2313cca1b768575ce961a17f3289db4aa7cf1f74 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 9 May 2018 18:46:33 -0400 Subject: [PATCH 08/17] Implement Second Repcode Check --- lib/compress/zstd_double_fast.c | 54 ++++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index beffe8661..1645acfc2 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -206,20 +206,46 @@ size_t ZSTD_compressBlock_doubleFast_generic( hashSmall[ZSTD_hashPtr(ip-2, hBitsS, mls)] = (U32)(ip-2-base); /* check immediate repcode */ - while ( dictMode == ZSTD_noDict - && (ip <= ilimit) - && ( (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 */ - hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = (U32)(ip-base); - hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = (U32)(ip-base); - ZSTD_storeSeq(seqStore, 0, anchor, 0, rLength-MINMATCH); - ip += rLength; - anchor = ip; - continue; /* faster when present ... (?) */ - } } } + 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 = dictMode == 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); + hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = current2; + hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = current2; + ip += repLength2; + anchor = ip; + continue; + } + break; + } + } + + if (dictMode == ZSTD_noDict) { + 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 */ + hashSmall[ZSTD_hashPtr(ip, hBitsS, mls)] = (U32)(ip-base); + hashLong[ZSTD_hashPtr(ip, hBitsL, 8)] = (U32)(ip-base); + ZSTD_storeSeq(seqStore, 0, anchor, 0, rLength-MINMATCH); + ip += rLength; + anchor = ip; + continue; /* faster when present ... (?) */ + } } } } /* save reps for next block */ rep[0] = offset_1 ? offset_1 : offsetSaved; From b97ad3f457d8162e2b7cd71bc2d2db29ab578684 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Tue, 15 May 2018 18:40:07 -0400 Subject: [PATCH 09/17] Port Changes Made to ZSTD_fast to ZSTD_dfast --- lib/compress/zstd_double_fast.c | 61 +++++++++++++++------------------ 1 file changed, 27 insertions(+), 34 deletions(-) diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index 1645acfc2..d415791be 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -62,8 +62,8 @@ size_t ZSTD_compressBlock_doubleFast_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]; @@ -74,28 +74,25 @@ size_t ZSTD_compressBlock_doubleFast_generic( dms->hashTable : NULL; const U32* const dictHashSmall = 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; - ptrdiff_t dictLowestLocalIndex = dictMode == ZSTD_dictMatchState ? - lowestDictIndex + dictIndexDelta : - localLowestIndex; assert(dictMode == ZSTD_noDict || dictMode == ZSTD_dictMatchState); /* init */ - ip += (dictMode == ZSTD_noDict && ip == localLowest); + ip += (dictMode == ZSTD_noDict && ip == prefixLowest); { U32 const maxRep = dictMode == ZSTD_dictMatchState ? (U32)(ip - dictLowest) : - (U32)(ip - localLowest); + (U32)(ip - prefixLowest); if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0; if (offset_1 > maxRep) offsetSaved = offset_1, offset_1 = 0; } @@ -110,48 +107,45 @@ size_t ZSTD_compressBlock_doubleFast_generic( U32 const matchIndexS = hashSmall[h]; const BYTE* matchLong = base + matchIndexL; const BYTE* match = base + matchIndexS; - const ptrdiff_t repIndex = (ptrdiff_t)current + 1 - offset_1; - const BYTE* repBase = (dictMode == ZSTD_dictMatchState - && repIndex < (ptrdiff_t)localLowestIndex) ? - dictBase - dictIndexDelta : base; - const BYTE* repMatch = repBase + repIndex; + const U32 repIndex = current + 1 - offset_1; + const BYTE* repMatch = (dictMode == ZSTD_dictMatchState + && repIndex < prefixLowestIndex) ? + dictBase + (repIndex - dictIndexDelta) : + base + repIndex; hashLong[h2] = hashSmall[h] = current; /* update hash tables */ if (dictMode == ZSTD_dictMatchState - && (((U32)((localLowestIndex-1) - repIndex) >= 3 /* intentional underflow */) - & (repIndex > dictLowestLocalIndex)) + && ((U32)((prefixLowestIndex-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 < 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); } else if ( dictMode == ZSTD_noDict - && (offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1))) { - assert(offset_1 <= current); /* supposed guaranteed by construction */ - /* favor repcode */ + && ((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 { U32 offset; - if ( (matchIndexL > localLowestIndex) && (MEM_read64(matchLong) == MEM_read64(ip)) ) { + if ( (matchIndexL > prefixLowestIndex) && (MEM_read64(matchLong) == MEM_read64(ip)) ) { mLength = ZSTD_count(ip+8, matchLong+8, iend) + 8; offset = (U32)(ip-matchLong); - while (((ip>anchor) & (matchLong>localLowest)) && (ip[-1] == matchLong[-1])) { ip--; matchLong--; mLength++; } /* catch up */ - } else if ( (matchIndexS > localLowestIndex) && (MEM_read32(match) == MEM_read32(ip)) ) { + while (((ip>anchor) & (matchLong>prefixLowest)) && (ip[-1] == matchLong[-1])) { ip--; matchLong--; mLength++; } /* catch up */ + } else if ( (matchIndexS > prefixLowestIndex) && (MEM_read32(match) == MEM_read32(ip)) ) { size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8); U32 const matchIndexL3 = hashLong[hl3]; const BYTE* matchL3 = base + matchIndexL3; hashLong[hl3] = current + 1; - if ( (matchIndexL3 > localLowestIndex) && (MEM_read64(matchL3) == MEM_read64(ip+1)) ) { + if ( (matchIndexL3 > prefixLowestIndex) && (MEM_read64(matchL3) == MEM_read64(ip+1)) ) { mLength = ZSTD_count(ip+9, matchL3+8, iend) + 8; ip++; offset = (U32)(ip-matchL3); - while (((ip>anchor) & (matchL3>localLowest)) && (ip[-1] == matchL3[-1])) { ip--; matchL3--; mLength++; } /* catch up */ + while (((ip>anchor) & (matchL3>prefixLowest)) && (ip[-1] == matchL3[-1])) { ip--; matchL3--; mLength++; } /* catch up */ } else { mLength = ZSTD_count(ip+4, match+4, iend) + 4; offset = (U32)(ip-match); - while (((ip>anchor) & (match>localLowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ + while (((ip>anchor) & (match>prefixLowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ } } else if (dictMode == ZSTD_dictMatchState) { U32 const dictMatchIndexL = dictHashLong[h2]; @@ -161,7 +155,7 @@ size_t ZSTD_compressBlock_doubleFast_generic( assert(dictMatchL < dictEnd); assert(dictMatchS < dictEnd); if (dictMatchL > dictLowest && MEM_read64(dictMatchL) == MEM_read64(ip)) { - mLength = ZSTD_count_2segments(ip+8, dictMatchL+8, iend, dictEnd, localLowest) + 8; + mLength = ZSTD_count_2segments(ip+8, dictMatchL+8, iend, dictEnd, prefixLowest) + 8; offset = (U32)(current - dictMatchIndexL - dictIndexDelta); while (((ip>anchor) & (dictMatchL>dictLowest)) && (ip[-1] == dictMatchL[-1])) { ip--; dictMatchL--; mLength++; } /* catch up */ } else if (dictMatchS > dictLowest && MEM_read32(dictMatchS) == MEM_read32(ip)) { @@ -170,7 +164,7 @@ size_t ZSTD_compressBlock_doubleFast_generic( const BYTE* dictMatchL3 = dictBase + dictMatchIndexL3; assert(dictMatchL3 < dictEnd); if (dictMatchL3 > dictLowest && MEM_read64(dictMatchL3) == MEM_read64(ip)) { - mLength = ZSTD_count_2segments(ip+1+8, dictMatchL3+8, iend, dictEnd, localLowest) + 8; + mLength = ZSTD_count_2segments(ip+1+8, dictMatchL3+8, iend, dictEnd, prefixLowest) + 8; ip++; offset = (U32)(current + 1 - dictMatchIndexL3 - dictIndexDelta); while (((ip>anchor) & (dictMatchL3>dictLowest)) && (ip[-1] == dictMatchL3[-1])) { ip--; dictMatchL3--; mLength++; } /* catch up */ @@ -209,15 +203,14 @@ size_t ZSTD_compressBlock_doubleFast_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 < prefixLowestIndex ? dictBase - dictIndexDelta + repIndex2 : base + repIndex2; - if ( (((U32)((localLowestIndex-1) - (U32)repIndex2) >= 3 /* intentional overflow */) - & (repIndex2 > dictLowestLocalIndex)) + if ( ((U32)((prefixLowestIndex-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 < 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 2bfe43267e63718be3f7e499f8dd3c2f5dd5e89f Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 23 May 2018 12:51:09 -0400 Subject: [PATCH 10/17] Disallow Too-Long Repcodes When Using an Attached Dict --- lib/compress/zstd_double_fast.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index d415791be..711bf41ab 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -85,17 +85,23 @@ size_t ZSTD_compressBlock_doubleFast_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); /* init */ - ip += (dictMode == ZSTD_noDict && ip == prefixLowest); - { U32 const maxRep = dictMode == ZSTD_dictMatchState ? - (U32)(ip - 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) */ From ec7efe88f5dc9d86049651ce6d367d051fc00f12 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 23 May 2018 16:45:58 -0400 Subject: [PATCH 11/17] Fix Off-By-One Error --- lib/compress/zstd_double_fast.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index 711bf41ab..b862289bb 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -169,7 +169,7 @@ size_t ZSTD_compressBlock_doubleFast_generic( U32 const dictMatchIndexL3 = dictHashLong[hl3]; const BYTE* dictMatchL3 = dictBase + dictMatchIndexL3; assert(dictMatchL3 < dictEnd); - if (dictMatchL3 > dictLowest && MEM_read64(dictMatchL3) == MEM_read64(ip)) { + if (dictMatchL3 > dictLowest && MEM_read64(dictMatchL3) == MEM_read64(ip+1)) { mLength = ZSTD_count_2segments(ip+1+8, dictMatchL3+8, iend, dictEnd, prefixLowest) + 8; ip++; offset = (U32)(current + 1 - dictMatchIndexL3 - dictIndexDelta); From 43606f9c8357ac959a86c937cc2032fa2c82d63f Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 23 May 2018 17:37:47 -0400 Subject: [PATCH 12/17] ... When I Said "HashTable", I Meant "ChainTable" --- lib/compress/zstd_double_fast.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index b862289bb..3e1a1d3c6 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -73,7 +73,7 @@ size_t ZSTD_compressBlock_doubleFast_generic( const U32* const dictHashLong = dictMode == ZSTD_dictMatchState ? dms->hashTable : NULL; const U32* const dictHashSmall = dictMode == ZSTD_dictMatchState ? - dms->hashTable : NULL; + dms->chainTable : NULL; const U32 dictLowestIndex = dictMode == ZSTD_dictMatchState ? dms->window.dictLimit : 0; const BYTE* const dictBase = dictMode == ZSTD_dictMatchState ? From 1850025156a2169bc0f1b022a9f3d5a6a18c9b05 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 16 May 2018 14:55:20 -0400 Subject: [PATCH 13/17] Refactor ZSTD_dfast to Use `goto`s --- lib/compress/zstd_double_fast.c | 146 ++++++++++++++++++-------------- 1 file changed, 84 insertions(+), 62 deletions(-) diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index 3e1a1d3c6..de1529006 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -106,6 +106,7 @@ size_t ZSTD_compressBlock_doubleFast_generic( /* Main Search Loop */ while (ip < ilimit) { /* < instead of <=, because repcode check at (ip+1) */ size_t mLength; + U32 offset; size_t const h2 = ZSTD_hashPtr(ip, hBitsL, 8); size_t const h = ZSTD_hashPtr(ip, hBitsS, mls); U32 const current = (U32)(ip-base); @@ -120,6 +121,7 @@ size_t ZSTD_compressBlock_doubleFast_generic( base + repIndex; hashLong[h2] = hashSmall[h] = current; /* update hash tables */ + /* check dictMatchState repcode */ if (dictMode == ZSTD_dictMatchState && ((U32)((prefixLowestIndex-1) - repIndex) >= 3 /* intentional underflow */) && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) { @@ -127,73 +129,93 @@ size_t ZSTD_compressBlock_doubleFast_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 ( dictMode == ZSTD_noDict - && ((offset_1 > 0) & (MEM_read32(ip+1-offset_1) == MEM_read32(ip+1)))) { + goto _match_stored; + } + + /* check noDict repcode */ + if ( dictMode == ZSTD_noDict + && ((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 { - U32 offset; - if ( (matchIndexL > prefixLowestIndex) && (MEM_read64(matchLong) == MEM_read64(ip)) ) { - mLength = ZSTD_count(ip+8, matchLong+8, iend) + 8; - offset = (U32)(ip-matchLong); - while (((ip>anchor) & (matchLong>prefixLowest)) && (ip[-1] == matchLong[-1])) { ip--; matchLong--; mLength++; } /* catch up */ - } else if ( (matchIndexS > prefixLowestIndex) && (MEM_read32(match) == MEM_read32(ip)) ) { - size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8); - U32 const matchIndexL3 = hashLong[hl3]; - const BYTE* matchL3 = base + matchIndexL3; - hashLong[hl3] = current + 1; - if ( (matchIndexL3 > prefixLowestIndex) && (MEM_read64(matchL3) == MEM_read64(ip+1)) ) { - mLength = ZSTD_count(ip+9, matchL3+8, iend) + 8; - ip++; - offset = (U32)(ip-matchL3); - while (((ip>anchor) & (matchL3>prefixLowest)) && (ip[-1] == matchL3[-1])) { ip--; matchL3--; mLength++; } /* catch up */ - } else { - mLength = ZSTD_count(ip+4, match+4, iend) + 4; - offset = (U32)(ip-match); - while (((ip>anchor) & (match>prefixLowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ - } - } else if (dictMode == ZSTD_dictMatchState) { - U32 const dictMatchIndexL = dictHashLong[h2]; - U32 const dictMatchIndexS = dictHashSmall[h]; - const BYTE* dictMatchL = dictBase + dictMatchIndexL; - const BYTE* dictMatchS = dictBase + dictMatchIndexS; - assert(dictMatchL < dictEnd); - assert(dictMatchS < dictEnd); - if (dictMatchL > dictLowest && MEM_read64(dictMatchL) == MEM_read64(ip)) { - mLength = ZSTD_count_2segments(ip+8, dictMatchL+8, iend, dictEnd, prefixLowest) + 8; - offset = (U32)(current - dictMatchIndexL - dictIndexDelta); - while (((ip>anchor) & (dictMatchL>dictLowest)) && (ip[-1] == dictMatchL[-1])) { ip--; dictMatchL--; mLength++; } /* catch up */ - } else if (dictMatchS > dictLowest && MEM_read32(dictMatchS) == MEM_read32(ip)) { - size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8); - U32 const dictMatchIndexL3 = dictHashLong[hl3]; - const BYTE* dictMatchL3 = dictBase + dictMatchIndexL3; - assert(dictMatchL3 < dictEnd); - if (dictMatchL3 > dictLowest && MEM_read64(dictMatchL3) == MEM_read64(ip+1)) { - mLength = ZSTD_count_2segments(ip+1+8, dictMatchL3+8, iend, dictEnd, prefixLowest) + 8; - ip++; - offset = (U32)(current + 1 - dictMatchIndexL3 - dictIndexDelta); - while (((ip>anchor) & (dictMatchL3>dictLowest)) && (ip[-1] == dictMatchL3[-1])) { ip--; dictMatchL3--; mLength++; } /* catch up */ - } else { - mLength = ZSTD_count_2segments(ip+4, dictMatchS+4, iend, dictEnd, istart) + 4; - offset = (U32)(current - dictMatchIndexS - dictIndexDelta); - while (((ip>anchor) & (dictMatchS>dictLowest)) && (ip[-1] == dictMatchS[-1])) { ip--; dictMatchS--; mLength++; } /* catch up */ - } - } else { - ip += ((ip-anchor) >> kSearchStrength) + 1; - continue; - } - } else { - ip += ((ip-anchor) >> kSearchStrength) + 1; - continue; - } - - offset_2 = offset_1; - offset_1 = offset; - - ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); + goto _match_stored; } + /* check prefix long match */ + if ( (matchIndexL > prefixLowestIndex) && (MEM_read64(matchLong) == MEM_read64(ip)) ) { + mLength = ZSTD_count(ip+8, matchLong+8, iend) + 8; + offset = (U32)(ip-matchLong); + while (((ip>anchor) & (matchLong>prefixLowest)) && (ip[-1] == matchLong[-1])) { ip--; matchLong--; mLength++; } /* catch up */ + goto _match_found; + } + + /* check prefix short match */ + if ( (matchIndexS > prefixLowestIndex) && (MEM_read32(match) == MEM_read32(ip)) ) { + size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8); + U32 const matchIndexL3 = hashLong[hl3]; + const BYTE* matchL3 = base + matchIndexL3; + hashLong[hl3] = current + 1; + + /* check prefix + 1 long match */ + if ( (matchIndexL3 > prefixLowestIndex) && (MEM_read64(matchL3) == MEM_read64(ip+1)) ) { + mLength = ZSTD_count(ip+9, matchL3+8, iend) + 8; + ip++; + offset = (U32)(ip-matchL3); + while (((ip>anchor) & (matchL3>prefixLowest)) && (ip[-1] == matchL3[-1])) { ip--; matchL3--; mLength++; } /* catch up */ + } else { + mLength = ZSTD_count(ip+4, match+4, iend) + 4; + offset = (U32)(ip-match); + while (((ip>anchor) & (match>prefixLowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ + } + goto _match_found; + } + + /* check dictMatchState matches */ + if (dictMode == ZSTD_dictMatchState) { + U32 const dictMatchIndexL = dictHashLong[h2]; + U32 const dictMatchIndexS = dictHashSmall[h]; + const BYTE* dictMatchL = dictBase + dictMatchIndexL; + const BYTE* dictMatchS = dictBase + dictMatchIndexS; + assert(dictMatchL < dictEnd); + assert(dictMatchS < dictEnd); + + if (dictMatchL > dictLowest && MEM_read64(dictMatchL) == MEM_read64(ip)) { + mLength = ZSTD_count_2segments(ip+8, dictMatchL+8, iend, dictEnd, prefixLowest) + 8; + offset = (U32)(current - dictMatchIndexL - dictIndexDelta); + while (((ip>anchor) & (dictMatchL>dictLowest)) && (ip[-1] == dictMatchL[-1])) { ip--; dictMatchL--; mLength++; } /* catch up */ + goto _match_found; + } + + if (dictMatchS > dictLowest && MEM_read32(dictMatchS) == MEM_read32(ip)) { + size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8); + U32 const dictMatchIndexL3 = dictHashLong[hl3]; + const BYTE* dictMatchL3 = dictBase + dictMatchIndexL3; + assert(dictMatchL3 < dictEnd); + if (dictMatchL3 > dictLowest && MEM_read64(dictMatchL3) == MEM_read64(ip)) { + mLength = ZSTD_count_2segments(ip+1+8, dictMatchL3+8, iend, dictEnd, prefixLowest) + 8; + ip++; + offset = (U32)(current + 1 - dictMatchIndexL3 - dictIndexDelta); + while (((ip>anchor) & (dictMatchL3>dictLowest)) && (ip[-1] == dictMatchL3[-1])) { ip--; dictMatchL3--; mLength++; } /* catch up */ + } else { + mLength = ZSTD_count_2segments(ip+4, dictMatchS+4, iend, dictEnd, istart) + 4; + offset = (U32)(current - dictMatchIndexS - dictIndexDelta); + while (((ip>anchor) & (dictMatchS>dictLowest)) && (ip[-1] == dictMatchS[-1])) { ip--; dictMatchS--; mLength++; } /* catch up */ + } + + goto _match_found; + } + } + + ip += ((ip-anchor) >> kSearchStrength) + 1; + continue; + +_match_found: + offset_2 = offset_1; + offset_1 = offset; + + ZSTD_storeSeq(seqStore, ip-anchor, anchor, offset + ZSTD_REP_MOVE, mLength-MINMATCH); + +_match_stored: /* match found */ ip += mLength; anchor = ip; From 88b733b380c99f0952d7ff2ea8ac6248b24a6a5a Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 16 May 2018 15:20:16 -0400 Subject: [PATCH 14/17] Interleave Prefix and Dict Searches --- lib/compress/zstd_double_fast.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index de1529006..cf58da161 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -149,6 +149,20 @@ size_t ZSTD_compressBlock_doubleFast_generic( goto _match_found; } + /* check dictMatchState long match */ + if (dictMode == ZSTD_dictMatchState) { + U32 const dictMatchIndexL = dictHashLong[h2]; + const BYTE* dictMatchL = dictBase + dictMatchIndexL; + assert(dictMatchL < dictEnd); + + if (dictMatchL > dictLowest && MEM_read64(dictMatchL) == MEM_read64(ip)) { + mLength = ZSTD_count_2segments(ip+8, dictMatchL+8, iend, dictEnd, prefixLowest) + 8; + offset = (U32)(current - dictMatchIndexL - dictIndexDelta); + while (((ip>anchor) & (dictMatchL>dictLowest)) && (ip[-1] == dictMatchL[-1])) { ip--; dictMatchL--; mLength++; } /* catch up */ + goto _match_found; + } + } + /* check prefix short match */ if ( (matchIndexS > prefixLowestIndex) && (MEM_read32(match) == MEM_read32(ip)) ) { size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8); @@ -170,22 +184,12 @@ size_t ZSTD_compressBlock_doubleFast_generic( goto _match_found; } - /* check dictMatchState matches */ + /* check dictMatchState short match */ if (dictMode == ZSTD_dictMatchState) { - U32 const dictMatchIndexL = dictHashLong[h2]; U32 const dictMatchIndexS = dictHashSmall[h]; - const BYTE* dictMatchL = dictBase + dictMatchIndexL; const BYTE* dictMatchS = dictBase + dictMatchIndexS; - assert(dictMatchL < dictEnd); assert(dictMatchS < dictEnd); - if (dictMatchL > dictLowest && MEM_read64(dictMatchL) == MEM_read64(ip)) { - mLength = ZSTD_count_2segments(ip+8, dictMatchL+8, iend, dictEnd, prefixLowest) + 8; - offset = (U32)(current - dictMatchIndexL - dictIndexDelta); - while (((ip>anchor) & (dictMatchL>dictLowest)) && (ip[-1] == dictMatchL[-1])) { ip--; dictMatchL--; mLength++; } /* catch up */ - goto _match_found; - } - if (dictMatchS > dictLowest && MEM_read32(dictMatchS) == MEM_read32(ip)) { size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8); U32 const dictMatchIndexL3 = dictHashLong[hl3]; From 5b292b5685e6bd1b2278e37663d581c42e426f21 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Wed, 23 May 2018 16:43:33 -0400 Subject: [PATCH 15/17] Check Long + 1 Matches in Both Prefix and Dict in Bothe Short Match Paths --- lib/compress/zstd_double_fast.c | 88 +++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 37 deletions(-) diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index cf58da161..401724715 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -111,7 +111,7 @@ size_t ZSTD_compressBlock_doubleFast_generic( size_t const h = ZSTD_hashPtr(ip, hBitsS, mls); U32 const current = (U32)(ip-base); U32 const matchIndexL = hashLong[h2]; - U32 const matchIndexS = hashSmall[h]; + U32 matchIndexS = hashSmall[h]; const BYTE* matchLong = base + matchIndexL; const BYTE* match = base + matchIndexS; const U32 repIndex = current + 1 - offset_1; @@ -165,54 +165,68 @@ size_t ZSTD_compressBlock_doubleFast_generic( /* check prefix short match */ if ( (matchIndexS > prefixLowestIndex) && (MEM_read32(match) == MEM_read32(ip)) ) { - size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8); - U32 const matchIndexL3 = hashLong[hl3]; - const BYTE* matchL3 = base + matchIndexL3; - hashLong[hl3] = current + 1; - - /* check prefix + 1 long match */ - if ( (matchIndexL3 > prefixLowestIndex) && (MEM_read64(matchL3) == MEM_read64(ip+1)) ) { - mLength = ZSTD_count(ip+9, matchL3+8, iend) + 8; - ip++; - offset = (U32)(ip-matchL3); - while (((ip>anchor) & (matchL3>prefixLowest)) && (ip[-1] == matchL3[-1])) { ip--; matchL3--; mLength++; } /* catch up */ - } else { - mLength = ZSTD_count(ip+4, match+4, iend) + 4; - offset = (U32)(ip-match); - while (((ip>anchor) & (match>prefixLowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ - } - goto _match_found; + goto _search_next_long; } /* check dictMatchState short match */ if (dictMode == ZSTD_dictMatchState) { U32 const dictMatchIndexS = dictHashSmall[h]; - const BYTE* dictMatchS = dictBase + dictMatchIndexS; - assert(dictMatchS < dictEnd); + match = dictBase + dictMatchIndexS; + matchIndexS = dictMatchIndexS + dictIndexDelta; - if (dictMatchS > dictLowest && MEM_read32(dictMatchS) == MEM_read32(ip)) { - size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8); - U32 const dictMatchIndexL3 = dictHashLong[hl3]; - const BYTE* dictMatchL3 = dictBase + dictMatchIndexL3; - assert(dictMatchL3 < dictEnd); - if (dictMatchL3 > dictLowest && MEM_read64(dictMatchL3) == MEM_read64(ip)) { - mLength = ZSTD_count_2segments(ip+1+8, dictMatchL3+8, iend, dictEnd, prefixLowest) + 8; - ip++; - offset = (U32)(current + 1 - dictMatchIndexL3 - dictIndexDelta); - while (((ip>anchor) & (dictMatchL3>dictLowest)) && (ip[-1] == dictMatchL3[-1])) { ip--; dictMatchL3--; mLength++; } /* catch up */ - } else { - mLength = ZSTD_count_2segments(ip+4, dictMatchS+4, iend, dictEnd, istart) + 4; - offset = (U32)(current - dictMatchIndexS - dictIndexDelta); - while (((ip>anchor) & (dictMatchS>dictLowest)) && (ip[-1] == dictMatchS[-1])) { ip--; dictMatchS--; mLength++; } /* catch up */ - } - - goto _match_found; + if (match > dictLowest && MEM_read32(match) == MEM_read32(ip)) { + goto _search_next_long; } } ip += ((ip-anchor) >> kSearchStrength) + 1; continue; +_search_next_long: + + { + size_t const hl3 = ZSTD_hashPtr(ip+1, hBitsL, 8); + U32 const matchIndexL3 = hashLong[hl3]; + const BYTE* matchL3 = base + matchIndexL3; + hashLong[hl3] = current + 1; + + /* check prefix long +1 match */ + if ( (matchIndexL3 > prefixLowestIndex) && (MEM_read64(matchL3) == MEM_read64(ip+1)) ) { + mLength = ZSTD_count(ip+9, matchL3+8, iend) + 8; + ip++; + offset = (U32)(ip-matchL3); + while (((ip>anchor) & (matchL3>prefixLowest)) && (ip[-1] == matchL3[-1])) { ip--; matchL3--; mLength++; } /* catch up */ + goto _match_found; + } + + /* check dict long +1 match */ + if (dictMode == ZSTD_dictMatchState) { + U32 const dictMatchIndexL3 = dictHashLong[hl3]; + const BYTE* dictMatchL3 = dictBase + dictMatchIndexL3; + assert(dictMatchL3 < dictEnd); + if (dictMatchL3 > dictLowest && MEM_read64(dictMatchL3) == MEM_read64(ip+1)) { + mLength = ZSTD_count_2segments(ip+1+8, dictMatchL3+8, iend, dictEnd, prefixLowest) + 8; + ip++; + offset = (U32)(current + 1 - dictMatchIndexL3 - dictIndexDelta); + while (((ip>anchor) & (dictMatchL3>dictLowest)) && (ip[-1] == dictMatchL3[-1])) { ip--; dictMatchL3--; mLength++; } /* catch up */ + goto _match_found; + } + } + } + + /* if no long +1 match, explore the short match we found */ + if (dictMode == ZSTD_dictMatchState && matchIndexS < prefixLowestIndex) { + mLength = ZSTD_count_2segments(ip+4, match+4, iend, dictEnd, istart) + 4; + offset = (U32)(current - matchIndexS); + while (((ip>anchor) & (match>dictLowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ + } else { + mLength = ZSTD_count(ip+4, match+4, iend) + 4; + offset = (U32)(ip - match); + while (((ip>anchor) & (match>prefixLowest)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */ + } + + /* fall-through */ + _match_found: offset_2 = offset_1; offset_1 = offset; From f86796639ede24a5ae83ad24a0906e708e33c1af Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Thu, 31 May 2018 16:55:50 -0400 Subject: [PATCH 16/17] Remove Incorrect and Extraneous Repcode Bounds Check --- lib/compress/zstd_double_fast.c | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/compress/zstd_double_fast.c b/lib/compress/zstd_double_fast.c index 401724715..fbd354043 100644 --- a/lib/compress/zstd_double_fast.c +++ b/lib/compress/zstd_double_fast.c @@ -272,7 +272,6 @@ _match_stored: 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 48deab92de4c507720903b959c2223b985a2a831 Mon Sep 17 00:00:00 2001 From: "W. Felix Handte" Date: Fri, 25 May 2018 15:19:37 -0400 Subject: [PATCH 17/17] Allow Different Dict Attachment Cut-Offs for Different Strategies --- lib/compress/zstd_compress.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 46a3fe541..68beea0c9 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1207,7 +1207,18 @@ 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. */ - const int attachDict = ( pledgedSrcSize <= 8 KB + const U64 attachDictSizeCutoffs[(unsigned)ZSTD_btultra+1] = { + 8 KB, /* unused */ + 8 KB, /* ZSTD_fast */ + 16 KB, /* ZSTD_dfast */ + 16 KB, /* ZSTD_greedy */ + 16 KB, /* ZSTD_lazy */ + 16 KB, /* ZSTD_lazy2 */ + 16 KB, /* ZSTD_btlazy2 */ + 16 KB, /* ZSTD_btopt */ + 16 KB /* ZSTD_btultra */ + }; + const int attachDict = ( pledgedSrcSize <= attachDictSizeCutoffs[cdict->cParams.strategy] || pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN ) && !params.forceWindow /* dictMatchState isn't correctly * handled in _enforceMaxDist */