feat(compress): move ordinary block emission into Rust

Move the post-sequence-store body of ZSTD_compressBlock_internal behind a
small C/Rust state projection. C continues to build the sequence store and
handle the no-compress and sequence-producer error paths, while Rust now owns
sequence collection, entropy emission, the legacy non-first-frame RLE gate,
compressed-block confirmation, and offcode repeat cleanup. Remove the C
wrappers that became dead after those leaves moved behind the Rust body.

The first integration run exposed that leaving the old C finalization label in
place confirmed compressed block state twice, undoing Rust's pointer swap and
breaking a later sparse-file checksum. The C wrapper now returns directly for
the Rust-owned path and retains only its C-owned no-compress cleanup.

Test Plan:
- cargo test --manifest-path rust/Cargo.toml --lib -- --test-threads=1
- cargo check --manifest-path rust/Cargo.toml --lib
- cargo clippy --manifest-path rust/Cargo.toml --lib -- -D warnings
- cargo clippy --manifest-path rust/Cargo.toml -- -D warnings
- cargo +nightly fmt --manifest-path rust/Cargo.toml -- --check
- make -B -C lib -j2 lib
- make -B -C tests -j2 test-zstd
- make -B -C tests -j2 test-cli-tests
- ZSTREAM_TESTTIME=-T2s make -B -C tests -j2 test-zstream
- FUZZERTEST=-T5s make -B -C tests -j2 test-fuzzer
- make -B -C tests/fuzz -j2 all
- make -B -C tests/fuzz -j2 sequence_compression_api
- cargo clippy --manifest-path rust/Cargo.toml --tests -- -D warnings (pre-existing manual_repeat_n failure)
- cargo clippy --manifest-path rust/Cargo.toml --benches -- -D warnings (pre-existing manual_repeat_n failure)
This commit is contained in:
2026-07-18 21:04:07 +02:00
parent 62732ff31d
commit 1ae1361652
3 changed files with 384 additions and 97 deletions
+53 -96
View File
@@ -83,7 +83,7 @@ int ZSTD_rust_dictTooBig(size_t loadedDictSize);
* Matchfinder/window state, sequence-store construction, and outer repeat-mode
* cleanup remain in C. */
typedef struct {
SeqStore_t* seqStore;
const SeqStore_t* seqStore;
ZSTD_compressedBlockState_t** prevCBlock;
ZSTD_compressedBlockState_t** nextCBlock;
void* tmpWorkspace;
@@ -165,6 +165,42 @@ typedef char ZSTD_rust_split_block_state_layout[
&& sizeof(ZSTD_rust_splitBlockState)
== 10 * sizeof(void*) + 4 * sizeof(int))
? 1 : -1];
/* The ordinary sequence-block body only needs this projection of ZSTD_CCtx.
* Sequence-store construction and the no-compress fallback remain in C. */
typedef struct {
const SeqStore_t* seqStore;
ZSTD_compressedBlockState_t** prevCBlock;
ZSTD_compressedBlockState_t** nextCBlock;
void* tmpWorkspace;
size_t tmpWkspSize;
SeqCollector* seqCollector;
int strategy;
int disableLiteralCompression;
int bmi2;
int isFirstBlock;
} ZSTD_rust_blockInternalState;
size_t ZSTD_rust_compressBlockInternal(
const ZSTD_rust_blockInternalState* state,
void* dst, size_t dstCapacity,
const void* src, size_t srcSize,
U32 frame);
typedef char ZSTD_rust_block_internal_state_layout[
(offsetof(ZSTD_rust_blockInternalState, seqStore) == 0
&& offsetof(ZSTD_rust_blockInternalState, prevCBlock) == sizeof(void*)
&& offsetof(ZSTD_rust_blockInternalState, nextCBlock) == 2 * sizeof(void*)
&& offsetof(ZSTD_rust_blockInternalState, tmpWorkspace) == 3 * sizeof(void*)
&& offsetof(ZSTD_rust_blockInternalState, tmpWkspSize) == 4 * sizeof(void*)
&& offsetof(ZSTD_rust_blockInternalState, seqCollector) == 5 * sizeof(void*)
&& offsetof(ZSTD_rust_blockInternalState, strategy) == 6 * sizeof(void*)
&& offsetof(ZSTD_rust_blockInternalState, disableLiteralCompression)
== 6 * sizeof(void*) + sizeof(int)
&& offsetof(ZSTD_rust_blockInternalState, bmi2)
== 6 * sizeof(void*) + 2 * sizeof(int)
&& offsetof(ZSTD_rust_blockInternalState, isFirstBlock)
== 6 * sizeof(void*) + 3 * sizeof(int)
&& sizeof(ZSTD_rust_blockInternalState)
== 6 * sizeof(void*) + 4 * sizeof(int))
? 1 : -1];
size_t ZSTD_rust_nextInputSizeHint(int inBufferMode,
size_t blockSizeMax,
size_t stableInNotConsumed,
@@ -405,15 +441,6 @@ size_t ZSTD_rust_entropyCompressSeqStore_internal(
int strategy, int disableLiteralCompression,
void* entropyWorkspace, size_t entropyWkspSize,
int bmi2);
size_t ZSTD_rust_entropyCompressSeqStore(
const SeqStore_t* seqStorePtr,
const ZSTD_entropyCTables_t* prevEntropy,
ZSTD_entropyCTables_t* nextEntropy,
int strategy, int disableLiteralCompression,
void* dst, size_t dstCapacity,
size_t srcSize,
void* entropyWorkspace, size_t entropyWkspSize,
int bmi2);
size_t ZSTD_rust_buildBlockEntropyStats(
const SeqStore_t* seqStorePtr,
const ZSTD_entropyCTables_t* prevEntropy,
@@ -421,9 +448,6 @@ size_t ZSTD_rust_buildBlockEntropyStats(
int strategy, int disableLiteralCompression,
ZSTD_entropyCTablesMetadata_t* entropyMetadata,
void* workspace, size_t wkspSize);
size_t ZSTD_rust_copyBlockSequences(
SeqCollector* seqCollector, const SeqStore_t* seqStore,
const U32 prevRepcodes[ZSTD_REP_NUM]);
void ZSTD_rust_resetCompressedBlockState(ZSTD_compressedBlockState_t* bs);
void ZSTD_rust_invalidateRepCodes(U32 rep[ZSTD_REP_NUM]);
typedef char ZSTD_rust_invalidate_rep_count[(ZSTD_REP_NUM == 3) ? 1 : -1];
@@ -440,7 +464,6 @@ 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);
int ZSTD_rust_isRLE(const BYTE* src, size_t length);
size_t ZSTD_rust_postProcessSequenceProducerResult(
ZSTD_Sequence* outSeqs, size_t nbExternalSeqs,
size_t outSeqsCapacity, size_t srcSize);
@@ -2309,27 +2332,6 @@ ZSTD_entropyCompressSeqStore_internal(
entropyWorkspace, entropyWkspSize, bmi2);
}
static size_t
ZSTD_entropyCompressSeqStore(
const SeqStore_t* seqStorePtr,
const ZSTD_entropyCTables_t* prevEntropy,
ZSTD_entropyCTables_t* nextEntropy,
const ZSTD_CCtx_params* cctxParams,
void* dst, size_t dstCapacity,
size_t srcSize,
void* entropyWorkspace, size_t entropyWkspSize,
int bmi2)
{
return ZSTD_rust_entropyCompressSeqStore(
seqStorePtr, prevEntropy, nextEntropy,
(int)cctxParams->cParams.strategy,
ZSTD_literalsCompressionIsDisabled(cctxParams),
dst, dstCapacity,
srcSize,
entropyWorkspace, entropyWkspSize,
bmi2);
}
/* ZSTD_selectBlockCompressor() :
* Not static, but internal use only (used by long distance matcher)
* assumption : strat is a valid strategy */
@@ -2642,12 +2644,6 @@ static size_t ZSTD_buildSeqStore(ZSTD_CCtx* zc, const void* src, size_t srcSize)
return ZSTDbss_compress;
}
static size_t ZSTD_copyBlockSequences(SeqCollector* seqCollector, const SeqStore_t* seqStore, const U32 prevRepcodes[ZSTD_REP_NUM])
{
/* The implementation lives in rust/src/zstd_compress_stats.rs. */
return ZSTD_rust_copyBlockSequences(seqCollector, seqStore, prevRepcodes);
}
/* ZSTD_sequenceBound() lives in rust/src/zstd_compress_api.rs. */
size_t ZSTD_generateSequences(ZSTD_CCtx* zc, ZSTD_Sequence* outSeqs,
@@ -2687,11 +2683,6 @@ size_t ZSTD_generateSequences(ZSTD_CCtx* zc, ZSTD_Sequence* outSeqs,
/* ZSTD_mergeBlockDelimiters() lives in rust/src/zstd_compress_api.rs. */
/* Unrolled loop to read four size_ts of input at a time. Returns 1 if is RLE, 0 if not. */
static int ZSTD_isRLE(const BYTE* src, size_t length) {
return ZSTD_rust_isRLE(src, length);
}
static void
ZSTD_blockState_confirmRepcodesAndEntropyTables(ZSTD_blockState_t* const bs)
{
@@ -2816,14 +2807,7 @@ ZSTD_compressBlock_internal(ZSTD_CCtx* zc,
void* dst, size_t dstCapacity,
const void* src, size_t srcSize, U32 frame)
{
/* This is an estimated upper bound for the length of an rle block.
* This isn't the actual upper bound.
* Finding the real threshold needs further investigation.
*/
const U32 rleMaxLength = 25;
size_t cSize;
const BYTE* ip = (const BYTE*)src;
BYTE* op = (BYTE*)dst;
ZSTD_rust_blockInternalState state;
DEBUGLOG(5, "ZSTD_compressBlock_internal (dstCapacity=%u, dictLimit=%u, nextToUpdate=%u)",
(unsigned)dstCapacity, (unsigned)zc->blockState.matchState.window.dictLimit,
(unsigned)zc->blockState.matchState.nextToUpdate);
@@ -2832,51 +2816,24 @@ ZSTD_compressBlock_internal(ZSTD_CCtx* zc,
FORWARD_IF_ERROR(bss, "ZSTD_buildSeqStore failed");
if (bss == ZSTDbss_noCompress) {
RETURN_ERROR_IF(zc->seqCollector.collectSequences, sequenceProducer_failed, "Uncompressible block");
cSize = 0;
goto out;
if (zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode == FSE_repeat_valid)
zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode = FSE_repeat_check;
return 0;
}
}
if (zc->seqCollector.collectSequences) {
FORWARD_IF_ERROR(ZSTD_copyBlockSequences(&zc->seqCollector, ZSTD_getSeqStore(zc), zc->blockState.prevCBlock->rep), "copyBlockSequences failed");
ZSTD_blockState_confirmRepcodesAndEntropyTables(&zc->blockState);
return 0;
}
/* encode sequences and literals */
cSize = ZSTD_entropyCompressSeqStore(&zc->seqStore,
&zc->blockState.prevCBlock->entropy, &zc->blockState.nextCBlock->entropy,
&zc->appliedParams,
dst, dstCapacity,
srcSize,
zc->tmpWorkspace, zc->tmpWkspSize /* statically allocated in resetCCtx */,
zc->bmi2);
if (frame &&
/* We don't want to emit our first block as a RLE even if it qualifies because
* doing so will cause the decoder (cli only) to throw a "should consume all input error."
* This is only an issue for zstd <= v1.4.3
*/
!zc->isFirstBlock &&
cSize < rleMaxLength &&
ZSTD_isRLE(ip, srcSize))
{
cSize = 1;
op[0] = ip[0];
}
out:
if (!ZSTD_isError(cSize) && cSize > 1) {
ZSTD_blockState_confirmRepcodesAndEntropyTables(&zc->blockState);
}
/* We check that dictionaries have offset codes available for the first
* block. After the first block, the offcode table might not have large
* enough codes to represent the offsets in the data.
*/
if (zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode == FSE_repeat_valid)
zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode = FSE_repeat_check;
return cSize;
state.seqStore = &zc->seqStore;
state.prevCBlock = &zc->blockState.prevCBlock;
state.nextCBlock = &zc->blockState.nextCBlock;
state.tmpWorkspace = zc->tmpWorkspace;
state.tmpWkspSize = zc->tmpWkspSize;
state.seqCollector = &zc->seqCollector;
state.strategy = (int)zc->appliedParams.cParams.strategy;
state.disableLiteralCompression = ZSTD_literalsCompressionIsDisabled(&zc->appliedParams);
state.bmi2 = zc->bmi2;
state.isFirstBlock = zc->isFirstBlock;
return ZSTD_rust_compressBlockInternal(
&state, dst, dstCapacity, src, srcSize, frame);
}
static size_t ZSTD_compressBlock_targetCBlockSize(ZSTD_CCtx* zc,