feat(compress): move external producer success path into Rust

Move block-level external sequence producer invocation, result
post-processing, source-length validation, and transfer ordering into a
Rust-owned ABI leaf. Retain the private CCtx transfer callback and C-side
fallback/block-compressor selection, preserving producer-error fallback
eligibility and direct invalid-sequence/transfer failures.

Test Plan:
- cargo test --manifest-path rust/Cargo.toml --lib
- cargo clippy --manifest-path rust/Cargo.toml --all-targets -- -D warnings
- make -B -C programs -j1 zstd
- make -C tests -j1 test-zstream ZSTREAM_TESTTIME=-T1s
- focused external_sequence_producer unit tests
This commit is contained in:
2026-07-19 14:41:09 +02:00
parent 724c7c60fa
commit 9a68253710
2 changed files with 545 additions and 58 deletions
+85 -54
View File
@@ -1163,14 +1163,9 @@ void ZSTD_rust_confirmRepcodesAndEntropyTables(
void ZSTD_rust_storeLastLiterals(SeqStore_t* seqStorePtr,
const BYTE* anchor, size_t lastLLSize);
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);
BlockSummary ZSTD_rust_get1BlockSummary(const ZSTD_Sequence* seqs,
size_t nbSeqs);
size_t ZSTD_rust_postProcessSequenceProducerResult(
ZSTD_Sequence* outSeqs, size_t nbExternalSeqs,
size_t outSeqsCapacity, size_t srcSize);
size_t ZSTD_rust_deriveBlockSplits(
U32* partitions, U32 nbSeq,
const SeqStore_t* originalSeqStore,
@@ -1344,6 +1339,57 @@ typedef char ZSTD_rust_build_seq_store_state_layout[
== 7 * sizeof(void*) + sizeof(U32) + sizeof(int))
? 1 : -1];
typedef size_t (*ZSTD_rust_externalSequenceTransfer_f)(
void* context, ZSTD_SequencePosition* seqPos,
const ZSTD_Sequence* inSeqs, size_t inSeqsSize,
const void* src, size_t blockSize);
typedef struct {
void* callbackContext;
void* producerState;
ZSTD_sequenceProducer_F producer;
ZSTD_Sequence* extSeqBuf;
const size_t* extSeqBufCapacity;
const void* src;
const size_t* srcSize;
const int* compressionLevel;
const size_t* windowSize;
ZSTD_rust_externalSequenceTransfer_f transfer;
size_t* externalSeqCount;
int* seqStoreComplete;
int* allowFallback;
} ZSTD_rust_externalSequenceProducerState;
size_t ZSTD_rust_tryExternalSequenceProducer(
const ZSTD_rust_externalSequenceProducerState* state);
typedef char ZSTD_rust_external_sequence_producer_state_layout[
(offsetof(ZSTD_rust_externalSequenceProducerState, callbackContext) == 0
&& offsetof(ZSTD_rust_externalSequenceProducerState, producerState)
== sizeof(void*)
&& offsetof(ZSTD_rust_externalSequenceProducerState, producer)
== 2 * sizeof(void*)
&& offsetof(ZSTD_rust_externalSequenceProducerState, extSeqBuf)
== 3 * sizeof(void*)
&& offsetof(ZSTD_rust_externalSequenceProducerState, extSeqBufCapacity)
== 4 * sizeof(void*)
&& offsetof(ZSTD_rust_externalSequenceProducerState, src)
== 5 * sizeof(void*)
&& offsetof(ZSTD_rust_externalSequenceProducerState, srcSize)
== 6 * sizeof(void*)
&& offsetof(ZSTD_rust_externalSequenceProducerState, compressionLevel)
== 7 * sizeof(void*)
&& offsetof(ZSTD_rust_externalSequenceProducerState, windowSize)
== 8 * sizeof(void*)
&& offsetof(ZSTD_rust_externalSequenceProducerState, transfer)
== 9 * sizeof(void*)
&& offsetof(ZSTD_rust_externalSequenceProducerState, externalSeqCount)
== 10 * sizeof(void*)
&& offsetof(ZSTD_rust_externalSequenceProducerState, seqStoreComplete)
== 11 * sizeof(void*)
&& offsetof(ZSTD_rust_externalSequenceProducerState, allowFallback)
== 12 * sizeof(void*)
&& sizeof(ZSTD_rust_externalSequenceProducerState)
== 13 * sizeof(void*))
? 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. */
@@ -3419,29 +3465,6 @@ void ZSTD_resetSeqStore(SeqStore_t* ssPtr)
ZSTD_rust_resetSeqStore(ssPtr);
}
/* ZSTD_postProcessSequenceProducerResult() :
* Validates and post-processes sequences obtained through the external matchfinder API:
* - Checks whether nbExternalSeqs represents an error condition.
* - Appends a block delimiter to outSeqs if one is not already present.
* See zstd.h for context regarding block delimiters.
* Returns the number of sequences after post-processing, or an error code. */
static size_t ZSTD_postProcessSequenceProducerResult(
ZSTD_Sequence* outSeqs, size_t nbExternalSeqs, size_t outSeqsCapacity, size_t srcSize
) {
return ZSTD_rust_postProcessSequenceProducerResult(
outSeqs, nbExternalSeqs, outSeqsCapacity, srcSize);
}
/* ZSTD_fastSequenceLengthSum() :
* Returns sum(litLen) + sum(matchLen) + lastLits for *seqBuf*.
* Similar to another function in zstd_compress.c (determine_blockSize),
* except it doesn't check for a block delimiter to end summation.
* Removing the early exit allows the compiler to auto-vectorize (https://godbolt.org/z/cY1cajz9P).
* This function can be deleted and replaced by determine_blockSize after we resolve issue #3456. */
static size_t ZSTD_fastSequenceLengthSum(ZSTD_Sequence const* seqBuf, size_t seqBufSize) {
return ZSTD_rust_fastSequenceLengthSum(seqBuf, seqBufSize);
}
static size_t
ZSTD_transferSequences_wBlockDelim(ZSTD_CCtx* cctx,
ZSTD_SequencePosition* seqPos,
@@ -3449,6 +3472,17 @@ ZSTD_transferSequences_wBlockDelim(ZSTD_CCtx* cctx,
const void* src, size_t blockSize,
ZSTD_ParamSwitch_e externalRepSearch);
static size_t ZSTD_rust_externalSequenceProducer_transfer(
void* context, ZSTD_SequencePosition* seqPos,
const ZSTD_Sequence* inSeqs, size_t inSeqsSize,
const void* src, size_t blockSize)
{
ZSTD_CCtx* const zc = (ZSTD_CCtx*)context;
return ZSTD_transferSequences_wBlockDelim(
zc, seqPos, inSeqs, inSeqsSize, src, blockSize,
zc->appliedParams.searchForExternalRepcodes);
}
typedef enum { ZSTDbss_compress, ZSTDbss_noCompress } ZSTD_BuildSeqStore_e;
static void ZSTD_rust_buildSeqStore_skipSmallBlock(void* context, size_t srcSize)
@@ -3537,41 +3571,38 @@ static size_t ZSTD_rust_buildSeqStore_selectSequences(
zc->appliedParams.useRowMatchFinder, src, srcSize);
assert(ldmSeqStore.pos == ldmSeqStore.size);
} else if (ZSTD_hasExtSeqProd(&zc->appliedParams)) {
size_t nbExternalSeqs = 0;
int allowFallback = 0;
ZSTD_rust_externalSequenceProducerState state;
size_t const windowSize = (size_t)1 << zc->appliedParams.cParams.windowLog;
assert(zc->extSeqBufCapacity >= ZSTD_sequenceBound(srcSize));
assert(zc->appliedParams.extSeqProdFunc != NULL);
{ 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,
zc->appliedParams.compressionLevel, windowSize);
size_t const nbPostProcessedSeqs = ZSTD_postProcessSequenceProducerResult(
zc->extSeqBuf, nbExternalSeqs,
zc->extSeqBufCapacity, srcSize);
state.callbackContext = zc;
state.producerState = zc->appliedParams.extSeqProdState;
state.producer = zc->appliedParams.extSeqProdFunc;
state.extSeqBuf = zc->extSeqBuf;
state.extSeqBufCapacity = &zc->extSeqBufCapacity;
state.src = src;
state.srcSize = &srcSize;
state.compressionLevel = &zc->appliedParams.compressionLevel;
state.windowSize = &windowSize;
state.transfer = ZSTD_rust_externalSequenceProducer_transfer;
state.externalSeqCount = &nbExternalSeqs;
state.seqStoreComplete = seqStoreComplete;
state.allowFallback = &allowFallback;
/* 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!");
{ size_t const producerResult =
ZSTD_rust_tryExternalSequenceProducer(&state);
if (*seqStoreComplete) {
ms->ldmSeqStore = NULL;
*seqStoreComplete = 1;
DEBUGLOG(5, "Copied %lu sequences from external sequence producer to internal seqStore.",
(unsigned long)nbExternalSeqs);
return 0;
return producerResult;
}
if (!zc->appliedParams.enableMatchFinderFallback)
return nbPostProcessedSeqs;
if (!allowFallback || !zc->appliedParams.enableMatchFinderFallback)
return producerResult;
{ ZSTD_BlockCompressor_f const blockCompressor =
ZSTD_selectBlockCompressor(