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.
This commit is contained in:
2026-07-18 18:35:35 +02:00
parent 725c064df5
commit 4f55ce323f
2 changed files with 62 additions and 11 deletions
-7
View File
@@ -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);
+62 -4
View File
@@ -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<Vec<u8>> {
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];