xref: /freebsd/sys/contrib/zstd/lib/compress/zstd_ldm.c (revision 37f1f2684f2670b204080ef2d6c303becd28545f)
10c16b537SWarner Losh /*
2*37f1f268SConrad Meyer  * Copyright (c) 2016-2020, 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).
8*37f1f268SConrad 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 
13*37f1f268SConrad Meyer #include "../common/debug.h"
140c16b537SWarner Losh #include "zstd_fast.h"          /* ZSTD_fillHashTable() */
150c16b537SWarner Losh #include "zstd_double_fast.h"   /* ZSTD_fillDoubleHashTable() */
160c16b537SWarner Losh 
170c16b537SWarner Losh #define LDM_BUCKET_SIZE_LOG 3
180c16b537SWarner Losh #define LDM_MIN_MATCH_LENGTH 64
190c16b537SWarner Losh #define LDM_HASH_RLOG 7
200c16b537SWarner Losh #define LDM_HASH_CHAR_OFFSET 10
210c16b537SWarner Losh 
2219fcbaf1SConrad Meyer void ZSTD_ldm_adjustParameters(ldmParams_t* params,
2319fcbaf1SConrad Meyer                                ZSTD_compressionParameters const* cParams)
240c16b537SWarner Losh {
250f743729SConrad Meyer     params->windowLog = cParams->windowLog;
260c16b537SWarner Losh     ZSTD_STATIC_ASSERT(LDM_BUCKET_SIZE_LOG <= ZSTD_LDM_BUCKETSIZELOG_MAX);
2719fcbaf1SConrad Meyer     DEBUGLOG(4, "ZSTD_ldm_adjustParameters");
2819fcbaf1SConrad Meyer     if (!params->bucketSizeLog) params->bucketSizeLog = LDM_BUCKET_SIZE_LOG;
2919fcbaf1SConrad Meyer     if (!params->minMatchLength) params->minMatchLength = LDM_MIN_MATCH_LENGTH;
3019fcbaf1SConrad Meyer     if (cParams->strategy >= ZSTD_btopt) {
3119fcbaf1SConrad Meyer       /* Get out of the way of the optimal parser */
3219fcbaf1SConrad Meyer       U32 const minMatch = MAX(cParams->targetLength, params->minMatchLength);
3319fcbaf1SConrad Meyer       assert(minMatch >= ZSTD_LDM_MINMATCH_MIN);
3419fcbaf1SConrad Meyer       assert(minMatch <= ZSTD_LDM_MINMATCH_MAX);
3519fcbaf1SConrad Meyer       params->minMatchLength = minMatch;
360c16b537SWarner Losh     }
370c16b537SWarner Losh     if (params->hashLog == 0) {
380f743729SConrad Meyer         params->hashLog = MAX(ZSTD_HASHLOG_MIN, params->windowLog - LDM_HASH_RLOG);
390c16b537SWarner Losh         assert(params->hashLog <= ZSTD_HASHLOG_MAX);
400c16b537SWarner Losh     }
41a0483764SConrad Meyer     if (params->hashRateLog == 0) {
42a0483764SConrad Meyer         params->hashRateLog = params->windowLog < params->hashLog
430f743729SConrad Meyer                                    ? 0
440f743729SConrad Meyer                                    : params->windowLog - params->hashLog;
450c16b537SWarner Losh     }
460c16b537SWarner Losh     params->bucketSizeLog = MIN(params->bucketSizeLog, params->hashLog);
470c16b537SWarner Losh }
480c16b537SWarner Losh 
4919fcbaf1SConrad Meyer size_t ZSTD_ldm_getTableSize(ldmParams_t params)
5019fcbaf1SConrad Meyer {
5119fcbaf1SConrad Meyer     size_t const ldmHSize = ((size_t)1) << params.hashLog;
5219fcbaf1SConrad Meyer     size_t const ldmBucketSizeLog = MIN(params.bucketSizeLog, params.hashLog);
539cbefe25SConrad Meyer     size_t const ldmBucketSize = ((size_t)1) << (params.hashLog - ldmBucketSizeLog);
549cbefe25SConrad Meyer     size_t const totalSize = ZSTD_cwksp_alloc_size(ldmBucketSize)
559cbefe25SConrad Meyer                            + ZSTD_cwksp_alloc_size(ldmHSize * sizeof(ldmEntry_t));
5619fcbaf1SConrad Meyer     return params.enableLdm ? totalSize : 0;
5719fcbaf1SConrad Meyer }
5819fcbaf1SConrad Meyer 
5919fcbaf1SConrad Meyer size_t ZSTD_ldm_getMaxNbSeq(ldmParams_t params, size_t maxChunkSize)
6019fcbaf1SConrad Meyer {
6119fcbaf1SConrad Meyer     return params.enableLdm ? (maxChunkSize / params.minMatchLength) : 0;
620c16b537SWarner Losh }
630c16b537SWarner Losh 
640c16b537SWarner Losh /** ZSTD_ldm_getSmallHash() :
650c16b537SWarner Losh  *  numBits should be <= 32
660c16b537SWarner Losh  *  If numBits==0, returns 0.
670c16b537SWarner Losh  *  @return : the most significant numBits of value. */
680c16b537SWarner Losh static U32 ZSTD_ldm_getSmallHash(U64 value, U32 numBits)
690c16b537SWarner Losh {
700c16b537SWarner Losh     assert(numBits <= 32);
710c16b537SWarner Losh     return numBits == 0 ? 0 : (U32)(value >> (64 - numBits));
720c16b537SWarner Losh }
730c16b537SWarner Losh 
740c16b537SWarner Losh /** ZSTD_ldm_getChecksum() :
750c16b537SWarner Losh  *  numBitsToDiscard should be <= 32
760c16b537SWarner Losh  *  @return : the next most significant 32 bits after numBitsToDiscard */
770c16b537SWarner Losh static U32 ZSTD_ldm_getChecksum(U64 hash, U32 numBitsToDiscard)
780c16b537SWarner Losh {
790c16b537SWarner Losh     assert(numBitsToDiscard <= 32);
800c16b537SWarner Losh     return (hash >> (64 - 32 - numBitsToDiscard)) & 0xFFFFFFFF;
810c16b537SWarner Losh }
820c16b537SWarner Losh 
830c16b537SWarner Losh /** ZSTD_ldm_getTag() ;
840c16b537SWarner Losh  *  Given the hash, returns the most significant numTagBits bits
850c16b537SWarner Losh  *  after (32 + hbits) bits.
860c16b537SWarner Losh  *
870c16b537SWarner Losh  *  If there are not enough bits remaining, return the last
880c16b537SWarner Losh  *  numTagBits bits. */
890c16b537SWarner Losh static U32 ZSTD_ldm_getTag(U64 hash, U32 hbits, U32 numTagBits)
900c16b537SWarner Losh {
910c16b537SWarner Losh     assert(numTagBits < 32 && hbits <= 32);
920c16b537SWarner Losh     if (32 - hbits < numTagBits) {
930c16b537SWarner Losh         return hash & (((U32)1 << numTagBits) - 1);
940c16b537SWarner Losh     } else {
950c16b537SWarner Losh         return (hash >> (32 - hbits - numTagBits)) & (((U32)1 << numTagBits) - 1);
960c16b537SWarner Losh     }
970c16b537SWarner Losh }
980c16b537SWarner Losh 
990c16b537SWarner Losh /** ZSTD_ldm_getBucket() :
1000c16b537SWarner Losh  *  Returns a pointer to the start of the bucket associated with hash. */
1010c16b537SWarner Losh static ldmEntry_t* ZSTD_ldm_getBucket(
1020c16b537SWarner Losh         ldmState_t* ldmState, size_t hash, ldmParams_t const ldmParams)
1030c16b537SWarner Losh {
1040c16b537SWarner Losh     return ldmState->hashTable + (hash << ldmParams.bucketSizeLog);
1050c16b537SWarner Losh }
1060c16b537SWarner Losh 
1070c16b537SWarner Losh /** ZSTD_ldm_insertEntry() :
1080c16b537SWarner Losh  *  Insert the entry with corresponding hash into the hash table */
1090c16b537SWarner Losh static void ZSTD_ldm_insertEntry(ldmState_t* ldmState,
1100c16b537SWarner Losh                                  size_t const hash, const ldmEntry_t entry,
1110c16b537SWarner Losh                                  ldmParams_t const ldmParams)
1120c16b537SWarner Losh {
1130c16b537SWarner Losh     BYTE* const bucketOffsets = ldmState->bucketOffsets;
1140c16b537SWarner Losh     *(ZSTD_ldm_getBucket(ldmState, hash, ldmParams) + bucketOffsets[hash]) = entry;
1150c16b537SWarner Losh     bucketOffsets[hash]++;
1160c16b537SWarner Losh     bucketOffsets[hash] &= ((U32)1 << ldmParams.bucketSizeLog) - 1;
1170c16b537SWarner Losh }
1180c16b537SWarner Losh 
1190c16b537SWarner Losh /** ZSTD_ldm_makeEntryAndInsertByTag() :
1200c16b537SWarner Losh  *
1210c16b537SWarner Losh  *  Gets the small hash, checksum, and tag from the rollingHash.
1220c16b537SWarner Losh  *
123a0483764SConrad Meyer  *  If the tag matches (1 << ldmParams.hashRateLog)-1, then
1240c16b537SWarner Losh  *  creates an ldmEntry from the offset, and inserts it into the hash table.
1250c16b537SWarner Losh  *
1260c16b537SWarner Losh  *  hBits is the length of the small hash, which is the most significant hBits
1270c16b537SWarner Losh  *  of rollingHash. The checksum is the next 32 most significant bits, followed
128a0483764SConrad Meyer  *  by ldmParams.hashRateLog bits that make up the tag. */
1290c16b537SWarner Losh static void ZSTD_ldm_makeEntryAndInsertByTag(ldmState_t* ldmState,
1300c16b537SWarner Losh                                              U64 const rollingHash,
1310c16b537SWarner Losh                                              U32 const hBits,
1320c16b537SWarner Losh                                              U32 const offset,
1330c16b537SWarner Losh                                              ldmParams_t const ldmParams)
1340c16b537SWarner Losh {
135a0483764SConrad Meyer     U32 const tag = ZSTD_ldm_getTag(rollingHash, hBits, ldmParams.hashRateLog);
136a0483764SConrad Meyer     U32 const tagMask = ((U32)1 << ldmParams.hashRateLog) - 1;
1370c16b537SWarner Losh     if (tag == tagMask) {
1380c16b537SWarner Losh         U32 const hash = ZSTD_ldm_getSmallHash(rollingHash, hBits);
1390c16b537SWarner Losh         U32 const checksum = ZSTD_ldm_getChecksum(rollingHash, hBits);
1400c16b537SWarner Losh         ldmEntry_t entry;
1410c16b537SWarner Losh         entry.offset = offset;
1420c16b537SWarner Losh         entry.checksum = checksum;
1430c16b537SWarner Losh         ZSTD_ldm_insertEntry(ldmState, hash, entry, ldmParams);
1440c16b537SWarner Losh     }
1450c16b537SWarner Losh }
1460c16b537SWarner Losh 
1470c16b537SWarner Losh /** ZSTD_ldm_countBackwardsMatch() :
1480c16b537SWarner Losh  *  Returns the number of bytes that match backwards before pIn and pMatch.
1490c16b537SWarner Losh  *
1500c16b537SWarner Losh  *  We count only bytes where pMatch >= pBase and pIn >= pAnchor. */
1510c16b537SWarner Losh static size_t ZSTD_ldm_countBackwardsMatch(
1520c16b537SWarner Losh             const BYTE* pIn, const BYTE* pAnchor,
1530c16b537SWarner Losh             const BYTE* pMatch, const BYTE* pBase)
1540c16b537SWarner Losh {
1550c16b537SWarner Losh     size_t matchLength = 0;
1560c16b537SWarner Losh     while (pIn > pAnchor && pMatch > pBase && pIn[-1] == pMatch[-1]) {
1570c16b537SWarner Losh         pIn--;
1580c16b537SWarner Losh         pMatch--;
1590c16b537SWarner Losh         matchLength++;
1600c16b537SWarner Losh     }
1610c16b537SWarner Losh     return matchLength;
1620c16b537SWarner Losh }
1630c16b537SWarner Losh 
1640c16b537SWarner Losh /** ZSTD_ldm_fillFastTables() :
1650c16b537SWarner Losh  *
1660c16b537SWarner Losh  *  Fills the relevant tables for the ZSTD_fast and ZSTD_dfast strategies.
1670c16b537SWarner Losh  *  This is similar to ZSTD_loadDictionaryContent.
1680c16b537SWarner Losh  *
1690c16b537SWarner Losh  *  The tables for the other strategies are filled within their
1700c16b537SWarner Losh  *  block compressors. */
17119fcbaf1SConrad Meyer static size_t ZSTD_ldm_fillFastTables(ZSTD_matchState_t* ms,
17219fcbaf1SConrad Meyer                                       void const* end)
1730c16b537SWarner Losh {
1740c16b537SWarner Losh     const BYTE* const iend = (const BYTE*)end;
1750c16b537SWarner Losh 
1760f743729SConrad Meyer     switch(ms->cParams.strategy)
1770c16b537SWarner Losh     {
1780c16b537SWarner Losh     case ZSTD_fast:
1790f743729SConrad Meyer         ZSTD_fillHashTable(ms, iend, ZSTD_dtlm_fast);
1800c16b537SWarner Losh         break;
1810c16b537SWarner Losh 
1820c16b537SWarner Losh     case ZSTD_dfast:
1830f743729SConrad Meyer         ZSTD_fillDoubleHashTable(ms, iend, ZSTD_dtlm_fast);
1840c16b537SWarner Losh         break;
1850c16b537SWarner Losh 
1860c16b537SWarner Losh     case ZSTD_greedy:
1870c16b537SWarner Losh     case ZSTD_lazy:
1880c16b537SWarner Losh     case ZSTD_lazy2:
1890c16b537SWarner Losh     case ZSTD_btlazy2:
1900c16b537SWarner Losh     case ZSTD_btopt:
1910c16b537SWarner Losh     case ZSTD_btultra:
192a0483764SConrad Meyer     case ZSTD_btultra2:
1930c16b537SWarner Losh         break;
1940c16b537SWarner Losh     default:
1950c16b537SWarner Losh         assert(0);  /* not possible : not a valid strategy id */
1960c16b537SWarner Losh     }
1970c16b537SWarner Losh 
1980c16b537SWarner Losh     return 0;
1990c16b537SWarner Losh }
2000c16b537SWarner Losh 
2010c16b537SWarner Losh /** ZSTD_ldm_fillLdmHashTable() :
2020c16b537SWarner Losh  *
2030c16b537SWarner Losh  *  Fills hashTable from (lastHashed + 1) to iend (non-inclusive).
2040c16b537SWarner Losh  *  lastHash is the rolling hash that corresponds to lastHashed.
2050c16b537SWarner Losh  *
2060c16b537SWarner Losh  *  Returns the rolling hash corresponding to position iend-1. */
2070c16b537SWarner Losh static U64 ZSTD_ldm_fillLdmHashTable(ldmState_t* state,
2080c16b537SWarner Losh                                      U64 lastHash, const BYTE* lastHashed,
2090c16b537SWarner Losh                                      const BYTE* iend, const BYTE* base,
2100c16b537SWarner Losh                                      U32 hBits, ldmParams_t const ldmParams)
2110c16b537SWarner Losh {
2120c16b537SWarner Losh     U64 rollingHash = lastHash;
2130c16b537SWarner Losh     const BYTE* cur = lastHashed + 1;
2140c16b537SWarner Losh 
2150c16b537SWarner Losh     while (cur < iend) {
216a0483764SConrad Meyer         rollingHash = ZSTD_rollingHash_rotate(rollingHash, cur[-1],
2170c16b537SWarner Losh                                               cur[ldmParams.minMatchLength-1],
2180c16b537SWarner Losh                                               state->hashPower);
2190c16b537SWarner Losh         ZSTD_ldm_makeEntryAndInsertByTag(state,
2200c16b537SWarner Losh                                          rollingHash, hBits,
2210c16b537SWarner Losh                                          (U32)(cur - base), ldmParams);
2220c16b537SWarner Losh         ++cur;
2230c16b537SWarner Losh     }
2240c16b537SWarner Losh     return rollingHash;
2250c16b537SWarner Losh }
2260c16b537SWarner Losh 
227*37f1f268SConrad Meyer void ZSTD_ldm_fillHashTable(
228*37f1f268SConrad Meyer             ldmState_t* state, const BYTE* ip,
229*37f1f268SConrad Meyer             const BYTE* iend, ldmParams_t const* params)
230*37f1f268SConrad Meyer {
231*37f1f268SConrad Meyer     DEBUGLOG(5, "ZSTD_ldm_fillHashTable");
232*37f1f268SConrad Meyer     if ((size_t)(iend - ip) >= params->minMatchLength) {
233*37f1f268SConrad Meyer         U64 startingHash = ZSTD_rollingHash_compute(ip, params->minMatchLength);
234*37f1f268SConrad Meyer         ZSTD_ldm_fillLdmHashTable(
235*37f1f268SConrad Meyer             state, startingHash, ip, iend - params->minMatchLength, state->window.base,
236*37f1f268SConrad Meyer             params->hashLog - params->bucketSizeLog,
237*37f1f268SConrad Meyer             *params);
238*37f1f268SConrad Meyer     }
239*37f1f268SConrad Meyer }
240*37f1f268SConrad Meyer 
2410c16b537SWarner Losh 
2420c16b537SWarner Losh /** ZSTD_ldm_limitTableUpdate() :
2430c16b537SWarner Losh  *
2440c16b537SWarner Losh  *  Sets cctx->nextToUpdate to a position corresponding closer to anchor
2450c16b537SWarner Losh  *  if it is far way
2460c16b537SWarner Losh  *  (after a long match, only update tables a limited amount). */
24719fcbaf1SConrad Meyer static void ZSTD_ldm_limitTableUpdate(ZSTD_matchState_t* ms, const BYTE* anchor)
2480c16b537SWarner Losh {
24919fcbaf1SConrad Meyer     U32 const current = (U32)(anchor - ms->window.base);
25019fcbaf1SConrad Meyer     if (current > ms->nextToUpdate + 1024) {
25119fcbaf1SConrad Meyer         ms->nextToUpdate =
25219fcbaf1SConrad Meyer             current - MIN(512, current - ms->nextToUpdate - 1024);
2530c16b537SWarner Losh     }
2540c16b537SWarner Losh }
2550c16b537SWarner Losh 
25619fcbaf1SConrad Meyer static size_t ZSTD_ldm_generateSequences_internal(
25719fcbaf1SConrad Meyer         ldmState_t* ldmState, rawSeqStore_t* rawSeqStore,
25819fcbaf1SConrad Meyer         ldmParams_t const* params, void const* src, size_t srcSize)
2590c16b537SWarner Losh {
26019fcbaf1SConrad Meyer     /* LDM parameters */
26119fcbaf1SConrad Meyer     int const extDict = ZSTD_window_hasExtDict(ldmState->window);
26219fcbaf1SConrad Meyer     U32 const minMatchLength = params->minMatchLength;
26319fcbaf1SConrad Meyer     U64 const hashPower = ldmState->hashPower;
26419fcbaf1SConrad Meyer     U32 const hBits = params->hashLog - params->bucketSizeLog;
26519fcbaf1SConrad Meyer     U32 const ldmBucketSize = 1U << params->bucketSizeLog;
266a0483764SConrad Meyer     U32 const hashRateLog = params->hashRateLog;
267a0483764SConrad Meyer     U32 const ldmTagMask = (1U << params->hashRateLog) - 1;
26819fcbaf1SConrad Meyer     /* Prefix and extDict parameters */
26919fcbaf1SConrad Meyer     U32 const dictLimit = ldmState->window.dictLimit;
27019fcbaf1SConrad Meyer     U32 const lowestIndex = extDict ? ldmState->window.lowLimit : dictLimit;
27119fcbaf1SConrad Meyer     BYTE const* const base = ldmState->window.base;
27219fcbaf1SConrad Meyer     BYTE const* const dictBase = extDict ? ldmState->window.dictBase : NULL;
27319fcbaf1SConrad Meyer     BYTE const* const dictStart = extDict ? dictBase + lowestIndex : NULL;
27419fcbaf1SConrad Meyer     BYTE const* const dictEnd = extDict ? dictBase + dictLimit : NULL;
27519fcbaf1SConrad Meyer     BYTE const* const lowPrefixPtr = base + dictLimit;
27619fcbaf1SConrad Meyer     /* Input bounds */
27719fcbaf1SConrad Meyer     BYTE const* const istart = (BYTE const*)src;
27819fcbaf1SConrad Meyer     BYTE const* const iend = istart + srcSize;
27919fcbaf1SConrad Meyer     BYTE const* const ilimit = iend - MAX(minMatchLength, HASH_READ_SIZE);
28019fcbaf1SConrad Meyer     /* Input positions */
28119fcbaf1SConrad Meyer     BYTE const* anchor = istart;
28219fcbaf1SConrad Meyer     BYTE const* ip = istart;
28319fcbaf1SConrad Meyer     /* Rolling hash */
28419fcbaf1SConrad Meyer     BYTE const* lastHashed = NULL;
2850c16b537SWarner Losh     U64 rollingHash = 0;
2860c16b537SWarner Losh 
28719fcbaf1SConrad Meyer     while (ip <= ilimit) {
2880c16b537SWarner Losh         size_t mLength;
2890c16b537SWarner Losh         U32 const current = (U32)(ip - base);
2900c16b537SWarner Losh         size_t forwardMatchLength = 0, backwardMatchLength = 0;
2910c16b537SWarner Losh         ldmEntry_t* bestEntry = NULL;
2920c16b537SWarner Losh         if (ip != istart) {
293a0483764SConrad Meyer             rollingHash = ZSTD_rollingHash_rotate(rollingHash, lastHashed[0],
29419fcbaf1SConrad Meyer                                                   lastHashed[minMatchLength],
2950c16b537SWarner Losh                                                   hashPower);
2960c16b537SWarner Losh         } else {
297a0483764SConrad Meyer             rollingHash = ZSTD_rollingHash_compute(ip, minMatchLength);
2980c16b537SWarner Losh         }
2990c16b537SWarner Losh         lastHashed = ip;
3000c16b537SWarner Losh 
3010c16b537SWarner Losh         /* Do not insert and do not look for a match */
302a0483764SConrad Meyer         if (ZSTD_ldm_getTag(rollingHash, hBits, hashRateLog) != ldmTagMask) {
3030c16b537SWarner Losh            ip++;
3040c16b537SWarner Losh            continue;
3050c16b537SWarner Losh         }
3060c16b537SWarner Losh 
3070c16b537SWarner Losh         /* Get the best entry and compute the match lengths */
3080c16b537SWarner Losh         {
3090c16b537SWarner Losh             ldmEntry_t* const bucket =
3100c16b537SWarner Losh                 ZSTD_ldm_getBucket(ldmState,
3110c16b537SWarner Losh                                    ZSTD_ldm_getSmallHash(rollingHash, hBits),
31219fcbaf1SConrad Meyer                                    *params);
3130c16b537SWarner Losh             ldmEntry_t* cur;
3140c16b537SWarner Losh             size_t bestMatchLength = 0;
3150c16b537SWarner Losh             U32 const checksum = ZSTD_ldm_getChecksum(rollingHash, hBits);
3160c16b537SWarner Losh 
3170c16b537SWarner Losh             for (cur = bucket; cur < bucket + ldmBucketSize; ++cur) {
3180c16b537SWarner Losh                 size_t curForwardMatchLength, curBackwardMatchLength,
3190c16b537SWarner Losh                        curTotalMatchLength;
3200c16b537SWarner Losh                 if (cur->checksum != checksum || cur->offset <= lowestIndex) {
3210c16b537SWarner Losh                     continue;
3220c16b537SWarner Losh                 }
32319fcbaf1SConrad Meyer                 if (extDict) {
32419fcbaf1SConrad Meyer                     BYTE const* const curMatchBase =
32519fcbaf1SConrad Meyer                         cur->offset < dictLimit ? dictBase : base;
32619fcbaf1SConrad Meyer                     BYTE const* const pMatch = curMatchBase + cur->offset;
32719fcbaf1SConrad Meyer                     BYTE const* const matchEnd =
32819fcbaf1SConrad Meyer                         cur->offset < dictLimit ? dictEnd : iend;
32919fcbaf1SConrad Meyer                     BYTE const* const lowMatchPtr =
33019fcbaf1SConrad Meyer                         cur->offset < dictLimit ? dictStart : lowPrefixPtr;
3310c16b537SWarner Losh 
33219fcbaf1SConrad Meyer                     curForwardMatchLength = ZSTD_count_2segments(
33319fcbaf1SConrad Meyer                                                 ip, pMatch, iend,
33419fcbaf1SConrad Meyer                                                 matchEnd, lowPrefixPtr);
33519fcbaf1SConrad Meyer                     if (curForwardMatchLength < minMatchLength) {
3360c16b537SWarner Losh                         continue;
3370c16b537SWarner Losh                     }
33819fcbaf1SConrad Meyer                     curBackwardMatchLength =
33919fcbaf1SConrad Meyer                         ZSTD_ldm_countBackwardsMatch(ip, anchor, pMatch,
34019fcbaf1SConrad Meyer                                                      lowMatchPtr);
3410c16b537SWarner Losh                     curTotalMatchLength = curForwardMatchLength +
3420c16b537SWarner Losh                                           curBackwardMatchLength;
34319fcbaf1SConrad Meyer                 } else { /* !extDict */
34419fcbaf1SConrad Meyer                     BYTE const* const pMatch = base + cur->offset;
34519fcbaf1SConrad Meyer                     curForwardMatchLength = ZSTD_count(ip, pMatch, iend);
34619fcbaf1SConrad Meyer                     if (curForwardMatchLength < minMatchLength) {
34719fcbaf1SConrad Meyer                         continue;
34819fcbaf1SConrad Meyer                     }
34919fcbaf1SConrad Meyer                     curBackwardMatchLength =
35019fcbaf1SConrad Meyer                         ZSTD_ldm_countBackwardsMatch(ip, anchor, pMatch,
35119fcbaf1SConrad Meyer                                                      lowPrefixPtr);
35219fcbaf1SConrad Meyer                     curTotalMatchLength = curForwardMatchLength +
35319fcbaf1SConrad Meyer                                           curBackwardMatchLength;
35419fcbaf1SConrad Meyer                 }
3550c16b537SWarner Losh 
3560c16b537SWarner Losh                 if (curTotalMatchLength > bestMatchLength) {
3570c16b537SWarner Losh                     bestMatchLength = curTotalMatchLength;
3580c16b537SWarner Losh                     forwardMatchLength = curForwardMatchLength;
3590c16b537SWarner Losh                     backwardMatchLength = curBackwardMatchLength;
3600c16b537SWarner Losh                     bestEntry = cur;
3610c16b537SWarner Losh                 }
3620c16b537SWarner Losh             }
3630c16b537SWarner Losh         }
3640c16b537SWarner Losh 
3650c16b537SWarner Losh         /* No match found -- continue searching */
3660c16b537SWarner Losh         if (bestEntry == NULL) {
3670c16b537SWarner Losh             ZSTD_ldm_makeEntryAndInsertByTag(ldmState, rollingHash,
3680c16b537SWarner Losh                                              hBits, current,
36919fcbaf1SConrad Meyer                                              *params);
3700c16b537SWarner Losh             ip++;
3710c16b537SWarner Losh             continue;
3720c16b537SWarner Losh         }
3730c16b537SWarner Losh 
3740c16b537SWarner Losh         /* Match found */
3750c16b537SWarner Losh         mLength = forwardMatchLength + backwardMatchLength;
3760c16b537SWarner Losh         ip -= backwardMatchLength;
3770c16b537SWarner Losh 
3780c16b537SWarner Losh         {
37919fcbaf1SConrad Meyer             /* Store the sequence:
38019fcbaf1SConrad Meyer              * ip = current - backwardMatchLength
38119fcbaf1SConrad Meyer              * The match is at (bestEntry->offset - backwardMatchLength)
38219fcbaf1SConrad Meyer              */
3830c16b537SWarner Losh             U32 const matchIndex = bestEntry->offset;
38419fcbaf1SConrad Meyer             U32 const offset = current - matchIndex;
38519fcbaf1SConrad Meyer             rawSeq* const seq = rawSeqStore->seq + rawSeqStore->size;
3860c16b537SWarner Losh 
38719fcbaf1SConrad Meyer             /* Out of sequence storage */
38819fcbaf1SConrad Meyer             if (rawSeqStore->size == rawSeqStore->capacity)
38919fcbaf1SConrad Meyer                 return ERROR(dstSize_tooSmall);
39019fcbaf1SConrad Meyer             seq->litLength = (U32)(ip - anchor);
39119fcbaf1SConrad Meyer             seq->matchLength = (U32)mLength;
39219fcbaf1SConrad Meyer             seq->offset = offset;
39319fcbaf1SConrad Meyer             rawSeqStore->size++;
3940c16b537SWarner Losh         }
3950c16b537SWarner Losh 
3960c16b537SWarner Losh         /* Insert the current entry into the hash table */
3970c16b537SWarner Losh         ZSTD_ldm_makeEntryAndInsertByTag(ldmState, rollingHash, hBits,
3980c16b537SWarner Losh                                          (U32)(lastHashed - base),
39919fcbaf1SConrad Meyer                                          *params);
4000c16b537SWarner Losh 
4010c16b537SWarner Losh         assert(ip + backwardMatchLength == lastHashed);
4020c16b537SWarner Losh 
4030c16b537SWarner Losh         /* Fill the hash table from lastHashed+1 to ip+mLength*/
4040c16b537SWarner Losh         /* Heuristic: don't need to fill the entire table at end of block */
40519fcbaf1SConrad Meyer         if (ip + mLength <= ilimit) {
4060c16b537SWarner Losh             rollingHash = ZSTD_ldm_fillLdmHashTable(
4070c16b537SWarner Losh                               ldmState, rollingHash, lastHashed,
40819fcbaf1SConrad Meyer                               ip + mLength, base, hBits, *params);
4090c16b537SWarner Losh             lastHashed = ip + mLength - 1;
4100c16b537SWarner Losh         }
4110c16b537SWarner Losh         ip += mLength;
4120c16b537SWarner Losh         anchor = ip;
41319fcbaf1SConrad Meyer     }
41419fcbaf1SConrad Meyer     return iend - anchor;
41519fcbaf1SConrad Meyer }
4160c16b537SWarner Losh 
41719fcbaf1SConrad Meyer /*! ZSTD_ldm_reduceTable() :
41819fcbaf1SConrad Meyer  *  reduce table indexes by `reducerValue` */
41919fcbaf1SConrad Meyer static void ZSTD_ldm_reduceTable(ldmEntry_t* const table, U32 const size,
42019fcbaf1SConrad Meyer                                  U32 const reducerValue)
4210c16b537SWarner Losh {
42219fcbaf1SConrad Meyer     U32 u;
42319fcbaf1SConrad Meyer     for (u = 0; u < size; u++) {
42419fcbaf1SConrad Meyer         if (table[u].offset < reducerValue) table[u].offset = 0;
42519fcbaf1SConrad Meyer         else table[u].offset -= reducerValue;
4260c16b537SWarner Losh     }
4270c16b537SWarner Losh }
4280c16b537SWarner Losh 
42919fcbaf1SConrad Meyer size_t ZSTD_ldm_generateSequences(
43019fcbaf1SConrad Meyer         ldmState_t* ldmState, rawSeqStore_t* sequences,
43119fcbaf1SConrad Meyer         ldmParams_t const* params, void const* src, size_t srcSize)
4320c16b537SWarner Losh {
43319fcbaf1SConrad Meyer     U32 const maxDist = 1U << params->windowLog;
43419fcbaf1SConrad Meyer     BYTE const* const istart = (BYTE const*)src;
43519fcbaf1SConrad Meyer     BYTE const* const iend = istart + srcSize;
43619fcbaf1SConrad Meyer     size_t const kMaxChunkSize = 1 << 20;
43719fcbaf1SConrad Meyer     size_t const nbChunks = (srcSize / kMaxChunkSize) + ((srcSize % kMaxChunkSize) != 0);
43819fcbaf1SConrad Meyer     size_t chunk;
43919fcbaf1SConrad Meyer     size_t leftoverSize = 0;
44019fcbaf1SConrad Meyer 
44119fcbaf1SConrad Meyer     assert(ZSTD_CHUNKSIZE_MAX >= kMaxChunkSize);
44219fcbaf1SConrad Meyer     /* Check that ZSTD_window_update() has been called for this chunk prior
44319fcbaf1SConrad Meyer      * to passing it to this function.
44419fcbaf1SConrad Meyer      */
44519fcbaf1SConrad Meyer     assert(ldmState->window.nextSrc >= (BYTE const*)src + srcSize);
44619fcbaf1SConrad Meyer     /* The input could be very large (in zstdmt), so it must be broken up into
4472b9c00cbSConrad Meyer      * chunks to enforce the maximum distance and handle overflow correction.
44819fcbaf1SConrad Meyer      */
44919fcbaf1SConrad Meyer     assert(sequences->pos <= sequences->size);
45019fcbaf1SConrad Meyer     assert(sequences->size <= sequences->capacity);
45119fcbaf1SConrad Meyer     for (chunk = 0; chunk < nbChunks && sequences->size < sequences->capacity; ++chunk) {
45219fcbaf1SConrad Meyer         BYTE const* const chunkStart = istart + chunk * kMaxChunkSize;
45319fcbaf1SConrad Meyer         size_t const remaining = (size_t)(iend - chunkStart);
45419fcbaf1SConrad Meyer         BYTE const *const chunkEnd =
45519fcbaf1SConrad Meyer             (remaining < kMaxChunkSize) ? iend : chunkStart + kMaxChunkSize;
45619fcbaf1SConrad Meyer         size_t const chunkSize = chunkEnd - chunkStart;
45719fcbaf1SConrad Meyer         size_t newLeftoverSize;
45819fcbaf1SConrad Meyer         size_t const prevSize = sequences->size;
45919fcbaf1SConrad Meyer 
46019fcbaf1SConrad Meyer         assert(chunkStart < iend);
46119fcbaf1SConrad Meyer         /* 1. Perform overflow correction if necessary. */
46219fcbaf1SConrad Meyer         if (ZSTD_window_needOverflowCorrection(ldmState->window, chunkEnd)) {
46319fcbaf1SConrad Meyer             U32 const ldmHSize = 1U << params->hashLog;
46419fcbaf1SConrad Meyer             U32 const correction = ZSTD_window_correctOverflow(
4654d3f1eafSConrad Meyer                 &ldmState->window, /* cycleLog */ 0, maxDist, chunkStart);
46619fcbaf1SConrad Meyer             ZSTD_ldm_reduceTable(ldmState->hashTable, ldmHSize, correction);
467*37f1f268SConrad Meyer             /* invalidate dictionaries on overflow correction */
468*37f1f268SConrad Meyer             ldmState->loadedDictEnd = 0;
4690c16b537SWarner Losh         }
47019fcbaf1SConrad Meyer         /* 2. We enforce the maximum offset allowed.
47119fcbaf1SConrad Meyer          *
47219fcbaf1SConrad Meyer          * kMaxChunkSize should be small enough that we don't lose too much of
47319fcbaf1SConrad Meyer          * the window through early invalidation.
47419fcbaf1SConrad Meyer          * TODO: * Test the chunk size.
47519fcbaf1SConrad Meyer          *       * Try invalidation after the sequence generation and test the
47619fcbaf1SConrad Meyer          *         the offset against maxDist directly.
477*37f1f268SConrad Meyer          *
478*37f1f268SConrad Meyer          * NOTE: Because of dictionaries + sequence splitting we MUST make sure
479*37f1f268SConrad Meyer          * that any offset used is valid at the END of the sequence, since it may
480*37f1f268SConrad Meyer          * be split into two sequences. This condition holds when using
481*37f1f268SConrad Meyer          * ZSTD_window_enforceMaxDist(), but if we move to checking offsets
482*37f1f268SConrad Meyer          * against maxDist directly, we'll have to carefully handle that case.
48319fcbaf1SConrad Meyer          */
484*37f1f268SConrad Meyer         ZSTD_window_enforceMaxDist(&ldmState->window, chunkEnd, maxDist, &ldmState->loadedDictEnd, NULL);
48519fcbaf1SConrad Meyer         /* 3. Generate the sequences for the chunk, and get newLeftoverSize. */
48619fcbaf1SConrad Meyer         newLeftoverSize = ZSTD_ldm_generateSequences_internal(
48719fcbaf1SConrad Meyer             ldmState, sequences, params, chunkStart, chunkSize);
48819fcbaf1SConrad Meyer         if (ZSTD_isError(newLeftoverSize))
48919fcbaf1SConrad Meyer             return newLeftoverSize;
49019fcbaf1SConrad Meyer         /* 4. We add the leftover literals from previous iterations to the first
49119fcbaf1SConrad Meyer          *    newly generated sequence, or add the `newLeftoverSize` if none are
49219fcbaf1SConrad Meyer          *    generated.
49319fcbaf1SConrad Meyer          */
49419fcbaf1SConrad Meyer         /* Prepend the leftover literals from the last call */
49519fcbaf1SConrad Meyer         if (prevSize < sequences->size) {
49619fcbaf1SConrad Meyer             sequences->seq[prevSize].litLength += (U32)leftoverSize;
49719fcbaf1SConrad Meyer             leftoverSize = newLeftoverSize;
4980c16b537SWarner Losh         } else {
49919fcbaf1SConrad Meyer             assert(newLeftoverSize == chunkSize);
50019fcbaf1SConrad Meyer             leftoverSize += chunkSize;
5010c16b537SWarner Losh         }
50219fcbaf1SConrad Meyer     }
50319fcbaf1SConrad Meyer     return 0;
5040c16b537SWarner Losh }
5050c16b537SWarner Losh 
50619fcbaf1SConrad Meyer void ZSTD_ldm_skipSequences(rawSeqStore_t* rawSeqStore, size_t srcSize, U32 const minMatch) {
50719fcbaf1SConrad Meyer     while (srcSize > 0 && rawSeqStore->pos < rawSeqStore->size) {
50819fcbaf1SConrad Meyer         rawSeq* seq = rawSeqStore->seq + rawSeqStore->pos;
50919fcbaf1SConrad Meyer         if (srcSize <= seq->litLength) {
51019fcbaf1SConrad Meyer             /* Skip past srcSize literals */
51119fcbaf1SConrad Meyer             seq->litLength -= (U32)srcSize;
51219fcbaf1SConrad Meyer             return;
51319fcbaf1SConrad Meyer         }
51419fcbaf1SConrad Meyer         srcSize -= seq->litLength;
51519fcbaf1SConrad Meyer         seq->litLength = 0;
51619fcbaf1SConrad Meyer         if (srcSize < seq->matchLength) {
51719fcbaf1SConrad Meyer             /* Skip past the first srcSize of the match */
51819fcbaf1SConrad Meyer             seq->matchLength -= (U32)srcSize;
51919fcbaf1SConrad Meyer             if (seq->matchLength < minMatch) {
52019fcbaf1SConrad Meyer                 /* The match is too short, omit it */
52119fcbaf1SConrad Meyer                 if (rawSeqStore->pos + 1 < rawSeqStore->size) {
52219fcbaf1SConrad Meyer                     seq[1].litLength += seq[0].matchLength;
52319fcbaf1SConrad Meyer                 }
52419fcbaf1SConrad Meyer                 rawSeqStore->pos++;
52519fcbaf1SConrad Meyer             }
52619fcbaf1SConrad Meyer             return;
52719fcbaf1SConrad Meyer         }
52819fcbaf1SConrad Meyer         srcSize -= seq->matchLength;
52919fcbaf1SConrad Meyer         seq->matchLength = 0;
53019fcbaf1SConrad Meyer         rawSeqStore->pos++;
53119fcbaf1SConrad Meyer     }
53219fcbaf1SConrad Meyer }
53319fcbaf1SConrad Meyer 
53419fcbaf1SConrad Meyer /**
53519fcbaf1SConrad Meyer  * If the sequence length is longer than remaining then the sequence is split
53619fcbaf1SConrad Meyer  * between this block and the next.
53719fcbaf1SConrad Meyer  *
53819fcbaf1SConrad Meyer  * Returns the current sequence to handle, or if the rest of the block should
53919fcbaf1SConrad Meyer  * be literals, it returns a sequence with offset == 0.
54019fcbaf1SConrad Meyer  */
54119fcbaf1SConrad Meyer static rawSeq maybeSplitSequence(rawSeqStore_t* rawSeqStore,
54219fcbaf1SConrad Meyer                                  U32 const remaining, U32 const minMatch)
5430c16b537SWarner Losh {
54419fcbaf1SConrad Meyer     rawSeq sequence = rawSeqStore->seq[rawSeqStore->pos];
54519fcbaf1SConrad Meyer     assert(sequence.offset > 0);
54619fcbaf1SConrad Meyer     /* Likely: No partial sequence */
54719fcbaf1SConrad Meyer     if (remaining >= sequence.litLength + sequence.matchLength) {
54819fcbaf1SConrad Meyer         rawSeqStore->pos++;
54919fcbaf1SConrad Meyer         return sequence;
5500c16b537SWarner Losh     }
55119fcbaf1SConrad Meyer     /* Cut the sequence short (offset == 0 ==> rest is literals). */
55219fcbaf1SConrad Meyer     if (remaining <= sequence.litLength) {
55319fcbaf1SConrad Meyer         sequence.offset = 0;
55419fcbaf1SConrad Meyer     } else if (remaining < sequence.litLength + sequence.matchLength) {
55519fcbaf1SConrad Meyer         sequence.matchLength = remaining - sequence.litLength;
55619fcbaf1SConrad Meyer         if (sequence.matchLength < minMatch) {
55719fcbaf1SConrad Meyer             sequence.offset = 0;
5580c16b537SWarner Losh         }
5590c16b537SWarner Losh     }
56019fcbaf1SConrad Meyer     /* Skip past `remaining` bytes for the future sequences. */
56119fcbaf1SConrad Meyer     ZSTD_ldm_skipSequences(rawSeqStore, remaining, minMatch);
56219fcbaf1SConrad Meyer     return sequence;
5630c16b537SWarner Losh }
5640c16b537SWarner Losh 
56519fcbaf1SConrad Meyer size_t ZSTD_ldm_blockCompress(rawSeqStore_t* rawSeqStore,
56619fcbaf1SConrad Meyer     ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
5670f743729SConrad Meyer     void const* src, size_t srcSize)
5680c16b537SWarner Losh {
5690f743729SConrad Meyer     const ZSTD_compressionParameters* const cParams = &ms->cParams;
570a0483764SConrad Meyer     unsigned const minMatch = cParams->minMatch;
57119fcbaf1SConrad Meyer     ZSTD_blockCompressor const blockCompressor =
5720f743729SConrad Meyer         ZSTD_selectBlockCompressor(cParams->strategy, ZSTD_matchState_dictMode(ms));
57319fcbaf1SConrad Meyer     /* Input bounds */
57419fcbaf1SConrad Meyer     BYTE const* const istart = (BYTE const*)src;
57519fcbaf1SConrad Meyer     BYTE const* const iend = istart + srcSize;
57619fcbaf1SConrad Meyer     /* Input positions */
57719fcbaf1SConrad Meyer     BYTE const* ip = istart;
5780c16b537SWarner Losh 
5790f743729SConrad Meyer     DEBUGLOG(5, "ZSTD_ldm_blockCompress: srcSize=%zu", srcSize);
58019fcbaf1SConrad Meyer     assert(rawSeqStore->pos <= rawSeqStore->size);
58119fcbaf1SConrad Meyer     assert(rawSeqStore->size <= rawSeqStore->capacity);
58219fcbaf1SConrad Meyer     /* Loop through each sequence and apply the block compressor to the lits */
58319fcbaf1SConrad Meyer     while (rawSeqStore->pos < rawSeqStore->size && ip < iend) {
58419fcbaf1SConrad Meyer         /* maybeSplitSequence updates rawSeqStore->pos */
58519fcbaf1SConrad Meyer         rawSeq const sequence = maybeSplitSequence(rawSeqStore,
58619fcbaf1SConrad Meyer                                                    (U32)(iend - ip), minMatch);
58719fcbaf1SConrad Meyer         int i;
58819fcbaf1SConrad Meyer         /* End signal */
58919fcbaf1SConrad Meyer         if (sequence.offset == 0)
5900c16b537SWarner Losh             break;
59119fcbaf1SConrad Meyer 
59219fcbaf1SConrad Meyer         assert(ip + sequence.litLength + sequence.matchLength <= iend);
59319fcbaf1SConrad Meyer 
59419fcbaf1SConrad Meyer         /* Fill tables for block compressor */
59519fcbaf1SConrad Meyer         ZSTD_ldm_limitTableUpdate(ms, ip);
5960f743729SConrad Meyer         ZSTD_ldm_fillFastTables(ms, ip);
59719fcbaf1SConrad Meyer         /* Run the block compressor */
598*37f1f268SConrad Meyer         DEBUGLOG(5, "pos %u : calling block compressor on segment of size %u", (unsigned)(ip-istart), sequence.litLength);
59919fcbaf1SConrad Meyer         {
60019fcbaf1SConrad Meyer             size_t const newLitLength =
6010f743729SConrad Meyer                 blockCompressor(ms, seqStore, rep, ip, sequence.litLength);
60219fcbaf1SConrad Meyer             ip += sequence.litLength;
60319fcbaf1SConrad Meyer             /* Update the repcodes */
60419fcbaf1SConrad Meyer             for (i = ZSTD_REP_NUM - 1; i > 0; i--)
60519fcbaf1SConrad Meyer                 rep[i] = rep[i-1];
60619fcbaf1SConrad Meyer             rep[0] = sequence.offset;
60719fcbaf1SConrad Meyer             /* Store the sequence */
6089cbefe25SConrad Meyer             ZSTD_storeSeq(seqStore, newLitLength, ip - newLitLength, iend,
60919fcbaf1SConrad Meyer                           sequence.offset + ZSTD_REP_MOVE,
61019fcbaf1SConrad Meyer                           sequence.matchLength - MINMATCH);
61119fcbaf1SConrad Meyer             ip += sequence.matchLength;
6120c16b537SWarner Losh         }
6130c16b537SWarner Losh     }
61419fcbaf1SConrad Meyer     /* Fill the tables for the block compressor */
61519fcbaf1SConrad Meyer     ZSTD_ldm_limitTableUpdate(ms, ip);
6160f743729SConrad Meyer     ZSTD_ldm_fillFastTables(ms, ip);
61719fcbaf1SConrad Meyer     /* Compress the last literals */
6180f743729SConrad Meyer     return blockCompressor(ms, seqStore, rep, ip, iend - ip);
6190c16b537SWarner Losh }
620