10c16b537SWarner Losh /*
2*5ff13fbcSAllan Jude * Copyright (c) Yann Collet, Facebook, Inc.
30c16b537SWarner Losh * All rights reserved.
40c16b537SWarner Losh *
50c16b537SWarner Losh * This source code is licensed under both the BSD-style license (found in the
60c16b537SWarner Losh * LICENSE file in the root directory of this source tree) and the GPLv2 (found
70c16b537SWarner Losh * in the COPYING file in the root directory of this source tree).
837f1f268SConrad Meyer * You may select, at your option, one of the above-listed licenses.
90c16b537SWarner Losh */
100c16b537SWarner Losh
110c16b537SWarner Losh #include "zstd_ldm.h"
120c16b537SWarner Losh
1337f1f268SConrad Meyer #include "../common/debug.h"
14*5ff13fbcSAllan Jude #include "../common/xxhash.h"
150c16b537SWarner Losh #include "zstd_fast.h" /* ZSTD_fillHashTable() */
160c16b537SWarner Losh #include "zstd_double_fast.h" /* ZSTD_fillDoubleHashTable() */
17*5ff13fbcSAllan Jude #include "zstd_ldm_geartab.h"
180c16b537SWarner Losh
190c16b537SWarner Losh #define LDM_BUCKET_SIZE_LOG 3
200c16b537SWarner Losh #define LDM_MIN_MATCH_LENGTH 64
210c16b537SWarner Losh #define LDM_HASH_RLOG 7
22*5ff13fbcSAllan Jude
23*5ff13fbcSAllan Jude typedef struct {
24*5ff13fbcSAllan Jude U64 rolling;
25*5ff13fbcSAllan Jude U64 stopMask;
26*5ff13fbcSAllan Jude } ldmRollingHashState_t;
27*5ff13fbcSAllan Jude
28*5ff13fbcSAllan Jude /** ZSTD_ldm_gear_init():
29*5ff13fbcSAllan Jude *
30*5ff13fbcSAllan Jude * Initializes the rolling hash state such that it will honor the
31*5ff13fbcSAllan Jude * settings in params. */
ZSTD_ldm_gear_init(ldmRollingHashState_t * state,ldmParams_t const * params)32*5ff13fbcSAllan Jude static void ZSTD_ldm_gear_init(ldmRollingHashState_t* state, ldmParams_t const* params)
33*5ff13fbcSAllan Jude {
34*5ff13fbcSAllan Jude unsigned maxBitsInMask = MIN(params->minMatchLength, 64);
35*5ff13fbcSAllan Jude unsigned hashRateLog = params->hashRateLog;
36*5ff13fbcSAllan Jude
37*5ff13fbcSAllan Jude state->rolling = ~(U32)0;
38*5ff13fbcSAllan Jude
39*5ff13fbcSAllan Jude /* The choice of the splitting criterion is subject to two conditions:
40*5ff13fbcSAllan Jude * 1. it has to trigger on average every 2^(hashRateLog) bytes;
41*5ff13fbcSAllan Jude * 2. ideally, it has to depend on a window of minMatchLength bytes.
42*5ff13fbcSAllan Jude *
43*5ff13fbcSAllan Jude * In the gear hash algorithm, bit n depends on the last n bytes;
44*5ff13fbcSAllan Jude * so in order to obtain a good quality splitting criterion it is
45*5ff13fbcSAllan Jude * preferable to use bits with high weight.
46*5ff13fbcSAllan Jude *
47*5ff13fbcSAllan Jude * To match condition 1 we use a mask with hashRateLog bits set
48*5ff13fbcSAllan Jude * and, because of the previous remark, we make sure these bits
49*5ff13fbcSAllan Jude * have the highest possible weight while still respecting
50*5ff13fbcSAllan Jude * condition 2.
51*5ff13fbcSAllan Jude */
52*5ff13fbcSAllan Jude if (hashRateLog > 0 && hashRateLog <= maxBitsInMask) {
53*5ff13fbcSAllan Jude state->stopMask = (((U64)1 << hashRateLog) - 1) << (maxBitsInMask - hashRateLog);
54*5ff13fbcSAllan Jude } else {
55*5ff13fbcSAllan Jude /* In this degenerate case we simply honor the hash rate. */
56*5ff13fbcSAllan Jude state->stopMask = ((U64)1 << hashRateLog) - 1;
57*5ff13fbcSAllan Jude }
58*5ff13fbcSAllan Jude }
59*5ff13fbcSAllan Jude
60*5ff13fbcSAllan Jude /** ZSTD_ldm_gear_reset()
61*5ff13fbcSAllan Jude * Feeds [data, data + minMatchLength) into the hash without registering any
62*5ff13fbcSAllan Jude * splits. This effectively resets the hash state. This is used when skipping
63*5ff13fbcSAllan Jude * over data, either at the beginning of a block, or skipping sections.
64*5ff13fbcSAllan Jude */
ZSTD_ldm_gear_reset(ldmRollingHashState_t * state,BYTE const * data,size_t minMatchLength)65*5ff13fbcSAllan Jude static void ZSTD_ldm_gear_reset(ldmRollingHashState_t* state,
66*5ff13fbcSAllan Jude BYTE const* data, size_t minMatchLength)
67*5ff13fbcSAllan Jude {
68*5ff13fbcSAllan Jude U64 hash = state->rolling;
69*5ff13fbcSAllan Jude size_t n = 0;
70*5ff13fbcSAllan Jude
71*5ff13fbcSAllan Jude #define GEAR_ITER_ONCE() do { \
72*5ff13fbcSAllan Jude hash = (hash << 1) + ZSTD_ldm_gearTab[data[n] & 0xff]; \
73*5ff13fbcSAllan Jude n += 1; \
74*5ff13fbcSAllan Jude } while (0)
75*5ff13fbcSAllan Jude while (n + 3 < minMatchLength) {
76*5ff13fbcSAllan Jude GEAR_ITER_ONCE();
77*5ff13fbcSAllan Jude GEAR_ITER_ONCE();
78*5ff13fbcSAllan Jude GEAR_ITER_ONCE();
79*5ff13fbcSAllan Jude GEAR_ITER_ONCE();
80*5ff13fbcSAllan Jude }
81*5ff13fbcSAllan Jude while (n < minMatchLength) {
82*5ff13fbcSAllan Jude GEAR_ITER_ONCE();
83*5ff13fbcSAllan Jude }
84*5ff13fbcSAllan Jude #undef GEAR_ITER_ONCE
85*5ff13fbcSAllan Jude }
86*5ff13fbcSAllan Jude
87*5ff13fbcSAllan Jude /** ZSTD_ldm_gear_feed():
88*5ff13fbcSAllan Jude *
89*5ff13fbcSAllan Jude * Registers in the splits array all the split points found in the first
90*5ff13fbcSAllan Jude * size bytes following the data pointer. This function terminates when
91*5ff13fbcSAllan Jude * either all the data has been processed or LDM_BATCH_SIZE splits are
92*5ff13fbcSAllan Jude * present in the splits array.
93*5ff13fbcSAllan Jude *
94*5ff13fbcSAllan Jude * Precondition: The splits array must not be full.
95*5ff13fbcSAllan Jude * 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*5ff13fbcSAllan Jude static size_t ZSTD_ldm_gear_feed(ldmRollingHashState_t* state,
97*5ff13fbcSAllan Jude BYTE const* data, size_t size,
98*5ff13fbcSAllan Jude size_t* splits, unsigned* numSplits)
99*5ff13fbcSAllan Jude {
100*5ff13fbcSAllan Jude size_t n;
101*5ff13fbcSAllan Jude U64 hash, mask;
102*5ff13fbcSAllan Jude
103*5ff13fbcSAllan Jude hash = state->rolling;
104*5ff13fbcSAllan Jude mask = state->stopMask;
105*5ff13fbcSAllan Jude n = 0;
106*5ff13fbcSAllan Jude
107*5ff13fbcSAllan Jude #define GEAR_ITER_ONCE() do { \
108*5ff13fbcSAllan Jude hash = (hash << 1) + ZSTD_ldm_gearTab[data[n] & 0xff]; \
109*5ff13fbcSAllan Jude n += 1; \
110*5ff13fbcSAllan Jude if (UNLIKELY((hash & mask) == 0)) { \
111*5ff13fbcSAllan Jude splits[*numSplits] = n; \
112*5ff13fbcSAllan Jude *numSplits += 1; \
113*5ff13fbcSAllan Jude if (*numSplits == LDM_BATCH_SIZE) \
114*5ff13fbcSAllan Jude goto done; \
115*5ff13fbcSAllan Jude } \
116*5ff13fbcSAllan Jude } while (0)
117*5ff13fbcSAllan Jude
118*5ff13fbcSAllan Jude while (n + 3 < size) {
119*5ff13fbcSAllan Jude GEAR_ITER_ONCE();
120*5ff13fbcSAllan Jude GEAR_ITER_ONCE();
121*5ff13fbcSAllan Jude GEAR_ITER_ONCE();
122*5ff13fbcSAllan Jude GEAR_ITER_ONCE();
123*5ff13fbcSAllan Jude }
124*5ff13fbcSAllan Jude while (n < size) {
125*5ff13fbcSAllan Jude GEAR_ITER_ONCE();
126*5ff13fbcSAllan Jude }
127*5ff13fbcSAllan Jude
128*5ff13fbcSAllan Jude #undef GEAR_ITER_ONCE
129*5ff13fbcSAllan Jude
130*5ff13fbcSAllan Jude done:
131*5ff13fbcSAllan Jude state->rolling = hash;
132*5ff13fbcSAllan Jude return n;
133*5ff13fbcSAllan Jude }
1340c16b537SWarner Losh
ZSTD_ldm_adjustParameters(ldmParams_t * params,ZSTD_compressionParameters const * cParams)13519fcbaf1SConrad Meyer void ZSTD_ldm_adjustParameters(ldmParams_t* params,
13619fcbaf1SConrad Meyer ZSTD_compressionParameters const* cParams)
1370c16b537SWarner Losh {
1380f743729SConrad Meyer params->windowLog = cParams->windowLog;
1390c16b537SWarner Losh ZSTD_STATIC_ASSERT(LDM_BUCKET_SIZE_LOG <= ZSTD_LDM_BUCKETSIZELOG_MAX);
14019fcbaf1SConrad Meyer DEBUGLOG(4, "ZSTD_ldm_adjustParameters");
14119fcbaf1SConrad Meyer if (!params->bucketSizeLog) params->bucketSizeLog = LDM_BUCKET_SIZE_LOG;
14219fcbaf1SConrad Meyer if (!params->minMatchLength) params->minMatchLength = LDM_MIN_MATCH_LENGTH;
1430c16b537SWarner Losh if (params->hashLog == 0) {
1440f743729SConrad Meyer params->hashLog = MAX(ZSTD_HASHLOG_MIN, params->windowLog - LDM_HASH_RLOG);
1450c16b537SWarner Losh assert(params->hashLog <= ZSTD_HASHLOG_MAX);
1460c16b537SWarner Losh }
147a0483764SConrad Meyer if (params->hashRateLog == 0) {
148a0483764SConrad Meyer params->hashRateLog = params->windowLog < params->hashLog
1490f743729SConrad Meyer ? 0
1500f743729SConrad Meyer : params->windowLog - params->hashLog;
1510c16b537SWarner Losh }
1520c16b537SWarner Losh params->bucketSizeLog = MIN(params->bucketSizeLog, params->hashLog);
1530c16b537SWarner Losh }
1540c16b537SWarner Losh
ZSTD_ldm_getTableSize(ldmParams_t params)15519fcbaf1SConrad Meyer size_t ZSTD_ldm_getTableSize(ldmParams_t params)
15619fcbaf1SConrad Meyer {
15719fcbaf1SConrad Meyer size_t const ldmHSize = ((size_t)1) << params.hashLog;
15819fcbaf1SConrad Meyer size_t const ldmBucketSizeLog = MIN(params.bucketSizeLog, params.hashLog);
1599cbefe25SConrad Meyer size_t const ldmBucketSize = ((size_t)1) << (params.hashLog - ldmBucketSizeLog);
1609cbefe25SConrad Meyer size_t const totalSize = ZSTD_cwksp_alloc_size(ldmBucketSize)
1619cbefe25SConrad Meyer + ZSTD_cwksp_alloc_size(ldmHSize * sizeof(ldmEntry_t));
162*5ff13fbcSAllan Jude return params.enableLdm == ZSTD_ps_enable ? totalSize : 0;
16319fcbaf1SConrad Meyer }
16419fcbaf1SConrad Meyer
ZSTD_ldm_getMaxNbSeq(ldmParams_t params,size_t maxChunkSize)16519fcbaf1SConrad Meyer size_t ZSTD_ldm_getMaxNbSeq(ldmParams_t params, size_t maxChunkSize)
16619fcbaf1SConrad Meyer {
167*5ff13fbcSAllan Jude return params.enableLdm == ZSTD_ps_enable ? (maxChunkSize / params.minMatchLength) : 0;
1680c16b537SWarner Losh }
1690c16b537SWarner Losh
1700c16b537SWarner Losh /** ZSTD_ldm_getBucket() :
1710c16b537SWarner Losh * 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)1720c16b537SWarner Losh static ldmEntry_t* ZSTD_ldm_getBucket(
1730c16b537SWarner Losh ldmState_t* ldmState, size_t hash, ldmParams_t const ldmParams)
1740c16b537SWarner Losh {
1750c16b537SWarner Losh return ldmState->hashTable + (hash << ldmParams.bucketSizeLog);
1760c16b537SWarner Losh }
1770c16b537SWarner Losh
1780c16b537SWarner Losh /** ZSTD_ldm_insertEntry() :
1790c16b537SWarner Losh * 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)1800c16b537SWarner Losh static void ZSTD_ldm_insertEntry(ldmState_t* ldmState,
1810c16b537SWarner Losh size_t const hash, const ldmEntry_t entry,
1820c16b537SWarner Losh ldmParams_t const ldmParams)
1830c16b537SWarner Losh {
184*5ff13fbcSAllan Jude BYTE* const pOffset = ldmState->bucketOffsets + hash;
185*5ff13fbcSAllan Jude unsigned const offset = *pOffset;
1860c16b537SWarner Losh
187*5ff13fbcSAllan Jude *(ZSTD_ldm_getBucket(ldmState, hash, ldmParams) + offset) = entry;
188*5ff13fbcSAllan Jude *pOffset = (BYTE)((offset + 1) & ((1u << ldmParams.bucketSizeLog) - 1));
189*5ff13fbcSAllan Jude
1900c16b537SWarner Losh }
1910c16b537SWarner Losh
1920c16b537SWarner Losh /** ZSTD_ldm_countBackwardsMatch() :
1930c16b537SWarner Losh * Returns the number of bytes that match backwards before pIn and pMatch.
1940c16b537SWarner Losh *
1950c16b537SWarner Losh * 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)1960c16b537SWarner Losh static size_t ZSTD_ldm_countBackwardsMatch(
1970c16b537SWarner Losh const BYTE* pIn, const BYTE* pAnchor,
198f7cd7fe5SConrad Meyer const BYTE* pMatch, const BYTE* pMatchBase)
1990c16b537SWarner Losh {
2000c16b537SWarner Losh size_t matchLength = 0;
201f7cd7fe5SConrad Meyer while (pIn > pAnchor && pMatch > pMatchBase && pIn[-1] == pMatch[-1]) {
2020c16b537SWarner Losh pIn--;
2030c16b537SWarner Losh pMatch--;
2040c16b537SWarner Losh matchLength++;
2050c16b537SWarner Losh }
2060c16b537SWarner Losh return matchLength;
2070c16b537SWarner Losh }
2080c16b537SWarner Losh
209f7cd7fe5SConrad Meyer /** ZSTD_ldm_countBackwardsMatch_2segments() :
210f7cd7fe5SConrad Meyer * Returns the number of bytes that match backwards from pMatch,
211f7cd7fe5SConrad Meyer * even with the backwards match spanning 2 different segments.
212f7cd7fe5SConrad Meyer *
213f7cd7fe5SConrad Meyer * 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)214f7cd7fe5SConrad Meyer static size_t ZSTD_ldm_countBackwardsMatch_2segments(
215f7cd7fe5SConrad Meyer const BYTE* pIn, const BYTE* pAnchor,
216f7cd7fe5SConrad Meyer const BYTE* pMatch, const BYTE* pMatchBase,
217f7cd7fe5SConrad Meyer const BYTE* pExtDictStart, const BYTE* pExtDictEnd)
218f7cd7fe5SConrad Meyer {
219f7cd7fe5SConrad Meyer size_t matchLength = ZSTD_ldm_countBackwardsMatch(pIn, pAnchor, pMatch, pMatchBase);
220f7cd7fe5SConrad Meyer if (pMatch - matchLength != pMatchBase || pMatchBase == pExtDictStart) {
221f7cd7fe5SConrad Meyer /* If backwards match is entirely in the extDict or prefix, immediately return */
222f7cd7fe5SConrad Meyer return matchLength;
223f7cd7fe5SConrad Meyer }
224f7cd7fe5SConrad Meyer DEBUGLOG(7, "ZSTD_ldm_countBackwardsMatch_2segments: found 2-parts backwards match (length in prefix==%zu)", matchLength);
225f7cd7fe5SConrad Meyer matchLength += ZSTD_ldm_countBackwardsMatch(pIn - matchLength, pAnchor, pExtDictEnd, pExtDictStart);
226f7cd7fe5SConrad Meyer DEBUGLOG(7, "final backwards match length = %zu", matchLength);
227f7cd7fe5SConrad Meyer return matchLength;
228f7cd7fe5SConrad Meyer }
229f7cd7fe5SConrad Meyer
2300c16b537SWarner Losh /** ZSTD_ldm_fillFastTables() :
2310c16b537SWarner Losh *
2320c16b537SWarner Losh * Fills the relevant tables for the ZSTD_fast and ZSTD_dfast strategies.
2330c16b537SWarner Losh * This is similar to ZSTD_loadDictionaryContent.
2340c16b537SWarner Losh *
2350c16b537SWarner Losh * The tables for the other strategies are filled within their
2360c16b537SWarner Losh * block compressors. */
ZSTD_ldm_fillFastTables(ZSTD_matchState_t * ms,void const * end)23719fcbaf1SConrad Meyer static size_t ZSTD_ldm_fillFastTables(ZSTD_matchState_t* ms,
23819fcbaf1SConrad Meyer void const* end)
2390c16b537SWarner Losh {
2400c16b537SWarner Losh const BYTE* const iend = (const BYTE*)end;
2410c16b537SWarner Losh
2420f743729SConrad Meyer switch(ms->cParams.strategy)
2430c16b537SWarner Losh {
2440c16b537SWarner Losh case ZSTD_fast:
2450f743729SConrad Meyer ZSTD_fillHashTable(ms, iend, ZSTD_dtlm_fast);
2460c16b537SWarner Losh break;
2470c16b537SWarner Losh
2480c16b537SWarner Losh case ZSTD_dfast:
2490f743729SConrad Meyer ZSTD_fillDoubleHashTable(ms, iend, ZSTD_dtlm_fast);
2500c16b537SWarner Losh break;
2510c16b537SWarner Losh
2520c16b537SWarner Losh case ZSTD_greedy:
2530c16b537SWarner Losh case ZSTD_lazy:
2540c16b537SWarner Losh case ZSTD_lazy2:
2550c16b537SWarner Losh case ZSTD_btlazy2:
2560c16b537SWarner Losh case ZSTD_btopt:
2570c16b537SWarner Losh case ZSTD_btultra:
258a0483764SConrad Meyer case ZSTD_btultra2:
2590c16b537SWarner Losh break;
2600c16b537SWarner Losh default:
2610c16b537SWarner Losh assert(0); /* not possible : not a valid strategy id */
2620c16b537SWarner Losh }
2630c16b537SWarner Losh
2640c16b537SWarner Losh return 0;
2650c16b537SWarner Losh }
2660c16b537SWarner Losh
ZSTD_ldm_fillHashTable(ldmState_t * ldmState,const BYTE * ip,const BYTE * iend,ldmParams_t const * params)26737f1f268SConrad Meyer void ZSTD_ldm_fillHashTable(
268*5ff13fbcSAllan Jude ldmState_t* ldmState, const BYTE* ip,
26937f1f268SConrad Meyer const BYTE* iend, ldmParams_t const* params)
27037f1f268SConrad Meyer {
271*5ff13fbcSAllan Jude U32 const minMatchLength = params->minMatchLength;
272*5ff13fbcSAllan Jude U32 const hBits = params->hashLog - params->bucketSizeLog;
273*5ff13fbcSAllan Jude BYTE const* const base = ldmState->window.base;
274*5ff13fbcSAllan Jude BYTE const* const istart = ip;
275*5ff13fbcSAllan Jude ldmRollingHashState_t hashState;
276*5ff13fbcSAllan Jude size_t* const splits = ldmState->splitIndices;
277*5ff13fbcSAllan Jude unsigned numSplits;
278*5ff13fbcSAllan Jude
27937f1f268SConrad Meyer DEBUGLOG(5, "ZSTD_ldm_fillHashTable");
280*5ff13fbcSAllan Jude
281*5ff13fbcSAllan Jude ZSTD_ldm_gear_init(&hashState, params);
282*5ff13fbcSAllan Jude while (ip < iend) {
283*5ff13fbcSAllan Jude size_t hashed;
284*5ff13fbcSAllan Jude unsigned n;
285*5ff13fbcSAllan Jude
286*5ff13fbcSAllan Jude numSplits = 0;
287*5ff13fbcSAllan Jude hashed = ZSTD_ldm_gear_feed(&hashState, ip, iend - ip, splits, &numSplits);
288*5ff13fbcSAllan Jude
289*5ff13fbcSAllan Jude for (n = 0; n < numSplits; n++) {
290*5ff13fbcSAllan Jude if (ip + splits[n] >= istart + minMatchLength) {
291*5ff13fbcSAllan Jude BYTE const* const split = ip + splits[n] - minMatchLength;
292*5ff13fbcSAllan Jude U64 const xxhash = XXH64(split, minMatchLength, 0);
293*5ff13fbcSAllan Jude U32 const hash = (U32)(xxhash & (((U32)1 << hBits) - 1));
294*5ff13fbcSAllan Jude ldmEntry_t entry;
295*5ff13fbcSAllan Jude
296*5ff13fbcSAllan Jude entry.offset = (U32)(split - base);
297*5ff13fbcSAllan Jude entry.checksum = (U32)(xxhash >> 32);
298*5ff13fbcSAllan Jude ZSTD_ldm_insertEntry(ldmState, hash, entry, *params);
299*5ff13fbcSAllan Jude }
300*5ff13fbcSAllan Jude }
301*5ff13fbcSAllan Jude
302*5ff13fbcSAllan Jude ip += hashed;
30337f1f268SConrad Meyer }
30437f1f268SConrad Meyer }
30537f1f268SConrad Meyer
3060c16b537SWarner Losh
3070c16b537SWarner Losh /** ZSTD_ldm_limitTableUpdate() :
3080c16b537SWarner Losh *
3090c16b537SWarner Losh * Sets cctx->nextToUpdate to a position corresponding closer to anchor
3100c16b537SWarner Losh * if it is far way
3110c16b537SWarner Losh * (after a long match, only update tables a limited amount). */
ZSTD_ldm_limitTableUpdate(ZSTD_matchState_t * ms,const BYTE * anchor)31219fcbaf1SConrad Meyer static void ZSTD_ldm_limitTableUpdate(ZSTD_matchState_t* ms, const BYTE* anchor)
3130c16b537SWarner Losh {
314f7cd7fe5SConrad Meyer U32 const curr = (U32)(anchor - ms->window.base);
315f7cd7fe5SConrad Meyer if (curr > ms->nextToUpdate + 1024) {
31619fcbaf1SConrad Meyer ms->nextToUpdate =
317f7cd7fe5SConrad Meyer curr - MIN(512, curr - ms->nextToUpdate - 1024);
3180c16b537SWarner Losh }
3190c16b537SWarner Losh }
3200c16b537SWarner Losh
ZSTD_ldm_generateSequences_internal(ldmState_t * ldmState,rawSeqStore_t * rawSeqStore,ldmParams_t const * params,void const * src,size_t srcSize)32119fcbaf1SConrad Meyer static size_t ZSTD_ldm_generateSequences_internal(
32219fcbaf1SConrad Meyer ldmState_t* ldmState, rawSeqStore_t* rawSeqStore,
32319fcbaf1SConrad Meyer ldmParams_t const* params, void const* src, size_t srcSize)
3240c16b537SWarner Losh {
32519fcbaf1SConrad Meyer /* LDM parameters */
32619fcbaf1SConrad Meyer int const extDict = ZSTD_window_hasExtDict(ldmState->window);
32719fcbaf1SConrad Meyer U32 const minMatchLength = params->minMatchLength;
328*5ff13fbcSAllan Jude U32 const entsPerBucket = 1U << params->bucketSizeLog;
32919fcbaf1SConrad Meyer U32 const hBits = params->hashLog - params->bucketSizeLog;
33019fcbaf1SConrad Meyer /* Prefix and extDict parameters */
33119fcbaf1SConrad Meyer U32 const dictLimit = ldmState->window.dictLimit;
33219fcbaf1SConrad Meyer U32 const lowestIndex = extDict ? ldmState->window.lowLimit : dictLimit;
33319fcbaf1SConrad Meyer BYTE const* const base = ldmState->window.base;
33419fcbaf1SConrad Meyer BYTE const* const dictBase = extDict ? ldmState->window.dictBase : NULL;
33519fcbaf1SConrad Meyer BYTE const* const dictStart = extDict ? dictBase + lowestIndex : NULL;
33619fcbaf1SConrad Meyer BYTE const* const dictEnd = extDict ? dictBase + dictLimit : NULL;
33719fcbaf1SConrad Meyer BYTE const* const lowPrefixPtr = base + dictLimit;
33819fcbaf1SConrad Meyer /* Input bounds */
33919fcbaf1SConrad Meyer BYTE const* const istart = (BYTE const*)src;
34019fcbaf1SConrad Meyer BYTE const* const iend = istart + srcSize;
341*5ff13fbcSAllan Jude BYTE const* const ilimit = iend - HASH_READ_SIZE;
34219fcbaf1SConrad Meyer /* Input positions */
34319fcbaf1SConrad Meyer BYTE const* anchor = istart;
34419fcbaf1SConrad Meyer BYTE const* ip = istart;
345*5ff13fbcSAllan Jude /* Rolling hash state */
346*5ff13fbcSAllan Jude ldmRollingHashState_t hashState;
347*5ff13fbcSAllan Jude /* Arrays for staged-processing */
348*5ff13fbcSAllan Jude size_t* const splits = ldmState->splitIndices;
349*5ff13fbcSAllan Jude ldmMatchCandidate_t* const candidates = ldmState->matchCandidates;
350*5ff13fbcSAllan Jude unsigned numSplits;
3510c16b537SWarner Losh
352*5ff13fbcSAllan Jude if (srcSize < minMatchLength)
353*5ff13fbcSAllan Jude return iend - anchor;
354*5ff13fbcSAllan Jude
355*5ff13fbcSAllan Jude /* Initialize the rolling hash state with the first minMatchLength bytes */
356*5ff13fbcSAllan Jude ZSTD_ldm_gear_init(&hashState, params);
357*5ff13fbcSAllan Jude ZSTD_ldm_gear_reset(&hashState, ip, minMatchLength);
358*5ff13fbcSAllan Jude ip += minMatchLength;
359*5ff13fbcSAllan Jude
360*5ff13fbcSAllan Jude while (ip < ilimit) {
361*5ff13fbcSAllan Jude size_t hashed;
362*5ff13fbcSAllan Jude unsigned n;
363*5ff13fbcSAllan Jude
364*5ff13fbcSAllan Jude numSplits = 0;
365*5ff13fbcSAllan Jude hashed = ZSTD_ldm_gear_feed(&hashState, ip, ilimit - ip,
366*5ff13fbcSAllan Jude splits, &numSplits);
367*5ff13fbcSAllan Jude
368*5ff13fbcSAllan Jude for (n = 0; n < numSplits; n++) {
369*5ff13fbcSAllan Jude BYTE const* const split = ip + splits[n] - minMatchLength;
370*5ff13fbcSAllan Jude U64 const xxhash = XXH64(split, minMatchLength, 0);
371*5ff13fbcSAllan Jude U32 const hash = (U32)(xxhash & (((U32)1 << hBits) - 1));
372*5ff13fbcSAllan Jude
373*5ff13fbcSAllan Jude candidates[n].split = split;
374*5ff13fbcSAllan Jude candidates[n].hash = hash;
375*5ff13fbcSAllan Jude candidates[n].checksum = (U32)(xxhash >> 32);
376*5ff13fbcSAllan Jude candidates[n].bucket = ZSTD_ldm_getBucket(ldmState, hash, *params);
377*5ff13fbcSAllan Jude PREFETCH_L1(candidates[n].bucket);
3780c16b537SWarner Losh }
3790c16b537SWarner Losh
380*5ff13fbcSAllan Jude for (n = 0; n < numSplits; n++) {
381*5ff13fbcSAllan Jude size_t forwardMatchLength = 0, backwardMatchLength = 0,
382*5ff13fbcSAllan Jude bestMatchLength = 0, mLength;
383*5ff13fbcSAllan Jude U32 offset;
384*5ff13fbcSAllan Jude BYTE const* const split = candidates[n].split;
385*5ff13fbcSAllan Jude U32 const checksum = candidates[n].checksum;
386*5ff13fbcSAllan Jude U32 const hash = candidates[n].hash;
387*5ff13fbcSAllan Jude ldmEntry_t* const bucket = candidates[n].bucket;
388*5ff13fbcSAllan Jude ldmEntry_t const* cur;
389*5ff13fbcSAllan Jude ldmEntry_t const* bestEntry = NULL;
390*5ff13fbcSAllan Jude ldmEntry_t newEntry;
391*5ff13fbcSAllan Jude
392*5ff13fbcSAllan Jude newEntry.offset = (U32)(split - base);
393*5ff13fbcSAllan Jude newEntry.checksum = checksum;
394*5ff13fbcSAllan Jude
395*5ff13fbcSAllan Jude /* If a split point would generate a sequence overlapping with
396*5ff13fbcSAllan Jude * the previous one, we merely register it in the hash table and
397*5ff13fbcSAllan Jude * move on */
398*5ff13fbcSAllan Jude if (split < anchor) {
399*5ff13fbcSAllan Jude ZSTD_ldm_insertEntry(ldmState, hash, newEntry, *params);
4000c16b537SWarner Losh continue;
4010c16b537SWarner Losh }
4020c16b537SWarner Losh
403*5ff13fbcSAllan Jude for (cur = bucket; cur < bucket + entsPerBucket; cur++) {
4040c16b537SWarner Losh size_t curForwardMatchLength, curBackwardMatchLength,
4050c16b537SWarner Losh curTotalMatchLength;
4060c16b537SWarner Losh if (cur->checksum != checksum || cur->offset <= lowestIndex) {
4070c16b537SWarner Losh continue;
4080c16b537SWarner Losh }
40919fcbaf1SConrad Meyer if (extDict) {
41019fcbaf1SConrad Meyer BYTE const* const curMatchBase =
41119fcbaf1SConrad Meyer cur->offset < dictLimit ? dictBase : base;
41219fcbaf1SConrad Meyer BYTE const* const pMatch = curMatchBase + cur->offset;
41319fcbaf1SConrad Meyer BYTE const* const matchEnd =
41419fcbaf1SConrad Meyer cur->offset < dictLimit ? dictEnd : iend;
41519fcbaf1SConrad Meyer BYTE const* const lowMatchPtr =
41619fcbaf1SConrad Meyer cur->offset < dictLimit ? dictStart : lowPrefixPtr;
417*5ff13fbcSAllan Jude curForwardMatchLength =
418*5ff13fbcSAllan Jude ZSTD_count_2segments(split, pMatch, iend, matchEnd, lowPrefixPtr);
41919fcbaf1SConrad Meyer if (curForwardMatchLength < minMatchLength) {
4200c16b537SWarner Losh continue;
4210c16b537SWarner Losh }
422*5ff13fbcSAllan Jude curBackwardMatchLength = ZSTD_ldm_countBackwardsMatch_2segments(
423*5ff13fbcSAllan Jude split, anchor, pMatch, lowMatchPtr, dictStart, dictEnd);
42419fcbaf1SConrad Meyer } else { /* !extDict */
42519fcbaf1SConrad Meyer BYTE const* const pMatch = base + cur->offset;
426*5ff13fbcSAllan Jude curForwardMatchLength = ZSTD_count(split, pMatch, iend);
42719fcbaf1SConrad Meyer if (curForwardMatchLength < minMatchLength) {
42819fcbaf1SConrad Meyer continue;
42919fcbaf1SConrad Meyer }
43019fcbaf1SConrad Meyer curBackwardMatchLength =
431*5ff13fbcSAllan Jude ZSTD_ldm_countBackwardsMatch(split, anchor, pMatch, lowPrefixPtr);
43219fcbaf1SConrad Meyer }
433*5ff13fbcSAllan Jude curTotalMatchLength = curForwardMatchLength + curBackwardMatchLength;
4340c16b537SWarner Losh
4350c16b537SWarner Losh if (curTotalMatchLength > bestMatchLength) {
4360c16b537SWarner Losh bestMatchLength = curTotalMatchLength;
4370c16b537SWarner Losh forwardMatchLength = curForwardMatchLength;
4380c16b537SWarner Losh backwardMatchLength = curBackwardMatchLength;
4390c16b537SWarner Losh bestEntry = cur;
4400c16b537SWarner Losh }
4410c16b537SWarner Losh }
4420c16b537SWarner Losh
443*5ff13fbcSAllan Jude /* No match found -- insert an entry into the hash table
444*5ff13fbcSAllan Jude * and process the next candidate match */
4450c16b537SWarner Losh if (bestEntry == NULL) {
446*5ff13fbcSAllan Jude ZSTD_ldm_insertEntry(ldmState, hash, newEntry, *params);
4470c16b537SWarner Losh continue;
4480c16b537SWarner Losh }
4490c16b537SWarner Losh
4500c16b537SWarner Losh /* Match found */
451*5ff13fbcSAllan Jude offset = (U32)(split - base) - bestEntry->offset;
4520c16b537SWarner Losh mLength = forwardMatchLength + backwardMatchLength;
4530c16b537SWarner Losh {
45419fcbaf1SConrad Meyer rawSeq* const seq = rawSeqStore->seq + rawSeqStore->size;
4550c16b537SWarner Losh
45619fcbaf1SConrad Meyer /* Out of sequence storage */
45719fcbaf1SConrad Meyer if (rawSeqStore->size == rawSeqStore->capacity)
45819fcbaf1SConrad Meyer return ERROR(dstSize_tooSmall);
459*5ff13fbcSAllan Jude seq->litLength = (U32)(split - backwardMatchLength - anchor);
46019fcbaf1SConrad Meyer seq->matchLength = (U32)mLength;
46119fcbaf1SConrad Meyer seq->offset = offset;
46219fcbaf1SConrad Meyer rawSeqStore->size++;
4630c16b537SWarner Losh }
4640c16b537SWarner Losh
465*5ff13fbcSAllan Jude /* Insert the current entry into the hash table --- it must be
466*5ff13fbcSAllan Jude * done after the previous block to avoid clobbering bestEntry */
467*5ff13fbcSAllan Jude ZSTD_ldm_insertEntry(ldmState, hash, newEntry, *params);
4680c16b537SWarner Losh
469*5ff13fbcSAllan Jude anchor = split + forwardMatchLength;
4700c16b537SWarner Losh
471*5ff13fbcSAllan Jude /* If we find a match that ends after the data that we've hashed
472*5ff13fbcSAllan Jude * then we have a repeating, overlapping, pattern. E.g. all zeros.
473*5ff13fbcSAllan Jude * If one repetition of the pattern matches our `stopMask` then all
474*5ff13fbcSAllan Jude * repetitions will. We don't need to insert them all into out table,
475*5ff13fbcSAllan Jude * only the first one. So skip over overlapping matches.
476*5ff13fbcSAllan Jude * This is a major speed boost (20x) for compressing a single byte
477*5ff13fbcSAllan Jude * repeated, when that byte ends up in the table.
478*5ff13fbcSAllan Jude */
479*5ff13fbcSAllan Jude if (anchor > ip + hashed) {
480*5ff13fbcSAllan Jude ZSTD_ldm_gear_reset(&hashState, anchor - minMatchLength, minMatchLength);
481*5ff13fbcSAllan Jude /* Continue the outer loop at anchor (ip + hashed == anchor). */
482*5ff13fbcSAllan Jude ip = anchor - hashed;
483*5ff13fbcSAllan Jude break;
4840c16b537SWarner Losh }
48519fcbaf1SConrad Meyer }
486*5ff13fbcSAllan Jude
487*5ff13fbcSAllan Jude ip += hashed;
488*5ff13fbcSAllan Jude }
489*5ff13fbcSAllan Jude
49019fcbaf1SConrad Meyer return iend - anchor;
49119fcbaf1SConrad Meyer }
4920c16b537SWarner Losh
49319fcbaf1SConrad Meyer /*! ZSTD_ldm_reduceTable() :
49419fcbaf1SConrad Meyer * reduce table indexes by `reducerValue` */
ZSTD_ldm_reduceTable(ldmEntry_t * const table,U32 const size,U32 const reducerValue)49519fcbaf1SConrad Meyer static void ZSTD_ldm_reduceTable(ldmEntry_t* const table, U32 const size,
49619fcbaf1SConrad Meyer U32 const reducerValue)
4970c16b537SWarner Losh {
49819fcbaf1SConrad Meyer U32 u;
49919fcbaf1SConrad Meyer for (u = 0; u < size; u++) {
50019fcbaf1SConrad Meyer if (table[u].offset < reducerValue) table[u].offset = 0;
50119fcbaf1SConrad Meyer else table[u].offset -= reducerValue;
5020c16b537SWarner Losh }
5030c16b537SWarner Losh }
5040c16b537SWarner Losh
ZSTD_ldm_generateSequences(ldmState_t * ldmState,rawSeqStore_t * sequences,ldmParams_t const * params,void const * src,size_t srcSize)50519fcbaf1SConrad Meyer size_t ZSTD_ldm_generateSequences(
50619fcbaf1SConrad Meyer ldmState_t* ldmState, rawSeqStore_t* sequences,
50719fcbaf1SConrad Meyer ldmParams_t const* params, void const* src, size_t srcSize)
5080c16b537SWarner Losh {
50919fcbaf1SConrad Meyer U32 const maxDist = 1U << params->windowLog;
51019fcbaf1SConrad Meyer BYTE const* const istart = (BYTE const*)src;
51119fcbaf1SConrad Meyer BYTE const* const iend = istart + srcSize;
51219fcbaf1SConrad Meyer size_t const kMaxChunkSize = 1 << 20;
51319fcbaf1SConrad Meyer size_t const nbChunks = (srcSize / kMaxChunkSize) + ((srcSize % kMaxChunkSize) != 0);
51419fcbaf1SConrad Meyer size_t chunk;
51519fcbaf1SConrad Meyer size_t leftoverSize = 0;
51619fcbaf1SConrad Meyer
51719fcbaf1SConrad Meyer assert(ZSTD_CHUNKSIZE_MAX >= kMaxChunkSize);
51819fcbaf1SConrad Meyer /* Check that ZSTD_window_update() has been called for this chunk prior
51919fcbaf1SConrad Meyer * to passing it to this function.
52019fcbaf1SConrad Meyer */
52119fcbaf1SConrad Meyer assert(ldmState->window.nextSrc >= (BYTE const*)src + srcSize);
52219fcbaf1SConrad Meyer /* The input could be very large (in zstdmt), so it must be broken up into
5232b9c00cbSConrad Meyer * chunks to enforce the maximum distance and handle overflow correction.
52419fcbaf1SConrad Meyer */
52519fcbaf1SConrad Meyer assert(sequences->pos <= sequences->size);
52619fcbaf1SConrad Meyer assert(sequences->size <= sequences->capacity);
52719fcbaf1SConrad Meyer for (chunk = 0; chunk < nbChunks && sequences->size < sequences->capacity; ++chunk) {
52819fcbaf1SConrad Meyer BYTE const* const chunkStart = istart + chunk * kMaxChunkSize;
52919fcbaf1SConrad Meyer size_t const remaining = (size_t)(iend - chunkStart);
53019fcbaf1SConrad Meyer BYTE const *const chunkEnd =
53119fcbaf1SConrad Meyer (remaining < kMaxChunkSize) ? iend : chunkStart + kMaxChunkSize;
53219fcbaf1SConrad Meyer size_t const chunkSize = chunkEnd - chunkStart;
53319fcbaf1SConrad Meyer size_t newLeftoverSize;
53419fcbaf1SConrad Meyer size_t const prevSize = sequences->size;
53519fcbaf1SConrad Meyer
53619fcbaf1SConrad Meyer assert(chunkStart < iend);
53719fcbaf1SConrad Meyer /* 1. Perform overflow correction if necessary. */
538*5ff13fbcSAllan Jude if (ZSTD_window_needOverflowCorrection(ldmState->window, 0, maxDist, ldmState->loadedDictEnd, chunkStart, chunkEnd)) {
53919fcbaf1SConrad Meyer U32 const ldmHSize = 1U << params->hashLog;
54019fcbaf1SConrad Meyer U32 const correction = ZSTD_window_correctOverflow(
5414d3f1eafSConrad Meyer &ldmState->window, /* cycleLog */ 0, maxDist, chunkStart);
54219fcbaf1SConrad Meyer ZSTD_ldm_reduceTable(ldmState->hashTable, ldmHSize, correction);
54337f1f268SConrad Meyer /* invalidate dictionaries on overflow correction */
54437f1f268SConrad Meyer ldmState->loadedDictEnd = 0;
5450c16b537SWarner Losh }
54619fcbaf1SConrad Meyer /* 2. We enforce the maximum offset allowed.
54719fcbaf1SConrad Meyer *
54819fcbaf1SConrad Meyer * kMaxChunkSize should be small enough that we don't lose too much of
54919fcbaf1SConrad Meyer * the window through early invalidation.
55019fcbaf1SConrad Meyer * TODO: * Test the chunk size.
55119fcbaf1SConrad Meyer * * Try invalidation after the sequence generation and test the
55219fcbaf1SConrad Meyer * the offset against maxDist directly.
55337f1f268SConrad Meyer *
55437f1f268SConrad Meyer * NOTE: Because of dictionaries + sequence splitting we MUST make sure
55537f1f268SConrad Meyer * that any offset used is valid at the END of the sequence, since it may
55637f1f268SConrad Meyer * be split into two sequences. This condition holds when using
55737f1f268SConrad Meyer * ZSTD_window_enforceMaxDist(), but if we move to checking offsets
55837f1f268SConrad Meyer * against maxDist directly, we'll have to carefully handle that case.
55919fcbaf1SConrad Meyer */
56037f1f268SConrad Meyer ZSTD_window_enforceMaxDist(&ldmState->window, chunkEnd, maxDist, &ldmState->loadedDictEnd, NULL);
56119fcbaf1SConrad Meyer /* 3. Generate the sequences for the chunk, and get newLeftoverSize. */
56219fcbaf1SConrad Meyer newLeftoverSize = ZSTD_ldm_generateSequences_internal(
56319fcbaf1SConrad Meyer ldmState, sequences, params, chunkStart, chunkSize);
56419fcbaf1SConrad Meyer if (ZSTD_isError(newLeftoverSize))
56519fcbaf1SConrad Meyer return newLeftoverSize;
56619fcbaf1SConrad Meyer /* 4. We add the leftover literals from previous iterations to the first
56719fcbaf1SConrad Meyer * newly generated sequence, or add the `newLeftoverSize` if none are
56819fcbaf1SConrad Meyer * generated.
56919fcbaf1SConrad Meyer */
57019fcbaf1SConrad Meyer /* Prepend the leftover literals from the last call */
57119fcbaf1SConrad Meyer if (prevSize < sequences->size) {
57219fcbaf1SConrad Meyer sequences->seq[prevSize].litLength += (U32)leftoverSize;
57319fcbaf1SConrad Meyer leftoverSize = newLeftoverSize;
5740c16b537SWarner Losh } else {
57519fcbaf1SConrad Meyer assert(newLeftoverSize == chunkSize);
57619fcbaf1SConrad Meyer leftoverSize += chunkSize;
5770c16b537SWarner Losh }
57819fcbaf1SConrad Meyer }
57919fcbaf1SConrad Meyer return 0;
5800c16b537SWarner Losh }
5810c16b537SWarner Losh
582*5ff13fbcSAllan Jude void
ZSTD_ldm_skipSequences(rawSeqStore_t * rawSeqStore,size_t srcSize,U32 const minMatch)583*5ff13fbcSAllan Jude ZSTD_ldm_skipSequences(rawSeqStore_t* rawSeqStore, size_t srcSize, U32 const minMatch)
584*5ff13fbcSAllan Jude {
58519fcbaf1SConrad Meyer while (srcSize > 0 && rawSeqStore->pos < rawSeqStore->size) {
58619fcbaf1SConrad Meyer rawSeq* seq = rawSeqStore->seq + rawSeqStore->pos;
58719fcbaf1SConrad Meyer if (srcSize <= seq->litLength) {
58819fcbaf1SConrad Meyer /* Skip past srcSize literals */
58919fcbaf1SConrad Meyer seq->litLength -= (U32)srcSize;
59019fcbaf1SConrad Meyer return;
59119fcbaf1SConrad Meyer }
59219fcbaf1SConrad Meyer srcSize -= seq->litLength;
59319fcbaf1SConrad Meyer seq->litLength = 0;
59419fcbaf1SConrad Meyer if (srcSize < seq->matchLength) {
59519fcbaf1SConrad Meyer /* Skip past the first srcSize of the match */
59619fcbaf1SConrad Meyer seq->matchLength -= (U32)srcSize;
59719fcbaf1SConrad Meyer if (seq->matchLength < minMatch) {
59819fcbaf1SConrad Meyer /* The match is too short, omit it */
59919fcbaf1SConrad Meyer if (rawSeqStore->pos + 1 < rawSeqStore->size) {
60019fcbaf1SConrad Meyer seq[1].litLength += seq[0].matchLength;
60119fcbaf1SConrad Meyer }
60219fcbaf1SConrad Meyer rawSeqStore->pos++;
60319fcbaf1SConrad Meyer }
60419fcbaf1SConrad Meyer return;
60519fcbaf1SConrad Meyer }
60619fcbaf1SConrad Meyer srcSize -= seq->matchLength;
60719fcbaf1SConrad Meyer seq->matchLength = 0;
60819fcbaf1SConrad Meyer rawSeqStore->pos++;
60919fcbaf1SConrad Meyer }
61019fcbaf1SConrad Meyer }
61119fcbaf1SConrad Meyer
61219fcbaf1SConrad Meyer /**
61319fcbaf1SConrad Meyer * If the sequence length is longer than remaining then the sequence is split
61419fcbaf1SConrad Meyer * between this block and the next.
61519fcbaf1SConrad Meyer *
61619fcbaf1SConrad Meyer * Returns the current sequence to handle, or if the rest of the block should
61719fcbaf1SConrad Meyer * be literals, it returns a sequence with offset == 0.
61819fcbaf1SConrad Meyer */
maybeSplitSequence(rawSeqStore_t * rawSeqStore,U32 const remaining,U32 const minMatch)61919fcbaf1SConrad Meyer static rawSeq maybeSplitSequence(rawSeqStore_t* rawSeqStore,
62019fcbaf1SConrad Meyer U32 const remaining, U32 const minMatch)
6210c16b537SWarner Losh {
62219fcbaf1SConrad Meyer rawSeq sequence = rawSeqStore->seq[rawSeqStore->pos];
62319fcbaf1SConrad Meyer assert(sequence.offset > 0);
62419fcbaf1SConrad Meyer /* Likely: No partial sequence */
62519fcbaf1SConrad Meyer if (remaining >= sequence.litLength + sequence.matchLength) {
62619fcbaf1SConrad Meyer rawSeqStore->pos++;
62719fcbaf1SConrad Meyer return sequence;
6280c16b537SWarner Losh }
62919fcbaf1SConrad Meyer /* Cut the sequence short (offset == 0 ==> rest is literals). */
63019fcbaf1SConrad Meyer if (remaining <= sequence.litLength) {
63119fcbaf1SConrad Meyer sequence.offset = 0;
63219fcbaf1SConrad Meyer } else if (remaining < sequence.litLength + sequence.matchLength) {
63319fcbaf1SConrad Meyer sequence.matchLength = remaining - sequence.litLength;
63419fcbaf1SConrad Meyer if (sequence.matchLength < minMatch) {
63519fcbaf1SConrad Meyer sequence.offset = 0;
6360c16b537SWarner Losh }
6370c16b537SWarner Losh }
63819fcbaf1SConrad Meyer /* Skip past `remaining` bytes for the future sequences. */
63919fcbaf1SConrad Meyer ZSTD_ldm_skipSequences(rawSeqStore, remaining, minMatch);
64019fcbaf1SConrad Meyer return sequence;
6410c16b537SWarner Losh }
6420c16b537SWarner Losh
ZSTD_ldm_skipRawSeqStoreBytes(rawSeqStore_t * rawSeqStore,size_t nbBytes)643f7cd7fe5SConrad Meyer void ZSTD_ldm_skipRawSeqStoreBytes(rawSeqStore_t* rawSeqStore, size_t nbBytes) {
644f7cd7fe5SConrad Meyer U32 currPos = (U32)(rawSeqStore->posInSequence + nbBytes);
645f7cd7fe5SConrad Meyer while (currPos && rawSeqStore->pos < rawSeqStore->size) {
646f7cd7fe5SConrad Meyer rawSeq currSeq = rawSeqStore->seq[rawSeqStore->pos];
647f7cd7fe5SConrad Meyer if (currPos >= currSeq.litLength + currSeq.matchLength) {
648f7cd7fe5SConrad Meyer currPos -= currSeq.litLength + currSeq.matchLength;
649f7cd7fe5SConrad Meyer rawSeqStore->pos++;
650f7cd7fe5SConrad Meyer } else {
651f7cd7fe5SConrad Meyer rawSeqStore->posInSequence = currPos;
652f7cd7fe5SConrad Meyer break;
653f7cd7fe5SConrad Meyer }
654f7cd7fe5SConrad Meyer }
655f7cd7fe5SConrad Meyer if (currPos == 0 || rawSeqStore->pos == rawSeqStore->size) {
656f7cd7fe5SConrad Meyer rawSeqStore->posInSequence = 0;
657f7cd7fe5SConrad Meyer }
658f7cd7fe5SConrad Meyer }
659f7cd7fe5SConrad Meyer
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)66019fcbaf1SConrad Meyer size_t ZSTD_ldm_blockCompress(rawSeqStore_t* rawSeqStore,
66119fcbaf1SConrad Meyer ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
662*5ff13fbcSAllan Jude ZSTD_paramSwitch_e useRowMatchFinder,
6630f743729SConrad Meyer void const* src, size_t srcSize)
6640c16b537SWarner Losh {
6650f743729SConrad Meyer const ZSTD_compressionParameters* const cParams = &ms->cParams;
666a0483764SConrad Meyer unsigned const minMatch = cParams->minMatch;
66719fcbaf1SConrad Meyer ZSTD_blockCompressor const blockCompressor =
668*5ff13fbcSAllan Jude ZSTD_selectBlockCompressor(cParams->strategy, useRowMatchFinder, ZSTD_matchState_dictMode(ms));
66919fcbaf1SConrad Meyer /* Input bounds */
67019fcbaf1SConrad Meyer BYTE const* const istart = (BYTE const*)src;
67119fcbaf1SConrad Meyer BYTE const* const iend = istart + srcSize;
67219fcbaf1SConrad Meyer /* Input positions */
67319fcbaf1SConrad Meyer BYTE const* ip = istart;
6740c16b537SWarner Losh
6750f743729SConrad Meyer DEBUGLOG(5, "ZSTD_ldm_blockCompress: srcSize=%zu", srcSize);
676f7cd7fe5SConrad Meyer /* If using opt parser, use LDMs only as candidates rather than always accepting them */
677f7cd7fe5SConrad Meyer if (cParams->strategy >= ZSTD_btopt) {
678f7cd7fe5SConrad Meyer size_t lastLLSize;
679f7cd7fe5SConrad Meyer ms->ldmSeqStore = rawSeqStore;
680f7cd7fe5SConrad Meyer lastLLSize = blockCompressor(ms, seqStore, rep, src, srcSize);
681f7cd7fe5SConrad Meyer ZSTD_ldm_skipRawSeqStoreBytes(rawSeqStore, srcSize);
682f7cd7fe5SConrad Meyer return lastLLSize;
683f7cd7fe5SConrad Meyer }
684f7cd7fe5SConrad Meyer
68519fcbaf1SConrad Meyer assert(rawSeqStore->pos <= rawSeqStore->size);
68619fcbaf1SConrad Meyer assert(rawSeqStore->size <= rawSeqStore->capacity);
687*5ff13fbcSAllan Jude /* Loop through each sequence and apply the block compressor to the literals */
68819fcbaf1SConrad Meyer while (rawSeqStore->pos < rawSeqStore->size && ip < iend) {
68919fcbaf1SConrad Meyer /* maybeSplitSequence updates rawSeqStore->pos */
69019fcbaf1SConrad Meyer rawSeq const sequence = maybeSplitSequence(rawSeqStore,
69119fcbaf1SConrad Meyer (U32)(iend - ip), minMatch);
69219fcbaf1SConrad Meyer int i;
69319fcbaf1SConrad Meyer /* End signal */
69419fcbaf1SConrad Meyer if (sequence.offset == 0)
6950c16b537SWarner Losh break;
69619fcbaf1SConrad Meyer
69719fcbaf1SConrad Meyer assert(ip + sequence.litLength + sequence.matchLength <= iend);
69819fcbaf1SConrad Meyer
69919fcbaf1SConrad Meyer /* Fill tables for block compressor */
70019fcbaf1SConrad Meyer ZSTD_ldm_limitTableUpdate(ms, ip);
7010f743729SConrad Meyer ZSTD_ldm_fillFastTables(ms, ip);
70219fcbaf1SConrad Meyer /* Run the block compressor */
70337f1f268SConrad Meyer DEBUGLOG(5, "pos %u : calling block compressor on segment of size %u", (unsigned)(ip-istart), sequence.litLength);
70419fcbaf1SConrad Meyer {
70519fcbaf1SConrad Meyer size_t const newLitLength =
7060f743729SConrad Meyer blockCompressor(ms, seqStore, rep, ip, sequence.litLength);
70719fcbaf1SConrad Meyer ip += sequence.litLength;
70819fcbaf1SConrad Meyer /* Update the repcodes */
70919fcbaf1SConrad Meyer for (i = ZSTD_REP_NUM - 1; i > 0; i--)
71019fcbaf1SConrad Meyer rep[i] = rep[i-1];
71119fcbaf1SConrad Meyer rep[0] = sequence.offset;
71219fcbaf1SConrad Meyer /* Store the sequence */
7139cbefe25SConrad Meyer ZSTD_storeSeq(seqStore, newLitLength, ip - newLitLength, iend,
714*5ff13fbcSAllan Jude STORE_OFFSET(sequence.offset),
715*5ff13fbcSAllan Jude sequence.matchLength);
71619fcbaf1SConrad Meyer ip += sequence.matchLength;
7170c16b537SWarner Losh }
7180c16b537SWarner Losh }
71919fcbaf1SConrad Meyer /* Fill the tables for the block compressor */
72019fcbaf1SConrad Meyer ZSTD_ldm_limitTableUpdate(ms, ip);
7210f743729SConrad Meyer ZSTD_ldm_fillFastTables(ms, ip);
72219fcbaf1SConrad Meyer /* Compress the last literals */
7230f743729SConrad Meyer return blockCompressor(ms, seqStore, rep, ip, iend - ip);
7240c16b537SWarner Losh }
725