From 4663a3372e3a9d7d0a9fd490440f82bed261464d Mon Sep 17 00:00:00 2001 From: ddidderr Date: Sat, 18 Jul 2026 09:55:01 +0200 Subject: [PATCH] feat(compress): move repcode invalidation to Rust Keep ZSTD_invalidateRepCodes responsible for extracting the private previous block state and checking that the match window has no external dictionary. Delegate only the fixed three-entry repcode reset through a narrow pointer ABI, with a C assertion preserving the Rust side's ZSTD_REP_NUM assumption. Test Plan: - `cargo test --manifest-path rust/Cargo.toml --no-default-features --features compression zstd_compress::tests::invalidate_rep_codes_clears_all_entries` -- passed - `make -B -C lib -j2 lib` -- passed - `make -C tests -j2 fuzzer` and `./tests/fuzzer -s4560 -t47 -i48 -v` -- passed - Required clippy, nightly fmt, and diff checks -- passed --- lib/compress/zstd_compress.c | 6 ++++-- rust/src/zstd_compress.rs | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 4382b1043..ca07b2962 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -298,6 +298,8 @@ 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]; void ZSTD_rust_confirmRepcodesAndEntropyTables( ZSTD_compressedBlockState_t** prevCBlock, ZSTD_compressedBlockState_t** nextCBlock); @@ -1798,8 +1800,8 @@ static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc, * Note : only works with regular variant; * do not use with extDict variant ! */ void ZSTD_invalidateRepCodes(ZSTD_CCtx* cctx) { - int i; - for (i=0; iblockState.prevCBlock->rep[i] = 0; + ZSTD_compressedBlockState_t* const prevCBlock = cctx->blockState.prevCBlock; + ZSTD_rust_invalidateRepCodes(prevCBlock->rep); assert(!ZSTD_window_hasExtDict(cctx->blockState.matchState.window)); } diff --git a/rust/src/zstd_compress.rs b/rust/src/zstd_compress.rs index 49226d72e..d7fe62e9d 100644 --- a/rust/src/zstd_compress.rs +++ b/rust/src/zstd_compress.rs @@ -72,6 +72,7 @@ unsafe extern "C" { const ZSTD_FAST: c_int = 1; const ZSTD_DFAST: c_int = 2; +const ZSTD_REP_NUM: usize = 3; #[cfg(test)] const ZSTD_BM_BUFFERED: c_int = 0; const ZSTD_BM_STABLE: c_int = 1; @@ -365,6 +366,14 @@ pub unsafe extern "C" fn ZSTD_rust_copyCDictTableIntoCCtx( } } +/// Clear the previous block's repcodes before the next regular compression. +#[no_mangle] +pub unsafe extern "C" fn ZSTD_rust_invalidateRepCodes(rep: *mut u32) { + debug_assert!(!rep.is_null()); + let rep = unsafe { std::slice::from_raw_parts_mut(rep, ZSTD_REP_NUM) }; + rep.fill(0); +} + #[inline] fn zeroed_state() -> ZSTD_compressedBlockState_t { /* The state contains only integer arrays and enum fields. */ @@ -1118,6 +1127,15 @@ mod tests { ); } + #[test] + fn invalidate_rep_codes_clears_all_entries() { + let mut rep = [11u32, 22, 33]; + + unsafe { ZSTD_rust_invalidateRepCodes(rep.as_mut_ptr()) }; + + assert_eq!(rep, [0; ZSTD_REP_NUM]); + } + #[test] fn public_one_shot_abi_is_c_compatible() { let entry: unsafe extern "C" fn(*mut c_void, usize, *const c_void, usize, c_int) -> usize =