From 2afeb6ec25c9a2558e8c12fb7d2f92073e5a074d Mon Sep 17 00:00:00 2001 From: ddidderr Date: Sat, 18 Jul 2026 12:23:31 +0200 Subject: [PATCH] feat(compress): move block compressor index policy to Rust Keep ZSTD_selectBlockCompressor's compressor tables, callback pointers, and final dictionary-mode table lookup in C while moving only the scalar choice of row-based versus ordinary table index to the Rust parameter-policy module. The Rust helper reuses the existing row-matchfinder policy, returns 0..2 for row entries, and returns 3 plus the strategy for ordinary entries. C retains its public/internal signature, strategy and row-mode assertions, numeric fast strategy assumption, and all private callback ownership. Test Plan: - Rust clippy normal, benches, and tests before and after nightly formatting -- passed. - `cargo test --manifest-path rust/Cargo.toml --no-default-features --features compression zstd_compress_params` -- 41 passed. - `make -B -C lib -j2 lib` -- passed without new warnings. - `make -C tests test-rust-lib-smoke` -- passed. - `tests/fuzzer -s4560 -t47 -i48 -v` and `-s4560 -t56 -i57 -v` -- passed. - `make -C tests -j2 test-zstream` -- 84 named tests plus 6,708 and 8,980 randomized cases passed. - `git diff --check` and `git diff --cached --check` -- passed. - The pre-existing `tests/zstreamtest.c:1899` unterminated-string warning remains. --- lib/compress/zstd_compress.c | 10 +++-- rust/src/zstd_compress_params.rs | 77 ++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 3 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index f0063b65b..f3cd550e2 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -140,6 +140,7 @@ void ZSTD_rust_params_overrideCParams(ZSTD_compressionParameters* cParams, int ZSTD_rust_params_resolveExternalSequenceValidation(int mode); int ZSTD_rust_params_rowMatchFinderSupported(int strategy); int ZSTD_rust_params_rowMatchFinderUsed(int strategy, int mode); +int ZSTD_rust_params_selectBlockCompressor(int strategy, int mode); int ZSTD_rust_params_resolveRowMatchFinderMode( int mode, ZSTD_compressionParameters cParams); int ZSTD_rust_params_resolveBlockSplitterMode( @@ -2265,11 +2266,14 @@ ZSTD_BlockCompressor_f ZSTD_selectBlockCompressor(ZSTD_strategy strat, ZSTD_Para NULL } }; ZSTD_BlockCompressor_f selectedCompressor; + int selectedCompressorIndex; ZSTD_STATIC_ASSERT((unsigned)ZSTD_fast == 1); assert(ZSTD_cParam_withinBounds(ZSTD_c_strategy, (int)strat)); DEBUGLOG(5, "Selected block compressor: dictMode=%d strat=%d rowMatchfinder=%d", (int)dictMode, (int)strat, (int)useRowMatchFinder); - if (ZSTD_rowMatchFinderUsed(strat, useRowMatchFinder)) { + selectedCompressorIndex = ZSTD_rust_params_selectBlockCompressor( + (int)strat, (int)useRowMatchFinder); + if (selectedCompressorIndex < 3) { static const ZSTD_BlockCompressor_f rowBasedBlockCompressors[4][3] = { { ZSTD_COMPRESSBLOCK_GREEDY_ROW, @@ -2294,9 +2298,9 @@ ZSTD_BlockCompressor_f ZSTD_selectBlockCompressor(ZSTD_strategy strat, ZSTD_Para }; DEBUGLOG(5, "Selecting a row-based matchfinder"); assert(useRowMatchFinder != ZSTD_ps_auto); - selectedCompressor = rowBasedBlockCompressors[(int)dictMode][(int)strat - (int)ZSTD_greedy]; + selectedCompressor = rowBasedBlockCompressors[(int)dictMode][selectedCompressorIndex]; } else { - selectedCompressor = blockCompressor[(int)dictMode][(int)strat]; + selectedCompressor = blockCompressor[(int)dictMode][selectedCompressorIndex - 3]; } assert(selectedCompressor != NULL); return selectedCompressor; diff --git a/rust/src/zstd_compress_params.rs b/rust/src/zstd_compress_params.rs index db95b6f9d..3fab8a83f 100644 --- a/rust/src/zstd_compress_params.rs +++ b/rust/src/zstd_compress_params.rs @@ -335,6 +335,25 @@ pub extern "C" fn ZSTD_rust_params_rowMatchFinderUsed(strategy: c_int, mode: c_i c_int::from(row_match_finder_used(strategy, mode)) } +#[inline] +fn select_block_compressor(strategy: c_int, mode: c_int) -> c_int { + debug_assert!((ZSTD_FAST..=ZSTD_BTULTRA2).contains(&strategy)); + if row_match_finder_used(strategy, mode) { + strategy - ZSTD_GREEDY + } else { + 3 + strategy + } +} + +/// Returns the C block-compressor table index encoding. +/// +/// Row-based entries use indices 0..2. The ordinary table uses the strategy +/// index after the C caller subtracts 3 from the returned value. +#[no_mangle] +pub extern "C" fn ZSTD_rust_params_selectBlockCompressor(strategy: c_int, mode: c_int) -> c_int { + select_block_compressor(strategy, mode) +} + #[inline] fn resolve_row_match_finder(mode: c_int, cparams: ZSTD_compressionParameters) -> c_int { if mode != ZSTD_RUST_PS_AUTO { @@ -1307,6 +1326,64 @@ mod tests { ); } + #[test] + fn block_compressor_selector_maps_all_strategy_table_indices() { + for strategy in ZSTD_FAST..=ZSTD_BTULTRA2 { + assert_eq!( + ZSTD_rust_params_selectBlockCompressor(strategy, ZSTD_RUST_PS_DISABLE), + 3 + strategy + ); + } + + assert_eq!( + ZSTD_rust_params_selectBlockCompressor(ZSTD_GREEDY, ZSTD_RUST_PS_ENABLE), + 0 + ); + assert_eq!( + ZSTD_rust_params_selectBlockCompressor(ZSTD_LAZY, ZSTD_RUST_PS_ENABLE), + 1 + ); + assert_eq!( + ZSTD_rust_params_selectBlockCompressor(ZSTD_LAZY2, ZSTD_RUST_PS_ENABLE), + 2 + ); + + for strategy in [ + ZSTD_FAST, + ZSTD_DFAST, + ZSTD_BTLAZY2, + ZSTD_BTOPT, + ZSTD_BTULTRA, + ZSTD_BTULTRA2, + ] { + assert_eq!( + ZSTD_rust_params_selectBlockCompressor(strategy, ZSTD_RUST_PS_ENABLE), + 3 + strategy + ); + } + } + + #[cfg(debug_assertions)] + #[test] + #[should_panic] + fn block_compressor_selector_rejects_strategy_below_bounds() { + select_block_compressor(ZSTD_FAST - 1, ZSTD_RUST_PS_DISABLE); + } + + #[cfg(debug_assertions)] + #[test] + #[should_panic] + fn block_compressor_selector_rejects_strategy_above_bounds() { + select_block_compressor(ZSTD_BTULTRA2 + 1, ZSTD_RUST_PS_DISABLE); + } + + #[cfg(debug_assertions)] + #[test] + #[should_panic] + fn block_compressor_selector_rejects_auto_row_mode() { + select_block_compressor(ZSTD_GREEDY, ZSTD_RUST_PS_AUTO); + } + #[test] fn block_splitter_and_ldm_policy_match_window_boundaries() { assert_eq!(