From 4f55ce323fa7fd761f99c852567806a6ec638730 Mon Sep 17 00:00:00 2001 From: ddidderr Date: Sat, 18 Jul 2026 18:35:35 +0200 Subject: [PATCH] feat(compress): move simple strategy selection into Rust The Rust simple-compression entry points already owned the dispatch, but ZSTD_rust_compressCCtxStrategy still called the C parameter cascade to decide whether a fast Rust frame path was safe. That left a small policy leaf in the C implementation and made the dispatch boundary harder to test in isolation. Implement the helper in Rust by using the existing Rust table-selection and C-parameter-adjustment functions with the same source-size, dictionary-mode, and automatic-adjustment inputs as the C code. Leave only the C ABI declaration and add tests for the table thresholds, default-level behavior, and negative fast-level behavior. The private context reset and fallback compression paths remain unchanged. Test Plan: - `cargo test --manifest-path rust/Cargo.toml --lib zstd_compress -- --test-threads=1` -- passed (234 tests). - `cargo clippy --manifest-path rust/Cargo.toml --lib -- -D warnings` -- passed. - `cargo +nightly fmt --manifest-path rust/Cargo.toml -- --check` -- passed. - `make -B -C lib -j2 lib` -- passed. - `make -C tests -j2 test-rust-lib-smoke` -- passed. - Full-target clippy remains blocked by pre-existing test-only `manual_repeat_n` and `manual_dangling_ptr` warnings outside this seam. --- lib/compress/zstd_compress.c | 7 ---- rust/src/zstd_compress.rs | 66 +++++++++++++++++++++++++++++++++--- 2 files changed, 62 insertions(+), 11 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 594757046..91f0b608d 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -3849,13 +3849,6 @@ size_t ZSTD_compress_usingDict(ZSTD_CCtx* cctx, /* The Rust simple API still needs the C-owned context reset, but does not * cross the private context layout. */ -int ZSTD_rust_compressCCtxStrategy(size_t srcSize, int compressionLevel) -{ - ZSTD_parameters const params = ZSTD_getParams_internal( - compressionLevel, srcSize, 0, ZSTD_cpm_noAttachDict); - return (int)params.cParams.strategy; -} - size_t ZSTD_rust_resetCCtxForSimpleCompression(void* cctx) { return ZSTD_CCtx_reset((ZSTD_CCtx*)cctx, ZSTD_reset_session_and_parameters); diff --git a/rust/src/zstd_compress.rs b/rust/src/zstd_compress.rs index 99ef1070d..8e49f6162 100644 --- a/rust/src/zstd_compress.rs +++ b/rust/src/zstd_compress.rs @@ -23,7 +23,7 @@ use crate::zstd_compress_frame::{ use crate::zstd_compress_literals::min_gain; use crate::zstd_compress_params::{ ZSTD_rust_params_adjustCParams, ZSTD_rust_params_maxNbSeq, ZSTD_rust_params_selectCParams, - ZSTD_RUST_CPM_NO_ATTACH_DICT, ZSTD_RUST_PS_DISABLE, + ZSTD_RUST_CPM_NO_ATTACH_DICT, ZSTD_RUST_PS_AUTO, ZSTD_RUST_PS_DISABLE, }; use crate::zstd_compress_sequences::SeqDef; use crate::zstd_compress_stats::{ @@ -44,7 +44,6 @@ unsafe extern "C" { src_size: usize, compression_level: c_int, ) -> usize; - fn ZSTD_rust_compressCCtxStrategy(src_size: usize, compression_level: c_int) -> c_int; fn ZSTD_rust_resetCCtxForSimpleCompressionSession(cctx: *mut c_void) -> usize; fn ZSTD_rust_markSimpleCompression2Complete(cctx: *mut c_void); fn ZSTD_rust_simpleCompress2Level(cctx: *const c_void, src_size: usize) -> c_int; @@ -142,6 +141,33 @@ fn target_c_block_size_action( } } +/// Select the strategy used by the simple compression entry points. +/// +/// This is the Rust equivalent of the strategy portion of +/// `ZSTD_getParams_internal(..., ZSTD_cpm_noAttachDict)`: use the actual +/// source size for table selection, then apply the ordinary automatic +/// parameter adjustment before dispatching to the Rust or C frame path. +#[no_mangle] +pub extern "C" fn ZSTD_rust_compressCCtxStrategy( + src_size: usize, + compression_level: c_int, +) -> c_int { + let cparams = ZSTD_rust_params_selectCParams( + compression_level, + src_size as u64, + 0, + ZSTD_RUST_CPM_NO_ATTACH_DICT, + ); + ZSTD_rust_params_adjustCParams( + cparams, + src_size as u64, + 0, + ZSTD_RUST_CPM_NO_ATTACH_DICT, + ZSTD_RUST_PS_AUTO, + ) + .strategy +} + /// Classify the target-sized block policy without crossing the C context ABI. /// /// C calls this once before the superblock attempt to classify the RLE @@ -1205,7 +1231,7 @@ pub unsafe extern "C" fn ZSTD_compress( ) -> usize { #[cfg(not(test))] { - let strategy = unsafe { ZSTD_rust_compressCCtxStrategy(src_size, compression_level) }; + let strategy = ZSTD_rust_compressCCtxStrategy(src_size, compression_level); if strategy != ZSTD_FAST && strategy != ZSTD_DFAST { let cctx = unsafe { ZSTD_createCCtx() }; if cctx.is_null() { @@ -1250,7 +1276,7 @@ pub unsafe extern "C" fn ZSTD_compressCCtx( } #[cfg(not(test))] { - let strategy = unsafe { ZSTD_rust_compressCCtxStrategy(src_size, compression_level) }; + let strategy = ZSTD_rust_compressCCtxStrategy(src_size, compression_level); if strategy != ZSTD_FAST && strategy != ZSTD_DFAST { return unsafe { ZSTD_compress_usingDict( @@ -1394,6 +1420,12 @@ mod tests { use std::io::Write; use std::process::{Command, Stdio}; + const ZSTD_GREEDY: c_int = 3; + const ZSTD_LAZY: c_int = 4; + const ZSTD_BTULTRA: c_int = 8; + const ZSTD_BTOPT: c_int = 7; + const ZSTD_BTULTRA2: c_int = 9; + fn system_round_trip(compressed: &[u8]) -> Option> { let mut child = Command::new("zstd") .args(["-q", "-d", "-c"]) @@ -1431,6 +1463,32 @@ mod tests { output } + #[test] + fn simple_strategy_follows_source_size_tiers() { + assert_eq!(ZSTD_rust_compressCCtxStrategy(0, 1), ZSTD_FAST); + assert_eq!(ZSTD_rust_compressCCtxStrategy(16 * 1024, 5), ZSTD_LAZY); + assert_eq!( + ZSTD_rust_compressCCtxStrategy(16 * 1024 + 1, 5), + ZSTD_GREEDY + ); + assert_eq!(ZSTD_rust_compressCCtxStrategy(128 * 1024, 16), ZSTD_BTULTRA); + assert_eq!(ZSTD_rust_compressCCtxStrategy(256 * 1024, 16), ZSTD_BTULTRA); + assert_eq!( + ZSTD_rust_compressCCtxStrategy(256 * 1024 + 1, 16), + ZSTD_BTOPT + ); + } + + #[test] + fn simple_strategy_preserves_default_and_fast_level_selection() { + assert_eq!(ZSTD_rust_compressCCtxStrategy(64 * 1024, 0), ZSTD_DFAST); + assert_eq!(ZSTD_rust_compressCCtxStrategy(1024 * 1024, -5), ZSTD_FAST); + assert_eq!( + ZSTD_rust_compressCCtxStrategy(1024 * 1024, 22), + ZSTD_BTULTRA2 + ); + } + #[test] fn reduce_table_applies_threshold_and_wrapping_subtraction() { let mut table = [0, 1, 2, 3, 4, 5, 6, u32::MAX, 0, 0, 0, 0, 0, 0, 0, 0];