From ff57c578dbc2957991c2d5e995e4a00d4f2417e2 Mon Sep 17 00:00:00 2001 From: ddidderr Date: Sun, 19 Jul 2026 19:58:18 +0200 Subject: [PATCH] feat(compress): move window lifecycle predicates into Rust Move shared window initialization and the empty-window and external-dictionary predicates behind the Rust compression seam. Rust now owns the projected five-field lifecycle state, while C retains the containing window and its overflow-correction counter. Test Plan: - ulimit -v 41943040; CARGO_BUILD_JOBS=1; cargo test --manifest-path rust/Cargo.toml - ulimit -v 41943040; CARGO_BUILD_JOBS=1; cargo clippy --manifest-path rust/Cargo.toml --all-targets -- -D warnings - ulimit -v 41943040; make -B -C programs -j1 zstd - ulimit -v 41943040; make -C tests -j1 test-zstream ZSTREAM_TESTTIME=-T1s --- lib/compress/zstd_compress_internal.h | 25 +++++----- rust/src/zstd_compress.rs | 66 +++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 11 deletions(-) diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 2142dc12c..38ec19a6c 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -669,6 +669,10 @@ typedef char ZSTD_rust_window_update_state_layout[ && offsetof(ZSTD_rust_windowUpdateState, dictLimit) == 3 * sizeof(void*) && offsetof(ZSTD_rust_windowUpdateState, lowLimit) == 3 * sizeof(void*) + sizeof(U32) && sizeof(ZSTD_rust_windowUpdateState) == 3 * sizeof(void*) + 2 * sizeof(U32)) ? 1 : -1]; +void ZSTD_rust_windowInit(ZSTD_rust_windowUpdateState* state); +U32 ZSTD_rust_windowIsEmpty(const void* nextSrc, const void* base, + U32 dictLimit, U32 lowLimit); +U32 ZSTD_rust_windowHasExtDict(U32 dictLimit, U32 lowLimit); U32 ZSTD_rust_windowUpdate(ZSTD_rust_windowUpdateState* state, const void* src, size_t srcSize, int forceNonContiguous); @@ -1046,9 +1050,8 @@ MEM_STATIC U64 ZSTD_rollingHash_rotate(U64 hash, BYTE toRemove, BYTE toAdd, U64 MEM_STATIC U32 ZSTD_window_isEmpty(ZSTD_window_t const window) { - return window.dictLimit == ZSTD_WINDOW_START_INDEX && - window.lowLimit == ZSTD_WINDOW_START_INDEX && - (window.nextSrc - window.base) == ZSTD_WINDOW_START_INDEX; + return ZSTD_rust_windowIsEmpty(window.nextSrc, window.base, + window.dictLimit, window.lowLimit); } /** @@ -1057,7 +1060,7 @@ MEM_STATIC U32 ZSTD_window_isEmpty(ZSTD_window_t const window) */ MEM_STATIC U32 ZSTD_window_hasExtDict(ZSTD_window_t const window) { - return window.lowLimit < window.dictLimit; + return ZSTD_rust_windowHasExtDict(window.dictLimit, window.lowLimit); } /** @@ -1305,13 +1308,13 @@ ZSTD_checkDictValidity(const ZSTD_window_t* window, } MEM_STATIC void ZSTD_window_init(ZSTD_window_t* window) { - ZSTD_memset(window, 0, sizeof(*window)); - window->base = (BYTE const*)" "; - window->dictBase = (BYTE const*)" "; - ZSTD_STATIC_ASSERT(ZSTD_DUBT_UNSORTED_MARK < ZSTD_WINDOW_START_INDEX); /* Start above ZSTD_DUBT_UNSORTED_MARK */ - window->dictLimit = ZSTD_WINDOW_START_INDEX; /* start from >0, so that 1st position is valid */ - window->lowLimit = ZSTD_WINDOW_START_INDEX; /* it ensures first and later CCtx usages compress the same */ - window->nextSrc = window->base + ZSTD_WINDOW_START_INDEX; /* see issue #1241 */ + ZSTD_rust_windowUpdateState state; + ZSTD_rust_windowInit(&state); + window->nextSrc = (BYTE const*)state.nextSrc; + window->base = (BYTE const*)state.base; + window->dictBase = (BYTE const*)state.dictBase; + window->dictLimit = state.dictLimit; + window->lowLimit = state.lowLimit; window->nbOverflowCorrections = 0; } diff --git a/rust/src/zstd_compress.rs b/rust/src/zstd_compress.rs index 4739e0b1e..cb6292d8d 100644 --- a/rust/src/zstd_compress.rs +++ b/rust/src/zstd_compress.rs @@ -6818,6 +6818,45 @@ const _: () = { ); }; +static WINDOW_INIT_SENTINEL: [u8; 2] = [b' ', 0]; + +/// Initialize the projected window fields. C retains the containing object +/// and its overflow-correction counter. +#[no_mangle] +pub unsafe extern "C" fn ZSTD_rust_windowInit(state: *mut ZSTD_rust_windowUpdateState) { + if state.is_null() { + return; + } + let base = WINDOW_INIT_SENTINEL.as_ptr(); + unsafe { + *state = ZSTD_rust_windowUpdateState { + nextSrc: base.wrapping_add(2).cast(), + base: base.cast(), + dictBase: base.cast(), + dictLimit: ZSTD_WINDOW_START_INDEX, + lowLimit: ZSTD_WINDOW_START_INDEX, + }; + } +} + +#[no_mangle] +pub extern "C" fn ZSTD_rust_windowIsEmpty( + next_src: *const c_void, + base: *const c_void, + dict_limit: u32, + low_limit: u32, +) -> c_uint { + (dict_limit == ZSTD_WINDOW_START_INDEX + && low_limit == ZSTD_WINDOW_START_INDEX + && (next_src as usize).wrapping_sub(base as usize) == ZSTD_WINDOW_START_INDEX as usize) + as c_uint +} + +#[no_mangle] +pub extern "C" fn ZSTD_rust_windowHasExtDict(dict_limit: u32, low_limit: u32) -> c_uint { + (low_limit < dict_limit) as c_uint +} + /// Update the rolling prefix/ext-dictionary window for one input segment. /// C owns the containing match state; Rust owns this five-field transition. #[no_mangle] @@ -12608,6 +12647,33 @@ mod tests { assert_eq!(state, original); } + #[test] + fn window_init_sets_empty_sentinel_and_dictionary_predicates() { + let mut state = ZSTD_rust_windowUpdateState { + nextSrc: ptr::null(), + base: ptr::null(), + dictBase: ptr::null(), + dictLimit: 0, + lowLimit: 0, + }; + + unsafe { ZSTD_rust_windowInit(&mut state) }; + + assert_eq!(state.dictLimit, ZSTD_WINDOW_START_INDEX); + assert_eq!(state.lowLimit, ZSTD_WINDOW_START_INDEX); + assert_eq!(state.dictBase as usize, state.base as usize); + assert_eq!( + (state.nextSrc as usize).wrapping_sub(state.base as usize), + ZSTD_WINDOW_START_INDEX as usize + ); + assert_eq!( + ZSTD_rust_windowIsEmpty(state.nextSrc, state.base, state.dictLimit, state.lowLimit), + 1 + ); + assert_eq!(ZSTD_rust_windowHasExtDict(10, 2), 1); + assert_eq!(ZSTD_rust_windowHasExtDict(10, 10), 0); + } + #[test] fn window_update_preserves_contiguous_input_and_clips_overlapping_extdict() { let storage = [0u8; 64];