Path: blob/master/Utilities/cmzstd/lib/compress/zstd_opt.c
5015 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_compress_internal.h"11#include "hist.h"12#include "zstd_opt.h"1314#if !defined(ZSTD_EXCLUDE_BTLAZY2_BLOCK_COMPRESSOR) \15|| !defined(ZSTD_EXCLUDE_BTOPT_BLOCK_COMPRESSOR) \16|| !defined(ZSTD_EXCLUDE_BTULTRA_BLOCK_COMPRESSOR)1718#define ZSTD_LITFREQ_ADD 2 /* scaling factor for litFreq, so that frequencies adapt faster to new stats */19#define ZSTD_MAX_PRICE (1<<30)2021#define ZSTD_PREDEF_THRESHOLD 8 /* if srcSize < ZSTD_PREDEF_THRESHOLD, symbols' cost is assumed static, directly determined by pre-defined distributions */222324/*-*************************************25* Price functions for optimal parser26***************************************/2728#if 0 /* approximation at bit level (for tests) */29# define BITCOST_ACCURACY 030# define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY)31# define WEIGHT(stat, opt) ((void)(opt), ZSTD_bitWeight(stat))32#elif 0 /* fractional bit accuracy (for tests) */33# define BITCOST_ACCURACY 834# define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY)35# define WEIGHT(stat,opt) ((void)(opt), ZSTD_fracWeight(stat))36#else /* opt==approx, ultra==accurate */37# define BITCOST_ACCURACY 838# define BITCOST_MULTIPLIER (1 << BITCOST_ACCURACY)39# define WEIGHT(stat,opt) ((opt) ? ZSTD_fracWeight(stat) : ZSTD_bitWeight(stat))40#endif4142/* ZSTD_bitWeight() :43* provide estimated "cost" of a stat in full bits only */44MEM_STATIC U32 ZSTD_bitWeight(U32 stat)45{46return (ZSTD_highbit32(stat+1) * BITCOST_MULTIPLIER);47}4849/* ZSTD_fracWeight() :50* provide fractional-bit "cost" of a stat,51* using linear interpolation approximation */52MEM_STATIC U32 ZSTD_fracWeight(U32 rawStat)53{54U32 const stat = rawStat + 1;55U32 const hb = ZSTD_highbit32(stat);56U32 const BWeight = hb * BITCOST_MULTIPLIER;57/* Fweight was meant for "Fractional weight"58* but it's effectively a value between 1 and 259* using fixed point arithmetic */60U32 const FWeight = (stat << BITCOST_ACCURACY) >> hb;61U32 const weight = BWeight + FWeight;62assert(hb + BITCOST_ACCURACY < 31);63return weight;64}6566#if (DEBUGLEVEL>=2)67/* debugging function,68* @return price in bytes as fractional value69* for debug messages only */70MEM_STATIC double ZSTD_fCost(int price)71{72return (double)price / (BITCOST_MULTIPLIER*8);73}74#endif7576static int ZSTD_compressedLiterals(optState_t const* const optPtr)77{78return optPtr->literalCompressionMode != ZSTD_ps_disable;79}8081static void ZSTD_setBasePrices(optState_t* optPtr, int optLevel)82{83if (ZSTD_compressedLiterals(optPtr))84optPtr->litSumBasePrice = WEIGHT(optPtr->litSum, optLevel);85optPtr->litLengthSumBasePrice = WEIGHT(optPtr->litLengthSum, optLevel);86optPtr->matchLengthSumBasePrice = WEIGHT(optPtr->matchLengthSum, optLevel);87optPtr->offCodeSumBasePrice = WEIGHT(optPtr->offCodeSum, optLevel);88}899091static U32 sum_u32(const unsigned table[], size_t nbElts)92{93size_t n;94U32 total = 0;95for (n=0; n<nbElts; n++) {96total += table[n];97}98return total;99}100101typedef enum { base_0possible=0, base_1guaranteed=1 } base_directive_e;102103static U32104ZSTD_downscaleStats(unsigned* table, U32 lastEltIndex, U32 shift, base_directive_e base1)105{106U32 s, sum=0;107DEBUGLOG(5, "ZSTD_downscaleStats (nbElts=%u, shift=%u)",108(unsigned)lastEltIndex+1, (unsigned)shift );109assert(shift < 30);110for (s=0; s<lastEltIndex+1; s++) {111unsigned const base = base1 ? 1 : (table[s]>0);112unsigned const newStat = base + (table[s] >> shift);113sum += newStat;114table[s] = newStat;115}116return sum;117}118119/* ZSTD_scaleStats() :120* reduce all elt frequencies in table if sum too large121* return the resulting sum of elements */122static U32 ZSTD_scaleStats(unsigned* table, U32 lastEltIndex, U32 logTarget)123{124U32 const prevsum = sum_u32(table, lastEltIndex+1);125U32 const factor = prevsum >> logTarget;126DEBUGLOG(5, "ZSTD_scaleStats (nbElts=%u, target=%u)", (unsigned)lastEltIndex+1, (unsigned)logTarget);127assert(logTarget < 30);128if (factor <= 1) return prevsum;129return ZSTD_downscaleStats(table, lastEltIndex, ZSTD_highbit32(factor), base_1guaranteed);130}131132/* ZSTD_rescaleFreqs() :133* if first block (detected by optPtr->litLengthSum == 0) : init statistics134* take hints from dictionary if there is one135* and init from zero if there is none,136* using src for literals stats, and baseline stats for sequence symbols137* otherwise downscale existing stats, to be used as seed for next block.138*/139static void140ZSTD_rescaleFreqs(optState_t* const optPtr,141const BYTE* const src, size_t const srcSize,142int const optLevel)143{144int const compressedLiterals = ZSTD_compressedLiterals(optPtr);145DEBUGLOG(5, "ZSTD_rescaleFreqs (srcSize=%u)", (unsigned)srcSize);146optPtr->priceType = zop_dynamic;147148if (optPtr->litLengthSum == 0) { /* no literals stats collected -> first block assumed -> init */149150/* heuristic: use pre-defined stats for too small inputs */151if (srcSize <= ZSTD_PREDEF_THRESHOLD) {152DEBUGLOG(5, "srcSize <= %i : use predefined stats", ZSTD_PREDEF_THRESHOLD);153optPtr->priceType = zop_predef;154}155156assert(optPtr->symbolCosts != NULL);157if (optPtr->symbolCosts->huf.repeatMode == HUF_repeat_valid) {158159/* huffman stats covering the full value set : table presumed generated by dictionary */160optPtr->priceType = zop_dynamic;161162if (compressedLiterals) {163/* generate literals statistics from huffman table */164unsigned lit;165assert(optPtr->litFreq != NULL);166optPtr->litSum = 0;167for (lit=0; lit<=MaxLit; lit++) {168U32 const scaleLog = 11; /* scale to 2K */169U32 const bitCost = HUF_getNbBitsFromCTable(optPtr->symbolCosts->huf.CTable, lit);170assert(bitCost <= scaleLog);171optPtr->litFreq[lit] = bitCost ? 1 << (scaleLog-bitCost) : 1 /*minimum to calculate cost*/;172optPtr->litSum += optPtr->litFreq[lit];173} }174175{ unsigned ll;176FSE_CState_t llstate;177FSE_initCState(&llstate, optPtr->symbolCosts->fse.litlengthCTable);178optPtr->litLengthSum = 0;179for (ll=0; ll<=MaxLL; ll++) {180U32 const scaleLog = 10; /* scale to 1K */181U32 const bitCost = FSE_getMaxNbBits(llstate.symbolTT, ll);182assert(bitCost < scaleLog);183optPtr->litLengthFreq[ll] = bitCost ? 1 << (scaleLog-bitCost) : 1 /*minimum to calculate cost*/;184optPtr->litLengthSum += optPtr->litLengthFreq[ll];185} }186187{ unsigned ml;188FSE_CState_t mlstate;189FSE_initCState(&mlstate, optPtr->symbolCosts->fse.matchlengthCTable);190optPtr->matchLengthSum = 0;191for (ml=0; ml<=MaxML; ml++) {192U32 const scaleLog = 10;193U32 const bitCost = FSE_getMaxNbBits(mlstate.symbolTT, ml);194assert(bitCost < scaleLog);195optPtr->matchLengthFreq[ml] = bitCost ? 1 << (scaleLog-bitCost) : 1 /*minimum to calculate cost*/;196optPtr->matchLengthSum += optPtr->matchLengthFreq[ml];197} }198199{ unsigned of;200FSE_CState_t ofstate;201FSE_initCState(&ofstate, optPtr->symbolCosts->fse.offcodeCTable);202optPtr->offCodeSum = 0;203for (of=0; of<=MaxOff; of++) {204U32 const scaleLog = 10;205U32 const bitCost = FSE_getMaxNbBits(ofstate.symbolTT, of);206assert(bitCost < scaleLog);207optPtr->offCodeFreq[of] = bitCost ? 1 << (scaleLog-bitCost) : 1 /*minimum to calculate cost*/;208optPtr->offCodeSum += optPtr->offCodeFreq[of];209} }210211} else { /* first block, no dictionary */212213assert(optPtr->litFreq != NULL);214if (compressedLiterals) {215/* base initial cost of literals on direct frequency within src */216unsigned lit = MaxLit;217HIST_count_simple(optPtr->litFreq, &lit, src, srcSize); /* use raw first block to init statistics */218optPtr->litSum = ZSTD_downscaleStats(optPtr->litFreq, MaxLit, 8, base_0possible);219}220221{ unsigned const baseLLfreqs[MaxLL+1] = {2224, 2, 1, 1, 1, 1, 1, 1,2231, 1, 1, 1, 1, 1, 1, 1,2241, 1, 1, 1, 1, 1, 1, 1,2251, 1, 1, 1, 1, 1, 1, 1,2261, 1, 1, 1227};228ZSTD_memcpy(optPtr->litLengthFreq, baseLLfreqs, sizeof(baseLLfreqs));229optPtr->litLengthSum = sum_u32(baseLLfreqs, MaxLL+1);230}231232{ unsigned ml;233for (ml=0; ml<=MaxML; ml++)234optPtr->matchLengthFreq[ml] = 1;235}236optPtr->matchLengthSum = MaxML+1;237238{ unsigned const baseOFCfreqs[MaxOff+1] = {2396, 2, 1, 1, 2, 3, 4, 4,2404, 3, 2, 1, 1, 1, 1, 1,2411, 1, 1, 1, 1, 1, 1, 1,2421, 1, 1, 1, 1, 1, 1, 1243};244ZSTD_memcpy(optPtr->offCodeFreq, baseOFCfreqs, sizeof(baseOFCfreqs));245optPtr->offCodeSum = sum_u32(baseOFCfreqs, MaxOff+1);246}247248}249250} else { /* new block : scale down accumulated statistics */251252if (compressedLiterals)253optPtr->litSum = ZSTD_scaleStats(optPtr->litFreq, MaxLit, 12);254optPtr->litLengthSum = ZSTD_scaleStats(optPtr->litLengthFreq, MaxLL, 11);255optPtr->matchLengthSum = ZSTD_scaleStats(optPtr->matchLengthFreq, MaxML, 11);256optPtr->offCodeSum = ZSTD_scaleStats(optPtr->offCodeFreq, MaxOff, 11);257}258259ZSTD_setBasePrices(optPtr, optLevel);260}261262/* ZSTD_rawLiteralsCost() :263* price of literals (only) in specified segment (which length can be 0).264* does not include price of literalLength symbol */265static U32 ZSTD_rawLiteralsCost(const BYTE* const literals, U32 const litLength,266const optState_t* const optPtr,267int optLevel)268{269DEBUGLOG(8, "ZSTD_rawLiteralsCost (%u literals)", litLength);270if (litLength == 0) return 0;271272if (!ZSTD_compressedLiterals(optPtr))273return (litLength << 3) * BITCOST_MULTIPLIER; /* Uncompressed - 8 bytes per literal. */274275if (optPtr->priceType == zop_predef)276return (litLength*6) * BITCOST_MULTIPLIER; /* 6 bit per literal - no statistic used */277278/* dynamic statistics */279{ U32 price = optPtr->litSumBasePrice * litLength;280U32 const litPriceMax = optPtr->litSumBasePrice - BITCOST_MULTIPLIER;281U32 u;282assert(optPtr->litSumBasePrice >= BITCOST_MULTIPLIER);283for (u=0; u < litLength; u++) {284U32 litPrice = WEIGHT(optPtr->litFreq[literals[u]], optLevel);285if (UNLIKELY(litPrice > litPriceMax)) litPrice = litPriceMax;286price -= litPrice;287}288return price;289}290}291292/* ZSTD_litLengthPrice() :293* cost of literalLength symbol */294static U32 ZSTD_litLengthPrice(U32 const litLength, const optState_t* const optPtr, int optLevel)295{296assert(litLength <= ZSTD_BLOCKSIZE_MAX);297if (optPtr->priceType == zop_predef)298return WEIGHT(litLength, optLevel);299300/* ZSTD_LLcode() can't compute litLength price for sizes >= ZSTD_BLOCKSIZE_MAX301* because it isn't representable in the zstd format.302* So instead just pretend it would cost 1 bit more than ZSTD_BLOCKSIZE_MAX - 1.303* In such a case, the block would be all literals.304*/305if (litLength == ZSTD_BLOCKSIZE_MAX)306return BITCOST_MULTIPLIER + ZSTD_litLengthPrice(ZSTD_BLOCKSIZE_MAX - 1, optPtr, optLevel);307308/* dynamic statistics */309{ U32 const llCode = ZSTD_LLcode(litLength);310return (LL_bits[llCode] * BITCOST_MULTIPLIER)311+ optPtr->litLengthSumBasePrice312- WEIGHT(optPtr->litLengthFreq[llCode], optLevel);313}314}315316/* ZSTD_getMatchPrice() :317* Provides the cost of the match part (offset + matchLength) of a sequence.318* Must be combined with ZSTD_fullLiteralsCost() to get the full cost of a sequence.319* @offBase : sumtype, representing an offset or a repcode, and using numeric representation of ZSTD_storeSeq()320* @optLevel: when <2, favors small offset for decompression speed (improved cache efficiency)321*/322FORCE_INLINE_TEMPLATE U32323ZSTD_getMatchPrice(U32 const offBase,324U32 const matchLength,325const optState_t* const optPtr,326int const optLevel)327{328U32 price;329U32 const offCode = ZSTD_highbit32(offBase);330U32 const mlBase = matchLength - MINMATCH;331assert(matchLength >= MINMATCH);332333if (optPtr->priceType == zop_predef) /* fixed scheme, does not use statistics */334return WEIGHT(mlBase, optLevel)335+ ((16 + offCode) * BITCOST_MULTIPLIER); /* emulated offset cost */336337/* dynamic statistics */338price = (offCode * BITCOST_MULTIPLIER) + (optPtr->offCodeSumBasePrice - WEIGHT(optPtr->offCodeFreq[offCode], optLevel));339if ((optLevel<2) /*static*/ && offCode >= 20)340price += (offCode-19)*2 * BITCOST_MULTIPLIER; /* handicap for long distance offsets, favor decompression speed */341342/* match Length */343{ U32 const mlCode = ZSTD_MLcode(mlBase);344price += (ML_bits[mlCode] * BITCOST_MULTIPLIER) + (optPtr->matchLengthSumBasePrice - WEIGHT(optPtr->matchLengthFreq[mlCode], optLevel));345}346347price += BITCOST_MULTIPLIER / 5; /* heuristic : make matches a bit more costly to favor less sequences -> faster decompression speed */348349DEBUGLOG(8, "ZSTD_getMatchPrice(ml:%u) = %u", matchLength, price);350return price;351}352353/* ZSTD_updateStats() :354* assumption : literals + litLength <= iend */355static void ZSTD_updateStats(optState_t* const optPtr,356U32 litLength, const BYTE* literals,357U32 offBase, U32 matchLength)358{359/* literals */360if (ZSTD_compressedLiterals(optPtr)) {361U32 u;362for (u=0; u < litLength; u++)363optPtr->litFreq[literals[u]] += ZSTD_LITFREQ_ADD;364optPtr->litSum += litLength*ZSTD_LITFREQ_ADD;365}366367/* literal Length */368{ U32 const llCode = ZSTD_LLcode(litLength);369optPtr->litLengthFreq[llCode]++;370optPtr->litLengthSum++;371}372373/* offset code : follows storeSeq() numeric representation */374{ U32 const offCode = ZSTD_highbit32(offBase);375assert(offCode <= MaxOff);376optPtr->offCodeFreq[offCode]++;377optPtr->offCodeSum++;378}379380/* match Length */381{ U32 const mlBase = matchLength - MINMATCH;382U32 const mlCode = ZSTD_MLcode(mlBase);383optPtr->matchLengthFreq[mlCode]++;384optPtr->matchLengthSum++;385}386}387388389/* ZSTD_readMINMATCH() :390* function safe only for comparisons391* assumption : memPtr must be at least 4 bytes before end of buffer */392MEM_STATIC U32 ZSTD_readMINMATCH(const void* memPtr, U32 length)393{394switch (length)395{396default :397case 4 : return MEM_read32(memPtr);398case 3 : if (MEM_isLittleEndian())399return MEM_read32(memPtr)<<8;400else401return MEM_read32(memPtr)>>8;402}403}404405406/* Update hashTable3 up to ip (excluded)407Assumption : always within prefix (i.e. not within extDict) */408static409ZSTD_ALLOW_POINTER_OVERFLOW_ATTR410U32 ZSTD_insertAndFindFirstIndexHash3 (const ZSTD_MatchState_t* ms,411U32* nextToUpdate3,412const BYTE* const ip)413{414U32* const hashTable3 = ms->hashTable3;415U32 const hashLog3 = ms->hashLog3;416const BYTE* const base = ms->window.base;417U32 idx = *nextToUpdate3;418U32 const target = (U32)(ip - base);419size_t const hash3 = ZSTD_hash3Ptr(ip, hashLog3);420assert(hashLog3 > 0);421422while(idx < target) {423hashTable3[ZSTD_hash3Ptr(base+idx, hashLog3)] = idx;424idx++;425}426427*nextToUpdate3 = target;428return hashTable3[hash3];429}430431432/*-*************************************433* Binary Tree search434***************************************/435/** ZSTD_insertBt1() : add one or multiple positions to tree.436* @param ip assumed <= iend-8 .437* @param target The target of ZSTD_updateTree_internal() - we are filling to this position438* @return : nb of positions added */439static440ZSTD_ALLOW_POINTER_OVERFLOW_ATTR441U32 ZSTD_insertBt1(442const ZSTD_MatchState_t* ms,443const BYTE* const ip, const BYTE* const iend,444U32 const target,445U32 const mls, const int extDict)446{447const ZSTD_compressionParameters* const cParams = &ms->cParams;448U32* const hashTable = ms->hashTable;449U32 const hashLog = cParams->hashLog;450size_t const h = ZSTD_hashPtr(ip, hashLog, mls);451U32* const bt = ms->chainTable;452U32 const btLog = cParams->chainLog - 1;453U32 const btMask = (1 << btLog) - 1;454U32 matchIndex = hashTable[h];455size_t commonLengthSmaller=0, commonLengthLarger=0;456const BYTE* const base = ms->window.base;457const BYTE* const dictBase = ms->window.dictBase;458const U32 dictLimit = ms->window.dictLimit;459const BYTE* const dictEnd = dictBase + dictLimit;460const BYTE* const prefixStart = base + dictLimit;461const BYTE* match;462const U32 curr = (U32)(ip-base);463const U32 btLow = btMask >= curr ? 0 : curr - btMask;464U32* smallerPtr = bt + 2*(curr&btMask);465U32* largerPtr = smallerPtr + 1;466U32 dummy32; /* to be nullified at the end */467/* windowLow is based on target because468* we only need positions that will be in the window at the end of the tree update.469*/470U32 const windowLow = ZSTD_getLowestMatchIndex(ms, target, cParams->windowLog);471U32 matchEndIdx = curr+8+1;472size_t bestLength = 8;473U32 nbCompares = 1U << cParams->searchLog;474#ifdef ZSTD_C_PREDICT475U32 predictedSmall = *(bt + 2*((curr-1)&btMask) + 0);476U32 predictedLarge = *(bt + 2*((curr-1)&btMask) + 1);477predictedSmall += (predictedSmall>0);478predictedLarge += (predictedLarge>0);479#endif /* ZSTD_C_PREDICT */480481DEBUGLOG(8, "ZSTD_insertBt1 (%u)", curr);482483assert(curr <= target);484assert(ip <= iend-8); /* required for h calculation */485hashTable[h] = curr; /* Update Hash Table */486487assert(windowLow > 0);488for (; nbCompares && (matchIndex >= windowLow); --nbCompares) {489U32* const nextPtr = bt + 2*(matchIndex & btMask);490size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */491assert(matchIndex < curr);492493#ifdef ZSTD_C_PREDICT /* note : can create issues when hlog small <= 11 */494const U32* predictPtr = bt + 2*((matchIndex-1) & btMask); /* written this way, as bt is a roll buffer */495if (matchIndex == predictedSmall) {496/* no need to check length, result known */497*smallerPtr = matchIndex;498if (matchIndex <= btLow) { smallerPtr=&dummy32; break; } /* beyond tree size, stop the search */499smallerPtr = nextPtr+1; /* new "smaller" => larger of match */500matchIndex = nextPtr[1]; /* new matchIndex larger than previous (closer to current) */501predictedSmall = predictPtr[1] + (predictPtr[1]>0);502continue;503}504if (matchIndex == predictedLarge) {505*largerPtr = matchIndex;506if (matchIndex <= btLow) { largerPtr=&dummy32; break; } /* beyond tree size, stop the search */507largerPtr = nextPtr;508matchIndex = nextPtr[0];509predictedLarge = predictPtr[0] + (predictPtr[0]>0);510continue;511}512#endif513514if (!extDict || (matchIndex+matchLength >= dictLimit)) {515assert(matchIndex+matchLength >= dictLimit); /* might be wrong if actually extDict */516match = base + matchIndex;517matchLength += ZSTD_count(ip+matchLength, match+matchLength, iend);518} else {519match = dictBase + matchIndex;520matchLength += ZSTD_count_2segments(ip+matchLength, match+matchLength, iend, dictEnd, prefixStart);521if (matchIndex+matchLength >= dictLimit)522match = base + matchIndex; /* to prepare for next usage of match[matchLength] */523}524525if (matchLength > bestLength) {526bestLength = matchLength;527if (matchLength > matchEndIdx - matchIndex)528matchEndIdx = matchIndex + (U32)matchLength;529}530531if (ip+matchLength == iend) { /* equal : no way to know if inf or sup */532break; /* drop , to guarantee consistency ; miss a bit of compression, but other solutions can corrupt tree */533}534535if (match[matchLength] < ip[matchLength]) { /* necessarily within buffer */536/* match is smaller than current */537*smallerPtr = matchIndex; /* update smaller idx */538commonLengthSmaller = matchLength; /* all smaller will now have at least this guaranteed common length */539if (matchIndex <= btLow) { smallerPtr=&dummy32; break; } /* beyond tree size, stop searching */540smallerPtr = nextPtr+1; /* new "candidate" => larger than match, which was smaller than target */541matchIndex = nextPtr[1]; /* new matchIndex, larger than previous and closer to current */542} else {543/* match is larger than current */544*largerPtr = matchIndex;545commonLengthLarger = matchLength;546if (matchIndex <= btLow) { largerPtr=&dummy32; break; } /* beyond tree size, stop searching */547largerPtr = nextPtr;548matchIndex = nextPtr[0];549} }550551*smallerPtr = *largerPtr = 0;552{ U32 positions = 0;553if (bestLength > 384) positions = MIN(192, (U32)(bestLength - 384)); /* speed optimization */554assert(matchEndIdx > curr + 8);555return MAX(positions, matchEndIdx - (curr + 8));556}557}558559FORCE_INLINE_TEMPLATE560ZSTD_ALLOW_POINTER_OVERFLOW_ATTR561void ZSTD_updateTree_internal(562ZSTD_MatchState_t* ms,563const BYTE* const ip, const BYTE* const iend,564const U32 mls, const ZSTD_dictMode_e dictMode)565{566const BYTE* const base = ms->window.base;567U32 const target = (U32)(ip - base);568U32 idx = ms->nextToUpdate;569DEBUGLOG(7, "ZSTD_updateTree_internal, from %u to %u (dictMode:%u)",570idx, target, dictMode);571572while(idx < target) {573U32 const forward = ZSTD_insertBt1(ms, base+idx, iend, target, mls, dictMode == ZSTD_extDict);574assert(idx < (U32)(idx + forward));575idx += forward;576}577assert((size_t)(ip - base) <= (size_t)(U32)(-1));578assert((size_t)(iend - base) <= (size_t)(U32)(-1));579ms->nextToUpdate = target;580}581582void ZSTD_updateTree(ZSTD_MatchState_t* ms, const BYTE* ip, const BYTE* iend) {583ZSTD_updateTree_internal(ms, ip, iend, ms->cParams.minMatch, ZSTD_noDict);584}585586FORCE_INLINE_TEMPLATE587ZSTD_ALLOW_POINTER_OVERFLOW_ATTR588U32589ZSTD_insertBtAndGetAllMatches (590ZSTD_match_t* matches, /* store result (found matches) in this table (presumed large enough) */591ZSTD_MatchState_t* ms,592U32* nextToUpdate3,593const BYTE* const ip, const BYTE* const iLimit,594const ZSTD_dictMode_e dictMode,595const U32 rep[ZSTD_REP_NUM],596const U32 ll0, /* tells if associated literal length is 0 or not. This value must be 0 or 1 */597const U32 lengthToBeat,598const U32 mls /* template */)599{600const ZSTD_compressionParameters* const cParams = &ms->cParams;601U32 const sufficient_len = MIN(cParams->targetLength, ZSTD_OPT_NUM -1);602const BYTE* const base = ms->window.base;603U32 const curr = (U32)(ip-base);604U32 const hashLog = cParams->hashLog;605U32 const minMatch = (mls==3) ? 3 : 4;606U32* const hashTable = ms->hashTable;607size_t const h = ZSTD_hashPtr(ip, hashLog, mls);608U32 matchIndex = hashTable[h];609U32* const bt = ms->chainTable;610U32 const btLog = cParams->chainLog - 1;611U32 const btMask= (1U << btLog) - 1;612size_t commonLengthSmaller=0, commonLengthLarger=0;613const BYTE* const dictBase = ms->window.dictBase;614U32 const dictLimit = ms->window.dictLimit;615const BYTE* const dictEnd = dictBase + dictLimit;616const BYTE* const prefixStart = base + dictLimit;617U32 const btLow = (btMask >= curr) ? 0 : curr - btMask;618U32 const windowLow = ZSTD_getLowestMatchIndex(ms, curr, cParams->windowLog);619U32 const matchLow = windowLow ? windowLow : 1;620U32* smallerPtr = bt + 2*(curr&btMask);621U32* largerPtr = bt + 2*(curr&btMask) + 1;622U32 matchEndIdx = curr+8+1; /* farthest referenced position of any match => detects repetitive patterns */623U32 dummy32; /* to be nullified at the end */624U32 mnum = 0;625U32 nbCompares = 1U << cParams->searchLog;626627const ZSTD_MatchState_t* dms = dictMode == ZSTD_dictMatchState ? ms->dictMatchState : NULL;628const ZSTD_compressionParameters* const dmsCParams =629dictMode == ZSTD_dictMatchState ? &dms->cParams : NULL;630const BYTE* const dmsBase = dictMode == ZSTD_dictMatchState ? dms->window.base : NULL;631const BYTE* const dmsEnd = dictMode == ZSTD_dictMatchState ? dms->window.nextSrc : NULL;632U32 const dmsHighLimit = dictMode == ZSTD_dictMatchState ? (U32)(dmsEnd - dmsBase) : 0;633U32 const dmsLowLimit = dictMode == ZSTD_dictMatchState ? dms->window.lowLimit : 0;634U32 const dmsIndexDelta = dictMode == ZSTD_dictMatchState ? windowLow - dmsHighLimit : 0;635U32 const dmsHashLog = dictMode == ZSTD_dictMatchState ? dmsCParams->hashLog : hashLog;636U32 const dmsBtLog = dictMode == ZSTD_dictMatchState ? dmsCParams->chainLog - 1 : btLog;637U32 const dmsBtMask = dictMode == ZSTD_dictMatchState ? (1U << dmsBtLog) - 1 : 0;638U32 const dmsBtLow = dictMode == ZSTD_dictMatchState && dmsBtMask < dmsHighLimit - dmsLowLimit ? dmsHighLimit - dmsBtMask : dmsLowLimit;639640size_t bestLength = lengthToBeat-1;641DEBUGLOG(8, "ZSTD_insertBtAndGetAllMatches: current=%u", curr);642643/* check repCode */644assert(ll0 <= 1); /* necessarily 1 or 0 */645{ U32 const lastR = ZSTD_REP_NUM + ll0;646U32 repCode;647for (repCode = ll0; repCode < lastR; repCode++) {648U32 const repOffset = (repCode==ZSTD_REP_NUM) ? (rep[0] - 1) : rep[repCode];649U32 const repIndex = curr - repOffset;650U32 repLen = 0;651assert(curr >= dictLimit);652if (repOffset-1 /* intentional overflow, discards 0 and -1 */ < curr-dictLimit) { /* equivalent to `curr > repIndex >= dictLimit` */653/* We must validate the repcode offset because when we're using a dictionary the654* valid offset range shrinks when the dictionary goes out of bounds.655*/656if ((repIndex >= windowLow) & (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(ip - repOffset, minMatch))) {657repLen = (U32)ZSTD_count(ip+minMatch, ip+minMatch-repOffset, iLimit) + minMatch;658}659} else { /* repIndex < dictLimit || repIndex >= curr */660const BYTE* const repMatch = dictMode == ZSTD_dictMatchState ?661dmsBase + repIndex - dmsIndexDelta :662dictBase + repIndex;663assert(curr >= windowLow);664if ( dictMode == ZSTD_extDict665&& ( ((repOffset-1) /*intentional overflow*/ < curr - windowLow) /* equivalent to `curr > repIndex >= windowLow` */666& (ZSTD_index_overlap_check(dictLimit, repIndex)) )667&& (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(repMatch, minMatch)) ) {668repLen = (U32)ZSTD_count_2segments(ip+minMatch, repMatch+minMatch, iLimit, dictEnd, prefixStart) + minMatch;669}670if (dictMode == ZSTD_dictMatchState671&& ( ((repOffset-1) /*intentional overflow*/ < curr - (dmsLowLimit + dmsIndexDelta)) /* equivalent to `curr > repIndex >= dmsLowLimit` */672& (ZSTD_index_overlap_check(dictLimit, repIndex)) )673&& (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(repMatch, minMatch)) ) {674repLen = (U32)ZSTD_count_2segments(ip+minMatch, repMatch+minMatch, iLimit, dmsEnd, prefixStart) + minMatch;675} }676/* save longer solution */677if (repLen > bestLength) {678DEBUGLOG(8, "found repCode %u (ll0:%u, offset:%u) of length %u",679repCode, ll0, repOffset, repLen);680bestLength = repLen;681matches[mnum].off = REPCODE_TO_OFFBASE(repCode - ll0 + 1); /* expect value between 1 and 3 */682matches[mnum].len = (U32)repLen;683mnum++;684if ( (repLen > sufficient_len)685| (ip+repLen == iLimit) ) { /* best possible */686return mnum;687} } } }688689/* HC3 match finder */690if ((mls == 3) /*static*/ && (bestLength < mls)) {691U32 const matchIndex3 = ZSTD_insertAndFindFirstIndexHash3(ms, nextToUpdate3, ip);692if ((matchIndex3 >= matchLow)693& (curr - matchIndex3 < (1<<18)) /*heuristic : longer distance likely too expensive*/ ) {694size_t mlen;695if ((dictMode == ZSTD_noDict) /*static*/ || (dictMode == ZSTD_dictMatchState) /*static*/ || (matchIndex3 >= dictLimit)) {696const BYTE* const match = base + matchIndex3;697mlen = ZSTD_count(ip, match, iLimit);698} else {699const BYTE* const match = dictBase + matchIndex3;700mlen = ZSTD_count_2segments(ip, match, iLimit, dictEnd, prefixStart);701}702703/* save best solution */704if (mlen >= mls /* == 3 > bestLength */) {705DEBUGLOG(8, "found small match with hlog3, of length %u",706(U32)mlen);707bestLength = mlen;708assert(curr > matchIndex3);709assert(mnum==0); /* no prior solution */710matches[0].off = OFFSET_TO_OFFBASE(curr - matchIndex3);711matches[0].len = (U32)mlen;712mnum = 1;713if ( (mlen > sufficient_len) |714(ip+mlen == iLimit) ) { /* best possible length */715ms->nextToUpdate = curr+1; /* skip insertion */716return 1;717} } }718/* no dictMatchState lookup: dicts don't have a populated HC3 table */719} /* if (mls == 3) */720721hashTable[h] = curr; /* Update Hash Table */722723for (; nbCompares && (matchIndex >= matchLow); --nbCompares) {724U32* const nextPtr = bt + 2*(matchIndex & btMask);725const BYTE* match;726size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */727assert(curr > matchIndex);728729if ((dictMode == ZSTD_noDict) || (dictMode == ZSTD_dictMatchState) || (matchIndex+matchLength >= dictLimit)) {730assert(matchIndex+matchLength >= dictLimit); /* ensure the condition is correct when !extDict */731match = base + matchIndex;732if (matchIndex >= dictLimit) assert(memcmp(match, ip, matchLength) == 0); /* ensure early section of match is equal as expected */733matchLength += ZSTD_count(ip+matchLength, match+matchLength, iLimit);734} else {735match = dictBase + matchIndex;736assert(memcmp(match, ip, matchLength) == 0); /* ensure early section of match is equal as expected */737matchLength += ZSTD_count_2segments(ip+matchLength, match+matchLength, iLimit, dictEnd, prefixStart);738if (matchIndex+matchLength >= dictLimit)739match = base + matchIndex; /* prepare for match[matchLength] read */740}741742if (matchLength > bestLength) {743DEBUGLOG(8, "found match of length %u at distance %u (offBase=%u)",744(U32)matchLength, curr - matchIndex, OFFSET_TO_OFFBASE(curr - matchIndex));745assert(matchEndIdx > matchIndex);746if (matchLength > matchEndIdx - matchIndex)747matchEndIdx = matchIndex + (U32)matchLength;748bestLength = matchLength;749matches[mnum].off = OFFSET_TO_OFFBASE(curr - matchIndex);750matches[mnum].len = (U32)matchLength;751mnum++;752if ( (matchLength > ZSTD_OPT_NUM)753| (ip+matchLength == iLimit) /* equal : no way to know if inf or sup */) {754if (dictMode == ZSTD_dictMatchState) nbCompares = 0; /* break should also skip searching dms */755break; /* drop, to preserve bt consistency (miss a little bit of compression) */756} }757758if (match[matchLength] < ip[matchLength]) {759/* match smaller than current */760*smallerPtr = matchIndex; /* update smaller idx */761commonLengthSmaller = matchLength; /* all smaller will now have at least this guaranteed common length */762if (matchIndex <= btLow) { smallerPtr=&dummy32; break; } /* beyond tree size, stop the search */763smallerPtr = nextPtr+1; /* new candidate => larger than match, which was smaller than current */764matchIndex = nextPtr[1]; /* new matchIndex, larger than previous, closer to current */765} else {766*largerPtr = matchIndex;767commonLengthLarger = matchLength;768if (matchIndex <= btLow) { largerPtr=&dummy32; break; } /* beyond tree size, stop the search */769largerPtr = nextPtr;770matchIndex = nextPtr[0];771} }772773*smallerPtr = *largerPtr = 0;774775assert(nbCompares <= (1U << ZSTD_SEARCHLOG_MAX)); /* Check we haven't underflowed. */776if (dictMode == ZSTD_dictMatchState && nbCompares) {777size_t const dmsH = ZSTD_hashPtr(ip, dmsHashLog, mls);778U32 dictMatchIndex = dms->hashTable[dmsH];779const U32* const dmsBt = dms->chainTable;780commonLengthSmaller = commonLengthLarger = 0;781for (; nbCompares && (dictMatchIndex > dmsLowLimit); --nbCompares) {782const U32* const nextPtr = dmsBt + 2*(dictMatchIndex & dmsBtMask);783size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */784const BYTE* match = dmsBase + dictMatchIndex;785matchLength += ZSTD_count_2segments(ip+matchLength, match+matchLength, iLimit, dmsEnd, prefixStart);786if (dictMatchIndex+matchLength >= dmsHighLimit)787match = base + dictMatchIndex + dmsIndexDelta; /* to prepare for next usage of match[matchLength] */788789if (matchLength > bestLength) {790matchIndex = dictMatchIndex + dmsIndexDelta;791DEBUGLOG(8, "found dms match of length %u at distance %u (offBase=%u)",792(U32)matchLength, curr - matchIndex, OFFSET_TO_OFFBASE(curr - matchIndex));793if (matchLength > matchEndIdx - matchIndex)794matchEndIdx = matchIndex + (U32)matchLength;795bestLength = matchLength;796matches[mnum].off = OFFSET_TO_OFFBASE(curr - matchIndex);797matches[mnum].len = (U32)matchLength;798mnum++;799if ( (matchLength > ZSTD_OPT_NUM)800| (ip+matchLength == iLimit) /* equal : no way to know if inf or sup */) {801break; /* drop, to guarantee consistency (miss a little bit of compression) */802} }803804if (dictMatchIndex <= dmsBtLow) { break; } /* beyond tree size, stop the search */805if (match[matchLength] < ip[matchLength]) {806commonLengthSmaller = matchLength; /* all smaller will now have at least this guaranteed common length */807dictMatchIndex = nextPtr[1]; /* new matchIndex larger than previous (closer to current) */808} else {809/* match is larger than current */810commonLengthLarger = matchLength;811dictMatchIndex = nextPtr[0];812} } } /* if (dictMode == ZSTD_dictMatchState) */813814assert(matchEndIdx > curr+8);815ms->nextToUpdate = matchEndIdx - 8; /* skip repetitive patterns */816return mnum;817}818819typedef U32 (*ZSTD_getAllMatchesFn)(820ZSTD_match_t*,821ZSTD_MatchState_t*,822U32*,823const BYTE*,824const BYTE*,825const U32 rep[ZSTD_REP_NUM],826U32 const ll0,827U32 const lengthToBeat);828829FORCE_INLINE_TEMPLATE830ZSTD_ALLOW_POINTER_OVERFLOW_ATTR831U32 ZSTD_btGetAllMatches_internal(832ZSTD_match_t* matches,833ZSTD_MatchState_t* ms,834U32* nextToUpdate3,835const BYTE* ip,836const BYTE* const iHighLimit,837const U32 rep[ZSTD_REP_NUM],838U32 const ll0,839U32 const lengthToBeat,840const ZSTD_dictMode_e dictMode,841const U32 mls)842{843assert(BOUNDED(3, ms->cParams.minMatch, 6) == mls);844DEBUGLOG(8, "ZSTD_BtGetAllMatches(dictMode=%d, mls=%u)", (int)dictMode, mls);845if (ip < ms->window.base + ms->nextToUpdate)846return 0; /* skipped area */847ZSTD_updateTree_internal(ms, ip, iHighLimit, mls, dictMode);848return ZSTD_insertBtAndGetAllMatches(matches, ms, nextToUpdate3, ip, iHighLimit, dictMode, rep, ll0, lengthToBeat, mls);849}850851#define ZSTD_BT_GET_ALL_MATCHES_FN(dictMode, mls) ZSTD_btGetAllMatches_##dictMode##_##mls852853#define GEN_ZSTD_BT_GET_ALL_MATCHES_(dictMode, mls) \854static U32 ZSTD_BT_GET_ALL_MATCHES_FN(dictMode, mls)( \855ZSTD_match_t* matches, \856ZSTD_MatchState_t* ms, \857U32* nextToUpdate3, \858const BYTE* ip, \859const BYTE* const iHighLimit, \860const U32 rep[ZSTD_REP_NUM], \861U32 const ll0, \862U32 const lengthToBeat) \863{ \864return ZSTD_btGetAllMatches_internal( \865matches, ms, nextToUpdate3, ip, iHighLimit, \866rep, ll0, lengthToBeat, ZSTD_##dictMode, mls); \867}868869#define GEN_ZSTD_BT_GET_ALL_MATCHES(dictMode) \870GEN_ZSTD_BT_GET_ALL_MATCHES_(dictMode, 3) \871GEN_ZSTD_BT_GET_ALL_MATCHES_(dictMode, 4) \872GEN_ZSTD_BT_GET_ALL_MATCHES_(dictMode, 5) \873GEN_ZSTD_BT_GET_ALL_MATCHES_(dictMode, 6)874875GEN_ZSTD_BT_GET_ALL_MATCHES(noDict)876GEN_ZSTD_BT_GET_ALL_MATCHES(extDict)877GEN_ZSTD_BT_GET_ALL_MATCHES(dictMatchState)878879#define ZSTD_BT_GET_ALL_MATCHES_ARRAY(dictMode) \880{ \881ZSTD_BT_GET_ALL_MATCHES_FN(dictMode, 3), \882ZSTD_BT_GET_ALL_MATCHES_FN(dictMode, 4), \883ZSTD_BT_GET_ALL_MATCHES_FN(dictMode, 5), \884ZSTD_BT_GET_ALL_MATCHES_FN(dictMode, 6) \885}886887static ZSTD_getAllMatchesFn888ZSTD_selectBtGetAllMatches(ZSTD_MatchState_t const* ms, ZSTD_dictMode_e const dictMode)889{890ZSTD_getAllMatchesFn const getAllMatchesFns[3][4] = {891ZSTD_BT_GET_ALL_MATCHES_ARRAY(noDict),892ZSTD_BT_GET_ALL_MATCHES_ARRAY(extDict),893ZSTD_BT_GET_ALL_MATCHES_ARRAY(dictMatchState)894};895U32 const mls = BOUNDED(3, ms->cParams.minMatch, 6);896assert((U32)dictMode < 3);897assert(mls - 3 < 4);898return getAllMatchesFns[(int)dictMode][mls - 3];899}900901/*************************902* LDM helper functions *903*************************/904905/* Struct containing info needed to make decision about ldm inclusion */906typedef struct {907RawSeqStore_t seqStore; /* External match candidates store for this block */908U32 startPosInBlock; /* Start position of the current match candidate */909U32 endPosInBlock; /* End position of the current match candidate */910U32 offset; /* Offset of the match candidate */911} ZSTD_optLdm_t;912913/* ZSTD_optLdm_skipRawSeqStoreBytes():914* Moves forward in @rawSeqStore by @nbBytes,915* which will update the fields 'pos' and 'posInSequence'.916*/917static void ZSTD_optLdm_skipRawSeqStoreBytes(RawSeqStore_t* rawSeqStore, size_t nbBytes)918{919U32 currPos = (U32)(rawSeqStore->posInSequence + nbBytes);920while (currPos && rawSeqStore->pos < rawSeqStore->size) {921rawSeq currSeq = rawSeqStore->seq[rawSeqStore->pos];922if (currPos >= currSeq.litLength + currSeq.matchLength) {923currPos -= currSeq.litLength + currSeq.matchLength;924rawSeqStore->pos++;925} else {926rawSeqStore->posInSequence = currPos;927break;928}929}930if (currPos == 0 || rawSeqStore->pos == rawSeqStore->size) {931rawSeqStore->posInSequence = 0;932}933}934935/* ZSTD_opt_getNextMatchAndUpdateSeqStore():936* Calculates the beginning and end of the next match in the current block.937* Updates 'pos' and 'posInSequence' of the ldmSeqStore.938*/939static void940ZSTD_opt_getNextMatchAndUpdateSeqStore(ZSTD_optLdm_t* optLdm, U32 currPosInBlock,941U32 blockBytesRemaining)942{943rawSeq currSeq;944U32 currBlockEndPos;945U32 literalsBytesRemaining;946U32 matchBytesRemaining;947948/* Setting match end position to MAX to ensure we never use an LDM during this block */949if (optLdm->seqStore.size == 0 || optLdm->seqStore.pos >= optLdm->seqStore.size) {950optLdm->startPosInBlock = UINT_MAX;951optLdm->endPosInBlock = UINT_MAX;952return;953}954/* Calculate appropriate bytes left in matchLength and litLength955* after adjusting based on ldmSeqStore->posInSequence */956currSeq = optLdm->seqStore.seq[optLdm->seqStore.pos];957assert(optLdm->seqStore.posInSequence <= currSeq.litLength + currSeq.matchLength);958currBlockEndPos = currPosInBlock + blockBytesRemaining;959literalsBytesRemaining = (optLdm->seqStore.posInSequence < currSeq.litLength) ?960currSeq.litLength - (U32)optLdm->seqStore.posInSequence :9610;962matchBytesRemaining = (literalsBytesRemaining == 0) ?963currSeq.matchLength - ((U32)optLdm->seqStore.posInSequence - currSeq.litLength) :964currSeq.matchLength;965966/* If there are more literal bytes than bytes remaining in block, no ldm is possible */967if (literalsBytesRemaining >= blockBytesRemaining) {968optLdm->startPosInBlock = UINT_MAX;969optLdm->endPosInBlock = UINT_MAX;970ZSTD_optLdm_skipRawSeqStoreBytes(&optLdm->seqStore, blockBytesRemaining);971return;972}973974/* Matches may be < minMatch by this process. In that case, we will reject them975when we are deciding whether or not to add the ldm */976optLdm->startPosInBlock = currPosInBlock + literalsBytesRemaining;977optLdm->endPosInBlock = optLdm->startPosInBlock + matchBytesRemaining;978optLdm->offset = currSeq.offset;979980if (optLdm->endPosInBlock > currBlockEndPos) {981/* Match ends after the block ends, we can't use the whole match */982optLdm->endPosInBlock = currBlockEndPos;983ZSTD_optLdm_skipRawSeqStoreBytes(&optLdm->seqStore, currBlockEndPos - currPosInBlock);984} else {985/* Consume nb of bytes equal to size of sequence left */986ZSTD_optLdm_skipRawSeqStoreBytes(&optLdm->seqStore, literalsBytesRemaining + matchBytesRemaining);987}988}989990/* ZSTD_optLdm_maybeAddMatch():991* Adds a match if it's long enough,992* based on it's 'matchStartPosInBlock' and 'matchEndPosInBlock',993* into 'matches'. Maintains the correct ordering of 'matches'.994*/995static void ZSTD_optLdm_maybeAddMatch(ZSTD_match_t* matches, U32* nbMatches,996const ZSTD_optLdm_t* optLdm, U32 currPosInBlock,997U32 minMatch)998{999U32 const posDiff = currPosInBlock - optLdm->startPosInBlock;1000/* Note: ZSTD_match_t actually contains offBase and matchLength (before subtracting MINMATCH) */1001U32 const candidateMatchLength = optLdm->endPosInBlock - optLdm->startPosInBlock - posDiff;10021003/* Ensure that current block position is not outside of the match */1004if (currPosInBlock < optLdm->startPosInBlock1005|| currPosInBlock >= optLdm->endPosInBlock1006|| candidateMatchLength < minMatch) {1007return;1008}10091010if (*nbMatches == 0 || ((candidateMatchLength > matches[*nbMatches-1].len) && *nbMatches < ZSTD_OPT_NUM)) {1011U32 const candidateOffBase = OFFSET_TO_OFFBASE(optLdm->offset);1012DEBUGLOG(6, "ZSTD_optLdm_maybeAddMatch(): Adding ldm candidate match (offBase: %u matchLength %u) at block position=%u",1013candidateOffBase, candidateMatchLength, currPosInBlock);1014matches[*nbMatches].len = candidateMatchLength;1015matches[*nbMatches].off = candidateOffBase;1016(*nbMatches)++;1017}1018}10191020/* ZSTD_optLdm_processMatchCandidate():1021* Wrapper function to update ldm seq store and call ldm functions as necessary.1022*/1023static void1024ZSTD_optLdm_processMatchCandidate(ZSTD_optLdm_t* optLdm,1025ZSTD_match_t* matches, U32* nbMatches,1026U32 currPosInBlock, U32 remainingBytes,1027U32 minMatch)1028{1029if (optLdm->seqStore.size == 0 || optLdm->seqStore.pos >= optLdm->seqStore.size) {1030return;1031}10321033if (currPosInBlock >= optLdm->endPosInBlock) {1034if (currPosInBlock > optLdm->endPosInBlock) {1035/* The position at which ZSTD_optLdm_processMatchCandidate() is called is not necessarily1036* at the end of a match from the ldm seq store, and will often be some bytes1037* over beyond matchEndPosInBlock. As such, we need to correct for these "overshoots"1038*/1039U32 const posOvershoot = currPosInBlock - optLdm->endPosInBlock;1040ZSTD_optLdm_skipRawSeqStoreBytes(&optLdm->seqStore, posOvershoot);1041}1042ZSTD_opt_getNextMatchAndUpdateSeqStore(optLdm, currPosInBlock, remainingBytes);1043}1044ZSTD_optLdm_maybeAddMatch(matches, nbMatches, optLdm, currPosInBlock, minMatch);1045}104610471048/*-*******************************1049* Optimal parser1050*********************************/10511052#if 0 /* debug */10531054static void1055listStats(const U32* table, int lastEltID)1056{1057int const nbElts = lastEltID + 1;1058int enb;1059for (enb=0; enb < nbElts; enb++) {1060(void)table;1061/* RAWLOG(2, "%3i:%3i, ", enb, table[enb]); */1062RAWLOG(2, "%4i,", table[enb]);1063}1064RAWLOG(2, " \n");1065}10661067#endif10681069#define LIT_PRICE(_p) (int)ZSTD_rawLiteralsCost(_p, 1, optStatePtr, optLevel)1070#define LL_PRICE(_l) (int)ZSTD_litLengthPrice(_l, optStatePtr, optLevel)1071#define LL_INCPRICE(_l) (LL_PRICE(_l) - LL_PRICE(_l-1))10721073FORCE_INLINE_TEMPLATE1074ZSTD_ALLOW_POINTER_OVERFLOW_ATTR1075size_t1076ZSTD_compressBlock_opt_generic(ZSTD_MatchState_t* ms,1077SeqStore_t* seqStore,1078U32 rep[ZSTD_REP_NUM],1079const void* src, size_t srcSize,1080const int optLevel,1081const ZSTD_dictMode_e dictMode)1082{1083optState_t* const optStatePtr = &ms->opt;1084const BYTE* const istart = (const BYTE*)src;1085const BYTE* ip = istart;1086const BYTE* anchor = istart;1087const BYTE* const iend = istart + srcSize;1088const BYTE* const ilimit = iend - 8;1089const BYTE* const base = ms->window.base;1090const BYTE* const prefixStart = base + ms->window.dictLimit;1091const ZSTD_compressionParameters* const cParams = &ms->cParams;10921093ZSTD_getAllMatchesFn getAllMatches = ZSTD_selectBtGetAllMatches(ms, dictMode);10941095U32 const sufficient_len = MIN(cParams->targetLength, ZSTD_OPT_NUM -1);1096U32 const minMatch = (cParams->minMatch == 3) ? 3 : 4;1097U32 nextToUpdate3 = ms->nextToUpdate;10981099ZSTD_optimal_t* const opt = optStatePtr->priceTable;1100ZSTD_match_t* const matches = optStatePtr->matchTable;1101ZSTD_optimal_t lastStretch;1102ZSTD_optLdm_t optLdm;11031104ZSTD_memset(&lastStretch, 0, sizeof(ZSTD_optimal_t));11051106optLdm.seqStore = ms->ldmSeqStore ? *ms->ldmSeqStore : kNullRawSeqStore;1107optLdm.endPosInBlock = optLdm.startPosInBlock = optLdm.offset = 0;1108ZSTD_opt_getNextMatchAndUpdateSeqStore(&optLdm, (U32)(ip-istart), (U32)(iend-ip));11091110/* init */1111DEBUGLOG(5, "ZSTD_compressBlock_opt_generic: current=%u, prefix=%u, nextToUpdate=%u",1112(U32)(ip - base), ms->window.dictLimit, ms->nextToUpdate);1113assert(optLevel <= 2);1114ZSTD_rescaleFreqs(optStatePtr, (const BYTE*)src, srcSize, optLevel);1115ip += (ip==prefixStart);11161117/* Match Loop */1118while (ip < ilimit) {1119U32 cur, last_pos = 0;11201121/* find first match */1122{ U32 const litlen = (U32)(ip - anchor);1123U32 const ll0 = !litlen;1124U32 nbMatches = getAllMatches(matches, ms, &nextToUpdate3, ip, iend, rep, ll0, minMatch);1125ZSTD_optLdm_processMatchCandidate(&optLdm, matches, &nbMatches,1126(U32)(ip-istart), (U32)(iend-ip),1127minMatch);1128if (!nbMatches) {1129DEBUGLOG(8, "no match found at cPos %u", (unsigned)(ip-istart));1130ip++;1131continue;1132}11331134/* Match found: let's store this solution, and eventually find more candidates.1135* During this forward pass, @opt is used to store stretches,1136* defined as "a match followed by N literals".1137* Note how this is different from a Sequence, which is "N literals followed by a match".1138* Storing stretches allows us to store different match predecessors1139* for each literal position part of a literals run. */11401141/* initialize opt[0] */1142opt[0].mlen = 0; /* there are only literals so far */1143opt[0].litlen = litlen;1144/* No need to include the actual price of the literals before the first match1145* because it is static for the duration of the forward pass, and is included1146* in every subsequent price. But, we include the literal length because1147* the cost variation of litlen depends on the value of litlen.1148*/1149opt[0].price = LL_PRICE(litlen);1150ZSTD_STATIC_ASSERT(sizeof(opt[0].rep[0]) == sizeof(rep[0]));1151ZSTD_memcpy(&opt[0].rep, rep, sizeof(opt[0].rep));11521153/* large match -> immediate encoding */1154{ U32 const maxML = matches[nbMatches-1].len;1155U32 const maxOffBase = matches[nbMatches-1].off;1156DEBUGLOG(6, "found %u matches of maxLength=%u and maxOffBase=%u at cPos=%u => start new series",1157nbMatches, maxML, maxOffBase, (U32)(ip-prefixStart));11581159if (maxML > sufficient_len) {1160lastStretch.litlen = 0;1161lastStretch.mlen = maxML;1162lastStretch.off = maxOffBase;1163DEBUGLOG(6, "large match (%u>%u) => immediate encoding",1164maxML, sufficient_len);1165cur = 0;1166last_pos = maxML;1167goto _shortestPath;1168} }11691170/* set prices for first matches starting position == 0 */1171assert(opt[0].price >= 0);1172{ U32 pos;1173U32 matchNb;1174for (pos = 1; pos < minMatch; pos++) {1175opt[pos].price = ZSTD_MAX_PRICE;1176opt[pos].mlen = 0;1177opt[pos].litlen = litlen + pos;1178}1179for (matchNb = 0; matchNb < nbMatches; matchNb++) {1180U32 const offBase = matches[matchNb].off;1181U32 const end = matches[matchNb].len;1182for ( ; pos <= end ; pos++ ) {1183int const matchPrice = (int)ZSTD_getMatchPrice(offBase, pos, optStatePtr, optLevel);1184int const sequencePrice = opt[0].price + matchPrice;1185DEBUGLOG(7, "rPos:%u => set initial price : %.2f",1186pos, ZSTD_fCost(sequencePrice));1187opt[pos].mlen = pos;1188opt[pos].off = offBase;1189opt[pos].litlen = 0; /* end of match */1190opt[pos].price = sequencePrice + LL_PRICE(0);1191}1192}1193last_pos = pos-1;1194opt[pos].price = ZSTD_MAX_PRICE;1195}1196}11971198/* check further positions */1199for (cur = 1; cur <= last_pos; cur++) {1200const BYTE* const inr = ip + cur;1201assert(cur <= ZSTD_OPT_NUM);1202DEBUGLOG(7, "cPos:%i==rPos:%u", (int)(inr-istart), cur);12031204/* Fix current position with one literal if cheaper */1205{ U32 const litlen = opt[cur-1].litlen + 1;1206int const price = opt[cur-1].price1207+ LIT_PRICE(ip+cur-1)1208+ LL_INCPRICE(litlen);1209assert(price < 1000000000); /* overflow check */1210if (price <= opt[cur].price) {1211ZSTD_optimal_t const prevMatch = opt[cur];1212DEBUGLOG(7, "cPos:%i==rPos:%u : better price (%.2f<=%.2f) using literal (ll==%u) (hist:%u,%u,%u)",1213(int)(inr-istart), cur, ZSTD_fCost(price), ZSTD_fCost(opt[cur].price), litlen,1214opt[cur-1].rep[0], opt[cur-1].rep[1], opt[cur-1].rep[2]);1215opt[cur] = opt[cur-1];1216opt[cur].litlen = litlen;1217opt[cur].price = price;1218if ( (optLevel >= 1) /* additional check only for higher modes */1219&& (prevMatch.litlen == 0) /* replace a match */1220&& (LL_INCPRICE(1) < 0) /* ll1 is cheaper than ll0 */1221&& LIKELY(ip + cur < iend)1222) {1223/* check next position, in case it would be cheaper */1224int with1literal = prevMatch.price + LIT_PRICE(ip+cur) + LL_INCPRICE(1);1225int withMoreLiterals = price + LIT_PRICE(ip+cur) + LL_INCPRICE(litlen+1);1226DEBUGLOG(7, "then at next rPos %u : match+1lit %.2f vs %ulits %.2f",1227cur+1, ZSTD_fCost(with1literal), litlen+1, ZSTD_fCost(withMoreLiterals));1228if ( (with1literal < withMoreLiterals)1229&& (with1literal < opt[cur+1].price) ) {1230/* update offset history - before it disappears */1231U32 const prev = cur - prevMatch.mlen;1232Repcodes_t const newReps = ZSTD_newRep(opt[prev].rep, prevMatch.off, opt[prev].litlen==0);1233assert(cur >= prevMatch.mlen);1234DEBUGLOG(7, "==> match+1lit is cheaper (%.2f < %.2f) (hist:%u,%u,%u) !",1235ZSTD_fCost(with1literal), ZSTD_fCost(withMoreLiterals),1236newReps.rep[0], newReps.rep[1], newReps.rep[2] );1237opt[cur+1] = prevMatch; /* mlen & offbase */1238ZSTD_memcpy(opt[cur+1].rep, &newReps, sizeof(Repcodes_t));1239opt[cur+1].litlen = 1;1240opt[cur+1].price = with1literal;1241if (last_pos < cur+1) last_pos = cur+1;1242}1243}1244} else {1245DEBUGLOG(7, "cPos:%i==rPos:%u : literal would cost more (%.2f>%.2f)",1246(int)(inr-istart), cur, ZSTD_fCost(price), ZSTD_fCost(opt[cur].price));1247}1248}12491250/* Offset history is not updated during match comparison.1251* Do it here, now that the match is selected and confirmed.1252*/1253ZSTD_STATIC_ASSERT(sizeof(opt[cur].rep) == sizeof(Repcodes_t));1254assert(cur >= opt[cur].mlen);1255if (opt[cur].litlen == 0) {1256/* just finished a match => alter offset history */1257U32 const prev = cur - opt[cur].mlen;1258Repcodes_t const newReps = ZSTD_newRep(opt[prev].rep, opt[cur].off, opt[prev].litlen==0);1259ZSTD_memcpy(opt[cur].rep, &newReps, sizeof(Repcodes_t));1260}12611262/* last match must start at a minimum distance of 8 from oend */1263if (inr > ilimit) continue;12641265if (cur == last_pos) break;12661267if ( (optLevel==0) /*static_test*/1268&& (opt[cur+1].price <= opt[cur].price + (BITCOST_MULTIPLIER/2)) ) {1269DEBUGLOG(7, "skip current position : next rPos(%u) price is cheaper", cur+1);1270continue; /* skip unpromising positions; about ~+6% speed, -0.01 ratio */1271}12721273assert(opt[cur].price >= 0);1274{ U32 const ll0 = (opt[cur].litlen == 0);1275int const previousPrice = opt[cur].price;1276int const basePrice = previousPrice + LL_PRICE(0);1277U32 nbMatches = getAllMatches(matches, ms, &nextToUpdate3, inr, iend, opt[cur].rep, ll0, minMatch);1278U32 matchNb;12791280ZSTD_optLdm_processMatchCandidate(&optLdm, matches, &nbMatches,1281(U32)(inr-istart), (U32)(iend-inr),1282minMatch);12831284if (!nbMatches) {1285DEBUGLOG(7, "rPos:%u : no match found", cur);1286continue;1287}12881289{ U32 const longestML = matches[nbMatches-1].len;1290DEBUGLOG(7, "cPos:%i==rPos:%u, found %u matches, of longest ML=%u",1291(int)(inr-istart), cur, nbMatches, longestML);12921293if ( (longestML > sufficient_len)1294|| (cur + longestML >= ZSTD_OPT_NUM)1295|| (ip + cur + longestML >= iend) ) {1296lastStretch.mlen = longestML;1297lastStretch.off = matches[nbMatches-1].off;1298lastStretch.litlen = 0;1299last_pos = cur + longestML;1300goto _shortestPath;1301} }13021303/* set prices using matches found at position == cur */1304for (matchNb = 0; matchNb < nbMatches; matchNb++) {1305U32 const offset = matches[matchNb].off;1306U32 const lastML = matches[matchNb].len;1307U32 const startML = (matchNb>0) ? matches[matchNb-1].len+1 : minMatch;1308U32 mlen;13091310DEBUGLOG(7, "testing match %u => offBase=%4u, mlen=%2u, llen=%2u",1311matchNb, matches[matchNb].off, lastML, opt[cur].litlen);13121313for (mlen = lastML; mlen >= startML; mlen--) { /* scan downward */1314U32 const pos = cur + mlen;1315int const price = basePrice + (int)ZSTD_getMatchPrice(offset, mlen, optStatePtr, optLevel);13161317if ((pos > last_pos) || (price < opt[pos].price)) {1318DEBUGLOG(7, "rPos:%u (ml=%2u) => new better price (%.2f<%.2f)",1319pos, mlen, ZSTD_fCost(price), ZSTD_fCost(opt[pos].price));1320while (last_pos < pos) {1321/* fill empty positions, for future comparisons */1322last_pos++;1323opt[last_pos].price = ZSTD_MAX_PRICE;1324opt[last_pos].litlen = !0; /* just needs to be != 0, to mean "not an end of match" */1325}1326opt[pos].mlen = mlen;1327opt[pos].off = offset;1328opt[pos].litlen = 0;1329opt[pos].price = price;1330} else {1331DEBUGLOG(7, "rPos:%u (ml=%2u) => new price is worse (%.2f>=%.2f)",1332pos, mlen, ZSTD_fCost(price), ZSTD_fCost(opt[pos].price));1333if (optLevel==0) break; /* early update abort; gets ~+10% speed for about -0.01 ratio loss */1334}1335} } }1336opt[last_pos+1].price = ZSTD_MAX_PRICE;1337} /* for (cur = 1; cur <= last_pos; cur++) */13381339lastStretch = opt[last_pos];1340assert(cur >= lastStretch.mlen);1341cur = last_pos - lastStretch.mlen;13421343_shortestPath: /* cur, last_pos, best_mlen, best_off have to be set */1344assert(opt[0].mlen == 0);1345assert(last_pos >= lastStretch.mlen);1346assert(cur == last_pos - lastStretch.mlen);13471348if (lastStretch.mlen==0) {1349/* no solution : all matches have been converted into literals */1350assert(lastStretch.litlen == (ip - anchor) + last_pos);1351ip += last_pos;1352continue;1353}1354assert(lastStretch.off > 0);13551356/* Update offset history */1357if (lastStretch.litlen == 0) {1358/* finishing on a match : update offset history */1359Repcodes_t const reps = ZSTD_newRep(opt[cur].rep, lastStretch.off, opt[cur].litlen==0);1360ZSTD_memcpy(rep, &reps, sizeof(Repcodes_t));1361} else {1362ZSTD_memcpy(rep, lastStretch.rep, sizeof(Repcodes_t));1363assert(cur >= lastStretch.litlen);1364cur -= lastStretch.litlen;1365}13661367/* Let's write the shortest path solution.1368* It is stored in @opt in reverse order,1369* starting from @storeEnd (==cur+2),1370* effectively partially @opt overwriting.1371* Content is changed too:1372* - So far, @opt stored stretches, aka a match followed by literals1373* - Now, it will store sequences, aka literals followed by a match1374*/1375{ U32 const storeEnd = cur + 2;1376U32 storeStart = storeEnd;1377U32 stretchPos = cur;13781379DEBUGLOG(6, "start reverse traversal (last_pos:%u, cur:%u)",1380last_pos, cur); (void)last_pos;1381assert(storeEnd < ZSTD_OPT_SIZE);1382DEBUGLOG(6, "last stretch copied into pos=%u (llen=%u,mlen=%u,ofc=%u)",1383storeEnd, lastStretch.litlen, lastStretch.mlen, lastStretch.off);1384if (lastStretch.litlen > 0) {1385/* last "sequence" is unfinished: just a bunch of literals */1386opt[storeEnd].litlen = lastStretch.litlen;1387opt[storeEnd].mlen = 0;1388storeStart = storeEnd-1;1389opt[storeStart] = lastStretch;1390} {1391opt[storeEnd] = lastStretch; /* note: litlen will be fixed */1392storeStart = storeEnd;1393}1394while (1) {1395ZSTD_optimal_t nextStretch = opt[stretchPos];1396opt[storeStart].litlen = nextStretch.litlen;1397DEBUGLOG(6, "selected sequence (llen=%u,mlen=%u,ofc=%u)",1398opt[storeStart].litlen, opt[storeStart].mlen, opt[storeStart].off);1399if (nextStretch.mlen == 0) {1400/* reaching beginning of segment */1401break;1402}1403storeStart--;1404opt[storeStart] = nextStretch; /* note: litlen will be fixed */1405assert(nextStretch.litlen + nextStretch.mlen <= stretchPos);1406stretchPos -= nextStretch.litlen + nextStretch.mlen;1407}14081409/* save sequences */1410DEBUGLOG(6, "sending selected sequences into seqStore");1411{ U32 storePos;1412for (storePos=storeStart; storePos <= storeEnd; storePos++) {1413U32 const llen = opt[storePos].litlen;1414U32 const mlen = opt[storePos].mlen;1415U32 const offBase = opt[storePos].off;1416U32 const advance = llen + mlen;1417DEBUGLOG(6, "considering seq starting at %i, llen=%u, mlen=%u",1418(int)(anchor - istart), (unsigned)llen, (unsigned)mlen);14191420if (mlen==0) { /* only literals => must be last "sequence", actually starting a new stream of sequences */1421assert(storePos == storeEnd); /* must be last sequence */1422ip = anchor + llen; /* last "sequence" is a bunch of literals => don't progress anchor */1423continue; /* will finish */1424}14251426assert(anchor + llen <= iend);1427ZSTD_updateStats(optStatePtr, llen, anchor, offBase, mlen);1428ZSTD_storeSeq(seqStore, llen, anchor, iend, offBase, mlen);1429anchor += advance;1430ip = anchor;1431} }1432DEBUGLOG(7, "new offset history : %u, %u, %u", rep[0], rep[1], rep[2]);14331434/* update all costs */1435ZSTD_setBasePrices(optStatePtr, optLevel);1436}1437} /* while (ip < ilimit) */14381439/* Return the last literals size */1440return (size_t)(iend - anchor);1441}1442#endif /* build exclusions */14431444#ifndef ZSTD_EXCLUDE_BTOPT_BLOCK_COMPRESSOR1445static size_t ZSTD_compressBlock_opt0(1446ZSTD_MatchState_t* ms, SeqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],1447const void* src, size_t srcSize, const ZSTD_dictMode_e dictMode)1448{1449return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 0 /* optLevel */, dictMode);1450}1451#endif14521453#ifndef ZSTD_EXCLUDE_BTULTRA_BLOCK_COMPRESSOR1454static size_t ZSTD_compressBlock_opt2(1455ZSTD_MatchState_t* ms, SeqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],1456const void* src, size_t srcSize, const ZSTD_dictMode_e dictMode)1457{1458return ZSTD_compressBlock_opt_generic(ms, seqStore, rep, src, srcSize, 2 /* optLevel */, dictMode);1459}1460#endif14611462#ifndef ZSTD_EXCLUDE_BTOPT_BLOCK_COMPRESSOR1463size_t ZSTD_compressBlock_btopt(1464ZSTD_MatchState_t* ms, SeqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],1465const void* src, size_t srcSize)1466{1467DEBUGLOG(5, "ZSTD_compressBlock_btopt");1468return ZSTD_compressBlock_opt0(ms, seqStore, rep, src, srcSize, ZSTD_noDict);1469}1470#endif14711472147314741475#ifndef ZSTD_EXCLUDE_BTULTRA_BLOCK_COMPRESSOR1476/* ZSTD_initStats_ultra():1477* make a first compression pass, just to seed stats with more accurate starting values.1478* only works on first block, with no dictionary and no ldm.1479* this function cannot error out, its narrow contract must be respected.1480*/1481static1482ZSTD_ALLOW_POINTER_OVERFLOW_ATTR1483void ZSTD_initStats_ultra(ZSTD_MatchState_t* ms,1484SeqStore_t* seqStore,1485U32 rep[ZSTD_REP_NUM],1486const void* src, size_t srcSize)1487{1488U32 tmpRep[ZSTD_REP_NUM]; /* updated rep codes will sink here */1489ZSTD_memcpy(tmpRep, rep, sizeof(tmpRep));14901491DEBUGLOG(4, "ZSTD_initStats_ultra (srcSize=%zu)", srcSize);1492assert(ms->opt.litLengthSum == 0); /* first block */1493assert(seqStore->sequences == seqStore->sequencesStart); /* no ldm */1494assert(ms->window.dictLimit == ms->window.lowLimit); /* no dictionary */1495assert(ms->window.dictLimit - ms->nextToUpdate <= 1); /* no prefix (note: intentional overflow, defined as 2-complement) */14961497ZSTD_compressBlock_opt2(ms, seqStore, tmpRep, src, srcSize, ZSTD_noDict); /* generate stats into ms->opt*/14981499/* invalidate first scan from history, only keep entropy stats */1500ZSTD_resetSeqStore(seqStore);1501ms->window.base -= srcSize;1502ms->window.dictLimit += (U32)srcSize;1503ms->window.lowLimit = ms->window.dictLimit;1504ms->nextToUpdate = ms->window.dictLimit;15051506}15071508size_t ZSTD_compressBlock_btultra(1509ZSTD_MatchState_t* ms, SeqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],1510const void* src, size_t srcSize)1511{1512DEBUGLOG(5, "ZSTD_compressBlock_btultra (srcSize=%zu)", srcSize);1513return ZSTD_compressBlock_opt2(ms, seqStore, rep, src, srcSize, ZSTD_noDict);1514}15151516size_t ZSTD_compressBlock_btultra2(1517ZSTD_MatchState_t* ms, SeqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],1518const void* src, size_t srcSize)1519{1520U32 const curr = (U32)((const BYTE*)src - ms->window.base);1521DEBUGLOG(5, "ZSTD_compressBlock_btultra2 (srcSize=%zu)", srcSize);15221523/* 2-passes strategy:1524* this strategy makes a first pass over first block to collect statistics1525* in order to seed next round's statistics with it.1526* After 1st pass, function forgets history, and starts a new block.1527* Consequently, this can only work if no data has been previously loaded in tables,1528* aka, no dictionary, no prefix, no ldm preprocessing.1529* The compression ratio gain is generally small (~0.5% on first block),1530* the cost is 2x cpu time on first block. */1531assert(srcSize <= ZSTD_BLOCKSIZE_MAX);1532if ( (ms->opt.litLengthSum==0) /* first block */1533&& (seqStore->sequences == seqStore->sequencesStart) /* no ldm */1534&& (ms->window.dictLimit == ms->window.lowLimit) /* no dictionary */1535&& (curr == ms->window.dictLimit) /* start of frame, nothing already loaded nor skipped */1536&& (srcSize > ZSTD_PREDEF_THRESHOLD) /* input large enough to not employ default stats */1537) {1538ZSTD_initStats_ultra(ms, seqStore, rep, src, srcSize);1539}15401541return ZSTD_compressBlock_opt2(ms, seqStore, rep, src, srcSize, ZSTD_noDict);1542}1543#endif15441545#ifndef ZSTD_EXCLUDE_BTOPT_BLOCK_COMPRESSOR1546size_t ZSTD_compressBlock_btopt_dictMatchState(1547ZSTD_MatchState_t* ms, SeqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],1548const void* src, size_t srcSize)1549{1550return ZSTD_compressBlock_opt0(ms, seqStore, rep, src, srcSize, ZSTD_dictMatchState);1551}15521553size_t ZSTD_compressBlock_btopt_extDict(1554ZSTD_MatchState_t* ms, SeqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],1555const void* src, size_t srcSize)1556{1557return ZSTD_compressBlock_opt0(ms, seqStore, rep, src, srcSize, ZSTD_extDict);1558}1559#endif15601561#ifndef ZSTD_EXCLUDE_BTULTRA_BLOCK_COMPRESSOR1562size_t ZSTD_compressBlock_btultra_dictMatchState(1563ZSTD_MatchState_t* ms, SeqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],1564const void* src, size_t srcSize)1565{1566return ZSTD_compressBlock_opt2(ms, seqStore, rep, src, srcSize, ZSTD_dictMatchState);1567}15681569size_t ZSTD_compressBlock_btultra_extDict(1570ZSTD_MatchState_t* ms, SeqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],1571const void* src, size_t srcSize)1572{1573return ZSTD_compressBlock_opt2(ms, seqStore, rep, src, srcSize, ZSTD_extDict);1574}1575#endif15761577/* note : no btultra2 variant for extDict nor dictMatchState,1578* because btultra2 is not meant to work with dictionaries1579* and is only specific for the first block (no prefix) */158015811582