feat(compress): move sequence-store policy into Rust

Move sequence-store construction, small-block handling, repcode setup,
last-literal storage, block-body no-compress transitions, target/split
post-build policy, and public sequence conversion into Rust projections.
Keep matchfinders, LDM, external sequence producers, workspaces, and private
compression context state behind narrow C callbacks and preserve the existing
C-facing sequence API.

Test Plan:
- ulimit -v 41943040; make -B -C lib -j1 lib (passed)
- ulimit -v 41943040; make -B -C tests -j1 test-zstd (passed)
- ulimit -v 41943040; FUZZERTEST=-T5s make -B -C tests -j1 test-fuzzer (284 passed)
- ulimit -v 41943040; ZSTREAM_TESTTIME=-T2s make -B -C tests -j1 test-zstream (210 passed)
- ulimit -v 41943040; DECODECORPUS_TESTTIME=-T10 make -B -C tests -j1 test-decodecorpus (1608 passed)
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo test --manifest-path rust/Cargo.toml --no-default-features --features compression,decompression,dict-builder,legacy-v01,legacy-v02,legacy-v03,legacy-v04,legacy-v05,legacy-v06,legacy-v07 --all-targets -- --test-threads=1 (562 passed)
This commit is contained in:
2026-07-19 08:21:07 +02:00
parent 7282ec4046
commit 62389c002f
2 changed files with 794 additions and 287 deletions
+240 -283
View File
@@ -293,6 +293,11 @@ size_t ZSTD_rust_compressBlockTargetCBlockSize(
void* dst, size_t dstCapacity,
const void* src, size_t srcSize,
int bss, U32 lastBlock);
size_t ZSTD_rust_compressBlockTargetCBlockSizeAfterBuild(
const ZSTD_rust_targetCBlockSizeState* state,
void* dst, size_t dstCapacity,
const void* src, size_t srcSize,
int bss, U32 lastBlock);
typedef char ZSTD_rust_target_cblock_state_layout[
(offsetof(ZSTD_rust_targetCBlockSizeState, seqStore) == 0
&& offsetof(ZSTD_rust_targetCBlockSizeState, prevCBlock) == sizeof(void*)
@@ -337,6 +342,11 @@ size_t ZSTD_rust_compressBlockSplit(
void* dst, size_t dstCapacity,
const void* src, size_t blockSize,
U32 lastBlock, size_t numSplits);
size_t ZSTD_rust_compressBlockSplitAfterBuild(
const ZSTD_rust_splitBlockState* state,
void* dst, size_t dstCapacity,
const void* src, size_t blockSize,
U32 lastBlock, size_t numSplits, int bss);
typedef char ZSTD_rust_split_block_state_layout[
(offsetof(ZSTD_rust_splitBlockState, seqStore) == 0
&& offsetof(ZSTD_rust_splitBlockState, partitions) == sizeof(void*)
@@ -377,6 +387,11 @@ size_t ZSTD_rust_compressBlockInternal(
void* dst, size_t dstCapacity,
const void* src, size_t srcSize,
U32 frame);
size_t ZSTD_rust_compressBlockInternalAfterBuild(
const ZSTD_rust_blockInternalState* state,
void* dst, size_t dstCapacity,
const void* src, size_t srcSize,
U32 frame, int bss);
typedef char ZSTD_rust_block_internal_state_layout[
(offsetof(ZSTD_rust_blockInternalState, seqStore) == 0
&& offsetof(ZSTD_rust_blockInternalState, prevCBlock) == sizeof(void*)
@@ -653,8 +668,6 @@ void ZSTD_rust_resetSeqStore(SeqStore_t* ssPtr);
size_t ZSTD_rust_fastSequenceLengthSum(const ZSTD_Sequence* seqBuf,
size_t seqBufSize);
void ZSTD_rust_validateSeqStore(const SeqStore_t* seqStore, U32 minMatch);
size_t ZSTD_rust_convertSequencesNoRepcodes(
SeqDef* dstSeqs, const ZSTD_Sequence* inSeqs, size_t nbSequences);
BlockSummary ZSTD_rust_get1BlockSummary(const ZSTD_Sequence* seqs,
size_t nbSeqs);
size_t ZSTD_rust_postProcessSequenceProducerResult(
@@ -673,7 +686,6 @@ size_t ZSTD_rust_deriveBlockSplits(
void* workspace, size_t workspaceSize);
U32 ZSTD_rust_resolveRepcodeToRawOffset(const U32 rep[ZSTD_REP_NUM],
U32 offBase, U32 ll0);
U32 ZSTD_rust_finalizeOffBase(U32 rawOffset, const U32 rep[ZSTD_REP_NUM], U32 ll0);
size_t ZSTD_rust_loadCEntropy(ZSTD_compressedBlockState_t* bs, void* workspace,
const void* dict, size_t dictSize);
size_t ZSTD_rust_transferSequencesWBlockDelim(
@@ -689,6 +701,47 @@ size_t ZSTD_rust_optimalBlockSize(const void* src, size_t srcSize,
int strategy, S64 savings,
void* workspace, size_t workspaceSize);
/* Sequence-store construction is Rust-owned at the orchestration boundary.
* Matchfinder, LDM, and external sequence-producer operations retain access
* to the private CCtx through these narrow C callbacks. */
typedef void (*ZSTD_rust_buildSeqStoreSkip_f)(void* context, size_t srcSize);
typedef void (*ZSTD_rust_buildSeqStorePrepare_f)(void* context,
const void* src,
size_t srcSize);
typedef size_t (*ZSTD_rust_buildSeqStoreSelect_f)(
void* context, SeqStore_t* seqStore, U32 nextRep[ZSTD_REP_NUM],
const void* src, size_t srcSize, int* seqStoreComplete);
typedef struct {
SeqStore_t* seqStore;
ZSTD_compressedBlockState_t** prevCBlock;
ZSTD_compressedBlockState_t** nextCBlock;
void* callbackContext;
U32 minMatch;
int validateSeqStore;
ZSTD_rust_buildSeqStoreSkip_f skipSmallBlock;
ZSTD_rust_buildSeqStorePrepare_f prepareMatchState;
ZSTD_rust_buildSeqStoreSelect_f selectSequences;
} ZSTD_rust_buildSeqStoreState;
size_t ZSTD_rust_buildSeqStore(const ZSTD_rust_buildSeqStoreState* state,
const void* src, size_t srcSize);
typedef char ZSTD_rust_build_seq_store_state_layout[
(offsetof(ZSTD_rust_buildSeqStoreState, seqStore) == 0
&& offsetof(ZSTD_rust_buildSeqStoreState, prevCBlock) == sizeof(void*)
&& offsetof(ZSTD_rust_buildSeqStoreState, nextCBlock) == 2 * sizeof(void*)
&& offsetof(ZSTD_rust_buildSeqStoreState, callbackContext) == 3 * sizeof(void*)
&& offsetof(ZSTD_rust_buildSeqStoreState, minMatch) == 4 * sizeof(void*)
&& offsetof(ZSTD_rust_buildSeqStoreState, validateSeqStore)
== 4 * sizeof(void*) + sizeof(U32)
&& offsetof(ZSTD_rust_buildSeqStoreState, skipSmallBlock)
== 4 * sizeof(void*) + sizeof(U32) + sizeof(int)
&& offsetof(ZSTD_rust_buildSeqStoreState, prepareMatchState)
== 5 * sizeof(void*) + sizeof(U32) + sizeof(int)
&& offsetof(ZSTD_rust_buildSeqStoreState, selectSequences)
== 6 * sizeof(void*) + sizeof(U32) + sizeof(int)
&& sizeof(ZSTD_rust_buildSeqStoreState)
== 7 * sizeof(void*) + sizeof(U32) + sizeof(int))
? 1 : -1];
/* The sequence-compression loop receives only the state it actually reads or
* updates. In particular, neither ZSTD_CCtx nor a C function pointer crosses
* the Rust ABI. */
@@ -763,6 +816,25 @@ size_t ZSTD_rust_compressSequencesAndLiteralsInternal(
void* dst, size_t dstCapacity,
const ZSTD_Sequence* inSeqs, size_t nbSequences,
const void* literals, size_t litSize, size_t srcSize);
/* Public sequence conversion uses only the sequence store and the two
* compressed-block state slots. Keep the C entry point for existing callers,
* but move its conversion logic behind this explicit Rust projection. */
typedef struct {
SeqStore_t* seqStore;
ZSTD_compressedBlockState_t** prevCBlock;
ZSTD_compressedBlockState_t** nextCBlock;
} ZSTD_rust_convertBlockSequencesState;
size_t ZSTD_rust_convertBlockSequences(
const ZSTD_rust_convertBlockSequencesState* state,
const ZSTD_Sequence* inSeqs, size_t nbSequences,
int repcodeResolution);
typedef char ZSTD_rust_convert_block_sequences_state_layout[
(offsetof(ZSTD_rust_convertBlockSequencesState, seqStore) == 0
&& offsetof(ZSTD_rust_convertBlockSequencesState, prevCBlock) == sizeof(void*)
&& offsetof(ZSTD_rust_convertBlockSequencesState, nextCBlock) == 2 * sizeof(void*)
&& sizeof(ZSTD_rust_convertBlockSequencesState) == 3 * sizeof(void*))
? 1 : -1];
typedef char ZSTD_rust_sequence_literals_state_layout[
(offsetof(ZSTD_rust_sequenceLiteralsState, seqStore) == 0
&& offsetof(ZSTD_rust_sequenceLiteralsState, prevCBlock) == sizeof(void*)
@@ -2664,12 +2736,6 @@ ZSTD_BlockCompressor_f ZSTD_selectBlockCompressor(ZSTD_strategy strat, ZSTD_Para
return selectedCompressor;
}
static void ZSTD_storeLastLiterals(SeqStore_t* seqStorePtr,
const BYTE* anchor, size_t lastLLSize)
{
ZSTD_rust_storeLastLiterals(seqStorePtr, anchor, lastLLSize);
}
void ZSTD_resetSeqStore(SeqStore_t* ssPtr)
{
ZSTD_rust_resetSeqStore(ssPtr);
@@ -2698,19 +2764,6 @@ static size_t ZSTD_fastSequenceLengthSum(ZSTD_Sequence const* seqBuf, size_t seq
return ZSTD_rust_fastSequenceLengthSum(seqBuf, seqBufSize);
}
/**
* Function to validate sequences produced by a block compressor.
*/
static void ZSTD_validateSeqStore(const SeqStore_t* seqStore, const ZSTD_compressionParameters* cParams)
{
#if DEBUGLEVEL >= 1
ZSTD_rust_validateSeqStore(seqStore, (U32)cParams->minMatch);
#else
(void)seqStore;
(void)cParams;
#endif
}
static size_t
ZSTD_transferSequences_wBlockDelim(ZSTD_CCtx* cctx,
ZSTD_SequencePosition* seqPos,
@@ -2720,169 +2773,172 @@ ZSTD_transferSequences_wBlockDelim(ZSTD_CCtx* cctx,
typedef enum { ZSTDbss_compress, ZSTDbss_noCompress } ZSTD_BuildSeqStore_e;
static size_t ZSTD_buildSeqStore(ZSTD_CCtx* zc, const void* src, size_t srcSize)
static void ZSTD_rust_buildSeqStore_skipSmallBlock(void* context, size_t srcSize)
{
ZSTD_CCtx* const zc = (ZSTD_CCtx*)context;
if (zc->appliedParams.cParams.strategy >= ZSTD_btopt) {
ZSTD_ldm_skipRawSeqStoreBytes(&zc->externSeqStore, srcSize);
} else {
ZSTD_ldm_skipSequences(&zc->externSeqStore, srcSize,
zc->appliedParams.cParams.minMatch);
}
}
static void ZSTD_rust_buildSeqStore_prepareMatchState(void* context,
const void* src,
size_t srcSize)
{
ZSTD_CCtx* const zc = (ZSTD_CCtx*)context;
ZSTD_MatchState_t* const ms = &zc->blockState.matchState;
DEBUGLOG(5, "ZSTD_buildSeqStore (srcSize=%zu)", srcSize);
assert(srcSize <= ZSTD_BLOCKSIZE_MAX);
/* Assert that we have correctly flushed the ctx params into the ms's copy */
(void)srcSize;
/* Assert that we have correctly flushed the ctx params into the ms's copy. */
ZSTD_assertEqualCParams(zc->appliedParams.cParams, ms->cParams);
/* TODO: See 3090. We reduced MIN_CBLOCK_SIZE from 3 to 2 so to compensate we are adding
* additional 1. We need to revisit and change this logic to be more consistent */
if (srcSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1+1) {
if (zc->appliedParams.cParams.strategy >= ZSTD_btopt) {
ZSTD_ldm_skipRawSeqStoreBytes(&zc->externSeqStore, srcSize);
} else {
ZSTD_ldm_skipSequences(&zc->externSeqStore, srcSize, zc->appliedParams.cParams.minMatch);
}
return ZSTDbss_noCompress; /* don't even attempt compression below a certain srcSize */
}
ZSTD_resetSeqStore(&(zc->seqStore));
/* required for optimal parser to read stats from dictionary */
ms->opt.symbolCosts = &zc->blockState.prevCBlock->entropy;
/* tell the optimal parser how we expect to compress literals */
ms->opt.literalCompressionMode = zc->appliedParams.literalCompressionMode;
/* a gap between an attached dict and the current window is not safe,
* they must remain adjacent,
* and when that stops being the case, the dict must be unset */
* they must remain adjacent, and when that stops being the case, the
* dict must be unset */
assert(ms->dictMatchState == NULL || ms->loadedDictEnd == ms->window.dictLimit);
/* limited update after a very long match */
{ const BYTE* const base = ms->window.base;
const BYTE* const istart = (const BYTE*)src;
const U32 curr = (U32)(istart-base);
if (sizeof(ptrdiff_t)==8) assert(istart - base < (ptrdiff_t)(U32)(-1)); /* ensure no overflow */
if (sizeof(ptrdiff_t)==8) assert(istart - base < (ptrdiff_t)(U32)(-1));
if (curr > ms->nextToUpdate + 384)
ms->nextToUpdate = curr - MIN(192, (U32)(curr - ms->nextToUpdate - 384));
}
}
/* select and store sequences */
{ ZSTD_dictMode_e const dictMode = ZSTD_matchState_dictMode(ms);
size_t lastLLSize;
{ int i;
for (i = 0; i < ZSTD_REP_NUM; ++i)
zc->blockState.nextCBlock->rep[i] = zc->blockState.prevCBlock->rep[i];
}
if (zc->externSeqStore.pos < zc->externSeqStore.size) {
assert(zc->appliedParams.ldmParams.enableLdm == ZSTD_ps_disable);
static size_t ZSTD_rust_buildSeqStore_selectSequences(
void* context, SeqStore_t* seqStore, U32 nextRep[ZSTD_REP_NUM],
const void* src, size_t srcSize, int* seqStoreComplete)
{
ZSTD_CCtx* const zc = (ZSTD_CCtx*)context;
ZSTD_MatchState_t* const ms = &zc->blockState.matchState;
ZSTD_dictMode_e const dictMode = ZSTD_matchState_dictMode(ms);
size_t lastLLSize;
*seqStoreComplete = 0;
/* External matchfinder + LDM is technically possible, just not implemented yet.
* We need to revisit soon and implement it. */
RETURN_ERROR_IF(
ZSTD_hasExtSeqProd(&zc->appliedParams),
parameter_combination_unsupported,
"Long-distance matching with external sequence producer enabled is not currently supported."
);
if (zc->externSeqStore.pos < zc->externSeqStore.size) {
assert(zc->appliedParams.ldmParams.enableLdm == ZSTD_ps_disable);
/* Updates ldmSeqStore.pos */
lastLLSize =
ZSTD_ldm_blockCompress(&zc->externSeqStore,
ms, &zc->seqStore,
zc->blockState.nextCBlock->rep,
zc->appliedParams.useRowMatchFinder,
src, srcSize);
assert(zc->externSeqStore.pos <= zc->externSeqStore.size);
} else if (zc->appliedParams.ldmParams.enableLdm == ZSTD_ps_enable) {
RawSeqStore_t ldmSeqStore = kNullRawSeqStore;
/* External matchfinder + LDM is technically possible, just not
* implemented yet. */
RETURN_ERROR_IF(
ZSTD_hasExtSeqProd(&zc->appliedParams),
parameter_combination_unsupported,
"Long-distance matching with external sequence producer enabled is not currently supported."
);
/* External matchfinder + LDM is technically possible, just not implemented yet.
* We need to revisit soon and implement it. */
RETURN_ERROR_IF(
ZSTD_hasExtSeqProd(&zc->appliedParams),
parameter_combination_unsupported,
"Long-distance matching with external sequence producer enabled is not currently supported."
);
lastLLSize = ZSTD_ldm_blockCompress(
&zc->externSeqStore, ms, seqStore, nextRep,
zc->appliedParams.useRowMatchFinder, src, srcSize);
assert(zc->externSeqStore.pos <= zc->externSeqStore.size);
} else if (zc->appliedParams.ldmParams.enableLdm == ZSTD_ps_enable) {
RawSeqStore_t ldmSeqStore = kNullRawSeqStore;
ldmSeqStore.seq = zc->ldmSequences;
ldmSeqStore.capacity = zc->maxNbLdmSequences;
/* Updates ldmSeqStore.size */
FORWARD_IF_ERROR(ZSTD_ldm_generateSequences(&zc->ldmState, &ldmSeqStore,
&zc->appliedParams.ldmParams,
src, srcSize), "");
/* Updates ldmSeqStore.pos */
lastLLSize =
ZSTD_ldm_blockCompress(&ldmSeqStore,
ms, &zc->seqStore,
zc->blockState.nextCBlock->rep,
zc->appliedParams.useRowMatchFinder,
src, srcSize);
assert(ldmSeqStore.pos == ldmSeqStore.size);
} else if (ZSTD_hasExtSeqProd(&zc->appliedParams)) {
assert(
zc->extSeqBufCapacity >= ZSTD_sequenceBound(srcSize)
);
assert(zc->appliedParams.extSeqProdFunc != NULL);
/* External matchfinder + LDM is technically possible, just not
* implemented yet. */
RETURN_ERROR_IF(
ZSTD_hasExtSeqProd(&zc->appliedParams),
parameter_combination_unsupported,
"Long-distance matching with external sequence producer enabled is not currently supported."
);
{ U32 const windowSize = (U32)1 << zc->appliedParams.cParams.windowLog;
ldmSeqStore.seq = zc->ldmSequences;
ldmSeqStore.capacity = zc->maxNbLdmSequences;
FORWARD_IF_ERROR(ZSTD_ldm_generateSequences(
&zc->ldmState, &ldmSeqStore, &zc->appliedParams.ldmParams,
src, srcSize), "");
lastLLSize = ZSTD_ldm_blockCompress(
&ldmSeqStore, ms, seqStore, nextRep,
zc->appliedParams.useRowMatchFinder, src, srcSize);
assert(ldmSeqStore.pos == ldmSeqStore.size);
} else if (ZSTD_hasExtSeqProd(&zc->appliedParams)) {
assert(zc->extSeqBufCapacity >= ZSTD_sequenceBound(srcSize));
assert(zc->appliedParams.extSeqProdFunc != NULL);
size_t const nbExternalSeqs = (zc->appliedParams.extSeqProdFunc)(
{ U32 const windowSize = (U32)1 << zc->appliedParams.cParams.windowLog;
size_t const nbExternalSeqs = (zc->appliedParams.extSeqProdFunc)(
zc->appliedParams.extSeqProdState,
zc->extSeqBuf,
zc->extSeqBufCapacity,
src, srcSize,
NULL, 0, /* dict and dictSize, currently not supported */
zc->appliedParams.compressionLevel,
windowSize
);
zc->extSeqBuf, zc->extSeqBufCapacity,
src, srcSize, NULL, 0,
zc->appliedParams.compressionLevel, windowSize);
size_t const nbPostProcessedSeqs = ZSTD_postProcessSequenceProducerResult(
zc->extSeqBuf, nbExternalSeqs,
zc->extSeqBufCapacity, srcSize);
size_t const nbPostProcessedSeqs = ZSTD_postProcessSequenceProducerResult(
zc->extSeqBuf,
nbExternalSeqs,
zc->extSeqBufCapacity,
srcSize
);
/* Return early if there is no error, since the delimiter already
* carries the block's final literals. */
if (!ZSTD_isError(nbPostProcessedSeqs)) {
ZSTD_SequencePosition seqPos = {0,0,0};
size_t const seqLenSum = ZSTD_fastSequenceLengthSum(
zc->extSeqBuf, nbPostProcessedSeqs);
RETURN_ERROR_IF(seqLenSum > srcSize, externalSequences_invalid,
"External sequences imply too large a block!");
FORWARD_IF_ERROR(ZSTD_transferSequences_wBlockDelim(
zc, &seqPos, zc->extSeqBuf, nbPostProcessedSeqs,
src, srcSize,
zc->appliedParams.searchForExternalRepcodes),
"Failed to copy external sequences to seqStore!");
ms->ldmSeqStore = NULL;
*seqStoreComplete = 1;
DEBUGLOG(5, "Copied %lu sequences from external sequence producer to internal seqStore.",
(unsigned long)nbExternalSeqs);
return 0;
}
/* Return early if there is no error, since we don't need to worry about last literals */
if (!ZSTD_isError(nbPostProcessedSeqs)) {
ZSTD_SequencePosition seqPos = {0,0,0};
size_t const seqLenSum = ZSTD_fastSequenceLengthSum(zc->extSeqBuf, nbPostProcessedSeqs);
RETURN_ERROR_IF(seqLenSum > srcSize, externalSequences_invalid, "External sequences imply too large a block!");
FORWARD_IF_ERROR(
ZSTD_transferSequences_wBlockDelim(
zc, &seqPos,
zc->extSeqBuf, nbPostProcessedSeqs,
src, srcSize,
zc->appliedParams.searchForExternalRepcodes
),
"Failed to copy external sequences to seqStore!"
);
ms->ldmSeqStore = NULL;
DEBUGLOG(5, "Copied %lu sequences from external sequence producer to internal seqStore.", (unsigned long)nbExternalSeqs);
return ZSTDbss_compress;
}
if (!zc->appliedParams.enableMatchFinderFallback)
return nbPostProcessedSeqs;
/* Propagate the error if fallback is disabled */
if (!zc->appliedParams.enableMatchFinderFallback) {
return nbPostProcessedSeqs;
}
/* Fallback to software matchfinder */
{ ZSTD_BlockCompressor_f const blockCompressor =
{ ZSTD_BlockCompressor_f const blockCompressor =
ZSTD_selectBlockCompressor(
zc->appliedParams.cParams.strategy,
zc->appliedParams.useRowMatchFinder,
dictMode);
ms->ldmSeqStore = NULL;
DEBUGLOG(
5,
"External sequence producer returned error code %lu. Falling back to internal parser.",
(unsigned long)nbExternalSeqs
);
lastLLSize = blockCompressor(ms, &zc->seqStore, zc->blockState.nextCBlock->rep, src, srcSize);
} }
} else { /* not long range mode and no external matchfinder */
ZSTD_BlockCompressor_f const blockCompressor = ZSTD_selectBlockCompressor(
zc->appliedParams.cParams.strategy,
zc->appliedParams.useRowMatchFinder,
dictMode);
ms->ldmSeqStore = NULL;
lastLLSize = blockCompressor(ms, &zc->seqStore, zc->blockState.nextCBlock->rep, src, srcSize);
zc->appliedParams.cParams.strategy,
zc->appliedParams.useRowMatchFinder,
dictMode);
ms->ldmSeqStore = NULL;
DEBUGLOG(5, "External sequence producer returned error code %lu. Falling back to internal parser.",
(unsigned long)nbExternalSeqs);
lastLLSize = blockCompressor(ms, seqStore, nextRep, src, srcSize);
}
}
{ const BYTE* const lastLiterals = (const BYTE*)src + srcSize - lastLLSize;
ZSTD_storeLastLiterals(&zc->seqStore, lastLiterals, lastLLSize);
} }
ZSTD_validateSeqStore(&zc->seqStore, &zc->appliedParams.cParams);
return ZSTDbss_compress;
} else {
ZSTD_BlockCompressor_f const blockCompressor = ZSTD_selectBlockCompressor(
zc->appliedParams.cParams.strategy,
zc->appliedParams.useRowMatchFinder, dictMode);
ms->ldmSeqStore = NULL;
lastLLSize = blockCompressor(ms, seqStore, nextRep, src, srcSize);
}
return lastLLSize;
}
static void ZSTD_initBuildSeqStoreState(
ZSTD_CCtx* zc, ZSTD_rust_buildSeqStoreState* state)
{
state->seqStore = &zc->seqStore;
state->prevCBlock = &zc->blockState.prevCBlock;
state->nextCBlock = &zc->blockState.nextCBlock;
state->callbackContext = zc;
state->minMatch = zc->appliedParams.cParams.minMatch;
#if DEBUGLEVEL >= 1
state->validateSeqStore = 1;
#else
state->validateSeqStore = 0;
#endif
state->skipSmallBlock = ZSTD_rust_buildSeqStore_skipSmallBlock;
state->prepareMatchState = ZSTD_rust_buildSeqStore_prepareMatchState;
state->selectSequences = ZSTD_rust_buildSeqStore_selectSequences;
}
static size_t ZSTD_buildSeqStore(ZSTD_CCtx* zc, const void* src, size_t srcSize)
{
ZSTD_rust_buildSeqStoreState state;
ZSTD_initBuildSeqStoreState(zc, &state);
return ZSTD_rust_buildSeqStore(&state, src, srcSize);
}
/* ZSTD_sequenceBound() lives in rust/src/zstd_compress_api.rs. */
@@ -2980,11 +3036,16 @@ static size_t
ZSTD_compressBlock_splitBlock_internal(ZSTD_CCtx* zc,
void* dst, size_t dstCapacity,
const void* src, size_t blockSize,
U32 lastBlock, U32 nbSeq)
U32 lastBlock, U32 nbSeq, int bss)
{
ZSTD_rust_splitBlockState state;
U32* const partitions = zc->blockSplitCtx.partitions; /* splits plus the terminal boundary */
size_t const numSplits = ZSTD_deriveBlockSplits(zc, partitions, nbSeq);
size_t numSplits = 0;
if (bss == ZSTDbss_compress) {
numSplits = ZSTD_deriveBlockSplits(zc, partitions, nbSeq);
FORWARD_IF_ERROR(numSplits, "Deriving block splits failed!");
}
DEBUGLOG(5, "ZSTD_compressBlock_splitBlock_internal (dstCapacity=%u, dictLimit=%u, nextToUpdate=%u)",
(unsigned)dstCapacity, (unsigned)zc->blockState.matchState.window.dictLimit,
@@ -3003,8 +3064,9 @@ ZSTD_compressBlock_splitBlock_internal(ZSTD_CCtx* zc,
state.disableLiteralCompression = ZSTD_literalsCompressionIsDisabled(&zc->appliedParams);
state.bmi2 = zc->bmi2;
state.isFirstBlock = zc->isFirstBlock;
return ZSTD_rust_compressBlockSplit(
&state, dst, dstCapacity, src, blockSize, lastBlock, numSplits);
return ZSTD_rust_compressBlockSplitAfterBuild(
&state, dst, dstCapacity, src, blockSize, lastBlock,
numSplits, bss);
}
static size_t
@@ -3014,24 +3076,14 @@ ZSTD_compressBlock_splitBlock(ZSTD_CCtx* zc,
{
U32 nbSeq;
size_t cSize;
size_t const bss = ZSTD_buildSeqStore(zc, src, srcSize);
DEBUGLOG(5, "ZSTD_compressBlock_splitBlock");
assert(zc->appliedParams.postBlockSplitter == ZSTD_ps_enable);
FORWARD_IF_ERROR(bss, "ZSTD_buildSeqStore failed");
nbSeq = (U32)(zc->seqStore.sequences - zc->seqStore.sequencesStart);
{ const size_t bss = ZSTD_buildSeqStore(zc, src, srcSize);
FORWARD_IF_ERROR(bss, "ZSTD_buildSeqStore failed");
if (bss == ZSTDbss_noCompress) {
if (zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode == FSE_repeat_valid)
zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode = FSE_repeat_check;
RETURN_ERROR_IF(zc->seqCollector.collectSequences, sequenceProducer_failed, "Uncompressible block");
cSize = ZSTD_rust_noCompressBlock(dst, dstCapacity, src, srcSize, lastBlock);
FORWARD_IF_ERROR(cSize, "ZSTD_noCompressBlock failed");
DEBUGLOG(5, "ZSTD_compressBlock_splitBlock: Nocompress block");
return cSize;
}
nbSeq = (U32)(zc->seqStore.sequences - zc->seqStore.sequencesStart);
}
cSize = ZSTD_compressBlock_splitBlock_internal(zc, dst, dstCapacity, src, srcSize, lastBlock, nbSeq);
cSize = ZSTD_compressBlock_splitBlock_internal(
zc, dst, dstCapacity, src, srcSize, lastBlock, nbSeq, (int)bss);
FORWARD_IF_ERROR(cSize, "Splitting blocks failed!");
return cSize;
}
@@ -3042,19 +3094,11 @@ ZSTD_compressBlock_internal(ZSTD_CCtx* zc,
const void* src, size_t srcSize, U32 frame)
{
ZSTD_rust_blockInternalState state;
size_t const bss = ZSTD_buildSeqStore(zc, src, srcSize);
DEBUGLOG(5, "ZSTD_compressBlock_internal (dstCapacity=%u, dictLimit=%u, nextToUpdate=%u)",
(unsigned)dstCapacity, (unsigned)zc->blockState.matchState.window.dictLimit,
(unsigned)zc->blockState.matchState.nextToUpdate);
{ const size_t bss = ZSTD_buildSeqStore(zc, src, srcSize);
FORWARD_IF_ERROR(bss, "ZSTD_buildSeqStore failed");
if (bss == ZSTDbss_noCompress) {
RETURN_ERROR_IF(zc->seqCollector.collectSequences, sequenceProducer_failed, "Uncompressible block");
if (zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode == FSE_repeat_valid)
zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode = FSE_repeat_check;
return 0;
}
}
FORWARD_IF_ERROR(bss, "ZSTD_buildSeqStore failed");
state.seqStore = &zc->seqStore;
state.prevCBlock = &zc->blockState.prevCBlock;
@@ -3066,8 +3110,8 @@ ZSTD_compressBlock_internal(ZSTD_CCtx* zc,
state.disableLiteralCompression = ZSTD_literalsCompressionIsDisabled(&zc->appliedParams);
state.bmi2 = zc->bmi2;
state.isFirstBlock = zc->isFirstBlock;
return ZSTD_rust_compressBlockInternal(
&state, dst, dstCapacity, src, srcSize, frame);
return ZSTD_rust_compressBlockInternalAfterBuild(
&state, dst, dstCapacity, src, srcSize, frame, (int)bss);
}
static size_t ZSTD_compressBlock_targetCBlockSize(ZSTD_CCtx* zc,
@@ -3075,8 +3119,8 @@ static size_t ZSTD_compressBlock_targetCBlockSize(ZSTD_CCtx* zc,
const void* src, size_t srcSize,
U32 lastBlock)
{
size_t cSize = 0;
const size_t bss = ZSTD_buildSeqStore(zc, src, srcSize);
size_t cSize;
size_t const bss = ZSTD_buildSeqStore(zc, src, srcSize);
DEBUGLOG(5, "ZSTD_compressBlock_targetCBlockSize (dstCapacity=%u, dictLimit=%u, nextToUpdate=%u, srcSize=%zu)",
(unsigned)dstCapacity, (unsigned)zc->blockState.matchState.window.dictLimit, (unsigned)zc->blockState.matchState.nextToUpdate, srcSize);
FORWARD_IF_ERROR(bss, "ZSTD_buildSeqStore failed");
@@ -3093,14 +3137,10 @@ static size_t ZSTD_compressBlock_targetCBlockSize(ZSTD_CCtx* zc,
state.windowLog = zc->appliedParams.cParams.windowLog;
state.targetCBlockSize = zc->appliedParams.targetCBlockSize;
state.isFirstBlock = zc->isFirstBlock;
cSize = ZSTD_rust_compressBlockTargetCBlockSize(
cSize = ZSTD_rust_compressBlockTargetCBlockSizeAfterBuild(
&state, dst, dstCapacity, src, srcSize, (int)bss, lastBlock);
}
FORWARD_IF_ERROR(cSize, "ZSTD_compressBlock_targetCBlockSize_body failed");
if (zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode == FSE_repeat_valid)
zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode = FSE_repeat_check;
return cSize;
}
@@ -4952,12 +4992,6 @@ size_t ZSTD_compress2_c(ZSTD_CCtx* cctx,
}
}
/* Returns an offset code, given a sequence's raw offset, the ongoing repcode array, and whether litLength == 0 */
static U32 ZSTD_finalizeOffBase(U32 rawOffset, const U32 rep[ZSTD_REP_NUM], U32 ll0)
{
return ZSTD_rust_finalizeOffBase(rawOffset, rep, ll0);
}
/* The explicit-delimiter adapter is also used by the external sequence
* producer path. Keep that C-context-facing call site separate from the
* Rust-owned compressSequences block loop. */
@@ -5082,97 +5116,20 @@ size_t ZSTD_compressSequences(ZSTD_CCtx* cctx,
return cSize;
}
/* Sequence conversion is implemented in Rust; this wrapper keeps the
* existing C-side block orchestration and long-length bookkeeping intact. */
static size_t convertSequences_noRepcodes(
SeqDef* dstSeqs,
const ZSTD_Sequence* inSeqs,
size_t nbSequences)
{
return ZSTD_rust_convertSequencesNoRepcodes(dstSeqs, inSeqs, nbSequences);
}
/*
* Precondition: Sequences must end on an explicit Block Delimiter
* @return: 0 on success, or an error code.
* Note: Sequence validation functionality has been disabled (removed).
* This is helpful to generate a lean main pipeline, improving performance.
* It may be re-inserted later.
*/
/* The public symbol remains C-facing for fullbench and the sequence API, but
* the sequence-store conversion itself is Rust-owned. */
size_t ZSTD_convertBlockSequences(ZSTD_CCtx* cctx,
const ZSTD_Sequence* const inSeqs, size_t nbSequences,
int repcodeResolution)
{
Repcodes_t updatedRepcodes;
size_t seqNb = 0;
ZSTD_rust_convertBlockSequencesState state;
DEBUGLOG(5, "ZSTD_convertBlockSequences (nbSequences = %zu)", nbSequences);
RETURN_ERROR_IF(nbSequences >= cctx->seqStore.maxNbSeq, externalSequences_invalid,
"Not enough memory allocated. Try adjusting ZSTD_c_minMatch.");
ZSTD_memcpy(updatedRepcodes.rep, cctx->blockState.prevCBlock->rep, sizeof(Repcodes_t));
/* check end condition */
assert(nbSequences >= 1);
assert(inSeqs[nbSequences-1].matchLength == 0);
assert(inSeqs[nbSequences-1].offset == 0);
/* Convert Sequences from public format to internal format */
if (!repcodeResolution) {
size_t const longl = convertSequences_noRepcodes(cctx->seqStore.sequencesStart, inSeqs, nbSequences-1);
cctx->seqStore.sequences = cctx->seqStore.sequencesStart + nbSequences-1;
if (longl) {
DEBUGLOG(5, "long length");
assert(cctx->seqStore.longLengthType == ZSTD_llt_none);
if (longl <= nbSequences-1) {
DEBUGLOG(5, "long match length detected at pos %zu", longl-1);
cctx->seqStore.longLengthType = ZSTD_llt_matchLength;
cctx->seqStore.longLengthPos = (U32)(longl-1);
} else {
DEBUGLOG(5, "long literals length detected at pos %zu", longl-nbSequences);
assert(longl <= 2* (nbSequences-1));
cctx->seqStore.longLengthType = ZSTD_llt_literalLength;
cctx->seqStore.longLengthPos = (U32)(longl-(nbSequences-1)-1);
}
}
} else {
for (seqNb = 0; seqNb < nbSequences - 1 ; seqNb++) {
U32 const litLength = inSeqs[seqNb].litLength;
U32 const matchLength = inSeqs[seqNb].matchLength;
U32 const ll0 = (litLength == 0);
U32 const offBase = ZSTD_finalizeOffBase(inSeqs[seqNb].offset, updatedRepcodes.rep, ll0);
DEBUGLOG(6, "Storing sequence: (of: %u, ml: %u, ll: %u)", offBase, matchLength, litLength);
ZSTD_storeSeqOnly(&cctx->seqStore, litLength, offBase, matchLength);
ZSTD_updateRep(updatedRepcodes.rep, offBase, ll0);
}
}
/* If we skipped repcode search while parsing, we need to update repcodes now */
if (!repcodeResolution && nbSequences > 1) {
U32* const rep = updatedRepcodes.rep;
if (nbSequences >= 4) {
U32 lastSeqIdx = (U32)nbSequences - 2; /* index of last full sequence */
rep[2] = inSeqs[lastSeqIdx - 2].offset;
rep[1] = inSeqs[lastSeqIdx - 1].offset;
rep[0] = inSeqs[lastSeqIdx].offset;
} else if (nbSequences == 3) {
rep[2] = rep[0];
rep[1] = inSeqs[0].offset;
rep[0] = inSeqs[1].offset;
} else {
assert(nbSequences == 2);
rep[2] = rep[1];
rep[1] = rep[0];
rep[0] = inSeqs[0].offset;
}
}
ZSTD_memcpy(cctx->blockState.nextCBlock->rep, updatedRepcodes.rep, sizeof(Repcodes_t));
return 0;
state.seqStore = &cctx->seqStore;
state.prevCBlock = &cctx->blockState.prevCBlock;
state.nextCBlock = &cctx->blockState.nextCBlock;
return ZSTD_rust_convertBlockSequences(
&state, inSeqs, nbSequences, repcodeResolution);
}
static size_t ZSTD_convertBlockSequencesForRust(