xref: /freebsd/sys/contrib/zstd/lib/compress/zstd_ldm.c (revision 0f743729abbfc5c9d78a713f72241a4d4bd601ec)
10c16b537SWarner Losh /*
20c16b537SWarner Losh  * Copyright (c) 2016-present, 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).
80c16b537SWarner Losh  */
90c16b537SWarner Losh 
100c16b537SWarner Losh #include "zstd_ldm.h"
110c16b537SWarner Losh 
12*0f743729SConrad Meyer #include "debug.h"
130c16b537SWarner Losh #include "zstd_fast.h"          /* ZSTD_fillHashTable() */
140c16b537SWarner Losh #include "zstd_double_fast.h"   /* ZSTD_fillDoubleHashTable() */
150c16b537SWarner Losh 
160c16b537SWarner Losh #define LDM_BUCKET_SIZE_LOG 3
170c16b537SWarner Losh #define LDM_MIN_MATCH_LENGTH 64
180c16b537SWarner Losh #define LDM_HASH_RLOG 7
190c16b537SWarner Losh #define LDM_HASH_CHAR_OFFSET 10
200c16b537SWarner Losh 
2119fcbaf1SConrad Meyer void ZSTD_ldm_adjustParameters(ldmParams_t* params,
2219fcbaf1SConrad Meyer                                ZSTD_compressionParameters const* cParams)
230c16b537SWarner Losh {
24*0f743729SConrad Meyer     params->windowLog = cParams->windowLog;
250c16b537SWarner Losh     ZSTD_STATIC_ASSERT(LDM_BUCKET_SIZE_LOG <= ZSTD_LDM_BUCKETSIZELOG_MAX);
2619fcbaf1SConrad Meyer     DEBUGLOG(4, "ZSTD_ldm_adjustParameters");
2719fcbaf1SConrad Meyer     if (!params->bucketSizeLog) params->bucketSizeLog = LDM_BUCKET_SIZE_LOG;
2819fcbaf1SConrad Meyer     if (!params->minMatchLength) params->minMatchLength = LDM_MIN_MATCH_LENGTH;
2919fcbaf1SConrad Meyer     if (cParams->strategy >= ZSTD_btopt) {
3019fcbaf1SConrad Meyer       /* Get out of the way of the optimal parser */
3119fcbaf1SConrad Meyer       U32 const minMatch = MAX(cParams->targetLength, params->minMatchLength);
3219fcbaf1SConrad Meyer       assert(minMatch >= ZSTD_LDM_MINMATCH_MIN);
3319fcbaf1SConrad Meyer       assert(minMatch <= ZSTD_LDM_MINMATCH_MAX);
3419fcbaf1SConrad Meyer       params->minMatchLength = minMatch;
350c16b537SWarner Losh     }
360c16b537SWarner Losh     if (params->hashLog == 0) {
37*0f743729SConrad Meyer         params->hashLog = MAX(ZSTD_HASHLOG_MIN, params->windowLog - LDM_HASH_RLOG);
380c16b537SWarner Losh         assert(params->hashLog <= ZSTD_HASHLOG_MAX);
390c16b537SWarner Losh     }
4019fcbaf1SConrad Meyer     if (params->hashEveryLog == 0) {
41*0f743729SConrad Meyer         params->hashEveryLog = params->windowLog < params->hashLog
42*0f743729SConrad Meyer                                    ? 0
43*0f743729SConrad Meyer                                    : params->windowLog - params->hashLog;
440c16b537SWarner Losh     }
450c16b537SWarner Losh     params->bucketSizeLog = MIN(params->bucketSizeLog, params->hashLog);
460c16b537SWarner Losh }
470c16b537SWarner Losh 
4819fcbaf1SConrad Meyer size_t ZSTD_ldm_getTableSize(ldmParams_t params)
4919fcbaf1SConrad Meyer {
5019fcbaf1SConrad Meyer     size_t const ldmHSize = ((size_t)1) << params.hashLog;
5119fcbaf1SConrad Meyer     size_t const ldmBucketSizeLog = MIN(params.bucketSizeLog, params.hashLog);
520c16b537SWarner Losh     size_t const ldmBucketSize =
5319fcbaf1SConrad Meyer         ((size_t)1) << (params.hashLog - ldmBucketSizeLog);
5419fcbaf1SConrad Meyer     size_t const totalSize = ldmBucketSize + ldmHSize * sizeof(ldmEntry_t);
5519fcbaf1SConrad Meyer     return params.enableLdm ? totalSize : 0;
5619fcbaf1SConrad Meyer }
5719fcbaf1SConrad Meyer 
5819fcbaf1SConrad Meyer size_t ZSTD_ldm_getMaxNbSeq(ldmParams_t params, size_t maxChunkSize)
5919fcbaf1SConrad Meyer {
6019fcbaf1SConrad Meyer     return params.enableLdm ? (maxChunkSize / params.minMatchLength) : 0;
610c16b537SWarner Losh }
620c16b537SWarner Losh 
630c16b537SWarner Losh /** ZSTD_ldm_getSmallHash() :
640c16b537SWarner Losh  *  numBits should be <= 32
650c16b537SWarner Losh  *  If numBits==0, returns 0.
660c16b537SWarner Losh  *  @return : the most significant numBits of value. */
670c16b537SWarner Losh static U32 ZSTD_ldm_getSmallHash(U64 value, U32 numBits)
680c16b537SWarner Losh {
690c16b537SWarner Losh     assert(numBits <= 32);
700c16b537SWarner Losh     return numBits == 0 ? 0 : (U32)(value >> (64 - numBits));
710c16b537SWarner Losh }
720c16b537SWarner Losh 
730c16b537SWarner Losh /** ZSTD_ldm_getChecksum() :
740c16b537SWarner Losh  *  numBitsToDiscard should be <= 32
750c16b537SWarner Losh  *  @return : the next most significant 32 bits after numBitsToDiscard */
760c16b537SWarner Losh static U32 ZSTD_ldm_getChecksum(U64 hash, U32 numBitsToDiscard)
770c16b537SWarner Losh {
780c16b537SWarner Losh     assert(numBitsToDiscard <= 32);
790c16b537SWarner Losh     return (hash >> (64 - 32 - numBitsToDiscard)) & 0xFFFFFFFF;
800c16b537SWarner Losh }
810c16b537SWarner Losh 
820c16b537SWarner Losh /** ZSTD_ldm_getTag() ;
830c16b537SWarner Losh  *  Given the hash, returns the most significant numTagBits bits
840c16b537SWarner Losh  *  after (32 + hbits) bits.
850c16b537SWarner Losh  *
860c16b537SWarner Losh  *  If there are not enough bits remaining, return the last
870c16b537SWarner Losh  *  numTagBits bits. */
880c16b537SWarner Losh static U32 ZSTD_ldm_getTag(U64 hash, U32 hbits, U32 numTagBits)
890c16b537SWarner Losh {
900c16b537SWarner Losh     assert(numTagBits < 32 && hbits <= 32);
910c16b537SWarner Losh     if (32 - hbits < numTagBits) {
920c16b537SWarner Losh         return hash & (((U32)1 << numTagBits) - 1);
930c16b537SWarner Losh     } else {
940c16b537SWarner Losh         return (hash >> (32 - hbits - numTagBits)) & (((U32)1 << numTagBits) - 1);
950c16b537SWarner Losh     }
960c16b537SWarner Losh }
970c16b537SWarner Losh 
980c16b537SWarner Losh /** ZSTD_ldm_getBucket() :
990c16b537SWarner Losh  *  Returns a pointer to the start of the bucket associated with hash. */
1000c16b537SWarner Losh static ldmEntry_t* ZSTD_ldm_getBucket(
1010c16b537SWarner Losh         ldmState_t* ldmState, size_t hash, ldmParams_t const ldmParams)
1020c16b537SWarner Losh {
1030c16b537SWarner Losh     return ldmState->hashTable + (hash << ldmParams.bucketSizeLog);
1040c16b537SWarner Losh }
1050c16b537SWarner Losh 
1060c16b537SWarner Losh /** ZSTD_ldm_insertEntry() :
1070c16b537SWarner Losh  *  Insert the entry with corresponding hash into the hash table */
1080c16b537SWarner Losh static void ZSTD_ldm_insertEntry(ldmState_t* ldmState,
1090c16b537SWarner Losh                                  size_t const hash, const ldmEntry_t entry,
1100c16b537SWarner Losh                                  ldmParams_t const ldmParams)
1110c16b537SWarner Losh {
1120c16b537SWarner Losh     BYTE* const bucketOffsets = ldmState->bucketOffsets;
1130c16b537SWarner Losh     *(ZSTD_ldm_getBucket(ldmState, hash, ldmParams) + bucketOffsets[hash]) = entry;
1140c16b537SWarner Losh     bucketOffsets[hash]++;
1150c16b537SWarner Losh     bucketOffsets[hash] &= ((U32)1 << ldmParams.bucketSizeLog) - 1;
1160c16b537SWarner Losh }
1170c16b537SWarner Losh 
1180c16b537SWarner Losh /** ZSTD_ldm_makeEntryAndInsertByTag() :
1190c16b537SWarner Losh  *
1200c16b537SWarner Losh  *  Gets the small hash, checksum, and tag from the rollingHash.
1210c16b537SWarner Losh  *
1220c16b537SWarner Losh  *  If the tag matches (1 << ldmParams.hashEveryLog)-1, then
1230c16b537SWarner Losh  *  creates an ldmEntry from the offset, and inserts it into the hash table.
1240c16b537SWarner Losh  *
1250c16b537SWarner Losh  *  hBits is the length of the small hash, which is the most significant hBits
1260c16b537SWarner Losh  *  of rollingHash. The checksum is the next 32 most significant bits, followed
1270c16b537SWarner Losh  *  by ldmParams.hashEveryLog bits that make up the tag. */
1280c16b537SWarner Losh static void ZSTD_ldm_makeEntryAndInsertByTag(ldmState_t* ldmState,
1290c16b537SWarner Losh                                              U64 const rollingHash,
1300c16b537SWarner Losh                                              U32 const hBits,
1310c16b537SWarner Losh                                              U32 const offset,
1320c16b537SWarner Losh                                              ldmParams_t const ldmParams)
1330c16b537SWarner Losh {
1340c16b537SWarner Losh     U32 const tag = ZSTD_ldm_getTag(rollingHash, hBits, ldmParams.hashEveryLog);
1350c16b537SWarner Losh     U32 const tagMask = ((U32)1 << ldmParams.hashEveryLog) - 1;
1360c16b537SWarner Losh     if (tag == tagMask) {
1370c16b537SWarner Losh         U32 const hash = ZSTD_ldm_getSmallHash(rollingHash, hBits);
1380c16b537SWarner Losh         U32 const checksum = ZSTD_ldm_getChecksum(rollingHash, hBits);
1390c16b537SWarner Losh         ldmEntry_t entry;
1400c16b537SWarner Losh         entry.offset = offset;
1410c16b537SWarner Losh         entry.checksum = checksum;
1420c16b537SWarner Losh         ZSTD_ldm_insertEntry(ldmState, hash, entry, ldmParams);
1430c16b537SWarner Losh     }
1440c16b537SWarner Losh }
1450c16b537SWarner Losh 
1460c16b537SWarner Losh /** ZSTD_ldm_getRollingHash() :
1470c16b537SWarner Losh  *  Get a 64-bit hash using the first len bytes from buf.
1480c16b537SWarner Losh  *
1490c16b537SWarner Losh  *  Giving bytes s = s_1, s_2, ... s_k, the hash is defined to be
1500c16b537SWarner Losh  *  H(s) = s_1*(a^(k-1)) + s_2*(a^(k-2)) + ... + s_k*(a^0)
1510c16b537SWarner Losh  *
1520c16b537SWarner Losh  *  where the constant a is defined to be prime8bytes.
1530c16b537SWarner Losh  *
1540c16b537SWarner Losh  *  The implementation adds an offset to each byte, so
1550c16b537SWarner Losh  *  H(s) = (s_1 + HASH_CHAR_OFFSET)*(a^(k-1)) + ... */
1560c16b537SWarner Losh static U64 ZSTD_ldm_getRollingHash(const BYTE* buf, U32 len)
1570c16b537SWarner Losh {
1580c16b537SWarner Losh     U64 ret = 0;
1590c16b537SWarner Losh     U32 i;
1600c16b537SWarner Losh     for (i = 0; i < len; i++) {
1610c16b537SWarner Losh         ret *= prime8bytes;
1620c16b537SWarner Losh         ret += buf[i] + LDM_HASH_CHAR_OFFSET;
1630c16b537SWarner Losh     }
1640c16b537SWarner Losh     return ret;
1650c16b537SWarner Losh }
1660c16b537SWarner Losh 
1670c16b537SWarner Losh /** ZSTD_ldm_ipow() :
1680c16b537SWarner Losh  *  Return base^exp. */
1690c16b537SWarner Losh static U64 ZSTD_ldm_ipow(U64 base, U64 exp)
1700c16b537SWarner Losh {
1710c16b537SWarner Losh     U64 ret = 1;
1720c16b537SWarner Losh     while (exp) {
1730c16b537SWarner Losh         if (exp & 1) { ret *= base; }
1740c16b537SWarner Losh         exp >>= 1;
1750c16b537SWarner Losh         base *= base;
1760c16b537SWarner Losh     }
1770c16b537SWarner Losh     return ret;
1780c16b537SWarner Losh }
1790c16b537SWarner Losh 
1800c16b537SWarner Losh U64 ZSTD_ldm_getHashPower(U32 minMatchLength) {
18119fcbaf1SConrad Meyer     DEBUGLOG(4, "ZSTD_ldm_getHashPower: mml=%u", minMatchLength);
1820c16b537SWarner Losh     assert(minMatchLength >= ZSTD_LDM_MINMATCH_MIN);
1830c16b537SWarner Losh     return ZSTD_ldm_ipow(prime8bytes, minMatchLength - 1);
1840c16b537SWarner Losh }
1850c16b537SWarner Losh 
1860c16b537SWarner Losh /** ZSTD_ldm_updateHash() :
1870c16b537SWarner Losh  *  Updates hash by removing toRemove and adding toAdd. */
1880c16b537SWarner Losh static U64 ZSTD_ldm_updateHash(U64 hash, BYTE toRemove, BYTE toAdd, U64 hashPower)
1890c16b537SWarner Losh {
1900c16b537SWarner Losh     hash -= ((toRemove + LDM_HASH_CHAR_OFFSET) * hashPower);
1910c16b537SWarner Losh     hash *= prime8bytes;
1920c16b537SWarner Losh     hash += toAdd + LDM_HASH_CHAR_OFFSET;
1930c16b537SWarner Losh     return hash;
1940c16b537SWarner Losh }
1950c16b537SWarner Losh 
1960c16b537SWarner Losh /** ZSTD_ldm_countBackwardsMatch() :
1970c16b537SWarner Losh  *  Returns the number of bytes that match backwards before pIn and pMatch.
1980c16b537SWarner Losh  *
1990c16b537SWarner Losh  *  We count only bytes where pMatch >= pBase and pIn >= pAnchor. */
2000c16b537SWarner Losh static size_t ZSTD_ldm_countBackwardsMatch(
2010c16b537SWarner Losh             const BYTE* pIn, const BYTE* pAnchor,
2020c16b537SWarner Losh             const BYTE* pMatch, const BYTE* pBase)
2030c16b537SWarner Losh {
2040c16b537SWarner Losh     size_t matchLength = 0;
2050c16b537SWarner Losh     while (pIn > pAnchor && pMatch > pBase && pIn[-1] == pMatch[-1]) {
2060c16b537SWarner Losh         pIn--;
2070c16b537SWarner Losh         pMatch--;
2080c16b537SWarner Losh         matchLength++;
2090c16b537SWarner Losh     }
2100c16b537SWarner Losh     return matchLength;
2110c16b537SWarner Losh }
2120c16b537SWarner Losh 
2130c16b537SWarner Losh /** ZSTD_ldm_fillFastTables() :
2140c16b537SWarner Losh  *
2150c16b537SWarner Losh  *  Fills the relevant tables for the ZSTD_fast and ZSTD_dfast strategies.
2160c16b537SWarner Losh  *  This is similar to ZSTD_loadDictionaryContent.
2170c16b537SWarner Losh  *
2180c16b537SWarner Losh  *  The tables for the other strategies are filled within their
2190c16b537SWarner Losh  *  block compressors. */
22019fcbaf1SConrad Meyer static size_t ZSTD_ldm_fillFastTables(ZSTD_matchState_t* ms,
22119fcbaf1SConrad Meyer                                       void const* end)
2220c16b537SWarner Losh {
2230c16b537SWarner Losh     const BYTE* const iend = (const BYTE*)end;
2240c16b537SWarner Losh 
225*0f743729SConrad Meyer     switch(ms->cParams.strategy)
2260c16b537SWarner Losh     {
2270c16b537SWarner Losh     case ZSTD_fast:
228*0f743729SConrad Meyer         ZSTD_fillHashTable(ms, iend, ZSTD_dtlm_fast);
2290c16b537SWarner Losh         break;
2300c16b537SWarner Losh 
2310c16b537SWarner Losh     case ZSTD_dfast:
232*0f743729SConrad Meyer         ZSTD_fillDoubleHashTable(ms, iend, ZSTD_dtlm_fast);
2330c16b537SWarner Losh         break;
2340c16b537SWarner Losh 
2350c16b537SWarner Losh     case ZSTD_greedy:
2360c16b537SWarner Losh     case ZSTD_lazy:
2370c16b537SWarner Losh     case ZSTD_lazy2:
2380c16b537SWarner Losh     case ZSTD_btlazy2:
2390c16b537SWarner Losh     case ZSTD_btopt:
2400c16b537SWarner Losh     case ZSTD_btultra:
2410c16b537SWarner Losh         break;
2420c16b537SWarner Losh     default:
2430c16b537SWarner Losh         assert(0);  /* not possible : not a valid strategy id */
2440c16b537SWarner Losh     }
2450c16b537SWarner Losh 
2460c16b537SWarner Losh     return 0;
2470c16b537SWarner Losh }
2480c16b537SWarner Losh 
2490c16b537SWarner Losh /** ZSTD_ldm_fillLdmHashTable() :
2500c16b537SWarner Losh  *
2510c16b537SWarner Losh  *  Fills hashTable from (lastHashed + 1) to iend (non-inclusive).
2520c16b537SWarner Losh  *  lastHash is the rolling hash that corresponds to lastHashed.
2530c16b537SWarner Losh  *
2540c16b537SWarner Losh  *  Returns the rolling hash corresponding to position iend-1. */
2550c16b537SWarner Losh static U64 ZSTD_ldm_fillLdmHashTable(ldmState_t* state,
2560c16b537SWarner Losh                                      U64 lastHash, const BYTE* lastHashed,
2570c16b537SWarner Losh                                      const BYTE* iend, const BYTE* base,
2580c16b537SWarner Losh                                      U32 hBits, ldmParams_t const ldmParams)
2590c16b537SWarner Losh {
2600c16b537SWarner Losh     U64 rollingHash = lastHash;
2610c16b537SWarner Losh     const BYTE* cur = lastHashed + 1;
2620c16b537SWarner Losh 
2630c16b537SWarner Losh     while (cur < iend) {
2640c16b537SWarner Losh         rollingHash = ZSTD_ldm_updateHash(rollingHash, cur[-1],
2650c16b537SWarner Losh                                           cur[ldmParams.minMatchLength-1],
2660c16b537SWarner Losh                                           state->hashPower);
2670c16b537SWarner Losh         ZSTD_ldm_makeEntryAndInsertByTag(state,
2680c16b537SWarner Losh                                          rollingHash, hBits,
2690c16b537SWarner Losh                                          (U32)(cur - base), ldmParams);
2700c16b537SWarner Losh         ++cur;
2710c16b537SWarner Losh     }
2720c16b537SWarner Losh     return rollingHash;
2730c16b537SWarner Losh }
2740c16b537SWarner Losh 
2750c16b537SWarner Losh 
2760c16b537SWarner Losh /** ZSTD_ldm_limitTableUpdate() :
2770c16b537SWarner Losh  *
2780c16b537SWarner Losh  *  Sets cctx->nextToUpdate to a position corresponding closer to anchor
2790c16b537SWarner Losh  *  if it is far way
2800c16b537SWarner Losh  *  (after a long match, only update tables a limited amount). */
28119fcbaf1SConrad Meyer static void ZSTD_ldm_limitTableUpdate(ZSTD_matchState_t* ms, const BYTE* anchor)
2820c16b537SWarner Losh {
28319fcbaf1SConrad Meyer     U32 const current = (U32)(anchor - ms->window.base);
28419fcbaf1SConrad Meyer     if (current > ms->nextToUpdate + 1024) {
28519fcbaf1SConrad Meyer         ms->nextToUpdate =
28619fcbaf1SConrad Meyer             current - MIN(512, current - ms->nextToUpdate - 1024);
2870c16b537SWarner Losh     }
2880c16b537SWarner Losh }
2890c16b537SWarner Losh 
29019fcbaf1SConrad Meyer static size_t ZSTD_ldm_generateSequences_internal(
29119fcbaf1SConrad Meyer         ldmState_t* ldmState, rawSeqStore_t* rawSeqStore,
29219fcbaf1SConrad Meyer         ldmParams_t const* params, void const* src, size_t srcSize)
2930c16b537SWarner Losh {
29419fcbaf1SConrad Meyer     /* LDM parameters */
29519fcbaf1SConrad Meyer     int const extDict = ZSTD_window_hasExtDict(ldmState->window);
29619fcbaf1SConrad Meyer     U32 const minMatchLength = params->minMatchLength;
29719fcbaf1SConrad Meyer     U64 const hashPower = ldmState->hashPower;
29819fcbaf1SConrad Meyer     U32 const hBits = params->hashLog - params->bucketSizeLog;
29919fcbaf1SConrad Meyer     U32 const ldmBucketSize = 1U << params->bucketSizeLog;
30019fcbaf1SConrad Meyer     U32 const hashEveryLog = params->hashEveryLog;
30119fcbaf1SConrad Meyer     U32 const ldmTagMask = (1U << params->hashEveryLog) - 1;
30219fcbaf1SConrad Meyer     /* Prefix and extDict parameters */
30319fcbaf1SConrad Meyer     U32 const dictLimit = ldmState->window.dictLimit;
30419fcbaf1SConrad Meyer     U32 const lowestIndex = extDict ? ldmState->window.lowLimit : dictLimit;
30519fcbaf1SConrad Meyer     BYTE const* const base = ldmState->window.base;
30619fcbaf1SConrad Meyer     BYTE const* const dictBase = extDict ? ldmState->window.dictBase : NULL;
30719fcbaf1SConrad Meyer     BYTE const* const dictStart = extDict ? dictBase + lowestIndex : NULL;
30819fcbaf1SConrad Meyer     BYTE const* const dictEnd = extDict ? dictBase + dictLimit : NULL;
30919fcbaf1SConrad Meyer     BYTE const* const lowPrefixPtr = base + dictLimit;
31019fcbaf1SConrad Meyer     /* Input bounds */
31119fcbaf1SConrad Meyer     BYTE const* const istart = (BYTE const*)src;
31219fcbaf1SConrad Meyer     BYTE const* const iend = istart + srcSize;
31319fcbaf1SConrad Meyer     BYTE const* const ilimit = iend - MAX(minMatchLength, HASH_READ_SIZE);
31419fcbaf1SConrad Meyer     /* Input positions */
31519fcbaf1SConrad Meyer     BYTE const* anchor = istart;
31619fcbaf1SConrad Meyer     BYTE const* ip = istart;
31719fcbaf1SConrad Meyer     /* Rolling hash */
31819fcbaf1SConrad Meyer     BYTE const* lastHashed = NULL;
3190c16b537SWarner Losh     U64 rollingHash = 0;
3200c16b537SWarner Losh 
32119fcbaf1SConrad Meyer     while (ip <= ilimit) {
3220c16b537SWarner Losh         size_t mLength;
3230c16b537SWarner Losh         U32 const current = (U32)(ip - base);
3240c16b537SWarner Losh         size_t forwardMatchLength = 0, backwardMatchLength = 0;
3250c16b537SWarner Losh         ldmEntry_t* bestEntry = NULL;
3260c16b537SWarner Losh         if (ip != istart) {
3270c16b537SWarner Losh             rollingHash = ZSTD_ldm_updateHash(rollingHash, lastHashed[0],
32819fcbaf1SConrad Meyer                                               lastHashed[minMatchLength],
3290c16b537SWarner Losh                                               hashPower);
3300c16b537SWarner Losh         } else {
33119fcbaf1SConrad Meyer             rollingHash = ZSTD_ldm_getRollingHash(ip, minMatchLength);
3320c16b537SWarner Losh         }
3330c16b537SWarner Losh         lastHashed = ip;
3340c16b537SWarner Losh 
3350c16b537SWarner Losh         /* Do not insert and do not look for a match */
33619fcbaf1SConrad Meyer         if (ZSTD_ldm_getTag(rollingHash, hBits, hashEveryLog) != ldmTagMask) {
3370c16b537SWarner Losh            ip++;
3380c16b537SWarner Losh            continue;
3390c16b537SWarner Losh         }
3400c16b537SWarner Losh 
3410c16b537SWarner Losh         /* Get the best entry and compute the match lengths */
3420c16b537SWarner Losh         {
3430c16b537SWarner Losh             ldmEntry_t* const bucket =
3440c16b537SWarner Losh                 ZSTD_ldm_getBucket(ldmState,
3450c16b537SWarner Losh                                    ZSTD_ldm_getSmallHash(rollingHash, hBits),
34619fcbaf1SConrad Meyer                                    *params);
3470c16b537SWarner Losh             ldmEntry_t* cur;
3480c16b537SWarner Losh             size_t bestMatchLength = 0;
3490c16b537SWarner Losh             U32 const checksum = ZSTD_ldm_getChecksum(rollingHash, hBits);
3500c16b537SWarner Losh 
3510c16b537SWarner Losh             for (cur = bucket; cur < bucket + ldmBucketSize; ++cur) {
3520c16b537SWarner Losh                 size_t curForwardMatchLength, curBackwardMatchLength,
3530c16b537SWarner Losh                        curTotalMatchLength;
3540c16b537SWarner Losh                 if (cur->checksum != checksum || cur->offset <= lowestIndex) {
3550c16b537SWarner Losh                     continue;
3560c16b537SWarner Losh                 }
35719fcbaf1SConrad Meyer                 if (extDict) {
35819fcbaf1SConrad Meyer                     BYTE const* const curMatchBase =
35919fcbaf1SConrad Meyer                         cur->offset < dictLimit ? dictBase : base;
36019fcbaf1SConrad Meyer                     BYTE const* const pMatch = curMatchBase + cur->offset;
36119fcbaf1SConrad Meyer                     BYTE const* const matchEnd =
36219fcbaf1SConrad Meyer                         cur->offset < dictLimit ? dictEnd : iend;
36319fcbaf1SConrad Meyer                     BYTE const* const lowMatchPtr =
36419fcbaf1SConrad Meyer                         cur->offset < dictLimit ? dictStart : lowPrefixPtr;
3650c16b537SWarner Losh 
36619fcbaf1SConrad Meyer                     curForwardMatchLength = ZSTD_count_2segments(
36719fcbaf1SConrad Meyer                                                 ip, pMatch, iend,
36819fcbaf1SConrad Meyer                                                 matchEnd, lowPrefixPtr);
36919fcbaf1SConrad Meyer                     if (curForwardMatchLength < minMatchLength) {
3700c16b537SWarner Losh                         continue;
3710c16b537SWarner Losh                     }
37219fcbaf1SConrad Meyer                     curBackwardMatchLength =
37319fcbaf1SConrad Meyer                         ZSTD_ldm_countBackwardsMatch(ip, anchor, pMatch,
37419fcbaf1SConrad Meyer                                                      lowMatchPtr);
3750c16b537SWarner Losh                     curTotalMatchLength = curForwardMatchLength +
3760c16b537SWarner Losh                                           curBackwardMatchLength;
37719fcbaf1SConrad Meyer                 } else { /* !extDict */
37819fcbaf1SConrad Meyer                     BYTE const* const pMatch = base + cur->offset;
37919fcbaf1SConrad Meyer                     curForwardMatchLength = ZSTD_count(ip, pMatch, iend);
38019fcbaf1SConrad Meyer                     if (curForwardMatchLength < minMatchLength) {
38119fcbaf1SConrad Meyer                         continue;
38219fcbaf1SConrad Meyer                     }
38319fcbaf1SConrad Meyer                     curBackwardMatchLength =
38419fcbaf1SConrad Meyer                         ZSTD_ldm_countBackwardsMatch(ip, anchor, pMatch,
38519fcbaf1SConrad Meyer                                                      lowPrefixPtr);
38619fcbaf1SConrad Meyer                     curTotalMatchLength = curForwardMatchLength +
38719fcbaf1SConrad Meyer                                           curBackwardMatchLength;
38819fcbaf1SConrad Meyer                 }
3890c16b537SWarner Losh 
3900c16b537SWarner Losh                 if (curTotalMatchLength > bestMatchLength) {
3910c16b537SWarner Losh                     bestMatchLength = curTotalMatchLength;
3920c16b537SWarner Losh                     forwardMatchLength = curForwardMatchLength;
3930c16b537SWarner Losh                     backwardMatchLength = curBackwardMatchLength;
3940c16b537SWarner Losh                     bestEntry = cur;
3950c16b537SWarner Losh                 }
3960c16b537SWarner Losh             }
3970c16b537SWarner Losh         }
3980c16b537SWarner Losh 
3990c16b537SWarner Losh         /* No match found -- continue searching */
4000c16b537SWarner Losh         if (bestEntry == NULL) {
4010c16b537SWarner Losh             ZSTD_ldm_makeEntryAndInsertByTag(ldmState, rollingHash,
4020c16b537SWarner Losh                                              hBits, current,
40319fcbaf1SConrad Meyer                                              *params);
4040c16b537SWarner Losh             ip++;
4050c16b537SWarner Losh             continue;
4060c16b537SWarner Losh         }
4070c16b537SWarner Losh 
4080c16b537SWarner Losh         /* Match found */
4090c16b537SWarner Losh         mLength = forwardMatchLength + backwardMatchLength;
4100c16b537SWarner Losh         ip -= backwardMatchLength;
4110c16b537SWarner Losh 
4120c16b537SWarner Losh         {
41319fcbaf1SConrad Meyer             /* Store the sequence:
41419fcbaf1SConrad Meyer              * ip = current - backwardMatchLength
41519fcbaf1SConrad Meyer              * The match is at (bestEntry->offset - backwardMatchLength)
41619fcbaf1SConrad Meyer              */
4170c16b537SWarner Losh             U32 const matchIndex = bestEntry->offset;
41819fcbaf1SConrad Meyer             U32 const offset = current - matchIndex;
41919fcbaf1SConrad Meyer             rawSeq* const seq = rawSeqStore->seq + rawSeqStore->size;
4200c16b537SWarner Losh 
42119fcbaf1SConrad Meyer             /* Out of sequence storage */
42219fcbaf1SConrad Meyer             if (rawSeqStore->size == rawSeqStore->capacity)
42319fcbaf1SConrad Meyer                 return ERROR(dstSize_tooSmall);
42419fcbaf1SConrad Meyer             seq->litLength = (U32)(ip - anchor);
42519fcbaf1SConrad Meyer             seq->matchLength = (U32)mLength;
42619fcbaf1SConrad Meyer             seq->offset = offset;
42719fcbaf1SConrad Meyer             rawSeqStore->size++;
4280c16b537SWarner Losh         }
4290c16b537SWarner Losh 
4300c16b537SWarner Losh         /* Insert the current entry into the hash table */
4310c16b537SWarner Losh         ZSTD_ldm_makeEntryAndInsertByTag(ldmState, rollingHash, hBits,
4320c16b537SWarner Losh                                          (U32)(lastHashed - base),
43319fcbaf1SConrad Meyer                                          *params);
4340c16b537SWarner Losh 
4350c16b537SWarner Losh         assert(ip + backwardMatchLength == lastHashed);
4360c16b537SWarner Losh 
4370c16b537SWarner Losh         /* Fill the hash table from lastHashed+1 to ip+mLength*/
4380c16b537SWarner Losh         /* Heuristic: don't need to fill the entire table at end of block */
43919fcbaf1SConrad Meyer         if (ip + mLength <= ilimit) {
4400c16b537SWarner Losh             rollingHash = ZSTD_ldm_fillLdmHashTable(
4410c16b537SWarner Losh                               ldmState, rollingHash, lastHashed,
44219fcbaf1SConrad Meyer                               ip + mLength, base, hBits, *params);
4430c16b537SWarner Losh             lastHashed = ip + mLength - 1;
4440c16b537SWarner Losh         }
4450c16b537SWarner Losh         ip += mLength;
4460c16b537SWarner Losh         anchor = ip;
44719fcbaf1SConrad Meyer     }
44819fcbaf1SConrad Meyer     return iend - anchor;
44919fcbaf1SConrad Meyer }
4500c16b537SWarner Losh 
45119fcbaf1SConrad Meyer /*! ZSTD_ldm_reduceTable() :
45219fcbaf1SConrad Meyer  *  reduce table indexes by `reducerValue` */
45319fcbaf1SConrad Meyer static void ZSTD_ldm_reduceTable(ldmEntry_t* const table, U32 const size,
45419fcbaf1SConrad Meyer                                  U32 const reducerValue)
4550c16b537SWarner Losh {
45619fcbaf1SConrad Meyer     U32 u;
45719fcbaf1SConrad Meyer     for (u = 0; u < size; u++) {
45819fcbaf1SConrad Meyer         if (table[u].offset < reducerValue) table[u].offset = 0;
45919fcbaf1SConrad Meyer         else table[u].offset -= reducerValue;
4600c16b537SWarner Losh     }
4610c16b537SWarner Losh }
4620c16b537SWarner Losh 
46319fcbaf1SConrad Meyer size_t ZSTD_ldm_generateSequences(
46419fcbaf1SConrad Meyer         ldmState_t* ldmState, rawSeqStore_t* sequences,
46519fcbaf1SConrad Meyer         ldmParams_t const* params, void const* src, size_t srcSize)
4660c16b537SWarner Losh {
46719fcbaf1SConrad Meyer     U32 const maxDist = 1U << params->windowLog;
46819fcbaf1SConrad Meyer     BYTE const* const istart = (BYTE const*)src;
46919fcbaf1SConrad Meyer     BYTE const* const iend = istart + srcSize;
47019fcbaf1SConrad Meyer     size_t const kMaxChunkSize = 1 << 20;
47119fcbaf1SConrad Meyer     size_t const nbChunks = (srcSize / kMaxChunkSize) + ((srcSize % kMaxChunkSize) != 0);
47219fcbaf1SConrad Meyer     size_t chunk;
47319fcbaf1SConrad Meyer     size_t leftoverSize = 0;
47419fcbaf1SConrad Meyer 
47519fcbaf1SConrad Meyer     assert(ZSTD_CHUNKSIZE_MAX >= kMaxChunkSize);
47619fcbaf1SConrad Meyer     /* Check that ZSTD_window_update() has been called for this chunk prior
47719fcbaf1SConrad Meyer      * to passing it to this function.
47819fcbaf1SConrad Meyer      */
47919fcbaf1SConrad Meyer     assert(ldmState->window.nextSrc >= (BYTE const*)src + srcSize);
48019fcbaf1SConrad Meyer     /* The input could be very large (in zstdmt), so it must be broken up into
48119fcbaf1SConrad Meyer      * chunks to enforce the maximmum distance and handle overflow correction.
48219fcbaf1SConrad Meyer      */
48319fcbaf1SConrad Meyer     assert(sequences->pos <= sequences->size);
48419fcbaf1SConrad Meyer     assert(sequences->size <= sequences->capacity);
48519fcbaf1SConrad Meyer     for (chunk = 0; chunk < nbChunks && sequences->size < sequences->capacity; ++chunk) {
48619fcbaf1SConrad Meyer         BYTE const* const chunkStart = istart + chunk * kMaxChunkSize;
48719fcbaf1SConrad Meyer         size_t const remaining = (size_t)(iend - chunkStart);
48819fcbaf1SConrad Meyer         BYTE const *const chunkEnd =
48919fcbaf1SConrad Meyer             (remaining < kMaxChunkSize) ? iend : chunkStart + kMaxChunkSize;
49019fcbaf1SConrad Meyer         size_t const chunkSize = chunkEnd - chunkStart;
49119fcbaf1SConrad Meyer         size_t newLeftoverSize;
49219fcbaf1SConrad Meyer         size_t const prevSize = sequences->size;
49319fcbaf1SConrad Meyer 
49419fcbaf1SConrad Meyer         assert(chunkStart < iend);
49519fcbaf1SConrad Meyer         /* 1. Perform overflow correction if necessary. */
49619fcbaf1SConrad Meyer         if (ZSTD_window_needOverflowCorrection(ldmState->window, chunkEnd)) {
49719fcbaf1SConrad Meyer             U32 const ldmHSize = 1U << params->hashLog;
49819fcbaf1SConrad Meyer             U32 const correction = ZSTD_window_correctOverflow(
49919fcbaf1SConrad Meyer                 &ldmState->window, /* cycleLog */ 0, maxDist, src);
50019fcbaf1SConrad Meyer             ZSTD_ldm_reduceTable(ldmState->hashTable, ldmHSize, correction);
5010c16b537SWarner Losh         }
50219fcbaf1SConrad Meyer         /* 2. We enforce the maximum offset allowed.
50319fcbaf1SConrad Meyer          *
50419fcbaf1SConrad Meyer          * kMaxChunkSize should be small enough that we don't lose too much of
50519fcbaf1SConrad Meyer          * the window through early invalidation.
50619fcbaf1SConrad Meyer          * TODO: * Test the chunk size.
50719fcbaf1SConrad Meyer          *       * Try invalidation after the sequence generation and test the
50819fcbaf1SConrad Meyer          *         the offset against maxDist directly.
50919fcbaf1SConrad Meyer          */
510*0f743729SConrad Meyer         ZSTD_window_enforceMaxDist(&ldmState->window, chunkEnd, maxDist, NULL, NULL);
51119fcbaf1SConrad Meyer         /* 3. Generate the sequences for the chunk, and get newLeftoverSize. */
51219fcbaf1SConrad Meyer         newLeftoverSize = ZSTD_ldm_generateSequences_internal(
51319fcbaf1SConrad Meyer             ldmState, sequences, params, chunkStart, chunkSize);
51419fcbaf1SConrad Meyer         if (ZSTD_isError(newLeftoverSize))
51519fcbaf1SConrad Meyer             return newLeftoverSize;
51619fcbaf1SConrad Meyer         /* 4. We add the leftover literals from previous iterations to the first
51719fcbaf1SConrad Meyer          *    newly generated sequence, or add the `newLeftoverSize` if none are
51819fcbaf1SConrad Meyer          *    generated.
51919fcbaf1SConrad Meyer          */
52019fcbaf1SConrad Meyer         /* Prepend the leftover literals from the last call */
52119fcbaf1SConrad Meyer         if (prevSize < sequences->size) {
52219fcbaf1SConrad Meyer             sequences->seq[prevSize].litLength += (U32)leftoverSize;
52319fcbaf1SConrad Meyer             leftoverSize = newLeftoverSize;
5240c16b537SWarner Losh         } else {
52519fcbaf1SConrad Meyer             assert(newLeftoverSize == chunkSize);
52619fcbaf1SConrad Meyer             leftoverSize += chunkSize;
5270c16b537SWarner Losh         }
52819fcbaf1SConrad Meyer     }
52919fcbaf1SConrad Meyer     return 0;
5300c16b537SWarner Losh }
5310c16b537SWarner Losh 
53219fcbaf1SConrad Meyer void ZSTD_ldm_skipSequences(rawSeqStore_t* rawSeqStore, size_t srcSize, U32 const minMatch) {
53319fcbaf1SConrad Meyer     while (srcSize > 0 && rawSeqStore->pos < rawSeqStore->size) {
53419fcbaf1SConrad Meyer         rawSeq* seq = rawSeqStore->seq + rawSeqStore->pos;
53519fcbaf1SConrad Meyer         if (srcSize <= seq->litLength) {
53619fcbaf1SConrad Meyer             /* Skip past srcSize literals */
53719fcbaf1SConrad Meyer             seq->litLength -= (U32)srcSize;
53819fcbaf1SConrad Meyer             return;
53919fcbaf1SConrad Meyer         }
54019fcbaf1SConrad Meyer         srcSize -= seq->litLength;
54119fcbaf1SConrad Meyer         seq->litLength = 0;
54219fcbaf1SConrad Meyer         if (srcSize < seq->matchLength) {
54319fcbaf1SConrad Meyer             /* Skip past the first srcSize of the match */
54419fcbaf1SConrad Meyer             seq->matchLength -= (U32)srcSize;
54519fcbaf1SConrad Meyer             if (seq->matchLength < minMatch) {
54619fcbaf1SConrad Meyer                 /* The match is too short, omit it */
54719fcbaf1SConrad Meyer                 if (rawSeqStore->pos + 1 < rawSeqStore->size) {
54819fcbaf1SConrad Meyer                     seq[1].litLength += seq[0].matchLength;
54919fcbaf1SConrad Meyer                 }
55019fcbaf1SConrad Meyer                 rawSeqStore->pos++;
55119fcbaf1SConrad Meyer             }
55219fcbaf1SConrad Meyer             return;
55319fcbaf1SConrad Meyer         }
55419fcbaf1SConrad Meyer         srcSize -= seq->matchLength;
55519fcbaf1SConrad Meyer         seq->matchLength = 0;
55619fcbaf1SConrad Meyer         rawSeqStore->pos++;
55719fcbaf1SConrad Meyer     }
55819fcbaf1SConrad Meyer }
55919fcbaf1SConrad Meyer 
56019fcbaf1SConrad Meyer /**
56119fcbaf1SConrad Meyer  * If the sequence length is longer than remaining then the sequence is split
56219fcbaf1SConrad Meyer  * between this block and the next.
56319fcbaf1SConrad Meyer  *
56419fcbaf1SConrad Meyer  * Returns the current sequence to handle, or if the rest of the block should
56519fcbaf1SConrad Meyer  * be literals, it returns a sequence with offset == 0.
56619fcbaf1SConrad Meyer  */
56719fcbaf1SConrad Meyer static rawSeq maybeSplitSequence(rawSeqStore_t* rawSeqStore,
56819fcbaf1SConrad Meyer                                  U32 const remaining, U32 const minMatch)
5690c16b537SWarner Losh {
57019fcbaf1SConrad Meyer     rawSeq sequence = rawSeqStore->seq[rawSeqStore->pos];
57119fcbaf1SConrad Meyer     assert(sequence.offset > 0);
57219fcbaf1SConrad Meyer     /* Likely: No partial sequence */
57319fcbaf1SConrad Meyer     if (remaining >= sequence.litLength + sequence.matchLength) {
57419fcbaf1SConrad Meyer         rawSeqStore->pos++;
57519fcbaf1SConrad Meyer         return sequence;
5760c16b537SWarner Losh     }
57719fcbaf1SConrad Meyer     /* Cut the sequence short (offset == 0 ==> rest is literals). */
57819fcbaf1SConrad Meyer     if (remaining <= sequence.litLength) {
57919fcbaf1SConrad Meyer         sequence.offset = 0;
58019fcbaf1SConrad Meyer     } else if (remaining < sequence.litLength + sequence.matchLength) {
58119fcbaf1SConrad Meyer         sequence.matchLength = remaining - sequence.litLength;
58219fcbaf1SConrad Meyer         if (sequence.matchLength < minMatch) {
58319fcbaf1SConrad Meyer             sequence.offset = 0;
5840c16b537SWarner Losh         }
5850c16b537SWarner Losh     }
58619fcbaf1SConrad Meyer     /* Skip past `remaining` bytes for the future sequences. */
58719fcbaf1SConrad Meyer     ZSTD_ldm_skipSequences(rawSeqStore, remaining, minMatch);
58819fcbaf1SConrad Meyer     return sequence;
5890c16b537SWarner Losh }
5900c16b537SWarner Losh 
59119fcbaf1SConrad Meyer size_t ZSTD_ldm_blockCompress(rawSeqStore_t* rawSeqStore,
59219fcbaf1SConrad Meyer     ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
593*0f743729SConrad Meyer     void const* src, size_t srcSize)
5940c16b537SWarner Losh {
595*0f743729SConrad Meyer     const ZSTD_compressionParameters* const cParams = &ms->cParams;
59619fcbaf1SConrad Meyer     unsigned const minMatch = cParams->searchLength;
59719fcbaf1SConrad Meyer     ZSTD_blockCompressor const blockCompressor =
598*0f743729SConrad Meyer         ZSTD_selectBlockCompressor(cParams->strategy, ZSTD_matchState_dictMode(ms));
59919fcbaf1SConrad Meyer     /* Input bounds */
60019fcbaf1SConrad Meyer     BYTE const* const istart = (BYTE const*)src;
60119fcbaf1SConrad Meyer     BYTE const* const iend = istart + srcSize;
60219fcbaf1SConrad Meyer     /* Input positions */
60319fcbaf1SConrad Meyer     BYTE const* ip = istart;
6040c16b537SWarner Losh 
605*0f743729SConrad Meyer     DEBUGLOG(5, "ZSTD_ldm_blockCompress: srcSize=%zu", srcSize);
60619fcbaf1SConrad Meyer     assert(rawSeqStore->pos <= rawSeqStore->size);
60719fcbaf1SConrad Meyer     assert(rawSeqStore->size <= rawSeqStore->capacity);
60819fcbaf1SConrad Meyer     /* Loop through each sequence and apply the block compressor to the lits */
60919fcbaf1SConrad Meyer     while (rawSeqStore->pos < rawSeqStore->size && ip < iend) {
61019fcbaf1SConrad Meyer         /* maybeSplitSequence updates rawSeqStore->pos */
61119fcbaf1SConrad Meyer         rawSeq const sequence = maybeSplitSequence(rawSeqStore,
61219fcbaf1SConrad Meyer                                                    (U32)(iend - ip), minMatch);
61319fcbaf1SConrad Meyer         int i;
61419fcbaf1SConrad Meyer         /* End signal */
61519fcbaf1SConrad Meyer         if (sequence.offset == 0)
6160c16b537SWarner Losh             break;
61719fcbaf1SConrad Meyer 
61819fcbaf1SConrad Meyer         assert(sequence.offset <= (1U << cParams->windowLog));
61919fcbaf1SConrad Meyer         assert(ip + sequence.litLength + sequence.matchLength <= iend);
62019fcbaf1SConrad Meyer 
62119fcbaf1SConrad Meyer         /* Fill tables for block compressor */
62219fcbaf1SConrad Meyer         ZSTD_ldm_limitTableUpdate(ms, ip);
623*0f743729SConrad Meyer         ZSTD_ldm_fillFastTables(ms, ip);
62419fcbaf1SConrad Meyer         /* Run the block compressor */
625*0f743729SConrad Meyer         DEBUGLOG(5, "calling block compressor on segment of size %u", sequence.litLength);
62619fcbaf1SConrad Meyer         {
62719fcbaf1SConrad Meyer             size_t const newLitLength =
628*0f743729SConrad Meyer                 blockCompressor(ms, seqStore, rep, ip, sequence.litLength);
62919fcbaf1SConrad Meyer             ip += sequence.litLength;
63019fcbaf1SConrad Meyer             /* Update the repcodes */
63119fcbaf1SConrad Meyer             for (i = ZSTD_REP_NUM - 1; i > 0; i--)
63219fcbaf1SConrad Meyer                 rep[i] = rep[i-1];
63319fcbaf1SConrad Meyer             rep[0] = sequence.offset;
63419fcbaf1SConrad Meyer             /* Store the sequence */
63519fcbaf1SConrad Meyer             ZSTD_storeSeq(seqStore, newLitLength, ip - newLitLength,
63619fcbaf1SConrad Meyer                           sequence.offset + ZSTD_REP_MOVE,
63719fcbaf1SConrad Meyer                           sequence.matchLength - MINMATCH);
63819fcbaf1SConrad Meyer             ip += sequence.matchLength;
6390c16b537SWarner Losh         }
6400c16b537SWarner Losh     }
64119fcbaf1SConrad Meyer     /* Fill the tables for the block compressor */
64219fcbaf1SConrad Meyer     ZSTD_ldm_limitTableUpdate(ms, ip);
643*0f743729SConrad Meyer     ZSTD_ldm_fillFastTables(ms, ip);
64419fcbaf1SConrad Meyer     /* Compress the last literals */
645*0f743729SConrad Meyer     return blockCompressor(ms, seqStore, rep, ip, iend - ip);
6460c16b537SWarner Losh }
647