feat(compress): move hash and window reset policies to Rust

Move hash-salt mixing and context-reset overflow predicates behind scalar Rust ABI shims. Keep the C-owned match state and window arithmetic in the C wrapper while preserving exact architecture-dependent thresholds.

Test Plan:

- cargo test --manifest-path rust/Cargo.toml --no-default-features --features compression (186 tests)

- cargo clippy for library, benches, tests, and CLI all-targets

- make -B -C lib -j2 lib

- make -B -C tests -j2 test-zstream (84 deterministic tests; fuzzers 6451 and 9879 cases)
This commit is contained in:
2026-07-18 05:06:32 +02:00
parent 52deffe66c
commit cba0373a2b
2 changed files with 87 additions and 13 deletions
+80
View File
@@ -64,6 +64,13 @@ const ZSTD_CONTENTSIZE_UNKNOWN: u64 = u64::MAX;
const ZSTD_ROWSIZE: usize = 16;
const ZSTD_WINDOW_START_INDEX: u32 = 2;
const ZSTD_DUBT_UNSORTED_MARK: u32 = 1;
const ZSTD_INDEXOVERFLOW_MARGIN: usize = 16usize << 20;
const ZSTD_CURRENT_MAX: usize = if size_of::<usize>() == 8 {
3500usize << 20
} else {
2000usize << 20
};
const ZSTD_CHUNKSIZE_MAX: usize = u32::MAX as usize - ZSTD_CURRENT_MAX;
#[cfg(not(test))]
const ZSTD_E_END: c_int = 2;
@@ -89,6 +96,48 @@ pub struct ZSTD_outBuffer {
* supported pointer widths. */
const TMP_WORKSPACE_SIZE: usize = 16 << 10;
#[inline]
fn bitmix(mut val: u64, len: u64) -> u64 {
val ^= val.rotate_right(49) ^ val.rotate_right(24);
val = val.wrapping_mul(0x9FB21C651E98DF25);
val ^= (val >> 35).wrapping_add(len);
val = val.wrapping_mul(0x9FB21C651E98DF25);
val ^ (val >> 28)
}
#[inline]
fn advance_hash_salt(hash_salt: u64, hash_salt_entropy: u64) -> u64 {
bitmix(hash_salt, 8) ^ bitmix(hash_salt_entropy, 4)
}
/// Advance the row-matchfinder salt without exposing C's private match state.
#[no_mangle]
pub extern "C" fn ZSTD_rust_advanceHashSalt(hash_salt: u64, hash_salt_entropy: u64) -> u64 {
advance_hash_salt(hash_salt, hash_salt_entropy)
}
#[inline]
fn index_too_close_to_max(next_src_base_offset: usize) -> bool {
next_src_base_offset > ZSTD_CURRENT_MAX - ZSTD_INDEXOVERFLOW_MARGIN
}
/// Return whether a scalar C window offset is within the overflow margin.
#[no_mangle]
pub extern "C" fn ZSTD_rust_indexTooCloseToMax(next_src_base_offset: usize) -> c_int {
index_too_close_to_max(next_src_base_offset) as c_int
}
#[inline]
fn dict_too_big(loaded_dict_size: usize) -> bool {
loaded_dict_size > ZSTD_CHUNKSIZE_MAX
}
/// Return whether a dictionary exceeds the maximum loadable chunk size.
#[no_mangle]
pub extern "C" fn ZSTD_rust_dictTooBig(loaded_dict_size: usize) -> c_int {
dict_too_big(loaded_dict_size) as c_int
}
#[inline]
fn reduce_table_internal(table: &mut [u32], reducer_value: u32, preserve_mark: bool) {
debug_assert_eq!(table.len() % ZSTD_ROWSIZE, 0);
@@ -657,6 +706,37 @@ mod tests {
assert_eq!(table[ZSTD_ROWSIZE * 2 - 1], u32::MAX - 4);
}
#[test]
fn bitmix_and_hash_salt_match_the_c_arithmetic() {
assert_eq!(bitmix(0x0123_4567_89ab_cdef, 8), 0xd498_d855_4e8d_d8cb);
assert_eq!(
advance_hash_salt(0x0123_4567_89ab_cdef, 0xfedc_ba98_7654_3210),
0xe5ee_f172_e5ff_3e57
);
assert_eq!(
ZSTD_rust_advanceHashSalt(0x0123_4567_89ab_cdef, 0xfedc_ba98_7654_3210),
0xe5ee_f172_e5ff_3e57
);
}
#[test]
fn index_too_close_to_max_uses_a_strict_margin_boundary() {
let threshold = ZSTD_CURRENT_MAX - ZSTD_INDEXOVERFLOW_MARGIN;
assert!(!index_too_close_to_max(threshold));
assert!(index_too_close_to_max(threshold + 1));
assert_eq!(ZSTD_rust_indexTooCloseToMax(threshold), 0);
assert_eq!(ZSTD_rust_indexTooCloseToMax(threshold + 1), 1);
}
#[test]
fn dict_too_big_uses_a_strict_chunk_size_boundary() {
assert!(!dict_too_big(0));
assert!(!dict_too_big(ZSTD_CHUNKSIZE_MAX));
assert!(dict_too_big(ZSTD_CHUNKSIZE_MAX + 1));
assert_eq!(ZSTD_rust_dictTooBig(ZSTD_CHUNKSIZE_MAX), 0);
assert_eq!(ZSTD_rust_dictTooBig(ZSTD_CHUNKSIZE_MAX + 1), 1);
}
#[test]
fn public_one_shot_abi_is_c_compatible() {
let entry: unsafe extern "C" fn(*mut c_void, usize, *const c_void, usize, c_int) -> usize =