feat(compress): move overflow policy arithmetic to Rust

The window overflow gate mixed two scalar decisions with C-owned pointer and
window state. The early correction test uses the block-start index and must
wait for dictionary invalidation, while the normal fallback uses the block-end
index and the platform-selected ZSTD_CURRENT_MAX threshold. Keeping those
subtractions and the ZSTD_window_t wrapper in C preserves the existing call
contract without making Rust depend on pointer width or private state.

Move the U32 cycle/MAX arithmetic, correction-count scaling, dictionary gate,
and frequent-policy decision into Rust. C passes the two pointer-derived
indices only through scalar results, and supplies the current-max threshold
and active build policy explicitly so 32/64-bit and fuzzing configurations
remain authoritative.

Test Plan:
- `cargo test --manifest-path rust/Cargo.toml --no-default-features
  --features compression` -- 348 passed.
- Compression clippy for the library, benches, and tests, followed by
  nightly fmt and the repeated three clippy checks -- passed.
- `make -C lib -j2 lib-mt` and `make -C lib -j2 lib-nomt` -- passed.
- `make -C tests -j2 fuzzer` and `./tests/fuzzer -s4142 -t63 -i64 -v` --
  passed all 64 cases.
- `make -C tests -j2 test-zstream` -- passed 84 deterministic, 5,477 first
  randomized, and 6,902 new-API randomized cases; the existing unterminated
  string initializer warning remains.
This commit is contained in:
2026-07-18 15:30:19 +02:00
parent 23450d8c3b
commit 293cf96892
2 changed files with 142 additions and 27 deletions
+126
View File
@@ -410,6 +410,76 @@ pub extern "C" fn ZSTD_rust_windowCorrectOverflow(curr: u32, cycle_log: u32, max
window_correct_overflow(curr, cycle_log, max_dist)
}
#[inline]
fn window_can_overflow_correct(
curr: u32,
cycle_log: u32,
max_dist: u32,
loaded_dict_end: u32,
nb_overflow_corrections: u32,
) -> bool {
let cycle_size = 1u32.wrapping_shl(cycle_log);
let min_index_to_overflow_correct = cycle_size
.wrapping_add(max_dist.max(cycle_size))
.wrapping_add(ZSTD_WINDOW_START_INDEX);
let adjustment = nb_overflow_corrections.wrapping_add(1);
let adjusted_index = min_index_to_overflow_correct
.wrapping_mul(adjustment)
.max(min_index_to_overflow_correct);
let index_large_enough = curr > adjusted_index;
let dictionary_invalidated = curr > max_dist.wrapping_add(loaded_dict_end);
index_large_enough && dictionary_invalidated
}
/// Return whether the C-owned window state should enter overflow correction.
///
/// C retains both pointer subtractions and the `ZSTD_window_t` state. The
/// early-correction result is supplied as a scalar because it is based on the
/// block-start index, while `curr` is the block-end index used by the normal
/// current-limit fallback. The current limit and fuzzing policy are supplied
/// by C so their 32/64-bit and build-mode choices remain authoritative.
#[inline]
fn window_need_overflow_correction(
curr: u32,
can_overflow_correct: bool,
current_max: u32,
overflow_correct_frequently: bool,
) -> bool {
(overflow_correct_frequently && can_overflow_correct) || curr > current_max
}
#[no_mangle]
pub extern "C" fn ZSTD_rust_windowCanOverflowCorrect(
curr: u32,
cycle_log: u32,
max_dist: u32,
loaded_dict_end: u32,
nb_overflow_corrections: u32,
) -> u32 {
window_can_overflow_correct(
curr,
cycle_log,
max_dist,
loaded_dict_end,
nb_overflow_corrections,
) as u32
}
#[no_mangle]
pub extern "C" fn ZSTD_rust_windowNeedOverflowCorrection(
curr: u32,
can_overflow_correct: u32,
current_max: u32,
overflow_correct_frequently: c_int,
) -> u32 {
window_need_overflow_correction(
curr,
can_overflow_correct != 0,
current_max,
overflow_correct_frequently != 0,
) as u32
}
#[inline]
fn next_input_size_hint(
in_buffer_mode: c_int,
@@ -1440,6 +1510,62 @@ mod tests {
);
}
#[test]
fn window_overflow_need_uses_the_explicit_current_max_boundary() {
assert!(!window_need_overflow_correction(1024, false, 1024, false));
assert!(window_need_overflow_correction(1025, false, 1024, false));
assert_eq!(ZSTD_rust_windowNeedOverflowCorrection(1024, 1, 1024, 0), 0);
assert_eq!(ZSTD_rust_windowNeedOverflowCorrection(1025, 0, 1024, 0), 1);
}
#[test]
fn window_overflow_need_honors_frequent_dictionary_policy() {
let can_correct = window_can_overflow_correct(27, 3, 16, 0, 0);
let dictionary_still_valid = window_can_overflow_correct(27, 3, 16, 20, 0);
assert!(can_correct);
assert!(!dictionary_still_valid);
assert!(window_need_overflow_correction(27, can_correct, 1024, true));
assert!(!window_need_overflow_correction(
27,
dictionary_still_valid,
1024,
true
));
assert_eq!(
ZSTD_rust_windowNeedOverflowCorrection(27, can_correct as u32, 1024, 1),
1
);
assert_eq!(
ZSTD_rust_windowNeedOverflowCorrection(27, dictionary_still_valid as u32, 1024, 1,),
0
);
assert!(!window_need_overflow_correction(27, true, 1024, false));
}
#[test]
fn window_overflow_need_scales_the_frequency_with_correction_count() {
assert!(window_can_overflow_correct(27, 3, 16, 0, 0));
assert!(!window_can_overflow_correct(27, 3, 16, 0, 1));
assert!(window_can_overflow_correct(53, 3, 16, 0, 1));
assert_eq!(ZSTD_rust_windowCanOverflowCorrect(53, 3, 16, 0, 1), 1);
}
#[test]
fn window_overflow_need_preserves_u32_wrap_boundaries() {
assert!(window_can_overflow_correct(
3,
31,
0x8000_0000,
0x8000_0000,
0,
));
assert!(window_can_overflow_correct(27, 3, 16, 0, u32::MAX,));
assert_eq!(
ZSTD_rust_windowCanOverflowCorrect(3, 31, 0x8000_0000, 0x8000_0000, 0),
1
);
}
#[test]
fn dict_too_big_uses_a_strict_chunk_size_boundary() {
assert!(!dict_too_big(0));