feat(compress): move match index reduction orchestration to Rust

ZSTD_reduceIndex previously selected and reduced each match table through
separate C helpers, even though the 16-cell reduction leaf already lived in
Rust. Keep C responsible for match-state access, table allocation policy,
nullable table selection, btlazy2 strategy selection, and the surrounding
workspace/window/dictionary state transition. Add one narrow Rust ABI that
receives only the validated hash, chain, and hash3 table slices and delegates
to the existing reducer, preserving chain markers only when requested and
keeping threshold and U32 wrapping arithmetic unchanged. Focused tests cover
zero-sized optional tables, marker routing, and threshold behavior.

Test Plan:
- `cargo test --manifest-path rust/Cargo.toml --no-default-features --features compression zstd_compress::tests::reduce_` -- 5 passed.
- `cargo test --manifest-path rust/Cargo.toml --no-default-features --features compression` -- 344 passed.
- `make -C lib -j2 lib-mt` and `make -C lib -j2 lib-nomt` -- passed.
- `make -C tests -j2 test-zstream` -- 84 named, 6,814 standard
  randomized, and 8,955 new-API randomized cases passed.
- Required compression clippy library/benches/tests, nightly fmt, and the repeated three clippy checks -- passed.
This commit is contained in:
2026-07-18 15:21:04 +02:00
parent a9b11394ce
commit 841806dcce
2 changed files with 108 additions and 30 deletions
+89 -2
View File
@@ -708,8 +708,8 @@ fn reduce_table_internal(table: &mut [u32], reducer_value: u32, preserve_mark: b
/// Rust implementation of the C match-table reduction leaf.
///
/// The C wrappers select the ordinary or btlazy2 policy by passing a clear
/// zero/one `preserve_mark` value; `ZSTD_reduceIndex` remains C-owned.
/// The C caller supplies a clear zero/one `preserve_mark` value for the
/// strategy-specific btlazy2 policy.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_reduceTable(
table: *mut u32,
@@ -728,6 +728,35 @@ pub unsafe extern "C" fn ZSTD_rust_reduceTable(
reduce_table_internal(table, reducer_value, preserve_mark != 0);
}
/// Reduce the match tables selected by C's stateful overflow-correction path.
///
/// C retains match-state access and table selection. Each non-zero size is
/// paired with a validated mutable table pointer; zero-sized optional tables
/// may use null pointers. Only the chain table receives the btlazy2 marker
/// policy.
#[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_reduceIndex(
hash_table: *mut u32,
hash_size: u32,
chain_table: *mut u32,
chain_size: u32,
hash_table3: *mut u32,
hash_size3: u32,
reducer_value: u32,
preserve_chain_mark: c_int,
) {
debug_assert!(!hash_table.is_null() || hash_size == 0);
debug_assert!(!chain_table.is_null() || chain_size == 0);
debug_assert!(!hash_table3.is_null() || hash_size3 == 0);
debug_assert!(preserve_chain_mark == 0 || preserve_chain_mark == 1);
unsafe {
ZSTD_rust_reduceTable(hash_table, hash_size, reducer_value, 0);
ZSTD_rust_reduceTable(chain_table, chain_size, reducer_value, preserve_chain_mark);
ZSTD_rust_reduceTable(hash_table3, hash_size3, reducer_value, 0);
}
}
/// Copies a CDict match table into a CCtx, removing short-cache tags when the
/// C-owned compression parameters say the source table is tagged.
#[no_mangle]
@@ -1266,6 +1295,64 @@ mod tests {
assert_eq!(table[ZSTD_ROWSIZE * 2 - 1], u32::MAX - 4);
}
#[test]
fn reduce_index_accepts_zero_sized_optional_tables() {
let mut hash_table = [0u32; ZSTD_ROWSIZE];
hash_table[..4].copy_from_slice(&[1, 2, 5, u32::MAX]);
unsafe {
ZSTD_rust_reduceIndex(
hash_table.as_mut_ptr(),
hash_table.len() as u32,
ptr::null_mut(),
0,
ptr::null_mut(),
0,
3,
0,
);
ZSTD_rust_reduceIndex(
ptr::null_mut(),
0,
ptr::null_mut(),
0,
ptr::null_mut(),
0,
u32::MAX,
1,
);
}
assert_eq!(&hash_table[..4], &[0, 0, 2, u32::MAX - 3]);
}
#[test]
fn reduce_index_preserves_the_marker_only_in_the_chain_table() {
let mut hash_table = [0u32; ZSTD_ROWSIZE];
let mut chain_table = [0u32; ZSTD_ROWSIZE];
let mut hash_table3 = [0u32; ZSTD_ROWSIZE];
hash_table[0] = ZSTD_DUBT_UNSORTED_MARK;
chain_table[0] = ZSTD_DUBT_UNSORTED_MARK;
hash_table3[0] = ZSTD_DUBT_UNSORTED_MARK;
unsafe {
ZSTD_rust_reduceIndex(
hash_table.as_mut_ptr(),
hash_table.len() as u32,
chain_table.as_mut_ptr(),
chain_table.len() as u32,
hash_table3.as_mut_ptr(),
hash_table3.len() as u32,
3,
1,
);
}
assert_eq!(hash_table[0], 0);
assert_eq!(chain_table[0], ZSTD_DUBT_UNSORTED_MARK);
assert_eq!(hash_table3[0], 0);
}
#[test]
fn copy_cdict_table_removes_short_cache_tags() {
let source = [0x1234_56ff, 0xdead_beef, 0x0000_0100, u32::MAX];