xref: /freebsd/sys/contrib/zstd/lib/compress/zstd_ldm.c (revision 0c16b53773565120a8f80a31a0af2ef56ccd368e)
1*0c16b537SWarner Losh /*
2*0c16b537SWarner Losh  * Copyright (c) 2016-present, Yann Collet, Facebook, Inc.
3*0c16b537SWarner Losh  * All rights reserved.
4*0c16b537SWarner Losh  *
5*0c16b537SWarner Losh  * This source code is licensed under both the BSD-style license (found in the
6*0c16b537SWarner Losh  * LICENSE file in the root directory of this source tree) and the GPLv2 (found
7*0c16b537SWarner Losh  * in the COPYING file in the root directory of this source tree).
8*0c16b537SWarner Losh  */
9*0c16b537SWarner Losh 
10*0c16b537SWarner Losh #include "zstd_ldm.h"
11*0c16b537SWarner Losh 
12*0c16b537SWarner Losh #include "zstd_fast.h"          /* ZSTD_fillHashTable() */
13*0c16b537SWarner Losh #include "zstd_double_fast.h"   /* ZSTD_fillDoubleHashTable() */
14*0c16b537SWarner Losh 
15*0c16b537SWarner Losh #define LDM_BUCKET_SIZE_LOG 3
16*0c16b537SWarner Losh #define LDM_MIN_MATCH_LENGTH 64
17*0c16b537SWarner Losh #define LDM_HASH_RLOG 7
18*0c16b537SWarner Losh #define LDM_HASH_CHAR_OFFSET 10
19*0c16b537SWarner Losh 
20*0c16b537SWarner Losh size_t ZSTD_ldm_initializeParameters(ldmParams_t* params, U32 enableLdm)
21*0c16b537SWarner Losh {
22*0c16b537SWarner Losh     ZSTD_STATIC_ASSERT(LDM_BUCKET_SIZE_LOG <= ZSTD_LDM_BUCKETSIZELOG_MAX);
23*0c16b537SWarner Losh     params->enableLdm = enableLdm>0;
24*0c16b537SWarner Losh     params->hashLog = 0;
25*0c16b537SWarner Losh     params->bucketSizeLog = LDM_BUCKET_SIZE_LOG;
26*0c16b537SWarner Losh     params->minMatchLength = LDM_MIN_MATCH_LENGTH;
27*0c16b537SWarner Losh     params->hashEveryLog = ZSTD_LDM_HASHEVERYLOG_NOTSET;
28*0c16b537SWarner Losh     return 0;
29*0c16b537SWarner Losh }
30*0c16b537SWarner Losh 
31*0c16b537SWarner Losh void ZSTD_ldm_adjustParameters(ldmParams_t* params, U32 windowLog)
32*0c16b537SWarner Losh {
33*0c16b537SWarner Losh     if (params->hashLog == 0) {
34*0c16b537SWarner Losh         params->hashLog = MAX(ZSTD_HASHLOG_MIN, windowLog - LDM_HASH_RLOG);
35*0c16b537SWarner Losh         assert(params->hashLog <= ZSTD_HASHLOG_MAX);
36*0c16b537SWarner Losh     }
37*0c16b537SWarner Losh     if (params->hashEveryLog == ZSTD_LDM_HASHEVERYLOG_NOTSET) {
38*0c16b537SWarner Losh         params->hashEveryLog =
39*0c16b537SWarner Losh                 windowLog < params->hashLog ? 0 : windowLog - params->hashLog;
40*0c16b537SWarner Losh     }
41*0c16b537SWarner Losh     params->bucketSizeLog = MIN(params->bucketSizeLog, params->hashLog);
42*0c16b537SWarner Losh }
43*0c16b537SWarner Losh 
44*0c16b537SWarner Losh size_t ZSTD_ldm_getTableSize(U32 hashLog, U32 bucketSizeLog) {
45*0c16b537SWarner Losh     size_t const ldmHSize = ((size_t)1) << hashLog;
46*0c16b537SWarner Losh     size_t const ldmBucketSizeLog = MIN(bucketSizeLog, hashLog);
47*0c16b537SWarner Losh     size_t const ldmBucketSize =
48*0c16b537SWarner Losh         ((size_t)1) << (hashLog - ldmBucketSizeLog);
49*0c16b537SWarner Losh     return ldmBucketSize + (ldmHSize * (sizeof(ldmEntry_t)));
50*0c16b537SWarner Losh }
51*0c16b537SWarner Losh 
52*0c16b537SWarner Losh /** ZSTD_ldm_getSmallHash() :
53*0c16b537SWarner Losh  *  numBits should be <= 32
54*0c16b537SWarner Losh  *  If numBits==0, returns 0.
55*0c16b537SWarner Losh  *  @return : the most significant numBits of value. */
56*0c16b537SWarner Losh static U32 ZSTD_ldm_getSmallHash(U64 value, U32 numBits)
57*0c16b537SWarner Losh {
58*0c16b537SWarner Losh     assert(numBits <= 32);
59*0c16b537SWarner Losh     return numBits == 0 ? 0 : (U32)(value >> (64 - numBits));
60*0c16b537SWarner Losh }
61*0c16b537SWarner Losh 
62*0c16b537SWarner Losh /** ZSTD_ldm_getChecksum() :
63*0c16b537SWarner Losh  *  numBitsToDiscard should be <= 32
64*0c16b537SWarner Losh  *  @return : the next most significant 32 bits after numBitsToDiscard */
65*0c16b537SWarner Losh static U32 ZSTD_ldm_getChecksum(U64 hash, U32 numBitsToDiscard)
66*0c16b537SWarner Losh {
67*0c16b537SWarner Losh     assert(numBitsToDiscard <= 32);
68*0c16b537SWarner Losh     return (hash >> (64 - 32 - numBitsToDiscard)) & 0xFFFFFFFF;
69*0c16b537SWarner Losh }
70*0c16b537SWarner Losh 
71*0c16b537SWarner Losh /** ZSTD_ldm_getTag() ;
72*0c16b537SWarner Losh  *  Given the hash, returns the most significant numTagBits bits
73*0c16b537SWarner Losh  *  after (32 + hbits) bits.
74*0c16b537SWarner Losh  *
75*0c16b537SWarner Losh  *  If there are not enough bits remaining, return the last
76*0c16b537SWarner Losh  *  numTagBits bits. */
77*0c16b537SWarner Losh static U32 ZSTD_ldm_getTag(U64 hash, U32 hbits, U32 numTagBits)
78*0c16b537SWarner Losh {
79*0c16b537SWarner Losh     assert(numTagBits < 32 && hbits <= 32);
80*0c16b537SWarner Losh     if (32 - hbits < numTagBits) {
81*0c16b537SWarner Losh         return hash & (((U32)1 << numTagBits) - 1);
82*0c16b537SWarner Losh     } else {
83*0c16b537SWarner Losh         return (hash >> (32 - hbits - numTagBits)) & (((U32)1 << numTagBits) - 1);
84*0c16b537SWarner Losh     }
85*0c16b537SWarner Losh }
86*0c16b537SWarner Losh 
87*0c16b537SWarner Losh /** ZSTD_ldm_getBucket() :
88*0c16b537SWarner Losh  *  Returns a pointer to the start of the bucket associated with hash. */
89*0c16b537SWarner Losh static ldmEntry_t* ZSTD_ldm_getBucket(
90*0c16b537SWarner Losh         ldmState_t* ldmState, size_t hash, ldmParams_t const ldmParams)
91*0c16b537SWarner Losh {
92*0c16b537SWarner Losh     return ldmState->hashTable + (hash << ldmParams.bucketSizeLog);
93*0c16b537SWarner Losh }
94*0c16b537SWarner Losh 
95*0c16b537SWarner Losh /** ZSTD_ldm_insertEntry() :
96*0c16b537SWarner Losh  *  Insert the entry with corresponding hash into the hash table */
97*0c16b537SWarner Losh static void ZSTD_ldm_insertEntry(ldmState_t* ldmState,
98*0c16b537SWarner Losh                                  size_t const hash, const ldmEntry_t entry,
99*0c16b537SWarner Losh                                  ldmParams_t const ldmParams)
100*0c16b537SWarner Losh {
101*0c16b537SWarner Losh     BYTE* const bucketOffsets = ldmState->bucketOffsets;
102*0c16b537SWarner Losh     *(ZSTD_ldm_getBucket(ldmState, hash, ldmParams) + bucketOffsets[hash]) = entry;
103*0c16b537SWarner Losh     bucketOffsets[hash]++;
104*0c16b537SWarner Losh     bucketOffsets[hash] &= ((U32)1 << ldmParams.bucketSizeLog) - 1;
105*0c16b537SWarner Losh }
106*0c16b537SWarner Losh 
107*0c16b537SWarner Losh /** ZSTD_ldm_makeEntryAndInsertByTag() :
108*0c16b537SWarner Losh  *
109*0c16b537SWarner Losh  *  Gets the small hash, checksum, and tag from the rollingHash.
110*0c16b537SWarner Losh  *
111*0c16b537SWarner Losh  *  If the tag matches (1 << ldmParams.hashEveryLog)-1, then
112*0c16b537SWarner Losh  *  creates an ldmEntry from the offset, and inserts it into the hash table.
113*0c16b537SWarner Losh  *
114*0c16b537SWarner Losh  *  hBits is the length of the small hash, which is the most significant hBits
115*0c16b537SWarner Losh  *  of rollingHash. The checksum is the next 32 most significant bits, followed
116*0c16b537SWarner Losh  *  by ldmParams.hashEveryLog bits that make up the tag. */
117*0c16b537SWarner Losh static void ZSTD_ldm_makeEntryAndInsertByTag(ldmState_t* ldmState,
118*0c16b537SWarner Losh                                              U64 const rollingHash,
119*0c16b537SWarner Losh                                              U32 const hBits,
120*0c16b537SWarner Losh                                              U32 const offset,
121*0c16b537SWarner Losh                                              ldmParams_t const ldmParams)
122*0c16b537SWarner Losh {
123*0c16b537SWarner Losh     U32 const tag = ZSTD_ldm_getTag(rollingHash, hBits, ldmParams.hashEveryLog);
124*0c16b537SWarner Losh     U32 const tagMask = ((U32)1 << ldmParams.hashEveryLog) - 1;
125*0c16b537SWarner Losh     if (tag == tagMask) {
126*0c16b537SWarner Losh         U32 const hash = ZSTD_ldm_getSmallHash(rollingHash, hBits);
127*0c16b537SWarner Losh         U32 const checksum = ZSTD_ldm_getChecksum(rollingHash, hBits);
128*0c16b537SWarner Losh         ldmEntry_t entry;
129*0c16b537SWarner Losh         entry.offset = offset;
130*0c16b537SWarner Losh         entry.checksum = checksum;
131*0c16b537SWarner Losh         ZSTD_ldm_insertEntry(ldmState, hash, entry, ldmParams);
132*0c16b537SWarner Losh     }
133*0c16b537SWarner Losh }
134*0c16b537SWarner Losh 
135*0c16b537SWarner Losh /** ZSTD_ldm_getRollingHash() :
136*0c16b537SWarner Losh  *  Get a 64-bit hash using the first len bytes from buf.
137*0c16b537SWarner Losh  *
138*0c16b537SWarner Losh  *  Giving bytes s = s_1, s_2, ... s_k, the hash is defined to be
139*0c16b537SWarner Losh  *  H(s) = s_1*(a^(k-1)) + s_2*(a^(k-2)) + ... + s_k*(a^0)
140*0c16b537SWarner Losh  *
141*0c16b537SWarner Losh  *  where the constant a is defined to be prime8bytes.
142*0c16b537SWarner Losh  *
143*0c16b537SWarner Losh  *  The implementation adds an offset to each byte, so
144*0c16b537SWarner Losh  *  H(s) = (s_1 + HASH_CHAR_OFFSET)*(a^(k-1)) + ... */
145*0c16b537SWarner Losh static U64 ZSTD_ldm_getRollingHash(const BYTE* buf, U32 len)
146*0c16b537SWarner Losh {
147*0c16b537SWarner Losh     U64 ret = 0;
148*0c16b537SWarner Losh     U32 i;
149*0c16b537SWarner Losh     for (i = 0; i < len; i++) {
150*0c16b537SWarner Losh         ret *= prime8bytes;
151*0c16b537SWarner Losh         ret += buf[i] + LDM_HASH_CHAR_OFFSET;
152*0c16b537SWarner Losh     }
153*0c16b537SWarner Losh     return ret;
154*0c16b537SWarner Losh }
155*0c16b537SWarner Losh 
156*0c16b537SWarner Losh /** ZSTD_ldm_ipow() :
157*0c16b537SWarner Losh  *  Return base^exp. */
158*0c16b537SWarner Losh static U64 ZSTD_ldm_ipow(U64 base, U64 exp)
159*0c16b537SWarner Losh {
160*0c16b537SWarner Losh     U64 ret = 1;
161*0c16b537SWarner Losh     while (exp) {
162*0c16b537SWarner Losh         if (exp & 1) { ret *= base; }
163*0c16b537SWarner Losh         exp >>= 1;
164*0c16b537SWarner Losh         base *= base;
165*0c16b537SWarner Losh     }
166*0c16b537SWarner Losh     return ret;
167*0c16b537SWarner Losh }
168*0c16b537SWarner Losh 
169*0c16b537SWarner Losh U64 ZSTD_ldm_getHashPower(U32 minMatchLength) {
170*0c16b537SWarner Losh     assert(minMatchLength >= ZSTD_LDM_MINMATCH_MIN);
171*0c16b537SWarner Losh     return ZSTD_ldm_ipow(prime8bytes, minMatchLength - 1);
172*0c16b537SWarner Losh }
173*0c16b537SWarner Losh 
174*0c16b537SWarner Losh /** ZSTD_ldm_updateHash() :
175*0c16b537SWarner Losh  *  Updates hash by removing toRemove and adding toAdd. */
176*0c16b537SWarner Losh static U64 ZSTD_ldm_updateHash(U64 hash, BYTE toRemove, BYTE toAdd, U64 hashPower)
177*0c16b537SWarner Losh {
178*0c16b537SWarner Losh     hash -= ((toRemove + LDM_HASH_CHAR_OFFSET) * hashPower);
179*0c16b537SWarner Losh     hash *= prime8bytes;
180*0c16b537SWarner Losh     hash += toAdd + LDM_HASH_CHAR_OFFSET;
181*0c16b537SWarner Losh     return hash;
182*0c16b537SWarner Losh }
183*0c16b537SWarner Losh 
184*0c16b537SWarner Losh /** ZSTD_ldm_countBackwardsMatch() :
185*0c16b537SWarner Losh  *  Returns the number of bytes that match backwards before pIn and pMatch.
186*0c16b537SWarner Losh  *
187*0c16b537SWarner Losh  *  We count only bytes where pMatch >= pBase and pIn >= pAnchor. */
188*0c16b537SWarner Losh static size_t ZSTD_ldm_countBackwardsMatch(
189*0c16b537SWarner Losh             const BYTE* pIn, const BYTE* pAnchor,
190*0c16b537SWarner Losh             const BYTE* pMatch, const BYTE* pBase)
191*0c16b537SWarner Losh {
192*0c16b537SWarner Losh     size_t matchLength = 0;
193*0c16b537SWarner Losh     while (pIn > pAnchor && pMatch > pBase && pIn[-1] == pMatch[-1]) {
194*0c16b537SWarner Losh         pIn--;
195*0c16b537SWarner Losh         pMatch--;
196*0c16b537SWarner Losh         matchLength++;
197*0c16b537SWarner Losh     }
198*0c16b537SWarner Losh     return matchLength;
199*0c16b537SWarner Losh }
200*0c16b537SWarner Losh 
201*0c16b537SWarner Losh /** ZSTD_ldm_fillFastTables() :
202*0c16b537SWarner Losh  *
203*0c16b537SWarner Losh  *  Fills the relevant tables for the ZSTD_fast and ZSTD_dfast strategies.
204*0c16b537SWarner Losh  *  This is similar to ZSTD_loadDictionaryContent.
205*0c16b537SWarner Losh  *
206*0c16b537SWarner Losh  *  The tables for the other strategies are filled within their
207*0c16b537SWarner Losh  *  block compressors. */
208*0c16b537SWarner Losh static size_t ZSTD_ldm_fillFastTables(ZSTD_CCtx* zc, const void* end)
209*0c16b537SWarner Losh {
210*0c16b537SWarner Losh     const BYTE* const iend = (const BYTE*)end;
211*0c16b537SWarner Losh     const U32 mls = zc->appliedParams.cParams.searchLength;
212*0c16b537SWarner Losh 
213*0c16b537SWarner Losh     switch(zc->appliedParams.cParams.strategy)
214*0c16b537SWarner Losh     {
215*0c16b537SWarner Losh     case ZSTD_fast:
216*0c16b537SWarner Losh         ZSTD_fillHashTable(zc, iend, mls);
217*0c16b537SWarner Losh         zc->nextToUpdate = (U32)(iend - zc->base);
218*0c16b537SWarner Losh         break;
219*0c16b537SWarner Losh 
220*0c16b537SWarner Losh     case ZSTD_dfast:
221*0c16b537SWarner Losh         ZSTD_fillDoubleHashTable(zc, iend, mls);
222*0c16b537SWarner Losh         zc->nextToUpdate = (U32)(iend - zc->base);
223*0c16b537SWarner Losh         break;
224*0c16b537SWarner Losh 
225*0c16b537SWarner Losh     case ZSTD_greedy:
226*0c16b537SWarner Losh     case ZSTD_lazy:
227*0c16b537SWarner Losh     case ZSTD_lazy2:
228*0c16b537SWarner Losh     case ZSTD_btlazy2:
229*0c16b537SWarner Losh     case ZSTD_btopt:
230*0c16b537SWarner Losh     case ZSTD_btultra:
231*0c16b537SWarner Losh         break;
232*0c16b537SWarner Losh     default:
233*0c16b537SWarner Losh         assert(0);  /* not possible : not a valid strategy id */
234*0c16b537SWarner Losh     }
235*0c16b537SWarner Losh 
236*0c16b537SWarner Losh     return 0;
237*0c16b537SWarner Losh }
238*0c16b537SWarner Losh 
239*0c16b537SWarner Losh /** ZSTD_ldm_fillLdmHashTable() :
240*0c16b537SWarner Losh  *
241*0c16b537SWarner Losh  *  Fills hashTable from (lastHashed + 1) to iend (non-inclusive).
242*0c16b537SWarner Losh  *  lastHash is the rolling hash that corresponds to lastHashed.
243*0c16b537SWarner Losh  *
244*0c16b537SWarner Losh  *  Returns the rolling hash corresponding to position iend-1. */
245*0c16b537SWarner Losh static U64 ZSTD_ldm_fillLdmHashTable(ldmState_t* state,
246*0c16b537SWarner Losh                                      U64 lastHash, const BYTE* lastHashed,
247*0c16b537SWarner Losh                                      const BYTE* iend, const BYTE* base,
248*0c16b537SWarner Losh                                      U32 hBits, ldmParams_t const ldmParams)
249*0c16b537SWarner Losh {
250*0c16b537SWarner Losh     U64 rollingHash = lastHash;
251*0c16b537SWarner Losh     const BYTE* cur = lastHashed + 1;
252*0c16b537SWarner Losh 
253*0c16b537SWarner Losh     while (cur < iend) {
254*0c16b537SWarner Losh         rollingHash = ZSTD_ldm_updateHash(rollingHash, cur[-1],
255*0c16b537SWarner Losh                                           cur[ldmParams.minMatchLength-1],
256*0c16b537SWarner Losh                                           state->hashPower);
257*0c16b537SWarner Losh         ZSTD_ldm_makeEntryAndInsertByTag(state,
258*0c16b537SWarner Losh                                          rollingHash, hBits,
259*0c16b537SWarner Losh                                          (U32)(cur - base), ldmParams);
260*0c16b537SWarner Losh         ++cur;
261*0c16b537SWarner Losh     }
262*0c16b537SWarner Losh     return rollingHash;
263*0c16b537SWarner Losh }
264*0c16b537SWarner Losh 
265*0c16b537SWarner Losh 
266*0c16b537SWarner Losh /** ZSTD_ldm_limitTableUpdate() :
267*0c16b537SWarner Losh  *
268*0c16b537SWarner Losh  *  Sets cctx->nextToUpdate to a position corresponding closer to anchor
269*0c16b537SWarner Losh  *  if it is far way
270*0c16b537SWarner Losh  *  (after a long match, only update tables a limited amount). */
271*0c16b537SWarner Losh static void ZSTD_ldm_limitTableUpdate(ZSTD_CCtx* cctx, const BYTE* anchor)
272*0c16b537SWarner Losh {
273*0c16b537SWarner Losh     U32 const current = (U32)(anchor - cctx->base);
274*0c16b537SWarner Losh     if (current > cctx->nextToUpdate + 1024) {
275*0c16b537SWarner Losh         cctx->nextToUpdate =
276*0c16b537SWarner Losh             current - MIN(512, current - cctx->nextToUpdate - 1024);
277*0c16b537SWarner Losh     }
278*0c16b537SWarner Losh }
279*0c16b537SWarner Losh 
280*0c16b537SWarner Losh typedef size_t (*ZSTD_blockCompressor) (ZSTD_CCtx* ctx, const void* src, size_t srcSize);
281*0c16b537SWarner Losh /* defined in zstd_compress.c */
282*0c16b537SWarner Losh ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, int extDict);
283*0c16b537SWarner Losh 
284*0c16b537SWarner Losh FORCE_INLINE_TEMPLATE
285*0c16b537SWarner Losh size_t ZSTD_compressBlock_ldm_generic(ZSTD_CCtx* cctx,
286*0c16b537SWarner Losh                                       const void* src, size_t srcSize)
287*0c16b537SWarner Losh {
288*0c16b537SWarner Losh     ldmState_t* const ldmState = &(cctx->ldmState);
289*0c16b537SWarner Losh     const ldmParams_t ldmParams = cctx->appliedParams.ldmParams;
290*0c16b537SWarner Losh     const U64 hashPower = ldmState->hashPower;
291*0c16b537SWarner Losh     const U32 hBits = ldmParams.hashLog - ldmParams.bucketSizeLog;
292*0c16b537SWarner Losh     const U32 ldmBucketSize = ((U32)1 << ldmParams.bucketSizeLog);
293*0c16b537SWarner Losh     const U32 ldmTagMask = ((U32)1 << ldmParams.hashEveryLog) - 1;
294*0c16b537SWarner Losh     seqStore_t* const seqStorePtr = &(cctx->seqStore);
295*0c16b537SWarner Losh     const BYTE* const base = cctx->base;
296*0c16b537SWarner Losh     const BYTE* const istart = (const BYTE*)src;
297*0c16b537SWarner Losh     const BYTE* ip = istart;
298*0c16b537SWarner Losh     const BYTE* anchor = istart;
299*0c16b537SWarner Losh     const U32   lowestIndex = cctx->dictLimit;
300*0c16b537SWarner Losh     const BYTE* const lowest = base + lowestIndex;
301*0c16b537SWarner Losh     const BYTE* const iend = istart + srcSize;
302*0c16b537SWarner Losh     const BYTE* const ilimit = iend - MAX(ldmParams.minMatchLength, HASH_READ_SIZE);
303*0c16b537SWarner Losh 
304*0c16b537SWarner Losh     const ZSTD_blockCompressor blockCompressor =
305*0c16b537SWarner Losh         ZSTD_selectBlockCompressor(cctx->appliedParams.cParams.strategy, 0);
306*0c16b537SWarner Losh     U32* const repToConfirm = seqStorePtr->repToConfirm;
307*0c16b537SWarner Losh     U32 savedRep[ZSTD_REP_NUM];
308*0c16b537SWarner Losh     U64 rollingHash = 0;
309*0c16b537SWarner Losh     const BYTE* lastHashed = NULL;
310*0c16b537SWarner Losh     size_t i, lastLiterals;
311*0c16b537SWarner Losh 
312*0c16b537SWarner Losh     /* Save seqStorePtr->rep and copy repToConfirm */
313*0c16b537SWarner Losh     for (i = 0; i < ZSTD_REP_NUM; i++)
314*0c16b537SWarner Losh         savedRep[i] = repToConfirm[i] = seqStorePtr->rep[i];
315*0c16b537SWarner Losh 
316*0c16b537SWarner Losh     /* Main Search Loop */
317*0c16b537SWarner Losh     while (ip < ilimit) {   /* < instead of <=, because repcode check at (ip+1) */
318*0c16b537SWarner Losh         size_t mLength;
319*0c16b537SWarner Losh         U32 const current = (U32)(ip - base);
320*0c16b537SWarner Losh         size_t forwardMatchLength = 0, backwardMatchLength = 0;
321*0c16b537SWarner Losh         ldmEntry_t* bestEntry = NULL;
322*0c16b537SWarner Losh         if (ip != istart) {
323*0c16b537SWarner Losh             rollingHash = ZSTD_ldm_updateHash(rollingHash, lastHashed[0],
324*0c16b537SWarner Losh                                               lastHashed[ldmParams.minMatchLength],
325*0c16b537SWarner Losh                                               hashPower);
326*0c16b537SWarner Losh         } else {
327*0c16b537SWarner Losh             rollingHash = ZSTD_ldm_getRollingHash(ip, ldmParams.minMatchLength);
328*0c16b537SWarner Losh         }
329*0c16b537SWarner Losh         lastHashed = ip;
330*0c16b537SWarner Losh 
331*0c16b537SWarner Losh         /* Do not insert and do not look for a match */
332*0c16b537SWarner Losh         if (ZSTD_ldm_getTag(rollingHash, hBits, ldmParams.hashEveryLog) !=
333*0c16b537SWarner Losh                 ldmTagMask) {
334*0c16b537SWarner Losh            ip++;
335*0c16b537SWarner Losh            continue;
336*0c16b537SWarner Losh         }
337*0c16b537SWarner Losh 
338*0c16b537SWarner Losh         /* Get the best entry and compute the match lengths */
339*0c16b537SWarner Losh         {
340*0c16b537SWarner Losh             ldmEntry_t* const bucket =
341*0c16b537SWarner Losh                 ZSTD_ldm_getBucket(ldmState,
342*0c16b537SWarner Losh                                    ZSTD_ldm_getSmallHash(rollingHash, hBits),
343*0c16b537SWarner Losh                                    ldmParams);
344*0c16b537SWarner Losh             ldmEntry_t* cur;
345*0c16b537SWarner Losh             size_t bestMatchLength = 0;
346*0c16b537SWarner Losh             U32 const checksum = ZSTD_ldm_getChecksum(rollingHash, hBits);
347*0c16b537SWarner Losh 
348*0c16b537SWarner Losh             for (cur = bucket; cur < bucket + ldmBucketSize; ++cur) {
349*0c16b537SWarner Losh                 const BYTE* const pMatch = cur->offset + base;
350*0c16b537SWarner Losh                 size_t curForwardMatchLength, curBackwardMatchLength,
351*0c16b537SWarner Losh                        curTotalMatchLength;
352*0c16b537SWarner Losh                 if (cur->checksum != checksum || cur->offset <= lowestIndex) {
353*0c16b537SWarner Losh                     continue;
354*0c16b537SWarner Losh                 }
355*0c16b537SWarner Losh 
356*0c16b537SWarner Losh                 curForwardMatchLength = ZSTD_count(ip, pMatch, iend);
357*0c16b537SWarner Losh                 if (curForwardMatchLength < ldmParams.minMatchLength) {
358*0c16b537SWarner Losh                     continue;
359*0c16b537SWarner Losh                 }
360*0c16b537SWarner Losh                 curBackwardMatchLength = ZSTD_ldm_countBackwardsMatch(
361*0c16b537SWarner Losh                                              ip, anchor, pMatch, lowest);
362*0c16b537SWarner Losh                 curTotalMatchLength = curForwardMatchLength +
363*0c16b537SWarner Losh                                       curBackwardMatchLength;
364*0c16b537SWarner Losh 
365*0c16b537SWarner Losh                 if (curTotalMatchLength > bestMatchLength) {
366*0c16b537SWarner Losh                     bestMatchLength = curTotalMatchLength;
367*0c16b537SWarner Losh                     forwardMatchLength = curForwardMatchLength;
368*0c16b537SWarner Losh                     backwardMatchLength = curBackwardMatchLength;
369*0c16b537SWarner Losh                     bestEntry = cur;
370*0c16b537SWarner Losh                 }
371*0c16b537SWarner Losh             }
372*0c16b537SWarner Losh         }
373*0c16b537SWarner Losh 
374*0c16b537SWarner Losh         /* No match found -- continue searching */
375*0c16b537SWarner Losh         if (bestEntry == NULL) {
376*0c16b537SWarner Losh             ZSTD_ldm_makeEntryAndInsertByTag(ldmState, rollingHash,
377*0c16b537SWarner Losh                                              hBits, current,
378*0c16b537SWarner Losh                                              ldmParams);
379*0c16b537SWarner Losh             ip++;
380*0c16b537SWarner Losh             continue;
381*0c16b537SWarner Losh         }
382*0c16b537SWarner Losh 
383*0c16b537SWarner Losh         /* Match found */
384*0c16b537SWarner Losh         mLength = forwardMatchLength + backwardMatchLength;
385*0c16b537SWarner Losh         ip -= backwardMatchLength;
386*0c16b537SWarner Losh 
387*0c16b537SWarner Losh         /* Call the block compressor on the remaining literals */
388*0c16b537SWarner Losh         {
389*0c16b537SWarner Losh             U32 const matchIndex = bestEntry->offset;
390*0c16b537SWarner Losh             const BYTE* const match = base + matchIndex - backwardMatchLength;
391*0c16b537SWarner Losh             U32 const offset = (U32)(ip - match);
392*0c16b537SWarner Losh 
393*0c16b537SWarner Losh             /* Overwrite rep codes */
394*0c16b537SWarner Losh             for (i = 0; i < ZSTD_REP_NUM; i++)
395*0c16b537SWarner Losh                 seqStorePtr->rep[i] = repToConfirm[i];
396*0c16b537SWarner Losh 
397*0c16b537SWarner Losh             /* Fill tables for block compressor */
398*0c16b537SWarner Losh             ZSTD_ldm_limitTableUpdate(cctx, anchor);
399*0c16b537SWarner Losh             ZSTD_ldm_fillFastTables(cctx, anchor);
400*0c16b537SWarner Losh 
401*0c16b537SWarner Losh             /* Call block compressor and get remaining literals */
402*0c16b537SWarner Losh             lastLiterals = blockCompressor(cctx, anchor, ip - anchor);
403*0c16b537SWarner Losh             cctx->nextToUpdate = (U32)(ip - base);
404*0c16b537SWarner Losh 
405*0c16b537SWarner Losh             /* Update repToConfirm with the new offset */
406*0c16b537SWarner Losh             for (i = ZSTD_REP_NUM - 1; i > 0; i--)
407*0c16b537SWarner Losh                 repToConfirm[i] = repToConfirm[i-1];
408*0c16b537SWarner Losh             repToConfirm[0] = offset;
409*0c16b537SWarner Losh 
410*0c16b537SWarner Losh             /* Store the sequence with the leftover literals */
411*0c16b537SWarner Losh             ZSTD_storeSeq(seqStorePtr, lastLiterals, ip - lastLiterals,
412*0c16b537SWarner Losh                           offset + ZSTD_REP_MOVE, mLength - MINMATCH);
413*0c16b537SWarner Losh         }
414*0c16b537SWarner Losh 
415*0c16b537SWarner Losh         /* Insert the current entry into the hash table */
416*0c16b537SWarner Losh         ZSTD_ldm_makeEntryAndInsertByTag(ldmState, rollingHash, hBits,
417*0c16b537SWarner Losh                                          (U32)(lastHashed - base),
418*0c16b537SWarner Losh                                          ldmParams);
419*0c16b537SWarner Losh 
420*0c16b537SWarner Losh         assert(ip + backwardMatchLength == lastHashed);
421*0c16b537SWarner Losh 
422*0c16b537SWarner Losh         /* Fill the hash table from lastHashed+1 to ip+mLength*/
423*0c16b537SWarner Losh         /* Heuristic: don't need to fill the entire table at end of block */
424*0c16b537SWarner Losh         if (ip + mLength < ilimit) {
425*0c16b537SWarner Losh             rollingHash = ZSTD_ldm_fillLdmHashTable(
426*0c16b537SWarner Losh                               ldmState, rollingHash, lastHashed,
427*0c16b537SWarner Losh                               ip + mLength, base, hBits, ldmParams);
428*0c16b537SWarner Losh             lastHashed = ip + mLength - 1;
429*0c16b537SWarner Losh         }
430*0c16b537SWarner Losh         ip += mLength;
431*0c16b537SWarner Losh         anchor = ip;
432*0c16b537SWarner Losh         /* Check immediate repcode */
433*0c16b537SWarner Losh         while ( (ip < ilimit)
434*0c16b537SWarner Losh              && ( (repToConfirm[1] > 0) && (repToConfirm[1] <= (U32)(ip-lowest))
435*0c16b537SWarner Losh              && (MEM_read32(ip) == MEM_read32(ip - repToConfirm[1])) )) {
436*0c16b537SWarner Losh 
437*0c16b537SWarner Losh             size_t const rLength = ZSTD_count(ip+4, ip+4-repToConfirm[1],
438*0c16b537SWarner Losh                                               iend) + 4;
439*0c16b537SWarner Losh             /* Swap repToConfirm[1] <=> repToConfirm[0] */
440*0c16b537SWarner Losh             {
441*0c16b537SWarner Losh                 U32 const tmpOff = repToConfirm[1];
442*0c16b537SWarner Losh                 repToConfirm[1] = repToConfirm[0];
443*0c16b537SWarner Losh                 repToConfirm[0] = tmpOff;
444*0c16b537SWarner Losh             }
445*0c16b537SWarner Losh 
446*0c16b537SWarner Losh             ZSTD_storeSeq(seqStorePtr, 0, anchor, 0, rLength-MINMATCH);
447*0c16b537SWarner Losh 
448*0c16b537SWarner Losh             /* Fill the  hash table from lastHashed+1 to ip+rLength*/
449*0c16b537SWarner Losh             if (ip + rLength < ilimit) {
450*0c16b537SWarner Losh                 rollingHash = ZSTD_ldm_fillLdmHashTable(
451*0c16b537SWarner Losh                                 ldmState, rollingHash, lastHashed,
452*0c16b537SWarner Losh                                 ip + rLength, base, hBits, ldmParams);
453*0c16b537SWarner Losh                 lastHashed = ip + rLength - 1;
454*0c16b537SWarner Losh             }
455*0c16b537SWarner Losh             ip += rLength;
456*0c16b537SWarner Losh             anchor = ip;
457*0c16b537SWarner Losh         }
458*0c16b537SWarner Losh     }
459*0c16b537SWarner Losh 
460*0c16b537SWarner Losh     /* Overwrite rep */
461*0c16b537SWarner Losh     for (i = 0; i < ZSTD_REP_NUM; i++)
462*0c16b537SWarner Losh         seqStorePtr->rep[i] = repToConfirm[i];
463*0c16b537SWarner Losh 
464*0c16b537SWarner Losh     ZSTD_ldm_limitTableUpdate(cctx, anchor);
465*0c16b537SWarner Losh     ZSTD_ldm_fillFastTables(cctx, anchor);
466*0c16b537SWarner Losh 
467*0c16b537SWarner Losh     lastLiterals = blockCompressor(cctx, anchor, iend - anchor);
468*0c16b537SWarner Losh     cctx->nextToUpdate = (U32)(iend - base);
469*0c16b537SWarner Losh 
470*0c16b537SWarner Losh     /* Restore seqStorePtr->rep */
471*0c16b537SWarner Losh     for (i = 0; i < ZSTD_REP_NUM; i++)
472*0c16b537SWarner Losh         seqStorePtr->rep[i] = savedRep[i];
473*0c16b537SWarner Losh 
474*0c16b537SWarner Losh     /* Return the last literals size */
475*0c16b537SWarner Losh     return lastLiterals;
476*0c16b537SWarner Losh }
477*0c16b537SWarner Losh 
478*0c16b537SWarner Losh size_t ZSTD_compressBlock_ldm(ZSTD_CCtx* ctx,
479*0c16b537SWarner Losh                               const void* src, size_t srcSize)
480*0c16b537SWarner Losh {
481*0c16b537SWarner Losh     return ZSTD_compressBlock_ldm_generic(ctx, src, srcSize);
482*0c16b537SWarner Losh }
483*0c16b537SWarner Losh 
484*0c16b537SWarner Losh static size_t ZSTD_compressBlock_ldm_extDict_generic(
485*0c16b537SWarner Losh                                  ZSTD_CCtx* ctx,
486*0c16b537SWarner Losh                                  const void* src, size_t srcSize)
487*0c16b537SWarner Losh {
488*0c16b537SWarner Losh     ldmState_t* const ldmState = &(ctx->ldmState);
489*0c16b537SWarner Losh     const ldmParams_t ldmParams = ctx->appliedParams.ldmParams;
490*0c16b537SWarner Losh     const U64 hashPower = ldmState->hashPower;
491*0c16b537SWarner Losh     const U32 hBits = ldmParams.hashLog - ldmParams.bucketSizeLog;
492*0c16b537SWarner Losh     const U32 ldmBucketSize = ((U32)1 << ldmParams.bucketSizeLog);
493*0c16b537SWarner Losh     const U32 ldmTagMask = ((U32)1 << ldmParams.hashEveryLog) - 1;
494*0c16b537SWarner Losh     seqStore_t* const seqStorePtr = &(ctx->seqStore);
495*0c16b537SWarner Losh     const BYTE* const base = ctx->base;
496*0c16b537SWarner Losh     const BYTE* const dictBase = ctx->dictBase;
497*0c16b537SWarner Losh     const BYTE* const istart = (const BYTE*)src;
498*0c16b537SWarner Losh     const BYTE* ip = istart;
499*0c16b537SWarner Losh     const BYTE* anchor = istart;
500*0c16b537SWarner Losh     const U32   lowestIndex = ctx->lowLimit;
501*0c16b537SWarner Losh     const BYTE* const dictStart = dictBase + lowestIndex;
502*0c16b537SWarner Losh     const U32   dictLimit = ctx->dictLimit;
503*0c16b537SWarner Losh     const BYTE* const lowPrefixPtr = base + dictLimit;
504*0c16b537SWarner Losh     const BYTE* const dictEnd = dictBase + dictLimit;
505*0c16b537SWarner Losh     const BYTE* const iend = istart + srcSize;
506*0c16b537SWarner Losh     const BYTE* const ilimit = iend - MAX(ldmParams.minMatchLength, HASH_READ_SIZE);
507*0c16b537SWarner Losh 
508*0c16b537SWarner Losh     const ZSTD_blockCompressor blockCompressor =
509*0c16b537SWarner Losh         ZSTD_selectBlockCompressor(ctx->appliedParams.cParams.strategy, 1);
510*0c16b537SWarner Losh     U32* const repToConfirm = seqStorePtr->repToConfirm;
511*0c16b537SWarner Losh     U32 savedRep[ZSTD_REP_NUM];
512*0c16b537SWarner Losh     U64 rollingHash = 0;
513*0c16b537SWarner Losh     const BYTE* lastHashed = NULL;
514*0c16b537SWarner Losh     size_t i, lastLiterals;
515*0c16b537SWarner Losh 
516*0c16b537SWarner Losh     /* Save seqStorePtr->rep and copy repToConfirm */
517*0c16b537SWarner Losh     for (i = 0; i < ZSTD_REP_NUM; i++) {
518*0c16b537SWarner Losh         savedRep[i] = repToConfirm[i] = seqStorePtr->rep[i];
519*0c16b537SWarner Losh     }
520*0c16b537SWarner Losh 
521*0c16b537SWarner Losh     /* Search Loop */
522*0c16b537SWarner Losh     while (ip < ilimit) {  /* < instead of <=, because (ip+1) */
523*0c16b537SWarner Losh         size_t mLength;
524*0c16b537SWarner Losh         const U32 current = (U32)(ip-base);
525*0c16b537SWarner Losh         size_t forwardMatchLength = 0, backwardMatchLength = 0;
526*0c16b537SWarner Losh         ldmEntry_t* bestEntry = NULL;
527*0c16b537SWarner Losh         if (ip != istart) {
528*0c16b537SWarner Losh           rollingHash = ZSTD_ldm_updateHash(rollingHash, lastHashed[0],
529*0c16b537SWarner Losh                                        lastHashed[ldmParams.minMatchLength],
530*0c16b537SWarner Losh                                        hashPower);
531*0c16b537SWarner Losh         } else {
532*0c16b537SWarner Losh             rollingHash = ZSTD_ldm_getRollingHash(ip, ldmParams.minMatchLength);
533*0c16b537SWarner Losh         }
534*0c16b537SWarner Losh         lastHashed = ip;
535*0c16b537SWarner Losh 
536*0c16b537SWarner Losh         if (ZSTD_ldm_getTag(rollingHash, hBits, ldmParams.hashEveryLog) !=
537*0c16b537SWarner Losh                 ldmTagMask) {
538*0c16b537SWarner Losh             /* Don't insert and don't look for a match */
539*0c16b537SWarner Losh            ip++;
540*0c16b537SWarner Losh            continue;
541*0c16b537SWarner Losh         }
542*0c16b537SWarner Losh 
543*0c16b537SWarner Losh         /* Get the best entry and compute the match lengths */
544*0c16b537SWarner Losh         {
545*0c16b537SWarner Losh             ldmEntry_t* const bucket =
546*0c16b537SWarner Losh                 ZSTD_ldm_getBucket(ldmState,
547*0c16b537SWarner Losh                                    ZSTD_ldm_getSmallHash(rollingHash, hBits),
548*0c16b537SWarner Losh                                    ldmParams);
549*0c16b537SWarner Losh             ldmEntry_t* cur;
550*0c16b537SWarner Losh             size_t bestMatchLength = 0;
551*0c16b537SWarner Losh             U32 const checksum = ZSTD_ldm_getChecksum(rollingHash, hBits);
552*0c16b537SWarner Losh 
553*0c16b537SWarner Losh             for (cur = bucket; cur < bucket + ldmBucketSize; ++cur) {
554*0c16b537SWarner Losh                 const BYTE* const curMatchBase =
555*0c16b537SWarner Losh                     cur->offset < dictLimit ? dictBase : base;
556*0c16b537SWarner Losh                 const BYTE* const pMatch = curMatchBase + cur->offset;
557*0c16b537SWarner Losh                 const BYTE* const matchEnd =
558*0c16b537SWarner Losh                     cur->offset < dictLimit ? dictEnd : iend;
559*0c16b537SWarner Losh                 const BYTE* const lowMatchPtr =
560*0c16b537SWarner Losh                     cur->offset < dictLimit ? dictStart : lowPrefixPtr;
561*0c16b537SWarner Losh                 size_t curForwardMatchLength, curBackwardMatchLength,
562*0c16b537SWarner Losh                        curTotalMatchLength;
563*0c16b537SWarner Losh 
564*0c16b537SWarner Losh                 if (cur->checksum != checksum || cur->offset <= lowestIndex) {
565*0c16b537SWarner Losh                     continue;
566*0c16b537SWarner Losh                 }
567*0c16b537SWarner Losh 
568*0c16b537SWarner Losh                 curForwardMatchLength = ZSTD_count_2segments(
569*0c16b537SWarner Losh                                             ip, pMatch, iend,
570*0c16b537SWarner Losh                                             matchEnd, lowPrefixPtr);
571*0c16b537SWarner Losh                 if (curForwardMatchLength < ldmParams.minMatchLength) {
572*0c16b537SWarner Losh                     continue;
573*0c16b537SWarner Losh                 }
574*0c16b537SWarner Losh                 curBackwardMatchLength = ZSTD_ldm_countBackwardsMatch(
575*0c16b537SWarner Losh                                              ip, anchor, pMatch, lowMatchPtr);
576*0c16b537SWarner Losh                 curTotalMatchLength = curForwardMatchLength +
577*0c16b537SWarner Losh                                       curBackwardMatchLength;
578*0c16b537SWarner Losh 
579*0c16b537SWarner Losh                 if (curTotalMatchLength > bestMatchLength) {
580*0c16b537SWarner Losh                     bestMatchLength = curTotalMatchLength;
581*0c16b537SWarner Losh                     forwardMatchLength = curForwardMatchLength;
582*0c16b537SWarner Losh                     backwardMatchLength = curBackwardMatchLength;
583*0c16b537SWarner Losh                     bestEntry = cur;
584*0c16b537SWarner Losh                 }
585*0c16b537SWarner Losh             }
586*0c16b537SWarner Losh         }
587*0c16b537SWarner Losh 
588*0c16b537SWarner Losh         /* No match found -- continue searching */
589*0c16b537SWarner Losh         if (bestEntry == NULL) {
590*0c16b537SWarner Losh             ZSTD_ldm_makeEntryAndInsertByTag(ldmState, rollingHash, hBits,
591*0c16b537SWarner Losh                                              (U32)(lastHashed - base),
592*0c16b537SWarner Losh                                              ldmParams);
593*0c16b537SWarner Losh             ip++;
594*0c16b537SWarner Losh             continue;
595*0c16b537SWarner Losh         }
596*0c16b537SWarner Losh 
597*0c16b537SWarner Losh         /* Match found */
598*0c16b537SWarner Losh         mLength = forwardMatchLength + backwardMatchLength;
599*0c16b537SWarner Losh         ip -= backwardMatchLength;
600*0c16b537SWarner Losh 
601*0c16b537SWarner Losh         /* Call the block compressor on the remaining literals */
602*0c16b537SWarner Losh         {
603*0c16b537SWarner Losh             /* ip = current - backwardMatchLength
604*0c16b537SWarner Losh              * The match is at (bestEntry->offset - backwardMatchLength) */
605*0c16b537SWarner Losh             U32 const matchIndex = bestEntry->offset;
606*0c16b537SWarner Losh             U32 const offset = current - matchIndex;
607*0c16b537SWarner Losh 
608*0c16b537SWarner Losh             /* Overwrite rep codes */
609*0c16b537SWarner Losh             for (i = 0; i < ZSTD_REP_NUM; i++)
610*0c16b537SWarner Losh                 seqStorePtr->rep[i] = repToConfirm[i];
611*0c16b537SWarner Losh 
612*0c16b537SWarner Losh             /* Fill the hash table for the block compressor */
613*0c16b537SWarner Losh             ZSTD_ldm_limitTableUpdate(ctx, anchor);
614*0c16b537SWarner Losh             ZSTD_ldm_fillFastTables(ctx, anchor);
615*0c16b537SWarner Losh 
616*0c16b537SWarner Losh             /* Call block compressor and get remaining literals  */
617*0c16b537SWarner Losh             lastLiterals = blockCompressor(ctx, anchor, ip - anchor);
618*0c16b537SWarner Losh             ctx->nextToUpdate = (U32)(ip - base);
619*0c16b537SWarner Losh 
620*0c16b537SWarner Losh             /* Update repToConfirm with the new offset */
621*0c16b537SWarner Losh             for (i = ZSTD_REP_NUM - 1; i > 0; i--)
622*0c16b537SWarner Losh                 repToConfirm[i] = repToConfirm[i-1];
623*0c16b537SWarner Losh             repToConfirm[0] = offset;
624*0c16b537SWarner Losh 
625*0c16b537SWarner Losh             /* Store the sequence with the leftover literals */
626*0c16b537SWarner Losh             ZSTD_storeSeq(seqStorePtr, lastLiterals, ip - lastLiterals,
627*0c16b537SWarner Losh                           offset + ZSTD_REP_MOVE, mLength - MINMATCH);
628*0c16b537SWarner Losh         }
629*0c16b537SWarner Losh 
630*0c16b537SWarner Losh         /* Insert the current entry into the hash table */
631*0c16b537SWarner Losh         ZSTD_ldm_makeEntryAndInsertByTag(ldmState, rollingHash, hBits,
632*0c16b537SWarner Losh                                          (U32)(lastHashed - base),
633*0c16b537SWarner Losh                                          ldmParams);
634*0c16b537SWarner Losh 
635*0c16b537SWarner Losh         /* Fill the hash table from lastHashed+1 to ip+mLength */
636*0c16b537SWarner Losh         assert(ip + backwardMatchLength == lastHashed);
637*0c16b537SWarner Losh         if (ip + mLength < ilimit) {
638*0c16b537SWarner Losh             rollingHash = ZSTD_ldm_fillLdmHashTable(
639*0c16b537SWarner Losh                               ldmState, rollingHash, lastHashed,
640*0c16b537SWarner Losh                               ip + mLength, base, hBits,
641*0c16b537SWarner Losh                               ldmParams);
642*0c16b537SWarner Losh             lastHashed = ip + mLength - 1;
643*0c16b537SWarner Losh         }
644*0c16b537SWarner Losh         ip += mLength;
645*0c16b537SWarner Losh         anchor = ip;
646*0c16b537SWarner Losh 
647*0c16b537SWarner Losh         /* check immediate repcode */
648*0c16b537SWarner Losh         while (ip < ilimit) {
649*0c16b537SWarner Losh             U32 const current2 = (U32)(ip-base);
650*0c16b537SWarner Losh             U32 const repIndex2 = current2 - repToConfirm[1];
651*0c16b537SWarner Losh             const BYTE* repMatch2 = repIndex2 < dictLimit ?
652*0c16b537SWarner Losh                                     dictBase + repIndex2 : base + repIndex2;
653*0c16b537SWarner Losh             if ( (((U32)((dictLimit-1) - repIndex2) >= 3) &
654*0c16b537SWarner Losh                         (repIndex2 > lowestIndex))  /* intentional overflow */
655*0c16b537SWarner Losh                && (MEM_read32(repMatch2) == MEM_read32(ip)) ) {
656*0c16b537SWarner Losh                 const BYTE* const repEnd2 = repIndex2 < dictLimit ?
657*0c16b537SWarner Losh                                             dictEnd : iend;
658*0c16b537SWarner Losh                 size_t const repLength2 =
659*0c16b537SWarner Losh                         ZSTD_count_2segments(ip+4, repMatch2+4, iend,
660*0c16b537SWarner Losh                                              repEnd2, lowPrefixPtr) + 4;
661*0c16b537SWarner Losh 
662*0c16b537SWarner Losh                 U32 tmpOffset = repToConfirm[1];
663*0c16b537SWarner Losh                 repToConfirm[1] = repToConfirm[0];
664*0c16b537SWarner Losh                 repToConfirm[0] = tmpOffset;
665*0c16b537SWarner Losh 
666*0c16b537SWarner Losh                 ZSTD_storeSeq(seqStorePtr, 0, anchor, 0, repLength2-MINMATCH);
667*0c16b537SWarner Losh 
668*0c16b537SWarner Losh                 /* Fill the  hash table from lastHashed+1 to ip+repLength2*/
669*0c16b537SWarner Losh                 if (ip + repLength2 < ilimit) {
670*0c16b537SWarner Losh                     rollingHash = ZSTD_ldm_fillLdmHashTable(
671*0c16b537SWarner Losh                                       ldmState, rollingHash, lastHashed,
672*0c16b537SWarner Losh                                       ip + repLength2, base, hBits,
673*0c16b537SWarner Losh                                       ldmParams);
674*0c16b537SWarner Losh                     lastHashed = ip + repLength2 - 1;
675*0c16b537SWarner Losh                 }
676*0c16b537SWarner Losh                 ip += repLength2;
677*0c16b537SWarner Losh                 anchor = ip;
678*0c16b537SWarner Losh                 continue;
679*0c16b537SWarner Losh             }
680*0c16b537SWarner Losh             break;
681*0c16b537SWarner Losh         }
682*0c16b537SWarner Losh     }
683*0c16b537SWarner Losh 
684*0c16b537SWarner Losh     /* Overwrite rep */
685*0c16b537SWarner Losh     for (i = 0; i < ZSTD_REP_NUM; i++)
686*0c16b537SWarner Losh         seqStorePtr->rep[i] = repToConfirm[i];
687*0c16b537SWarner Losh 
688*0c16b537SWarner Losh     ZSTD_ldm_limitTableUpdate(ctx, anchor);
689*0c16b537SWarner Losh     ZSTD_ldm_fillFastTables(ctx, anchor);
690*0c16b537SWarner Losh 
691*0c16b537SWarner Losh     /* Call the block compressor one last time on the last literals */
692*0c16b537SWarner Losh     lastLiterals = blockCompressor(ctx, anchor, iend - anchor);
693*0c16b537SWarner Losh     ctx->nextToUpdate = (U32)(iend - base);
694*0c16b537SWarner Losh 
695*0c16b537SWarner Losh     /* Restore seqStorePtr->rep */
696*0c16b537SWarner Losh     for (i = 0; i < ZSTD_REP_NUM; i++)
697*0c16b537SWarner Losh         seqStorePtr->rep[i] = savedRep[i];
698*0c16b537SWarner Losh 
699*0c16b537SWarner Losh     /* Return the last literals size */
700*0c16b537SWarner Losh     return lastLiterals;
701*0c16b537SWarner Losh }
702*0c16b537SWarner Losh 
703*0c16b537SWarner Losh size_t ZSTD_compressBlock_ldm_extDict(ZSTD_CCtx* ctx,
704*0c16b537SWarner Losh                                       const void* src, size_t srcSize)
705*0c16b537SWarner Losh {
706*0c16b537SWarner Losh     return ZSTD_compressBlock_ldm_extDict_generic(ctx, src, srcSize);
707*0c16b537SWarner Losh }
708