feat(compress): move window overflow arithmetic to Rust
The window-overflow helper mixed pointer arithmetic and ZSTD_window_t state
updates with the scalar U32 cycle calculation that determines the correction.
That made the arithmetic boundary depend on the host pointer implementation.
Add a narrow Rust ABI leaf that accepts only the current index, cycle log, and
maximum distance. Rust preserves the start-index adjustment, cycle alignment,
cycle-size versus max-distance selection, and explicit U32 wrapping. C derives
newCurrent from the returned correction so it can retain every invariant check,
pointer update, workspace/table transition, counter update, and dictionary
invalidation in the existing stateful wrapper.
Test Plan:
- Rust compression clippy, bench clippy, test clippy, nightly fmt, and the
repeated three clippy checks -- passed.
- `cargo test --manifest-path rust/Cargo.toml --no-default-features
--features compression` -- 340 passed.
- `make -C lib -j2 lib-mt` and `make -C lib -j2 lib-nomt` -- passed.
- `./tests/fuzzer -s4142 -t63 -i64 -v` -- focused overflow coverage passed.
- `make -C tests -j2 test-zstream` -- 84 deterministic, 5,018 standard
randomized, and 8,851 new-API randomized cases passed.
- `make -C tests -j2 test-fuzzer` reaches the pre-existing CCtx-reuse test 56
failure; a pristine 837e4f099 worktree reproduces it, while the focused
test-56 run passes.
This commit is contained in:
@@ -654,6 +654,7 @@ size_t ZSTD_rust_noCompressBlock(void* dst, size_t dstCapacity,
|
||||
const void* src, size_t srcSize, U32 lastBlock);
|
||||
size_t ZSTD_rust_rleCompressBlock(void* dst, size_t dstCapacity, BYTE src,
|
||||
size_t srcSize, U32 lastBlock);
|
||||
U32 ZSTD_rust_windowCorrectOverflow(U32 curr, U32 cycleLog, U32 maxDist);
|
||||
|
||||
|
||||
/* ZSTD_minGain() :
|
||||
@@ -1161,24 +1162,17 @@ U32 ZSTD_window_correctOverflow(ZSTD_window_t* window, U32 cycleLog,
|
||||
* 3. (cctx->lowLimit + 1<<windowLog) < 1<<32:
|
||||
* windowLog <= 31 ==> 3<<29 + 1<<windowLog < 7<<29 < 1<<32.
|
||||
*/
|
||||
U32 const cycleSize = 1u << cycleLog;
|
||||
U32 const cycleMask = cycleSize - 1;
|
||||
U32 const curr = (U32)((BYTE const*)src - window->base);
|
||||
U32 const currentCycle = curr & cycleMask;
|
||||
/* Ensure newCurrent - maxDist >= ZSTD_WINDOW_START_INDEX. */
|
||||
U32 const currentCycleCorrection = currentCycle < ZSTD_WINDOW_START_INDEX
|
||||
? MAX(cycleSize, ZSTD_WINDOW_START_INDEX)
|
||||
: 0;
|
||||
U32 const newCurrent = currentCycle
|
||||
+ currentCycleCorrection
|
||||
+ MAX(maxDist, cycleSize);
|
||||
U32 const correction = curr - newCurrent;
|
||||
U32 const correction = ZSTD_rust_windowCorrectOverflow(curr, cycleLog, maxDist);
|
||||
U32 const newCurrent = curr - correction;
|
||||
/* maxDist must be a power of two so that:
|
||||
* (newCurrent & cycleMask) == (curr & cycleMask)
|
||||
* (newCurrent & cycleMask) == (curr & cycleMask), where cycleMask is
|
||||
* (1 << cycleLog) - 1
|
||||
* This is required to not corrupt the chains / binary tree.
|
||||
*/
|
||||
assert((maxDist & (maxDist - 1)) == 0);
|
||||
assert((curr & cycleMask) == (newCurrent & cycleMask));
|
||||
assert((curr & ((1u << cycleLog) - 1)) ==
|
||||
(newCurrent & ((1u << cycleLog) - 1)));
|
||||
assert(curr > newCurrent);
|
||||
if (!ZSTD_WINDOW_OVERFLOW_CORRECT_FREQUENTLY) {
|
||||
/* Loose bound, should be around 1<<29 (see above) */
|
||||
@@ -1204,6 +1198,7 @@ U32 ZSTD_window_correctOverflow(ZSTD_window_t* window, U32 cycleLog,
|
||||
/* Ensure that lowLimit and dictLimit didn't underflow. */
|
||||
assert(window->lowLimit <= newCurrent);
|
||||
assert(window->dictLimit <= newCurrent);
|
||||
(void)newCurrent;
|
||||
|
||||
++window->nbOverflowCorrections;
|
||||
|
||||
|
||||
@@ -384,6 +384,32 @@ pub extern "C" fn ZSTD_rust_frameProgression(
|
||||
frame_progression(consumed_src_size, buffered, produced_c_size)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn window_correct_overflow(curr: u32, cycle_log: u32, max_dist: u32) -> u32 {
|
||||
let cycle_size = 1u32.wrapping_shl(cycle_log);
|
||||
let cycle_mask = cycle_size.wrapping_sub(1);
|
||||
let current_cycle = curr & cycle_mask;
|
||||
let current_cycle_correction = if current_cycle < ZSTD_WINDOW_START_INDEX {
|
||||
cycle_size.max(ZSTD_WINDOW_START_INDEX)
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let new_current = current_cycle
|
||||
.wrapping_add(current_cycle_correction)
|
||||
.wrapping_add(max_dist.max(cycle_size));
|
||||
curr.wrapping_sub(new_current)
|
||||
}
|
||||
|
||||
/// Return the scalar correction for C's window-overflow state transition.
|
||||
///
|
||||
/// C retains pointer arithmetic, invariant checks, and all `ZSTD_window_t`
|
||||
/// mutation. This leaf owns only the U32 cycle arithmetic so its wrapping
|
||||
/// behavior is explicit and independent of the host pointer width.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn ZSTD_rust_windowCorrectOverflow(curr: u32, cycle_log: u32, max_dist: u32) -> u32 {
|
||||
window_correct_overflow(curr, cycle_log, max_dist)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn next_input_size_hint(
|
||||
in_buffer_mode: c_int,
|
||||
@@ -1296,6 +1322,37 @@ mod tests {
|
||||
assert_eq!(ZSTD_rust_indexTooCloseToMax(threshold + 1), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn window_correction_handles_current_cycle_start_boundary() {
|
||||
assert_eq!(window_correct_overflow(0x100, 3, 8), 0xf0);
|
||||
assert_eq!(window_correct_overflow(0x101, 3, 8), 0xf0);
|
||||
assert_eq!(window_correct_overflow(0x102, 3, 8), 0xf8);
|
||||
|
||||
assert_eq!(
|
||||
ZSTD_rust_windowCorrectOverflow(0x100, 3, 8),
|
||||
window_correct_overflow(0x100, 3, 8)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn window_correction_selects_larger_of_cycle_and_max_distance() {
|
||||
assert_eq!(window_correct_overflow(0x100, 5, 8), 0xc0);
|
||||
assert_eq!(window_correct_overflow(0x200, 5, 64), 0x1a0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn window_correction_wraps_u32_intermediates() {
|
||||
assert_eq!(window_correct_overflow(0, 0, 1), u32::MAX - 2);
|
||||
assert_eq!(
|
||||
window_correct_overflow(0x8000_0000, 31, 0x8000_0000),
|
||||
0x8000_0000
|
||||
);
|
||||
assert_eq!(
|
||||
ZSTD_rust_windowCorrectOverflow(0x8000_0000, 31, 0x8000_0000),
|
||||
0x8000_0000
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dict_too_big_uses_a_strict_chunk_size_boundary() {
|
||||
assert!(!dict_too_big(0));
|
||||
|
||||
Reference in New Issue
Block a user