Path: blob/master/Utilities/cmzstd/lib/compress/zstd_ldm.c
5030 views
/*1* Copyright (c) Meta Platforms, Inc. and affiliates.2* All rights reserved.3*4* This source code is licensed under both the BSD-style license (found in the5* LICENSE file in the root directory of this source tree) and the GPLv2 (found6* in the COPYING file in the root directory of this source tree).7* You may select, at your option, one of the above-listed licenses.8*/910#include "zstd_ldm.h"1112#include "../common/debug.h"13#include "../common/xxhash.h"14#include "zstd_fast.h" /* ZSTD_fillHashTable() */15#include "zstd_double_fast.h" /* ZSTD_fillDoubleHashTable() */16#include "zstd_ldm_geartab.h"1718#define LDM_BUCKET_SIZE_LOG 419#define LDM_MIN_MATCH_LENGTH 6420#define LDM_HASH_RLOG 72122typedef struct {23U64 rolling;24U64 stopMask;25} ldmRollingHashState_t;2627/** ZSTD_ldm_gear_init():28*29* Initializes the rolling hash state such that it will honor the30* settings in params. */31static void ZSTD_ldm_gear_init(ldmRollingHashState_t* state, ldmParams_t const* params)32{33unsigned maxBitsInMask = MIN(params->minMatchLength, 64);34unsigned hashRateLog = params->hashRateLog;3536state->rolling = ~(U32)0;3738/* The choice of the splitting criterion is subject to two conditions:39* 1. it has to trigger on average every 2^(hashRateLog) bytes;40* 2. ideally, it has to depend on a window of minMatchLength bytes.41*42* In the gear hash algorithm, bit n depends on the last n bytes;43* so in order to obtain a good quality splitting criterion it is44* preferable to use bits with high weight.45*46* To match condition 1 we use a mask with hashRateLog bits set47* and, because of the previous remark, we make sure these bits48* have the highest possible weight while still respecting49* condition 2.50*/51if (hashRateLog > 0 && hashRateLog <= maxBitsInMask) {52state->stopMask = (((U64)1 << hashRateLog) - 1) << (maxBitsInMask - hashRateLog);53} else {54/* In this degenerate case we simply honor the hash rate. */55state->stopMask = ((U64)1 << hashRateLog) - 1;56}57}5859/** ZSTD_ldm_gear_reset()60* Feeds [data, data + minMatchLength) into the hash without registering any61* splits. This effectively resets the hash state. This is used when skipping62* over data, either at the beginning of a block, or skipping sections.63*/64static void ZSTD_ldm_gear_reset(ldmRollingHashState_t* state,65BYTE const* data, size_t minMatchLength)66{67U64 hash = state->rolling;68size_t n = 0;6970#define GEAR_ITER_ONCE() do { \71hash = (hash << 1) + ZSTD_ldm_gearTab[data[n] & 0xff]; \72n += 1; \73} while (0)74while (n + 3 < minMatchLength) {75GEAR_ITER_ONCE();76GEAR_ITER_ONCE();77GEAR_ITER_ONCE();78GEAR_ITER_ONCE();79}80while (n < minMatchLength) {81GEAR_ITER_ONCE();82}83#undef GEAR_ITER_ONCE84}8586/** ZSTD_ldm_gear_feed():87*88* Registers in the splits array all the split points found in the first89* size bytes following the data pointer. This function terminates when90* either all the data has been processed or LDM_BATCH_SIZE splits are91* present in the splits array.92*93* Precondition: The splits array must not be full.94* Returns: The number of bytes processed. */95static size_t ZSTD_ldm_gear_feed(ldmRollingHashState_t* state,96BYTE const* data, size_t size,97size_t* splits, unsigned* numSplits)98{99size_t n;100U64 hash, mask;101102hash = state->rolling;103mask = state->stopMask;104n = 0;105106#define GEAR_ITER_ONCE() do { \107hash = (hash << 1) + ZSTD_ldm_gearTab[data[n] & 0xff]; \108n += 1; \109if (UNLIKELY((hash & mask) == 0)) { \110splits[*numSplits] = n; \111*numSplits += 1; \112if (*numSplits == LDM_BATCH_SIZE) \113goto done; \114} \115} while (0)116117while (n + 3 < size) {118GEAR_ITER_ONCE();119GEAR_ITER_ONCE();120GEAR_ITER_ONCE();121GEAR_ITER_ONCE();122}123while (n < size) {124GEAR_ITER_ONCE();125}126127#undef GEAR_ITER_ONCE128129done:130state->rolling = hash;131return n;132}133134void ZSTD_ldm_adjustParameters(ldmParams_t* params,135const ZSTD_compressionParameters* cParams)136{137params->windowLog = cParams->windowLog;138ZSTD_STATIC_ASSERT(LDM_BUCKET_SIZE_LOG <= ZSTD_LDM_BUCKETSIZELOG_MAX);139DEBUGLOG(4, "ZSTD_ldm_adjustParameters");140if (params->hashRateLog == 0) {141if (params->hashLog > 0) {142/* if params->hashLog is set, derive hashRateLog from it */143assert(params->hashLog <= ZSTD_HASHLOG_MAX);144if (params->windowLog > params->hashLog) {145params->hashRateLog = params->windowLog - params->hashLog;146}147} else {148assert(1 <= (int)cParams->strategy && (int)cParams->strategy <= 9);149/* mapping from [fast, rate7] to [btultra2, rate4] */150params->hashRateLog = 7 - (cParams->strategy/3);151}152}153if (params->hashLog == 0) {154params->hashLog = BOUNDED(ZSTD_HASHLOG_MIN, params->windowLog - params->hashRateLog, ZSTD_HASHLOG_MAX);155}156if (params->minMatchLength == 0) {157params->minMatchLength = LDM_MIN_MATCH_LENGTH;158if (cParams->strategy >= ZSTD_btultra)159params->minMatchLength /= 2;160}161if (params->bucketSizeLog==0) {162assert(1 <= (int)cParams->strategy && (int)cParams->strategy <= 9);163params->bucketSizeLog = BOUNDED(LDM_BUCKET_SIZE_LOG, (U32)cParams->strategy, ZSTD_LDM_BUCKETSIZELOG_MAX);164}165params->bucketSizeLog = MIN(params->bucketSizeLog, params->hashLog);166}167168size_t ZSTD_ldm_getTableSize(ldmParams_t params)169{170size_t const ldmHSize = ((size_t)1) << params.hashLog;171size_t const ldmBucketSizeLog = MIN(params.bucketSizeLog, params.hashLog);172size_t const ldmBucketSize = ((size_t)1) << (params.hashLog - ldmBucketSizeLog);173size_t const totalSize = ZSTD_cwksp_alloc_size(ldmBucketSize)174+ ZSTD_cwksp_alloc_size(ldmHSize * sizeof(ldmEntry_t));175return params.enableLdm == ZSTD_ps_enable ? totalSize : 0;176}177178size_t ZSTD_ldm_getMaxNbSeq(ldmParams_t params, size_t maxChunkSize)179{180return params.enableLdm == ZSTD_ps_enable ? (maxChunkSize / params.minMatchLength) : 0;181}182183/** ZSTD_ldm_getBucket() :184* Returns a pointer to the start of the bucket associated with hash. */185static ldmEntry_t* ZSTD_ldm_getBucket(186const ldmState_t* ldmState, size_t hash, U32 const bucketSizeLog)187{188return ldmState->hashTable + (hash << bucketSizeLog);189}190191/** ZSTD_ldm_insertEntry() :192* Insert the entry with corresponding hash into the hash table */193static void ZSTD_ldm_insertEntry(ldmState_t* ldmState,194size_t const hash, const ldmEntry_t entry,195U32 const bucketSizeLog)196{197BYTE* const pOffset = ldmState->bucketOffsets + hash;198unsigned const offset = *pOffset;199200*(ZSTD_ldm_getBucket(ldmState, hash, bucketSizeLog) + offset) = entry;201*pOffset = (BYTE)((offset + 1) & ((1u << bucketSizeLog) - 1));202203}204205/** ZSTD_ldm_countBackwardsMatch() :206* Returns the number of bytes that match backwards before pIn and pMatch.207*208* We count only bytes where pMatch >= pBase and pIn >= pAnchor. */209static size_t ZSTD_ldm_countBackwardsMatch(210const BYTE* pIn, const BYTE* pAnchor,211const BYTE* pMatch, const BYTE* pMatchBase)212{213size_t matchLength = 0;214while (pIn > pAnchor && pMatch > pMatchBase && pIn[-1] == pMatch[-1]) {215pIn--;216pMatch--;217matchLength++;218}219return matchLength;220}221222/** ZSTD_ldm_countBackwardsMatch_2segments() :223* Returns the number of bytes that match backwards from pMatch,224* even with the backwards match spanning 2 different segments.225*226* On reaching `pMatchBase`, start counting from mEnd */227static size_t ZSTD_ldm_countBackwardsMatch_2segments(228const BYTE* pIn, const BYTE* pAnchor,229const BYTE* pMatch, const BYTE* pMatchBase,230const BYTE* pExtDictStart, const BYTE* pExtDictEnd)231{232size_t matchLength = ZSTD_ldm_countBackwardsMatch(pIn, pAnchor, pMatch, pMatchBase);233if (pMatch - matchLength != pMatchBase || pMatchBase == pExtDictStart) {234/* If backwards match is entirely in the extDict or prefix, immediately return */235return matchLength;236}237DEBUGLOG(7, "ZSTD_ldm_countBackwardsMatch_2segments: found 2-parts backwards match (length in prefix==%zu)", matchLength);238matchLength += ZSTD_ldm_countBackwardsMatch(pIn - matchLength, pAnchor, pExtDictEnd, pExtDictStart);239DEBUGLOG(7, "final backwards match length = %zu", matchLength);240return matchLength;241}242243/** ZSTD_ldm_fillFastTables() :244*245* Fills the relevant tables for the ZSTD_fast and ZSTD_dfast strategies.246* This is similar to ZSTD_loadDictionaryContent.247*248* The tables for the other strategies are filled within their249* block compressors. */250static size_t ZSTD_ldm_fillFastTables(ZSTD_MatchState_t* ms,251void const* end)252{253const BYTE* const iend = (const BYTE*)end;254255switch(ms->cParams.strategy)256{257case ZSTD_fast:258ZSTD_fillHashTable(ms, iend, ZSTD_dtlm_fast, ZSTD_tfp_forCCtx);259break;260261case ZSTD_dfast:262#ifndef ZSTD_EXCLUDE_DFAST_BLOCK_COMPRESSOR263ZSTD_fillDoubleHashTable(ms, iend, ZSTD_dtlm_fast, ZSTD_tfp_forCCtx);264#else265assert(0); /* shouldn't be called: cparams should've been adjusted. */266#endif267break;268269case ZSTD_greedy:270case ZSTD_lazy:271case ZSTD_lazy2:272case ZSTD_btlazy2:273case ZSTD_btopt:274case ZSTD_btultra:275case ZSTD_btultra2:276break;277default:278assert(0); /* not possible : not a valid strategy id */279}280281return 0;282}283284void ZSTD_ldm_fillHashTable(285ldmState_t* ldmState, const BYTE* ip,286const BYTE* iend, ldmParams_t const* params)287{288U32 const minMatchLength = params->minMatchLength;289U32 const bucketSizeLog = params->bucketSizeLog;290U32 const hBits = params->hashLog - bucketSizeLog;291BYTE const* const base = ldmState->window.base;292BYTE const* const istart = ip;293ldmRollingHashState_t hashState;294size_t* const splits = ldmState->splitIndices;295unsigned numSplits;296297DEBUGLOG(5, "ZSTD_ldm_fillHashTable");298299ZSTD_ldm_gear_init(&hashState, params);300while (ip < iend) {301size_t hashed;302unsigned n;303304numSplits = 0;305hashed = ZSTD_ldm_gear_feed(&hashState, ip, (size_t)(iend - ip), splits, &numSplits);306307for (n = 0; n < numSplits; n++) {308if (ip + splits[n] >= istart + minMatchLength) {309BYTE const* const split = ip + splits[n] - minMatchLength;310U64 const xxhash = XXH64(split, minMatchLength, 0);311U32 const hash = (U32)(xxhash & (((U32)1 << hBits) - 1));312ldmEntry_t entry;313314entry.offset = (U32)(split - base);315entry.checksum = (U32)(xxhash >> 32);316ZSTD_ldm_insertEntry(ldmState, hash, entry, params->bucketSizeLog);317}318}319320ip += hashed;321}322}323324325/** ZSTD_ldm_limitTableUpdate() :326*327* Sets cctx->nextToUpdate to a position corresponding closer to anchor328* if it is far way329* (after a long match, only update tables a limited amount). */330static void ZSTD_ldm_limitTableUpdate(ZSTD_MatchState_t* ms, const BYTE* anchor)331{332U32 const curr = (U32)(anchor - ms->window.base);333if (curr > ms->nextToUpdate + 1024) {334ms->nextToUpdate =335curr - MIN(512, curr - ms->nextToUpdate - 1024);336}337}338339static340ZSTD_ALLOW_POINTER_OVERFLOW_ATTR341size_t ZSTD_ldm_generateSequences_internal(342ldmState_t* ldmState, RawSeqStore_t* rawSeqStore,343ldmParams_t const* params, void const* src, size_t srcSize)344{345/* LDM parameters */346int const extDict = ZSTD_window_hasExtDict(ldmState->window);347U32 const minMatchLength = params->minMatchLength;348U32 const entsPerBucket = 1U << params->bucketSizeLog;349U32 const hBits = params->hashLog - params->bucketSizeLog;350/* Prefix and extDict parameters */351U32 const dictLimit = ldmState->window.dictLimit;352U32 const lowestIndex = extDict ? ldmState->window.lowLimit : dictLimit;353BYTE const* const base = ldmState->window.base;354BYTE const* const dictBase = extDict ? ldmState->window.dictBase : NULL;355BYTE const* const dictStart = extDict ? dictBase + lowestIndex : NULL;356BYTE const* const dictEnd = extDict ? dictBase + dictLimit : NULL;357BYTE const* const lowPrefixPtr = base + dictLimit;358/* Input bounds */359BYTE const* const istart = (BYTE const*)src;360BYTE const* const iend = istart + srcSize;361BYTE const* const ilimit = iend - HASH_READ_SIZE;362/* Input positions */363BYTE const* anchor = istart;364BYTE const* ip = istart;365/* Rolling hash state */366ldmRollingHashState_t hashState;367/* Arrays for staged-processing */368size_t* const splits = ldmState->splitIndices;369ldmMatchCandidate_t* const candidates = ldmState->matchCandidates;370unsigned numSplits;371372if (srcSize < minMatchLength)373return iend - anchor;374375/* Initialize the rolling hash state with the first minMatchLength bytes */376ZSTD_ldm_gear_init(&hashState, params);377ZSTD_ldm_gear_reset(&hashState, ip, minMatchLength);378ip += minMatchLength;379380while (ip < ilimit) {381size_t hashed;382unsigned n;383384numSplits = 0;385hashed = ZSTD_ldm_gear_feed(&hashState, ip, ilimit - ip,386splits, &numSplits);387388for (n = 0; n < numSplits; n++) {389BYTE const* const split = ip + splits[n] - minMatchLength;390U64 const xxhash = XXH64(split, minMatchLength, 0);391U32 const hash = (U32)(xxhash & (((U32)1 << hBits) - 1));392393candidates[n].split = split;394candidates[n].hash = hash;395candidates[n].checksum = (U32)(xxhash >> 32);396candidates[n].bucket = ZSTD_ldm_getBucket(ldmState, hash, params->bucketSizeLog);397PREFETCH_L1(candidates[n].bucket);398}399400for (n = 0; n < numSplits; n++) {401size_t forwardMatchLength = 0, backwardMatchLength = 0,402bestMatchLength = 0, mLength;403U32 offset;404BYTE const* const split = candidates[n].split;405U32 const checksum = candidates[n].checksum;406U32 const hash = candidates[n].hash;407ldmEntry_t* const bucket = candidates[n].bucket;408ldmEntry_t const* cur;409ldmEntry_t const* bestEntry = NULL;410ldmEntry_t newEntry;411412newEntry.offset = (U32)(split - base);413newEntry.checksum = checksum;414415/* If a split point would generate a sequence overlapping with416* the previous one, we merely register it in the hash table and417* move on */418if (split < anchor) {419ZSTD_ldm_insertEntry(ldmState, hash, newEntry, params->bucketSizeLog);420continue;421}422423for (cur = bucket; cur < bucket + entsPerBucket; cur++) {424size_t curForwardMatchLength, curBackwardMatchLength,425curTotalMatchLength;426if (cur->checksum != checksum || cur->offset <= lowestIndex) {427continue;428}429if (extDict) {430BYTE const* const curMatchBase =431cur->offset < dictLimit ? dictBase : base;432BYTE const* const pMatch = curMatchBase + cur->offset;433BYTE const* const matchEnd =434cur->offset < dictLimit ? dictEnd : iend;435BYTE const* const lowMatchPtr =436cur->offset < dictLimit ? dictStart : lowPrefixPtr;437curForwardMatchLength =438ZSTD_count_2segments(split, pMatch, iend, matchEnd, lowPrefixPtr);439if (curForwardMatchLength < minMatchLength) {440continue;441}442curBackwardMatchLength = ZSTD_ldm_countBackwardsMatch_2segments(443split, anchor, pMatch, lowMatchPtr, dictStart, dictEnd);444} else { /* !extDict */445BYTE const* const pMatch = base + cur->offset;446curForwardMatchLength = ZSTD_count(split, pMatch, iend);447if (curForwardMatchLength < minMatchLength) {448continue;449}450curBackwardMatchLength =451ZSTD_ldm_countBackwardsMatch(split, anchor, pMatch, lowPrefixPtr);452}453curTotalMatchLength = curForwardMatchLength + curBackwardMatchLength;454455if (curTotalMatchLength > bestMatchLength) {456bestMatchLength = curTotalMatchLength;457forwardMatchLength = curForwardMatchLength;458backwardMatchLength = curBackwardMatchLength;459bestEntry = cur;460}461}462463/* No match found -- insert an entry into the hash table464* and process the next candidate match */465if (bestEntry == NULL) {466ZSTD_ldm_insertEntry(ldmState, hash, newEntry, params->bucketSizeLog);467continue;468}469470/* Match found */471offset = (U32)(split - base) - bestEntry->offset;472mLength = forwardMatchLength + backwardMatchLength;473{474rawSeq* const seq = rawSeqStore->seq + rawSeqStore->size;475476/* Out of sequence storage */477if (rawSeqStore->size == rawSeqStore->capacity)478return ERROR(dstSize_tooSmall);479seq->litLength = (U32)(split - backwardMatchLength - anchor);480seq->matchLength = (U32)mLength;481seq->offset = offset;482rawSeqStore->size++;483}484485/* Insert the current entry into the hash table --- it must be486* done after the previous block to avoid clobbering bestEntry */487ZSTD_ldm_insertEntry(ldmState, hash, newEntry, params->bucketSizeLog);488489anchor = split + forwardMatchLength;490491/* If we find a match that ends after the data that we've hashed492* then we have a repeating, overlapping, pattern. E.g. all zeros.493* If one repetition of the pattern matches our `stopMask` then all494* repetitions will. We don't need to insert them all into out table,495* only the first one. So skip over overlapping matches.496* This is a major speed boost (20x) for compressing a single byte497* repeated, when that byte ends up in the table.498*/499if (anchor > ip + hashed) {500ZSTD_ldm_gear_reset(&hashState, anchor - minMatchLength, minMatchLength);501/* Continue the outer loop at anchor (ip + hashed == anchor). */502ip = anchor - hashed;503break;504}505}506507ip += hashed;508}509510return iend - anchor;511}512513/*! ZSTD_ldm_reduceTable() :514* reduce table indexes by `reducerValue` */515static void ZSTD_ldm_reduceTable(ldmEntry_t* const table, U32 const size,516U32 const reducerValue)517{518U32 u;519for (u = 0; u < size; u++) {520if (table[u].offset < reducerValue) table[u].offset = 0;521else table[u].offset -= reducerValue;522}523}524525size_t ZSTD_ldm_generateSequences(526ldmState_t* ldmState, RawSeqStore_t* sequences,527ldmParams_t const* params, void const* src, size_t srcSize)528{529U32 const maxDist = 1U << params->windowLog;530BYTE const* const istart = (BYTE const*)src;531BYTE const* const iend = istart + srcSize;532size_t const kMaxChunkSize = 1 << 20;533size_t const nbChunks = (srcSize / kMaxChunkSize) + ((srcSize % kMaxChunkSize) != 0);534size_t chunk;535size_t leftoverSize = 0;536537assert(ZSTD_CHUNKSIZE_MAX >= kMaxChunkSize);538/* Check that ZSTD_window_update() has been called for this chunk prior539* to passing it to this function.540*/541assert(ldmState->window.nextSrc >= (BYTE const*)src + srcSize);542/* The input could be very large (in zstdmt), so it must be broken up into543* chunks to enforce the maximum distance and handle overflow correction.544*/545assert(sequences->pos <= sequences->size);546assert(sequences->size <= sequences->capacity);547for (chunk = 0; chunk < nbChunks && sequences->size < sequences->capacity; ++chunk) {548BYTE const* const chunkStart = istart + chunk * kMaxChunkSize;549size_t const remaining = (size_t)(iend - chunkStart);550BYTE const *const chunkEnd =551(remaining < kMaxChunkSize) ? iend : chunkStart + kMaxChunkSize;552size_t const chunkSize = chunkEnd - chunkStart;553size_t newLeftoverSize;554size_t const prevSize = sequences->size;555556assert(chunkStart < iend);557/* 1. Perform overflow correction if necessary. */558if (ZSTD_window_needOverflowCorrection(ldmState->window, 0, maxDist, ldmState->loadedDictEnd, chunkStart, chunkEnd)) {559U32 const ldmHSize = 1U << params->hashLog;560U32 const correction = ZSTD_window_correctOverflow(561&ldmState->window, /* cycleLog */ 0, maxDist, chunkStart);562ZSTD_ldm_reduceTable(ldmState->hashTable, ldmHSize, correction);563/* invalidate dictionaries on overflow correction */564ldmState->loadedDictEnd = 0;565}566/* 2. We enforce the maximum offset allowed.567*568* kMaxChunkSize should be small enough that we don't lose too much of569* the window through early invalidation.570* TODO: * Test the chunk size.571* * Try invalidation after the sequence generation and test the572* offset against maxDist directly.573*574* NOTE: Because of dictionaries + sequence splitting we MUST make sure575* that any offset used is valid at the END of the sequence, since it may576* be split into two sequences. This condition holds when using577* ZSTD_window_enforceMaxDist(), but if we move to checking offsets578* against maxDist directly, we'll have to carefully handle that case.579*/580ZSTD_window_enforceMaxDist(&ldmState->window, chunkEnd, maxDist, &ldmState->loadedDictEnd, NULL);581/* 3. Generate the sequences for the chunk, and get newLeftoverSize. */582newLeftoverSize = ZSTD_ldm_generateSequences_internal(583ldmState, sequences, params, chunkStart, chunkSize);584if (ZSTD_isError(newLeftoverSize))585return newLeftoverSize;586/* 4. We add the leftover literals from previous iterations to the first587* newly generated sequence, or add the `newLeftoverSize` if none are588* generated.589*/590/* Prepend the leftover literals from the last call */591if (prevSize < sequences->size) {592sequences->seq[prevSize].litLength += (U32)leftoverSize;593leftoverSize = newLeftoverSize;594} else {595assert(newLeftoverSize == chunkSize);596leftoverSize += chunkSize;597}598}599return 0;600}601602void603ZSTD_ldm_skipSequences(RawSeqStore_t* rawSeqStore, size_t srcSize, U32 const minMatch)604{605while (srcSize > 0 && rawSeqStore->pos < rawSeqStore->size) {606rawSeq* seq = rawSeqStore->seq + rawSeqStore->pos;607if (srcSize <= seq->litLength) {608/* Skip past srcSize literals */609seq->litLength -= (U32)srcSize;610return;611}612srcSize -= seq->litLength;613seq->litLength = 0;614if (srcSize < seq->matchLength) {615/* Skip past the first srcSize of the match */616seq->matchLength -= (U32)srcSize;617if (seq->matchLength < minMatch) {618/* The match is too short, omit it */619if (rawSeqStore->pos + 1 < rawSeqStore->size) {620seq[1].litLength += seq[0].matchLength;621}622rawSeqStore->pos++;623}624return;625}626srcSize -= seq->matchLength;627seq->matchLength = 0;628rawSeqStore->pos++;629}630}631632/**633* If the sequence length is longer than remaining then the sequence is split634* between this block and the next.635*636* Returns the current sequence to handle, or if the rest of the block should637* be literals, it returns a sequence with offset == 0.638*/639static rawSeq maybeSplitSequence(RawSeqStore_t* rawSeqStore,640U32 const remaining, U32 const minMatch)641{642rawSeq sequence = rawSeqStore->seq[rawSeqStore->pos];643assert(sequence.offset > 0);644/* Likely: No partial sequence */645if (remaining >= sequence.litLength + sequence.matchLength) {646rawSeqStore->pos++;647return sequence;648}649/* Cut the sequence short (offset == 0 ==> rest is literals). */650if (remaining <= sequence.litLength) {651sequence.offset = 0;652} else if (remaining < sequence.litLength + sequence.matchLength) {653sequence.matchLength = remaining - sequence.litLength;654if (sequence.matchLength < minMatch) {655sequence.offset = 0;656}657}658/* Skip past `remaining` bytes for the future sequences. */659ZSTD_ldm_skipSequences(rawSeqStore, remaining, minMatch);660return sequence;661}662663void ZSTD_ldm_skipRawSeqStoreBytes(RawSeqStore_t* rawSeqStore, size_t nbBytes) {664U32 currPos = (U32)(rawSeqStore->posInSequence + nbBytes);665while (currPos && rawSeqStore->pos < rawSeqStore->size) {666rawSeq currSeq = rawSeqStore->seq[rawSeqStore->pos];667if (currPos >= currSeq.litLength + currSeq.matchLength) {668currPos -= currSeq.litLength + currSeq.matchLength;669rawSeqStore->pos++;670} else {671rawSeqStore->posInSequence = currPos;672break;673}674}675if (currPos == 0 || rawSeqStore->pos == rawSeqStore->size) {676rawSeqStore->posInSequence = 0;677}678}679680size_t ZSTD_ldm_blockCompress(RawSeqStore_t* rawSeqStore,681ZSTD_MatchState_t* ms, SeqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],682ZSTD_ParamSwitch_e useRowMatchFinder,683void const* src, size_t srcSize)684{685const ZSTD_compressionParameters* const cParams = &ms->cParams;686unsigned const minMatch = cParams->minMatch;687ZSTD_BlockCompressor_f const blockCompressor =688ZSTD_selectBlockCompressor(cParams->strategy, useRowMatchFinder, ZSTD_matchState_dictMode(ms));689/* Input bounds */690BYTE const* const istart = (BYTE const*)src;691BYTE const* const iend = istart + srcSize;692/* Input positions */693BYTE const* ip = istart;694695DEBUGLOG(5, "ZSTD_ldm_blockCompress: srcSize=%zu", srcSize);696/* If using opt parser, use LDMs only as candidates rather than always accepting them */697if (cParams->strategy >= ZSTD_btopt) {698size_t lastLLSize;699ms->ldmSeqStore = rawSeqStore;700lastLLSize = blockCompressor(ms, seqStore, rep, src, srcSize);701ZSTD_ldm_skipRawSeqStoreBytes(rawSeqStore, srcSize);702return lastLLSize;703}704705assert(rawSeqStore->pos <= rawSeqStore->size);706assert(rawSeqStore->size <= rawSeqStore->capacity);707/* Loop through each sequence and apply the block compressor to the literals */708while (rawSeqStore->pos < rawSeqStore->size && ip < iend) {709/* maybeSplitSequence updates rawSeqStore->pos */710rawSeq const sequence = maybeSplitSequence(rawSeqStore,711(U32)(iend - ip), minMatch);712/* End signal */713if (sequence.offset == 0)714break;715716assert(ip + sequence.litLength + sequence.matchLength <= iend);717718/* Fill tables for block compressor */719ZSTD_ldm_limitTableUpdate(ms, ip);720ZSTD_ldm_fillFastTables(ms, ip);721/* Run the block compressor */722DEBUGLOG(5, "pos %u : calling block compressor on segment of size %u", (unsigned)(ip-istart), sequence.litLength);723{724int i;725size_t const newLitLength =726blockCompressor(ms, seqStore, rep, ip, sequence.litLength);727ip += sequence.litLength;728/* Update the repcodes */729for (i = ZSTD_REP_NUM - 1; i > 0; i--)730rep[i] = rep[i-1];731rep[0] = sequence.offset;732/* Store the sequence */733ZSTD_storeSeq(seqStore, newLitLength, ip - newLitLength, iend,734OFFSET_TO_OFFBASE(sequence.offset),735sequence.matchLength);736ip += sequence.matchLength;737}738}739/* Fill the tables for the block compressor */740ZSTD_ldm_limitTableUpdate(ms, ip);741ZSTD_ldm_fillFastTables(ms, ip);742/* Compress the last literals */743return blockCompressor(ms, seqStore, rep, ip, iend - ip);744}745746747