From effef1b8b1499963bec5da3059a67ed56fac9c40 Mon Sep 17 00:00:00 2001 From: ddidderr Date: Sat, 18 Jul 2026 11:08:27 +0200 Subject: [PATCH] feat(compress): move sequence copier selection to Rust MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ZSTD_selectSequenceCopier combined block-delimiter validation with its private C function-pointer dispatch. Move only the scalar mode policy behind a Rust ABI shim, retaining the function-pointer switch and both sequence transfer implementations in C. Rust preserves the 0..=1 debug validation and the release fallback to no-block-delimiters for unsupported values. Test Plan: - `cargo test --manifest-path rust/Cargo.toml --no-default-features --features compression` — 281 passed. - Required clippy checks for the library, benches, and tests passed before and after `cargo +nightly fmt`. - `make -B -C lib -j2 lib` and `make -B -C tests test-rust-lib-smoke` — passed. - `tests/fuzzer -s4560 -t47 -i48 -v` and `tests/fuzzer -s4560 -t56 -i57 -v` — passed. - `make -C tests -j2 test-zstream` — 84 named, 7,298 standard randomized, and 9,379 new-API randomized cases passed. - `git diff --check` and `git diff --cached --check` — passed. --- lib/compress/zstd_compress.c | 10 +++-- rust/src/zstd_compress.rs | 74 ++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 4 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index a596183a6..93eb854cb 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -356,6 +356,7 @@ size_t ZSTD_rust_blockSizeExplicitDelimiter(const ZSTD_Sequence* inSeqs, size_t ZSTD_rust_determineBlockSize(int mode, size_t blockSize, size_t remaining, const ZSTD_Sequence* inSeqs, size_t inSeqsSize, U32 seqIdx); +int ZSTD_rust_selectSequenceCopier(int mode); size_t ZSTD_rust_loadCEntropy(ZSTD_compressedBlockState_t* bs, void* workspace, const void* dict, size_t dictSize); size_t ZSTD_rust_transferSequencesWBlockDelim( @@ -5164,12 +5165,13 @@ typedef size_t (*ZSTD_SequenceCopier_f)(ZSTD_CCtx* cctx, static ZSTD_SequenceCopier_f ZSTD_selectSequenceCopier(ZSTD_SequenceFormat_e mode) { - assert(ZSTD_cParam_withinBounds(ZSTD_c_blockDelimiters, (int)mode)); - if (mode == ZSTD_sf_explicitBlockDelimiters) { + switch (ZSTD_rust_selectSequenceCopier((int)mode)) { + case ZSTD_sf_explicitBlockDelimiters: return ZSTD_transferSequences_wBlockDelim; + case ZSTD_sf_noBlockDelimiters: + default: + return ZSTD_transferSequences_noDelim; } - assert(mode == ZSTD_sf_noBlockDelimiters); - return ZSTD_transferSequences_noDelim; } static size_t determine_blockSize(ZSTD_SequenceFormat_e mode, diff --git a/rust/src/zstd_compress.rs b/rust/src/zstd_compress.rs index df4b3deb9..95b73ca68 100644 --- a/rust/src/zstd_compress.rs +++ b/rust/src/zstd_compress.rs @@ -76,6 +76,8 @@ const ZSTD_REP_NUM: usize = 3; #[cfg(test)] const ZSTD_BM_BUFFERED: c_int = 0; const ZSTD_BM_STABLE: c_int = 1; +const ZSTD_SF_NO_BLOCK_DELIMITERS: c_int = 0; +const ZSTD_SF_EXPLICIT_BLOCK_DELIMITERS: c_int = 1; const ZSTD_BLOCKSIZE_MAX: usize = 1 << 17; const ZSTD_CONTENTSIZE_UNKNOWN: u64 = u64::MAX; const ZSTD_TARGET_CBLOCK_BSS_COMPRESS: c_int = 0; @@ -196,6 +198,27 @@ pub extern "C" fn ZSTD_rust_inBufferForEndFlush( } } +#[inline] +fn select_sequence_copier(mode: c_int) -> c_int { + debug_assert!( + (ZSTD_SF_NO_BLOCK_DELIMITERS..=ZSTD_SF_EXPLICIT_BLOCK_DELIMITERS).contains(&mode) + ); + if mode == ZSTD_SF_EXPLICIT_BLOCK_DELIMITERS { + ZSTD_SF_EXPLICIT_BLOCK_DELIMITERS + } else { + debug_assert_eq!(mode, ZSTD_SF_NO_BLOCK_DELIMITERS); + ZSTD_SF_NO_BLOCK_DELIMITERS + } +} + +/// Select the C-side sequence transfer policy without crossing private +/// function pointers through the Rust ABI. Invalid values retain the C +/// release fallback to the no-delimiter policy after debug validation. +#[no_mangle] +pub extern "C" fn ZSTD_rust_selectSequenceCopier(mode: c_int) -> c_int { + select_sequence_copier(mode) +} + /* HUF_WORKSPACE_SIZE + (MaxSeq + 2) * sizeof(unsigned), rounded up. The * superblock leaf also accepts the larger pre-split workspace, so a fixed * 16 KiB buffer is sufficient for this first non-splitting path on both @@ -1197,6 +1220,57 @@ mod tests { } } + #[test] + fn sequence_copier_selector_returns_no_delimiters_mode() { + assert_eq!( + select_sequence_copier(ZSTD_SF_NO_BLOCK_DELIMITERS), + ZSTD_SF_NO_BLOCK_DELIMITERS + ); + assert_eq!( + ZSTD_rust_selectSequenceCopier(ZSTD_SF_NO_BLOCK_DELIMITERS), + ZSTD_SF_NO_BLOCK_DELIMITERS + ); + } + + #[test] + fn sequence_copier_selector_returns_explicit_delimiters_mode() { + assert_eq!( + select_sequence_copier(ZSTD_SF_EXPLICIT_BLOCK_DELIMITERS), + ZSTD_SF_EXPLICIT_BLOCK_DELIMITERS + ); + assert_eq!( + ZSTD_rust_selectSequenceCopier(ZSTD_SF_EXPLICIT_BLOCK_DELIMITERS), + ZSTD_SF_EXPLICIT_BLOCK_DELIMITERS + ); + } + + #[cfg(debug_assertions)] + #[test] + #[should_panic] + fn sequence_copier_selector_rejects_invalid_mode_in_debug() { + let _ = select_sequence_copier(-1); + } + + #[cfg(debug_assertions)] + #[test] + #[should_panic] + fn sequence_copier_selector_rejects_unsupported_mode_in_debug() { + let _ = select_sequence_copier(2); + } + + #[cfg(not(debug_assertions))] + #[test] + fn sequence_copier_selector_falls_back_to_no_delimiters_in_release() { + assert_eq!( + ZSTD_rust_selectSequenceCopier(-1), + ZSTD_SF_NO_BLOCK_DELIMITERS + ); + assert_eq!( + ZSTD_rust_selectSequenceCopier(2), + ZSTD_SF_NO_BLOCK_DELIMITERS + ); + } + #[test] fn invalidate_rep_codes_clears_all_entries() { let mut rep = [11u32, 22, 33];