refactor(compress): move simple-path eligibility to Rust

Project the private requested-parameter and context state into a fixed scalar ABI record, leaving only C's parameter-selection leaf and private layout access in the shim. Rust now owns the complete-input eligibility predicate, including advanced-state exclusions, LDM sentinel handling, and the final fast/double-fast strategy gate, while preserving the requested-level and INT_MIN results.

Add focused policy tests for fast and double-fast acceptance, representative advanced-state rejection, the accepted LDM sentinel, non-fast strategies, and null state.

Test Plan:

- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo +nightly fmt --manifest-path rust/Cargo.toml --all -- --check

- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo clippy --manifest-path rust/Cargo.toml --all-targets -- -D warnings

- ulimit -v 41943040; CARGO_BUILD_JOBS=1 make -j1

- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo clippy --manifest-path rust/cli/Cargo.toml --all-targets -- -D warnings

- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo test --manifest-path rust/cli/Cargo.toml --all-targets

- ulimit -v 41943040; CARGO_BUILD_JOBS=1 make -j1 -C tests test
This commit is contained in:
2026-07-21 12:36:26 +02:00
parent 0735a5fd7f
commit 5c6302a3c3
2 changed files with 304 additions and 68 deletions
+186
View File
@@ -151,6 +151,143 @@ const _: () = {
pub type ZSTD_rust_simpleCompress2LevelFn = unsafe extern "C" fn(*const c_void, usize) -> c_int;
const ZSTD_DICT_DEFAULT_ATTACH: c_int = 0;
const ZSTD_LCM_AUTO: c_int = 0;
const ZSTD_LDM_UNUSED_LOG: c_int = 9999;
/// Scalar projection of the private context and requested parameters used to
/// select the complete-input simple-compression fast path.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct ZSTD_rust_simpleCompress2LevelState {
format: c_int,
content_size_flag: c_int,
checksum_flag: c_int,
cparams_window_log: c_int,
cparams_chain_log: c_int,
cparams_hash_log: c_int,
cparams_search_log: c_int,
cparams_min_match: c_int,
cparams_target_length: c_int,
requested_strategy: c_int,
force_window: c_int,
target_c_block_size_set: c_int,
src_size_hint_set: c_int,
attach_dict_pref: c_int,
literal_compression_mode: c_int,
nb_workers_set: c_int,
job_size_set: c_int,
overlap_log_set: c_int,
rsyncable: c_int,
ldm_enable: c_int,
ldm_hash_log_set: c_int,
ldm_bucket_size_log: c_int,
ldm_min_match_length_set: c_int,
ldm_hash_rate_log: c_int,
ldm_window_log_set: c_int,
dedicated_dict_search: c_int,
in_buffer_mode: c_int,
out_buffer_mode: c_int,
block_delimiters: c_int,
validate_sequences: c_int,
post_block_splitter: c_int,
pre_block_splitter_level_set: c_int,
max_block_size_set: c_int,
max_block_size_set_by_api: c_int,
use_row_match_finder: c_int,
deterministic_ref_prefix: c_int,
prefetch_cdict_tables: c_int,
match_finder_fallback: c_int,
ext_seq_prod_func_set: c_int,
search_for_external_repcodes: c_int,
pool_set: c_int,
collect_sequences: c_int,
cdict_set: c_int,
prefix_dict_set: c_int,
local_dict_set: c_int,
compression_level: c_int,
selected_strategy: c_int,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_simpleCompress2LevelState, format) == 0);
assert!(
offset_of!(ZSTD_rust_simpleCompress2LevelState, selected_strategy)
== 46 * size_of::<c_int>()
);
assert!(size_of::<ZSTD_rust_simpleCompress2LevelState>() == 47 * size_of::<c_int>());
};
#[inline]
fn simple_compress2_level_policy(state: &ZSTD_rust_simpleCompress2LevelState) -> c_int {
if state.format != ZSTD_F_ZSTD1
|| state.content_size_flag == 0
|| state.checksum_flag != 0
|| state.cparams_window_log != 0
|| state.cparams_chain_log != 0
|| state.cparams_hash_log != 0
|| state.cparams_search_log != 0
|| state.cparams_min_match != 0
|| state.cparams_target_length != 0
|| state.requested_strategy != 0
|| state.force_window != 0
|| state.target_c_block_size_set != 0
|| state.src_size_hint_set != 0
|| state.attach_dict_pref != ZSTD_DICT_DEFAULT_ATTACH
|| state.literal_compression_mode != ZSTD_LCM_AUTO
|| state.nb_workers_set != 0
|| state.job_size_set != 0
|| state.overlap_log_set != 0
|| state.rsyncable != 0
|| state.ldm_enable != ZSTD_RUST_PS_AUTO
|| state.ldm_hash_log_set != 0
|| (state.ldm_bucket_size_log != 0 && state.ldm_bucket_size_log != ZSTD_LDM_UNUSED_LOG)
|| state.ldm_min_match_length_set != 0
|| (state.ldm_hash_rate_log != 0 && state.ldm_hash_rate_log != ZSTD_LDM_UNUSED_LOG)
|| state.ldm_window_log_set != 0
|| state.dedicated_dict_search != 0
|| state.in_buffer_mode != ZSTD_BM_BUFFERED
|| state.out_buffer_mode != ZSTD_BM_BUFFERED
|| state.block_delimiters != ZSTD_SF_NO_BLOCK_DELIMITERS
|| state.validate_sequences != 0
|| state.post_block_splitter != ZSTD_RUST_PS_AUTO
|| state.pre_block_splitter_level_set != 0
|| state.max_block_size_set != 0
|| state.max_block_size_set_by_api != 0
|| state.use_row_match_finder != ZSTD_RUST_PS_AUTO
|| state.deterministic_ref_prefix != 0
|| state.prefetch_cdict_tables != ZSTD_RUST_PS_AUTO
|| state.match_finder_fallback != 0
|| state.ext_seq_prod_func_set != 0
|| state.search_for_external_repcodes != ZSTD_RUST_PS_AUTO
|| state.pool_set != 0
|| state.collect_sequences != 0
|| state.cdict_set != 0
|| state.prefix_dict_set != 0
|| state.local_dict_set != 0
{
return c_int::MIN;
}
if state.selected_strategy != ZSTD_FAST && state.selected_strategy != ZSTD_DFAST {
return c_int::MIN;
}
state.compression_level
}
/// Decide whether a projected context can use the complete-input Rust frame
/// compressor. C retains private-layout projection and parameter selection;
/// Rust owns the eligibility predicate and strategy gate.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_simpleCompress2LevelPolicy(
state: *const ZSTD_rust_simpleCompress2LevelState,
) -> c_int {
let Some(state) = (unsafe { state.as_ref() }) else {
return c_int::MIN;
};
simple_compress2_level_policy(state)
}
const ZSTD_WINDOW_START_INDEX: u32 = 2;
const ZSTD_DUBT_UNSORTED_MARK: u32 = 1;
const ZSTD_INDEXOVERFLOW_MARGIN: usize = 16usize << 20;
@@ -14834,6 +14971,55 @@ mod tests {
);
}
fn simple_compress2_level_policy_state() -> ZSTD_rust_simpleCompress2LevelState {
ZSTD_rust_simpleCompress2LevelState {
format: ZSTD_F_ZSTD1,
content_size_flag: 1,
compression_level: 5,
selected_strategy: ZSTD_FAST,
..Default::default()
}
}
#[test]
fn simple_compress2_level_policy_accepts_fast_and_double_fast() {
for strategy in [ZSTD_FAST, ZSTD_DFAST] {
let mut state = simple_compress2_level_policy_state();
state.selected_strategy = strategy;
assert_eq!(simple_compress2_level_policy(&state), 5);
}
}
#[test]
fn simple_compress2_level_policy_rejects_advanced_state() {
let mut state = simple_compress2_level_policy_state();
state.checksum_flag = 1;
assert_eq!(simple_compress2_level_policy(&state), c_int::MIN);
let mut state = simple_compress2_level_policy_state();
state.cdict_set = 1;
assert_eq!(simple_compress2_level_policy(&state), c_int::MIN);
let mut state = simple_compress2_level_policy_state();
state.ldm_bucket_size_log = 1;
assert_eq!(simple_compress2_level_policy(&state), c_int::MIN);
let mut state = simple_compress2_level_policy_state();
state.ldm_bucket_size_log = ZSTD_LDM_UNUSED_LOG;
assert_eq!(simple_compress2_level_policy(&state), 5);
}
#[test]
fn simple_compress2_level_policy_rejects_non_fast_or_null_state() {
let mut state = simple_compress2_level_policy_state();
state.selected_strategy = ZSTD_BTLAZY2;
assert_eq!(simple_compress2_level_policy(&state), c_int::MIN);
assert_eq!(
unsafe { ZSTD_rust_simpleCompress2LevelPolicy(ptr::null()) },
c_int::MIN
);
}
#[derive(Default)]
struct SimpleStreamPolicyTestContext {
calls: usize,