xref: /freebsd/sys/contrib/zstd/lib/compress/zstd_ldm.c (revision 5ff13fbc199bdf5f0572845351c68ee5ca828e71)
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