1*01826a49SYabin Cui /*
2*01826a49SYabin Cui * Copyright (c) Meta Platforms, Inc. and affiliates.
3*01826a49SYabin Cui * All rights reserved.
4*01826a49SYabin Cui *
5*01826a49SYabin Cui * This source code is licensed under both the BSD-style license (found in the
6*01826a49SYabin Cui * LICENSE file in the root directory of this source tree) and the GPLv2 (found
7*01826a49SYabin Cui * in the COPYING file in the root directory of this source tree).
8*01826a49SYabin Cui * You may select, at your option, one of the above-listed licenses.
9*01826a49SYabin Cui */
10*01826a49SYabin Cui
11*01826a49SYabin Cui #include "zstd_ldm.h"
12*01826a49SYabin Cui
13*01826a49SYabin Cui #include "../common/debug.h"
14*01826a49SYabin Cui #include "../common/xxhash.h"
15*01826a49SYabin Cui #include "zstd_fast.h" /* ZSTD_fillHashTable() */
16*01826a49SYabin Cui #include "zstd_double_fast.h" /* ZSTD_fillDoubleHashTable() */
17*01826a49SYabin Cui #include "zstd_ldm_geartab.h"
18*01826a49SYabin Cui
19*01826a49SYabin Cui #define LDM_BUCKET_SIZE_LOG 3
20*01826a49SYabin Cui #define LDM_MIN_MATCH_LENGTH 64
21*01826a49SYabin Cui #define LDM_HASH_RLOG 7
22*01826a49SYabin Cui
23*01826a49SYabin Cui typedef struct {
24*01826a49SYabin Cui U64 rolling;
25*01826a49SYabin Cui U64 stopMask;
26*01826a49SYabin Cui } ldmRollingHashState_t;
27*01826a49SYabin Cui
28*01826a49SYabin Cui /** ZSTD_ldm_gear_init():
29*01826a49SYabin Cui *
30*01826a49SYabin Cui * Initializes the rolling hash state such that it will honor the
31*01826a49SYabin Cui * settings in params. */
ZSTD_ldm_gear_init(ldmRollingHashState_t * state,ldmParams_t const * params)32*01826a49SYabin Cui static void ZSTD_ldm_gear_init(ldmRollingHashState_t* state, ldmParams_t const* params)
33*01826a49SYabin Cui {
34*01826a49SYabin Cui unsigned maxBitsInMask = MIN(params->minMatchLength, 64);
35*01826a49SYabin Cui unsigned hashRateLog = params->hashRateLog;
36*01826a49SYabin Cui
37*01826a49SYabin Cui state->rolling = ~(U32)0;
38*01826a49SYabin Cui
39*01826a49SYabin Cui /* The choice of the splitting criterion is subject to two conditions:
40*01826a49SYabin Cui * 1. it has to trigger on average every 2^(hashRateLog) bytes;
41*01826a49SYabin Cui * 2. ideally, it has to depend on a window of minMatchLength bytes.
42*01826a49SYabin Cui *
43*01826a49SYabin Cui * In the gear hash algorithm, bit n depends on the last n bytes;
44*01826a49SYabin Cui * so in order to obtain a good quality splitting criterion it is
45*01826a49SYabin Cui * preferable to use bits with high weight.
46*01826a49SYabin Cui *
47*01826a49SYabin Cui * To match condition 1 we use a mask with hashRateLog bits set
48*01826a49SYabin Cui * and, because of the previous remark, we make sure these bits
49*01826a49SYabin Cui * have the highest possible weight while still respecting
50*01826a49SYabin Cui * condition 2.
51*01826a49SYabin Cui */
52*01826a49SYabin Cui if (hashRateLog > 0 && hashRateLog <= maxBitsInMask) {
53*01826a49SYabin Cui state->stopMask = (((U64)1 << hashRateLog) - 1) << (maxBitsInMask - hashRateLog);
54*01826a49SYabin Cui } else {
55*01826a49SYabin Cui /* In this degenerate case we simply honor the hash rate. */
56*01826a49SYabin Cui state->stopMask = ((U64)1 << hashRateLog) - 1;
57*01826a49SYabin Cui }
58*01826a49SYabin Cui }
59*01826a49SYabin Cui
60*01826a49SYabin Cui /** ZSTD_ldm_gear_reset()
61*01826a49SYabin Cui * Feeds [data, data + minMatchLength) into the hash without registering any
62*01826a49SYabin Cui * splits. This effectively resets the hash state. This is used when skipping
63*01826a49SYabin Cui * over data, either at the beginning of a block, or skipping sections.
64*01826a49SYabin Cui */
ZSTD_ldm_gear_reset(ldmRollingHashState_t * state,BYTE const * data,size_t minMatchLength)65*01826a49SYabin Cui static void ZSTD_ldm_gear_reset(ldmRollingHashState_t* state,
66*01826a49SYabin Cui BYTE const* data, size_t minMatchLength)
67*01826a49SYabin Cui {
68*01826a49SYabin Cui U64 hash = state->rolling;
69*01826a49SYabin Cui size_t n = 0;
70*01826a49SYabin Cui
71*01826a49SYabin Cui #define GEAR_ITER_ONCE() do { \
72*01826a49SYabin Cui hash = (hash << 1) + ZSTD_ldm_gearTab[data[n] & 0xff]; \
73*01826a49SYabin Cui n += 1; \
74*01826a49SYabin Cui } while (0)
75*01826a49SYabin Cui while (n + 3 < minMatchLength) {
76*01826a49SYabin Cui GEAR_ITER_ONCE();
77*01826a49SYabin Cui GEAR_ITER_ONCE();
78*01826a49SYabin Cui GEAR_ITER_ONCE();
79*01826a49SYabin Cui GEAR_ITER_ONCE();
80*01826a49SYabin Cui }
81*01826a49SYabin Cui while (n < minMatchLength) {
82*01826a49SYabin Cui GEAR_ITER_ONCE();
83*01826a49SYabin Cui }
84*01826a49SYabin Cui #undef GEAR_ITER_ONCE
85*01826a49SYabin Cui }
86*01826a49SYabin Cui
87*01826a49SYabin Cui /** ZSTD_ldm_gear_feed():
88*01826a49SYabin Cui *
89*01826a49SYabin Cui * Registers in the splits array all the split points found in the first
90*01826a49SYabin Cui * size bytes following the data pointer. This function terminates when
91*01826a49SYabin Cui * either all the data has been processed or LDM_BATCH_SIZE splits are
92*01826a49SYabin Cui * present in the splits array.
93*01826a49SYabin Cui *
94*01826a49SYabin Cui * Precondition: The splits array must not be full.
95*01826a49SYabin Cui * Returns: The number of bytes processed. */
ZSTD_ldm_gear_feed(ldmRollingHashState_t * state,BYTE const * data,size_t size,size_t * splits,unsigned * numSplits)96*01826a49SYabin Cui static size_t ZSTD_ldm_gear_feed(ldmRollingHashState_t* state,
97*01826a49SYabin Cui BYTE const* data, size_t size,
98*01826a49SYabin Cui size_t* splits, unsigned* numSplits)
99*01826a49SYabin Cui {
100*01826a49SYabin Cui size_t n;
101*01826a49SYabin Cui U64 hash, mask;
102*01826a49SYabin Cui
103*01826a49SYabin Cui hash = state->rolling;
104*01826a49SYabin Cui mask = state->stopMask;
105*01826a49SYabin Cui n = 0;
106*01826a49SYabin Cui
107*01826a49SYabin Cui #define GEAR_ITER_ONCE() do { \
108*01826a49SYabin Cui hash = (hash << 1) + ZSTD_ldm_gearTab[data[n] & 0xff]; \
109*01826a49SYabin Cui n += 1; \
110*01826a49SYabin Cui if (UNLIKELY((hash & mask) == 0)) { \
111*01826a49SYabin Cui splits[*numSplits] = n; \
112*01826a49SYabin Cui *numSplits += 1; \
113*01826a49SYabin Cui if (*numSplits == LDM_BATCH_SIZE) \
114*01826a49SYabin Cui goto done; \
115*01826a49SYabin Cui } \
116*01826a49SYabin Cui } while (0)
117*01826a49SYabin Cui
118*01826a49SYabin Cui while (n + 3 < size) {
119*01826a49SYabin Cui GEAR_ITER_ONCE();
120*01826a49SYabin Cui GEAR_ITER_ONCE();
121*01826a49SYabin Cui GEAR_ITER_ONCE();
122*01826a49SYabin Cui GEAR_ITER_ONCE();
123*01826a49SYabin Cui }
124*01826a49SYabin Cui while (n < size) {
125*01826a49SYabin Cui GEAR_ITER_ONCE();
126*01826a49SYabin Cui }
127*01826a49SYabin Cui
128*01826a49SYabin Cui #undef GEAR_ITER_ONCE
129*01826a49SYabin Cui
130*01826a49SYabin Cui done:
131*01826a49SYabin Cui state->rolling = hash;
132*01826a49SYabin Cui return n;
133*01826a49SYabin Cui }
134*01826a49SYabin Cui
ZSTD_ldm_adjustParameters(ldmParams_t * params,ZSTD_compressionParameters const * cParams)135*01826a49SYabin Cui void ZSTD_ldm_adjustParameters(ldmParams_t* params,
136*01826a49SYabin Cui ZSTD_compressionParameters const* cParams)
137*01826a49SYabin Cui {
138*01826a49SYabin Cui params->windowLog = cParams->windowLog;
139*01826a49SYabin Cui ZSTD_STATIC_ASSERT(LDM_BUCKET_SIZE_LOG <= ZSTD_LDM_BUCKETSIZELOG_MAX);
140*01826a49SYabin Cui DEBUGLOG(4, "ZSTD_ldm_adjustParameters");
141*01826a49SYabin Cui if (!params->bucketSizeLog) params->bucketSizeLog = LDM_BUCKET_SIZE_LOG;
142*01826a49SYabin Cui if (!params->minMatchLength) params->minMatchLength = LDM_MIN_MATCH_LENGTH;
143*01826a49SYabin Cui if (params->hashLog == 0) {
144*01826a49SYabin Cui params->hashLog = MAX(ZSTD_HASHLOG_MIN, params->windowLog - LDM_HASH_RLOG);
145*01826a49SYabin Cui assert(params->hashLog <= ZSTD_HASHLOG_MAX);
146*01826a49SYabin Cui }
147*01826a49SYabin Cui if (params->hashRateLog == 0) {
148*01826a49SYabin Cui params->hashRateLog = params->windowLog < params->hashLog
149*01826a49SYabin Cui ? 0
150*01826a49SYabin Cui : params->windowLog - params->hashLog;
151*01826a49SYabin Cui }
152*01826a49SYabin Cui params->bucketSizeLog = MIN(params->bucketSizeLog, params->hashLog);
153*01826a49SYabin Cui }
154*01826a49SYabin Cui
ZSTD_ldm_getTableSize(ldmParams_t params)155*01826a49SYabin Cui size_t ZSTD_ldm_getTableSize(ldmParams_t params)
156*01826a49SYabin Cui {
157*01826a49SYabin Cui size_t const ldmHSize = ((size_t)1) << params.hashLog;
158*01826a49SYabin Cui size_t const ldmBucketSizeLog = MIN(params.bucketSizeLog, params.hashLog);
159*01826a49SYabin Cui size_t const ldmBucketSize = ((size_t)1) << (params.hashLog - ldmBucketSizeLog);
160*01826a49SYabin Cui size_t const totalSize = ZSTD_cwksp_alloc_size(ldmBucketSize)
161*01826a49SYabin Cui + ZSTD_cwksp_alloc_size(ldmHSize * sizeof(ldmEntry_t));
162*01826a49SYabin Cui return params.enableLdm == ZSTD_ps_enable ? totalSize : 0;
163*01826a49SYabin Cui }
164*01826a49SYabin Cui
ZSTD_ldm_getMaxNbSeq(ldmParams_t params,size_t maxChunkSize)165*01826a49SYabin Cui size_t ZSTD_ldm_getMaxNbSeq(ldmParams_t params, size_t maxChunkSize)
166*01826a49SYabin Cui {
167*01826a49SYabin Cui return params.enableLdm == ZSTD_ps_enable ? (maxChunkSize / params.minMatchLength) : 0;
168*01826a49SYabin Cui }
169*01826a49SYabin Cui
170*01826a49SYabin Cui /** ZSTD_ldm_getBucket() :
171*01826a49SYabin Cui * Returns a pointer to the start of the bucket associated with hash. */
ZSTD_ldm_getBucket(ldmState_t * ldmState,size_t hash,ldmParams_t const ldmParams)172*01826a49SYabin Cui static ldmEntry_t* ZSTD_ldm_getBucket(
173*01826a49SYabin Cui ldmState_t* ldmState, size_t hash, ldmParams_t const ldmParams)
174*01826a49SYabin Cui {
175*01826a49SYabin Cui return ldmState->hashTable + (hash << ldmParams.bucketSizeLog);
176*01826a49SYabin Cui }
177*01826a49SYabin Cui
178*01826a49SYabin Cui /** ZSTD_ldm_insertEntry() :
179*01826a49SYabin Cui * Insert the entry with corresponding hash into the hash table */
ZSTD_ldm_insertEntry(ldmState_t * ldmState,size_t const hash,const ldmEntry_t entry,ldmParams_t const ldmParams)180*01826a49SYabin Cui static void ZSTD_ldm_insertEntry(ldmState_t* ldmState,
181*01826a49SYabin Cui size_t const hash, const ldmEntry_t entry,
182*01826a49SYabin Cui ldmParams_t const ldmParams)
183*01826a49SYabin Cui {
184*01826a49SYabin Cui BYTE* const pOffset = ldmState->bucketOffsets + hash;
185*01826a49SYabin Cui unsigned const offset = *pOffset;
186*01826a49SYabin Cui
187*01826a49SYabin Cui *(ZSTD_ldm_getBucket(ldmState, hash, ldmParams) + offset) = entry;
188*01826a49SYabin Cui *pOffset = (BYTE)((offset + 1) & ((1u << ldmParams.bucketSizeLog) - 1));
189*01826a49SYabin Cui
190*01826a49SYabin Cui }
191*01826a49SYabin Cui
192*01826a49SYabin Cui /** ZSTD_ldm_countBackwardsMatch() :
193*01826a49SYabin Cui * Returns the number of bytes that match backwards before pIn and pMatch.
194*01826a49SYabin Cui *
195*01826a49SYabin Cui * We count only bytes where pMatch >= pBase and pIn >= pAnchor. */
ZSTD_ldm_countBackwardsMatch(const BYTE * pIn,const BYTE * pAnchor,const BYTE * pMatch,const BYTE * pMatchBase)196*01826a49SYabin Cui static size_t ZSTD_ldm_countBackwardsMatch(
197*01826a49SYabin Cui const BYTE* pIn, const BYTE* pAnchor,
198*01826a49SYabin Cui const BYTE* pMatch, const BYTE* pMatchBase)
199*01826a49SYabin Cui {
200*01826a49SYabin Cui size_t matchLength = 0;
201*01826a49SYabin Cui while (pIn > pAnchor && pMatch > pMatchBase && pIn[-1] == pMatch[-1]) {
202*01826a49SYabin Cui pIn--;
203*01826a49SYabin Cui pMatch--;
204*01826a49SYabin Cui matchLength++;
205*01826a49SYabin Cui }
206*01826a49SYabin Cui return matchLength;
207*01826a49SYabin Cui }
208*01826a49SYabin Cui
209*01826a49SYabin Cui /** ZSTD_ldm_countBackwardsMatch_2segments() :
210*01826a49SYabin Cui * Returns the number of bytes that match backwards from pMatch,
211*01826a49SYabin Cui * even with the backwards match spanning 2 different segments.
212*01826a49SYabin Cui *
213*01826a49SYabin Cui * On reaching `pMatchBase`, start counting from mEnd */
ZSTD_ldm_countBackwardsMatch_2segments(const BYTE * pIn,const BYTE * pAnchor,const BYTE * pMatch,const BYTE * pMatchBase,const BYTE * pExtDictStart,const BYTE * pExtDictEnd)214*01826a49SYabin Cui static size_t ZSTD_ldm_countBackwardsMatch_2segments(
215*01826a49SYabin Cui const BYTE* pIn, const BYTE* pAnchor,
216*01826a49SYabin Cui const BYTE* pMatch, const BYTE* pMatchBase,
217*01826a49SYabin Cui const BYTE* pExtDictStart, const BYTE* pExtDictEnd)
218*01826a49SYabin Cui {
219*01826a49SYabin Cui size_t matchLength = ZSTD_ldm_countBackwardsMatch(pIn, pAnchor, pMatch, pMatchBase);
220*01826a49SYabin Cui if (pMatch - matchLength != pMatchBase || pMatchBase == pExtDictStart) {
221*01826a49SYabin Cui /* If backwards match is entirely in the extDict or prefix, immediately return */
222*01826a49SYabin Cui return matchLength;
223*01826a49SYabin Cui }
224*01826a49SYabin Cui DEBUGLOG(7, "ZSTD_ldm_countBackwardsMatch_2segments: found 2-parts backwards match (length in prefix==%zu)", matchLength);
225*01826a49SYabin Cui matchLength += ZSTD_ldm_countBackwardsMatch(pIn - matchLength, pAnchor, pExtDictEnd, pExtDictStart);
226*01826a49SYabin Cui DEBUGLOG(7, "final backwards match length = %zu", matchLength);
227*01826a49SYabin Cui return matchLength;
228*01826a49SYabin Cui }
229*01826a49SYabin Cui
230*01826a49SYabin Cui /** ZSTD_ldm_fillFastTables() :
231*01826a49SYabin Cui *
232*01826a49SYabin Cui * Fills the relevant tables for the ZSTD_fast and ZSTD_dfast strategies.
233*01826a49SYabin Cui * This is similar to ZSTD_loadDictionaryContent.
234*01826a49SYabin Cui *
235*01826a49SYabin Cui * The tables for the other strategies are filled within their
236*01826a49SYabin Cui * block compressors. */
ZSTD_ldm_fillFastTables(ZSTD_matchState_t * ms,void const * end)237*01826a49SYabin Cui static size_t ZSTD_ldm_fillFastTables(ZSTD_matchState_t* ms,
238*01826a49SYabin Cui void const* end)
239*01826a49SYabin Cui {
240*01826a49SYabin Cui const BYTE* const iend = (const BYTE*)end;
241*01826a49SYabin Cui
242*01826a49SYabin Cui switch(ms->cParams.strategy)
243*01826a49SYabin Cui {
244*01826a49SYabin Cui case ZSTD_fast:
245*01826a49SYabin Cui ZSTD_fillHashTable(ms, iend, ZSTD_dtlm_fast, ZSTD_tfp_forCCtx);
246*01826a49SYabin Cui break;
247*01826a49SYabin Cui
248*01826a49SYabin Cui case ZSTD_dfast:
249*01826a49SYabin Cui #ifndef ZSTD_EXCLUDE_DFAST_BLOCK_COMPRESSOR
250*01826a49SYabin Cui ZSTD_fillDoubleHashTable(ms, iend, ZSTD_dtlm_fast, ZSTD_tfp_forCCtx);
251*01826a49SYabin Cui #else
252*01826a49SYabin Cui assert(0); /* shouldn't be called: cparams should've been adjusted. */
253*01826a49SYabin Cui #endif
254*01826a49SYabin Cui break;
255*01826a49SYabin Cui
256*01826a49SYabin Cui case ZSTD_greedy:
257*01826a49SYabin Cui case ZSTD_lazy:
258*01826a49SYabin Cui case ZSTD_lazy2:
259*01826a49SYabin Cui case ZSTD_btlazy2:
260*01826a49SYabin Cui case ZSTD_btopt:
261*01826a49SYabin Cui case ZSTD_btultra:
262*01826a49SYabin Cui case ZSTD_btultra2:
263*01826a49SYabin Cui break;
264*01826a49SYabin Cui default:
265*01826a49SYabin Cui assert(0); /* not possible : not a valid strategy id */
266*01826a49SYabin Cui }
267*01826a49SYabin Cui
268*01826a49SYabin Cui return 0;
269*01826a49SYabin Cui }
270*01826a49SYabin Cui
ZSTD_ldm_fillHashTable(ldmState_t * ldmState,const BYTE * ip,const BYTE * iend,ldmParams_t const * params)271*01826a49SYabin Cui void ZSTD_ldm_fillHashTable(
272*01826a49SYabin Cui ldmState_t* ldmState, const BYTE* ip,
273*01826a49SYabin Cui const BYTE* iend, ldmParams_t const* params)
274*01826a49SYabin Cui {
275*01826a49SYabin Cui U32 const minMatchLength = params->minMatchLength;
276*01826a49SYabin Cui U32 const hBits = params->hashLog - params->bucketSizeLog;
277*01826a49SYabin Cui BYTE const* const base = ldmState->window.base;
278*01826a49SYabin Cui BYTE const* const istart = ip;
279*01826a49SYabin Cui ldmRollingHashState_t hashState;
280*01826a49SYabin Cui size_t* const splits = ldmState->splitIndices;
281*01826a49SYabin Cui unsigned numSplits;
282*01826a49SYabin Cui
283*01826a49SYabin Cui DEBUGLOG(5, "ZSTD_ldm_fillHashTable");
284*01826a49SYabin Cui
285*01826a49SYabin Cui ZSTD_ldm_gear_init(&hashState, params);
286*01826a49SYabin Cui while (ip < iend) {
287*01826a49SYabin Cui size_t hashed;
288*01826a49SYabin Cui unsigned n;
289*01826a49SYabin Cui
290*01826a49SYabin Cui numSplits = 0;
291*01826a49SYabin Cui hashed = ZSTD_ldm_gear_feed(&hashState, ip, iend - ip, splits, &numSplits);
292*01826a49SYabin Cui
293*01826a49SYabin Cui for (n = 0; n < numSplits; n++) {
294*01826a49SYabin Cui if (ip + splits[n] >= istart + minMatchLength) {
295*01826a49SYabin Cui BYTE const* const split = ip + splits[n] - minMatchLength;
296*01826a49SYabin Cui U64 const xxhash = XXH64(split, minMatchLength, 0);
297*01826a49SYabin Cui U32 const hash = (U32)(xxhash & (((U32)1 << hBits) - 1));
298*01826a49SYabin Cui ldmEntry_t entry;
299*01826a49SYabin Cui
300*01826a49SYabin Cui entry.offset = (U32)(split - base);
301*01826a49SYabin Cui entry.checksum = (U32)(xxhash >> 32);
302*01826a49SYabin Cui ZSTD_ldm_insertEntry(ldmState, hash, entry, *params);
303*01826a49SYabin Cui }
304*01826a49SYabin Cui }
305*01826a49SYabin Cui
306*01826a49SYabin Cui ip += hashed;
307*01826a49SYabin Cui }
308*01826a49SYabin Cui }
309*01826a49SYabin Cui
310*01826a49SYabin Cui
311*01826a49SYabin Cui /** ZSTD_ldm_limitTableUpdate() :
312*01826a49SYabin Cui *
313*01826a49SYabin Cui * Sets cctx->nextToUpdate to a position corresponding closer to anchor
314*01826a49SYabin Cui * if it is far way
315*01826a49SYabin Cui * (after a long match, only update tables a limited amount). */
ZSTD_ldm_limitTableUpdate(ZSTD_matchState_t * ms,const BYTE * anchor)316*01826a49SYabin Cui static void ZSTD_ldm_limitTableUpdate(ZSTD_matchState_t* ms, const BYTE* anchor)
317*01826a49SYabin Cui {
318*01826a49SYabin Cui U32 const curr = (U32)(anchor - ms->window.base);
319*01826a49SYabin Cui if (curr > ms->nextToUpdate + 1024) {
320*01826a49SYabin Cui ms->nextToUpdate =
321*01826a49SYabin Cui curr - MIN(512, curr - ms->nextToUpdate - 1024);
322*01826a49SYabin Cui }
323*01826a49SYabin Cui }
324*01826a49SYabin Cui
325*01826a49SYabin Cui static
326*01826a49SYabin Cui ZSTD_ALLOW_POINTER_OVERFLOW_ATTR
ZSTD_ldm_generateSequences_internal(ldmState_t * ldmState,rawSeqStore_t * rawSeqStore,ldmParams_t const * params,void const * src,size_t srcSize)327*01826a49SYabin Cui size_t ZSTD_ldm_generateSequences_internal(
328*01826a49SYabin Cui ldmState_t* ldmState, rawSeqStore_t* rawSeqStore,
329*01826a49SYabin Cui ldmParams_t const* params, void const* src, size_t srcSize)
330*01826a49SYabin Cui {
331*01826a49SYabin Cui /* LDM parameters */
332*01826a49SYabin Cui int const extDict = ZSTD_window_hasExtDict(ldmState->window);
333*01826a49SYabin Cui U32 const minMatchLength = params->minMatchLength;
334*01826a49SYabin Cui U32 const entsPerBucket = 1U << params->bucketSizeLog;
335*01826a49SYabin Cui U32 const hBits = params->hashLog - params->bucketSizeLog;
336*01826a49SYabin Cui /* Prefix and extDict parameters */
337*01826a49SYabin Cui U32 const dictLimit = ldmState->window.dictLimit;
338*01826a49SYabin Cui U32 const lowestIndex = extDict ? ldmState->window.lowLimit : dictLimit;
339*01826a49SYabin Cui BYTE const* const base = ldmState->window.base;
340*01826a49SYabin Cui BYTE const* const dictBase = extDict ? ldmState->window.dictBase : NULL;
341*01826a49SYabin Cui BYTE const* const dictStart = extDict ? dictBase + lowestIndex : NULL;
342*01826a49SYabin Cui BYTE const* const dictEnd = extDict ? dictBase + dictLimit : NULL;
343*01826a49SYabin Cui BYTE const* const lowPrefixPtr = base + dictLimit;
344*01826a49SYabin Cui /* Input bounds */
345*01826a49SYabin Cui BYTE const* const istart = (BYTE const*)src;
346*01826a49SYabin Cui BYTE const* const iend = istart + srcSize;
347*01826a49SYabin Cui BYTE const* const ilimit = iend - HASH_READ_SIZE;
348*01826a49SYabin Cui /* Input positions */
349*01826a49SYabin Cui BYTE const* anchor = istart;
350*01826a49SYabin Cui BYTE const* ip = istart;
351*01826a49SYabin Cui /* Rolling hash state */
352*01826a49SYabin Cui ldmRollingHashState_t hashState;
353*01826a49SYabin Cui /* Arrays for staged-processing */
354*01826a49SYabin Cui size_t* const splits = ldmState->splitIndices;
355*01826a49SYabin Cui ldmMatchCandidate_t* const candidates = ldmState->matchCandidates;
356*01826a49SYabin Cui unsigned numSplits;
357*01826a49SYabin Cui
358*01826a49SYabin Cui if (srcSize < minMatchLength)
359*01826a49SYabin Cui return iend - anchor;
360*01826a49SYabin Cui
361*01826a49SYabin Cui /* Initialize the rolling hash state with the first minMatchLength bytes */
362*01826a49SYabin Cui ZSTD_ldm_gear_init(&hashState, params);
363*01826a49SYabin Cui ZSTD_ldm_gear_reset(&hashState, ip, minMatchLength);
364*01826a49SYabin Cui ip += minMatchLength;
365*01826a49SYabin Cui
366*01826a49SYabin Cui while (ip < ilimit) {
367*01826a49SYabin Cui size_t hashed;
368*01826a49SYabin Cui unsigned n;
369*01826a49SYabin Cui
370*01826a49SYabin Cui numSplits = 0;
371*01826a49SYabin Cui hashed = ZSTD_ldm_gear_feed(&hashState, ip, ilimit - ip,
372*01826a49SYabin Cui splits, &numSplits);
373*01826a49SYabin Cui
374*01826a49SYabin Cui for (n = 0; n < numSplits; n++) {
375*01826a49SYabin Cui BYTE const* const split = ip + splits[n] - minMatchLength;
376*01826a49SYabin Cui U64 const xxhash = XXH64(split, minMatchLength, 0);
377*01826a49SYabin Cui U32 const hash = (U32)(xxhash & (((U32)1 << hBits) - 1));
378*01826a49SYabin Cui
379*01826a49SYabin Cui candidates[n].split = split;
380*01826a49SYabin Cui candidates[n].hash = hash;
381*01826a49SYabin Cui candidates[n].checksum = (U32)(xxhash >> 32);
382*01826a49SYabin Cui candidates[n].bucket = ZSTD_ldm_getBucket(ldmState, hash, *params);
383*01826a49SYabin Cui PREFETCH_L1(candidates[n].bucket);
384*01826a49SYabin Cui }
385*01826a49SYabin Cui
386*01826a49SYabin Cui for (n = 0; n < numSplits; n++) {
387*01826a49SYabin Cui size_t forwardMatchLength = 0, backwardMatchLength = 0,
388*01826a49SYabin Cui bestMatchLength = 0, mLength;
389*01826a49SYabin Cui U32 offset;
390*01826a49SYabin Cui BYTE const* const split = candidates[n].split;
391*01826a49SYabin Cui U32 const checksum = candidates[n].checksum;
392*01826a49SYabin Cui U32 const hash = candidates[n].hash;
393*01826a49SYabin Cui ldmEntry_t* const bucket = candidates[n].bucket;
394*01826a49SYabin Cui ldmEntry_t const* cur;
395*01826a49SYabin Cui ldmEntry_t const* bestEntry = NULL;
396*01826a49SYabin Cui ldmEntry_t newEntry;
397*01826a49SYabin Cui
398*01826a49SYabin Cui newEntry.offset = (U32)(split - base);
399*01826a49SYabin Cui newEntry.checksum = checksum;
400*01826a49SYabin Cui
401*01826a49SYabin Cui /* If a split point would generate a sequence overlapping with
402*01826a49SYabin Cui * the previous one, we merely register it in the hash table and
403*01826a49SYabin Cui * move on */
404*01826a49SYabin Cui if (split < anchor) {
405*01826a49SYabin Cui ZSTD_ldm_insertEntry(ldmState, hash, newEntry, *params);
406*01826a49SYabin Cui continue;
407*01826a49SYabin Cui }
408*01826a49SYabin Cui
409*01826a49SYabin Cui for (cur = bucket; cur < bucket + entsPerBucket; cur++) {
410*01826a49SYabin Cui size_t curForwardMatchLength, curBackwardMatchLength,
411*01826a49SYabin Cui curTotalMatchLength;
412*01826a49SYabin Cui if (cur->checksum != checksum || cur->offset <= lowestIndex) {
413*01826a49SYabin Cui continue;
414*01826a49SYabin Cui }
415*01826a49SYabin Cui if (extDict) {
416*01826a49SYabin Cui BYTE const* const curMatchBase =
417*01826a49SYabin Cui cur->offset < dictLimit ? dictBase : base;
418*01826a49SYabin Cui BYTE const* const pMatch = curMatchBase + cur->offset;
419*01826a49SYabin Cui BYTE const* const matchEnd =
420*01826a49SYabin Cui cur->offset < dictLimit ? dictEnd : iend;
421*01826a49SYabin Cui BYTE const* const lowMatchPtr =
422*01826a49SYabin Cui cur->offset < dictLimit ? dictStart : lowPrefixPtr;
423*01826a49SYabin Cui curForwardMatchLength =
424*01826a49SYabin Cui ZSTD_count_2segments(split, pMatch, iend, matchEnd, lowPrefixPtr);
425*01826a49SYabin Cui if (curForwardMatchLength < minMatchLength) {
426*01826a49SYabin Cui continue;
427*01826a49SYabin Cui }
428*01826a49SYabin Cui curBackwardMatchLength = ZSTD_ldm_countBackwardsMatch_2segments(
429*01826a49SYabin Cui split, anchor, pMatch, lowMatchPtr, dictStart, dictEnd);
430*01826a49SYabin Cui } else { /* !extDict */
431*01826a49SYabin Cui BYTE const* const pMatch = base + cur->offset;
432*01826a49SYabin Cui curForwardMatchLength = ZSTD_count(split, pMatch, iend);
433*01826a49SYabin Cui if (curForwardMatchLength < minMatchLength) {
434*01826a49SYabin Cui continue;
435*01826a49SYabin Cui }
436*01826a49SYabin Cui curBackwardMatchLength =
437*01826a49SYabin Cui ZSTD_ldm_countBackwardsMatch(split, anchor, pMatch, lowPrefixPtr);
438*01826a49SYabin Cui }
439*01826a49SYabin Cui curTotalMatchLength = curForwardMatchLength + curBackwardMatchLength;
440*01826a49SYabin Cui
441*01826a49SYabin Cui if (curTotalMatchLength > bestMatchLength) {
442*01826a49SYabin Cui bestMatchLength = curTotalMatchLength;
443*01826a49SYabin Cui forwardMatchLength = curForwardMatchLength;
444*01826a49SYabin Cui backwardMatchLength = curBackwardMatchLength;
445*01826a49SYabin Cui bestEntry = cur;
446*01826a49SYabin Cui }
447*01826a49SYabin Cui }
448*01826a49SYabin Cui
449*01826a49SYabin Cui /* No match found -- insert an entry into the hash table
450*01826a49SYabin Cui * and process the next candidate match */
451*01826a49SYabin Cui if (bestEntry == NULL) {
452*01826a49SYabin Cui ZSTD_ldm_insertEntry(ldmState, hash, newEntry, *params);
453*01826a49SYabin Cui continue;
454*01826a49SYabin Cui }
455*01826a49SYabin Cui
456*01826a49SYabin Cui /* Match found */
457*01826a49SYabin Cui offset = (U32)(split - base) - bestEntry->offset;
458*01826a49SYabin Cui mLength = forwardMatchLength + backwardMatchLength;
459*01826a49SYabin Cui {
460*01826a49SYabin Cui rawSeq* const seq = rawSeqStore->seq + rawSeqStore->size;
461*01826a49SYabin Cui
462*01826a49SYabin Cui /* Out of sequence storage */
463*01826a49SYabin Cui if (rawSeqStore->size == rawSeqStore->capacity)
464*01826a49SYabin Cui return ERROR(dstSize_tooSmall);
465*01826a49SYabin Cui seq->litLength = (U32)(split - backwardMatchLength - anchor);
466*01826a49SYabin Cui seq->matchLength = (U32)mLength;
467*01826a49SYabin Cui seq->offset = offset;
468*01826a49SYabin Cui rawSeqStore->size++;
469*01826a49SYabin Cui }
470*01826a49SYabin Cui
471*01826a49SYabin Cui /* Insert the current entry into the hash table --- it must be
472*01826a49SYabin Cui * done after the previous block to avoid clobbering bestEntry */
473*01826a49SYabin Cui ZSTD_ldm_insertEntry(ldmState, hash, newEntry, *params);
474*01826a49SYabin Cui
475*01826a49SYabin Cui anchor = split + forwardMatchLength;
476*01826a49SYabin Cui
477*01826a49SYabin Cui /* If we find a match that ends after the data that we've hashed
478*01826a49SYabin Cui * then we have a repeating, overlapping, pattern. E.g. all zeros.
479*01826a49SYabin Cui * If one repetition of the pattern matches our `stopMask` then all
480*01826a49SYabin Cui * repetitions will. We don't need to insert them all into out table,
481*01826a49SYabin Cui * only the first one. So skip over overlapping matches.
482*01826a49SYabin Cui * This is a major speed boost (20x) for compressing a single byte
483*01826a49SYabin Cui * repeated, when that byte ends up in the table.
484*01826a49SYabin Cui */
485*01826a49SYabin Cui if (anchor > ip + hashed) {
486*01826a49SYabin Cui ZSTD_ldm_gear_reset(&hashState, anchor - minMatchLength, minMatchLength);
487*01826a49SYabin Cui /* Continue the outer loop at anchor (ip + hashed == anchor). */
488*01826a49SYabin Cui ip = anchor - hashed;
489*01826a49SYabin Cui break;
490*01826a49SYabin Cui }
491*01826a49SYabin Cui }
492*01826a49SYabin Cui
493*01826a49SYabin Cui ip += hashed;
494*01826a49SYabin Cui }
495*01826a49SYabin Cui
496*01826a49SYabin Cui return iend - anchor;
497*01826a49SYabin Cui }
498*01826a49SYabin Cui
499*01826a49SYabin Cui /*! ZSTD_ldm_reduceTable() :
500*01826a49SYabin Cui * reduce table indexes by `reducerValue` */
ZSTD_ldm_reduceTable(ldmEntry_t * const table,U32 const size,U32 const reducerValue)501*01826a49SYabin Cui static void ZSTD_ldm_reduceTable(ldmEntry_t* const table, U32 const size,
502*01826a49SYabin Cui U32 const reducerValue)
503*01826a49SYabin Cui {
504*01826a49SYabin Cui U32 u;
505*01826a49SYabin Cui for (u = 0; u < size; u++) {
506*01826a49SYabin Cui if (table[u].offset < reducerValue) table[u].offset = 0;
507*01826a49SYabin Cui else table[u].offset -= reducerValue;
508*01826a49SYabin Cui }
509*01826a49SYabin Cui }
510*01826a49SYabin Cui
ZSTD_ldm_generateSequences(ldmState_t * ldmState,rawSeqStore_t * sequences,ldmParams_t const * params,void const * src,size_t srcSize)511*01826a49SYabin Cui size_t ZSTD_ldm_generateSequences(
512*01826a49SYabin Cui ldmState_t* ldmState, rawSeqStore_t* sequences,
513*01826a49SYabin Cui ldmParams_t const* params, void const* src, size_t srcSize)
514*01826a49SYabin Cui {
515*01826a49SYabin Cui U32 const maxDist = 1U << params->windowLog;
516*01826a49SYabin Cui BYTE const* const istart = (BYTE const*)src;
517*01826a49SYabin Cui BYTE const* const iend = istart + srcSize;
518*01826a49SYabin Cui size_t const kMaxChunkSize = 1 << 20;
519*01826a49SYabin Cui size_t const nbChunks = (srcSize / kMaxChunkSize) + ((srcSize % kMaxChunkSize) != 0);
520*01826a49SYabin Cui size_t chunk;
521*01826a49SYabin Cui size_t leftoverSize = 0;
522*01826a49SYabin Cui
523*01826a49SYabin Cui assert(ZSTD_CHUNKSIZE_MAX >= kMaxChunkSize);
524*01826a49SYabin Cui /* Check that ZSTD_window_update() has been called for this chunk prior
525*01826a49SYabin Cui * to passing it to this function.
526*01826a49SYabin Cui */
527*01826a49SYabin Cui assert(ldmState->window.nextSrc >= (BYTE const*)src + srcSize);
528*01826a49SYabin Cui /* The input could be very large (in zstdmt), so it must be broken up into
529*01826a49SYabin Cui * chunks to enforce the maximum distance and handle overflow correction.
530*01826a49SYabin Cui */
531*01826a49SYabin Cui assert(sequences->pos <= sequences->size);
532*01826a49SYabin Cui assert(sequences->size <= sequences->capacity);
533*01826a49SYabin Cui for (chunk = 0; chunk < nbChunks && sequences->size < sequences->capacity; ++chunk) {
534*01826a49SYabin Cui BYTE const* const chunkStart = istart + chunk * kMaxChunkSize;
535*01826a49SYabin Cui size_t const remaining = (size_t)(iend - chunkStart);
536*01826a49SYabin Cui BYTE const *const chunkEnd =
537*01826a49SYabin Cui (remaining < kMaxChunkSize) ? iend : chunkStart + kMaxChunkSize;
538*01826a49SYabin Cui size_t const chunkSize = chunkEnd - chunkStart;
539*01826a49SYabin Cui size_t newLeftoverSize;
540*01826a49SYabin Cui size_t const prevSize = sequences->size;
541*01826a49SYabin Cui
542*01826a49SYabin Cui assert(chunkStart < iend);
543*01826a49SYabin Cui /* 1. Perform overflow correction if necessary. */
544*01826a49SYabin Cui if (ZSTD_window_needOverflowCorrection(ldmState->window, 0, maxDist, ldmState->loadedDictEnd, chunkStart, chunkEnd)) {
545*01826a49SYabin Cui U32 const ldmHSize = 1U << params->hashLog;
546*01826a49SYabin Cui U32 const correction = ZSTD_window_correctOverflow(
547*01826a49SYabin Cui &ldmState->window, /* cycleLog */ 0, maxDist, chunkStart);
548*01826a49SYabin Cui ZSTD_ldm_reduceTable(ldmState->hashTable, ldmHSize, correction);
549*01826a49SYabin Cui /* invalidate dictionaries on overflow correction */
550*01826a49SYabin Cui ldmState->loadedDictEnd = 0;
551*01826a49SYabin Cui }
552*01826a49SYabin Cui /* 2. We enforce the maximum offset allowed.
553*01826a49SYabin Cui *
554*01826a49SYabin Cui * kMaxChunkSize should be small enough that we don't lose too much of
555*01826a49SYabin Cui * the window through early invalidation.
556*01826a49SYabin Cui * TODO: * Test the chunk size.
557*01826a49SYabin Cui * * Try invalidation after the sequence generation and test the
558*01826a49SYabin Cui * offset against maxDist directly.
559*01826a49SYabin Cui *
560*01826a49SYabin Cui * NOTE: Because of dictionaries + sequence splitting we MUST make sure
561*01826a49SYabin Cui * that any offset used is valid at the END of the sequence, since it may
562*01826a49SYabin Cui * be split into two sequences. This condition holds when using
563*01826a49SYabin Cui * ZSTD_window_enforceMaxDist(), but if we move to checking offsets
564*01826a49SYabin Cui * against maxDist directly, we'll have to carefully handle that case.
565*01826a49SYabin Cui */
566*01826a49SYabin Cui ZSTD_window_enforceMaxDist(&ldmState->window, chunkEnd, maxDist, &ldmState->loadedDictEnd, NULL);
567*01826a49SYabin Cui /* 3. Generate the sequences for the chunk, and get newLeftoverSize. */
568*01826a49SYabin Cui newLeftoverSize = ZSTD_ldm_generateSequences_internal(
569*01826a49SYabin Cui ldmState, sequences, params, chunkStart, chunkSize);
570*01826a49SYabin Cui if (ZSTD_isError(newLeftoverSize))
571*01826a49SYabin Cui return newLeftoverSize;
572*01826a49SYabin Cui /* 4. We add the leftover literals from previous iterations to the first
573*01826a49SYabin Cui * newly generated sequence, or add the `newLeftoverSize` if none are
574*01826a49SYabin Cui * generated.
575*01826a49SYabin Cui */
576*01826a49SYabin Cui /* Prepend the leftover literals from the last call */
577*01826a49SYabin Cui if (prevSize < sequences->size) {
578*01826a49SYabin Cui sequences->seq[prevSize].litLength += (U32)leftoverSize;
579*01826a49SYabin Cui leftoverSize = newLeftoverSize;
580*01826a49SYabin Cui } else {
581*01826a49SYabin Cui assert(newLeftoverSize == chunkSize);
582*01826a49SYabin Cui leftoverSize += chunkSize;
583*01826a49SYabin Cui }
584*01826a49SYabin Cui }
585*01826a49SYabin Cui return 0;
586*01826a49SYabin Cui }
587*01826a49SYabin Cui
588*01826a49SYabin Cui void
ZSTD_ldm_skipSequences(rawSeqStore_t * rawSeqStore,size_t srcSize,U32 const minMatch)589*01826a49SYabin Cui ZSTD_ldm_skipSequences(rawSeqStore_t* rawSeqStore, size_t srcSize, U32 const minMatch)
590*01826a49SYabin Cui {
591*01826a49SYabin Cui while (srcSize > 0 && rawSeqStore->pos < rawSeqStore->size) {
592*01826a49SYabin Cui rawSeq* seq = rawSeqStore->seq + rawSeqStore->pos;
593*01826a49SYabin Cui if (srcSize <= seq->litLength) {
594*01826a49SYabin Cui /* Skip past srcSize literals */
595*01826a49SYabin Cui seq->litLength -= (U32)srcSize;
596*01826a49SYabin Cui return;
597*01826a49SYabin Cui }
598*01826a49SYabin Cui srcSize -= seq->litLength;
599*01826a49SYabin Cui seq->litLength = 0;
600*01826a49SYabin Cui if (srcSize < seq->matchLength) {
601*01826a49SYabin Cui /* Skip past the first srcSize of the match */
602*01826a49SYabin Cui seq->matchLength -= (U32)srcSize;
603*01826a49SYabin Cui if (seq->matchLength < minMatch) {
604*01826a49SYabin Cui /* The match is too short, omit it */
605*01826a49SYabin Cui if (rawSeqStore->pos + 1 < rawSeqStore->size) {
606*01826a49SYabin Cui seq[1].litLength += seq[0].matchLength;
607*01826a49SYabin Cui }
608*01826a49SYabin Cui rawSeqStore->pos++;
609*01826a49SYabin Cui }
610*01826a49SYabin Cui return;
611*01826a49SYabin Cui }
612*01826a49SYabin Cui srcSize -= seq->matchLength;
613*01826a49SYabin Cui seq->matchLength = 0;
614*01826a49SYabin Cui rawSeqStore->pos++;
615*01826a49SYabin Cui }
616*01826a49SYabin Cui }
617*01826a49SYabin Cui
618*01826a49SYabin Cui /**
619*01826a49SYabin Cui * If the sequence length is longer than remaining then the sequence is split
620*01826a49SYabin Cui * between this block and the next.
621*01826a49SYabin Cui *
622*01826a49SYabin Cui * Returns the current sequence to handle, or if the rest of the block should
623*01826a49SYabin Cui * be literals, it returns a sequence with offset == 0.
624*01826a49SYabin Cui */
maybeSplitSequence(rawSeqStore_t * rawSeqStore,U32 const remaining,U32 const minMatch)625*01826a49SYabin Cui static rawSeq maybeSplitSequence(rawSeqStore_t* rawSeqStore,
626*01826a49SYabin Cui U32 const remaining, U32 const minMatch)
627*01826a49SYabin Cui {
628*01826a49SYabin Cui rawSeq sequence = rawSeqStore->seq[rawSeqStore->pos];
629*01826a49SYabin Cui assert(sequence.offset > 0);
630*01826a49SYabin Cui /* Likely: No partial sequence */
631*01826a49SYabin Cui if (remaining >= sequence.litLength + sequence.matchLength) {
632*01826a49SYabin Cui rawSeqStore->pos++;
633*01826a49SYabin Cui return sequence;
634*01826a49SYabin Cui }
635*01826a49SYabin Cui /* Cut the sequence short (offset == 0 ==> rest is literals). */
636*01826a49SYabin Cui if (remaining <= sequence.litLength) {
637*01826a49SYabin Cui sequence.offset = 0;
638*01826a49SYabin Cui } else if (remaining < sequence.litLength + sequence.matchLength) {
639*01826a49SYabin Cui sequence.matchLength = remaining - sequence.litLength;
640*01826a49SYabin Cui if (sequence.matchLength < minMatch) {
641*01826a49SYabin Cui sequence.offset = 0;
642*01826a49SYabin Cui }
643*01826a49SYabin Cui }
644*01826a49SYabin Cui /* Skip past `remaining` bytes for the future sequences. */
645*01826a49SYabin Cui ZSTD_ldm_skipSequences(rawSeqStore, remaining, minMatch);
646*01826a49SYabin Cui return sequence;
647*01826a49SYabin Cui }
648*01826a49SYabin Cui
ZSTD_ldm_skipRawSeqStoreBytes(rawSeqStore_t * rawSeqStore,size_t nbBytes)649*01826a49SYabin Cui void ZSTD_ldm_skipRawSeqStoreBytes(rawSeqStore_t* rawSeqStore, size_t nbBytes) {
650*01826a49SYabin Cui U32 currPos = (U32)(rawSeqStore->posInSequence + nbBytes);
651*01826a49SYabin Cui while (currPos && rawSeqStore->pos < rawSeqStore->size) {
652*01826a49SYabin Cui rawSeq currSeq = rawSeqStore->seq[rawSeqStore->pos];
653*01826a49SYabin Cui if (currPos >= currSeq.litLength + currSeq.matchLength) {
654*01826a49SYabin Cui currPos -= currSeq.litLength + currSeq.matchLength;
655*01826a49SYabin Cui rawSeqStore->pos++;
656*01826a49SYabin Cui } else {
657*01826a49SYabin Cui rawSeqStore->posInSequence = currPos;
658*01826a49SYabin Cui break;
659*01826a49SYabin Cui }
660*01826a49SYabin Cui }
661*01826a49SYabin Cui if (currPos == 0 || rawSeqStore->pos == rawSeqStore->size) {
662*01826a49SYabin Cui rawSeqStore->posInSequence = 0;
663*01826a49SYabin Cui }
664*01826a49SYabin Cui }
665*01826a49SYabin Cui
ZSTD_ldm_blockCompress(rawSeqStore_t * rawSeqStore,ZSTD_matchState_t * ms,seqStore_t * seqStore,U32 rep[ZSTD_REP_NUM],ZSTD_paramSwitch_e useRowMatchFinder,void const * src,size_t srcSize)666*01826a49SYabin Cui size_t ZSTD_ldm_blockCompress(rawSeqStore_t* rawSeqStore,
667*01826a49SYabin Cui ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
668*01826a49SYabin Cui ZSTD_paramSwitch_e useRowMatchFinder,
669*01826a49SYabin Cui void const* src, size_t srcSize)
670*01826a49SYabin Cui {
671*01826a49SYabin Cui const ZSTD_compressionParameters* const cParams = &ms->cParams;
672*01826a49SYabin Cui unsigned const minMatch = cParams->minMatch;
673*01826a49SYabin Cui ZSTD_blockCompressor const blockCompressor =
674*01826a49SYabin Cui ZSTD_selectBlockCompressor(cParams->strategy, useRowMatchFinder, ZSTD_matchState_dictMode(ms));
675*01826a49SYabin Cui /* Input bounds */
676*01826a49SYabin Cui BYTE const* const istart = (BYTE const*)src;
677*01826a49SYabin Cui BYTE const* const iend = istart + srcSize;
678*01826a49SYabin Cui /* Input positions */
679*01826a49SYabin Cui BYTE const* ip = istart;
680*01826a49SYabin Cui
681*01826a49SYabin Cui DEBUGLOG(5, "ZSTD_ldm_blockCompress: srcSize=%zu", srcSize);
682*01826a49SYabin Cui /* If using opt parser, use LDMs only as candidates rather than always accepting them */
683*01826a49SYabin Cui if (cParams->strategy >= ZSTD_btopt) {
684*01826a49SYabin Cui size_t lastLLSize;
685*01826a49SYabin Cui ms->ldmSeqStore = rawSeqStore;
686*01826a49SYabin Cui lastLLSize = blockCompressor(ms, seqStore, rep, src, srcSize);
687*01826a49SYabin Cui ZSTD_ldm_skipRawSeqStoreBytes(rawSeqStore, srcSize);
688*01826a49SYabin Cui return lastLLSize;
689*01826a49SYabin Cui }
690*01826a49SYabin Cui
691*01826a49SYabin Cui assert(rawSeqStore->pos <= rawSeqStore->size);
692*01826a49SYabin Cui assert(rawSeqStore->size <= rawSeqStore->capacity);
693*01826a49SYabin Cui /* Loop through each sequence and apply the block compressor to the literals */
694*01826a49SYabin Cui while (rawSeqStore->pos < rawSeqStore->size && ip < iend) {
695*01826a49SYabin Cui /* maybeSplitSequence updates rawSeqStore->pos */
696*01826a49SYabin Cui rawSeq const sequence = maybeSplitSequence(rawSeqStore,
697*01826a49SYabin Cui (U32)(iend - ip), minMatch);
698*01826a49SYabin Cui /* End signal */
699*01826a49SYabin Cui if (sequence.offset == 0)
700*01826a49SYabin Cui break;
701*01826a49SYabin Cui
702*01826a49SYabin Cui assert(ip + sequence.litLength + sequence.matchLength <= iend);
703*01826a49SYabin Cui
704*01826a49SYabin Cui /* Fill tables for block compressor */
705*01826a49SYabin Cui ZSTD_ldm_limitTableUpdate(ms, ip);
706*01826a49SYabin Cui ZSTD_ldm_fillFastTables(ms, ip);
707*01826a49SYabin Cui /* Run the block compressor */
708*01826a49SYabin Cui DEBUGLOG(5, "pos %u : calling block compressor on segment of size %u", (unsigned)(ip-istart), sequence.litLength);
709*01826a49SYabin Cui {
710*01826a49SYabin Cui int i;
711*01826a49SYabin Cui size_t const newLitLength =
712*01826a49SYabin Cui blockCompressor(ms, seqStore, rep, ip, sequence.litLength);
713*01826a49SYabin Cui ip += sequence.litLength;
714*01826a49SYabin Cui /* Update the repcodes */
715*01826a49SYabin Cui for (i = ZSTD_REP_NUM - 1; i > 0; i--)
716*01826a49SYabin Cui rep[i] = rep[i-1];
717*01826a49SYabin Cui rep[0] = sequence.offset;
718*01826a49SYabin Cui /* Store the sequence */
719*01826a49SYabin Cui ZSTD_storeSeq(seqStore, newLitLength, ip - newLitLength, iend,
720*01826a49SYabin Cui OFFSET_TO_OFFBASE(sequence.offset),
721*01826a49SYabin Cui sequence.matchLength);
722*01826a49SYabin Cui ip += sequence.matchLength;
723*01826a49SYabin Cui }
724*01826a49SYabin Cui }
725*01826a49SYabin Cui /* Fill the tables for the block compressor */
726*01826a49SYabin Cui ZSTD_ldm_limitTableUpdate(ms, ip);
727*01826a49SYabin Cui ZSTD_ldm_fillFastTables(ms, ip);
728*01826a49SYabin Cui /* Compress the last literals */
729*01826a49SYabin Cui return blockCompressor(ms, seqStore, rep, ip, iend - ip);
730*01826a49SYabin Cui }
731