From c057bcbbdf536c95ebd89d4d13abfda05203c492 Mon Sep 17 00:00:00 2001 From: ddidderr Date: Sat, 18 Jul 2026 17:23:33 +0200 Subject: [PATCH] feat(compress): move window clearing leaf into Rust Keep the private ZSTD_window_t layout and pointer-difference calculation in C, then route the size_t-to-U32 conversion and paired limit writes through a small Rust ABI leaf. This removes the inline C window-clear implementation while preserving its overflow behavior at both single-threaded and multithreaded call sites. Test Plan: - cargo test --manifest-path rust/Cargo.toml --lib zstd_compress::tests (77 passed) - make -C programs -j2 zstd - rustfmt +nightly --edition 2021 rust/src/zstd_compress.rs --check - make -C tests -j2 test-cli-tests (41 passed) - make -C tests -j2 test-legacy test-invalidDictionaries test-decodecorpus test-rust-lib-smoke (passed) - git diff --check --- lib/compress/zstd_compress.c | 9 ++++-- lib/compress/zstd_compress_internal.h | 14 +-------- lib/compress/zstdmt_compress.c | 5 +++- rust/src/zstd_compress.rs | 42 +++++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 16 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 69382709d..2623f027e 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1486,7 +1486,8 @@ void ZSTD_reset_compressedBlockState(ZSTD_compressedBlockState_t* bs) */ static void ZSTD_invalidateMatchState(ZSTD_MatchState_t* ms) { - ZSTD_window_clear(&ms->window); + ZSTD_rust_windowClear((size_t)(ms->window.nextSrc - ms->window.base), + &ms->window.lowLimit, &ms->window.dictLimit); ms->nextToUpdate = ms->window.dictLimit; ms->loadedDictEnd = 0; @@ -1893,7 +1894,11 @@ ZSTD_resetCCtx_byAttachingCDict(ZSTD_CCtx* cctx, if (cctx->blockState.matchState.window.dictLimit < cdictEnd) { cctx->blockState.matchState.window.nextSrc = cctx->blockState.matchState.window.base + cdictEnd; - ZSTD_window_clear(&cctx->blockState.matchState.window); + ZSTD_rust_windowClear( + (size_t)(cctx->blockState.matchState.window.nextSrc - + cctx->blockState.matchState.window.base), + &cctx->blockState.matchState.window.lowLimit, + &cctx->blockState.matchState.window.dictLimit); } /* loadedDictEnd is expressed within the referential of the active context */ cctx->blockState.matchState.loadedDictEnd = cctx->blockState.matchState.window.dictLimit; diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index e60b4f5db..93b6d4525 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -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); +void ZSTD_rust_windowClear(size_t endT, U32* lowLimit, U32* dictLimit); U32 ZSTD_rust_windowCorrectOverflow(U32 curr, U32 cycleLog, U32 maxDist); U32 ZSTD_rust_windowCanOverflowCorrect(U32 curr, U32 cycleLog, U32 maxDist, U32 loadedDictEnd, @@ -1025,19 +1026,6 @@ MEM_STATIC U64 ZSTD_rollingHash_rotate(U64 hash, BYTE toRemove, BYTE toAdd, U64 ( ((U32)-1) /* Maximum ending current index */ \ - ZSTD_CURRENT_MAX) /* Maximum beginning lowLimit */ -/** - * ZSTD_window_clear(): - * Clears the window containing the history by simply setting it to empty. - */ -MEM_STATIC void ZSTD_window_clear(ZSTD_window_t* window) -{ - size_t const endT = (size_t)(window->nextSrc - window->base); - U32 const end = (U32)endT; - - window->lowLimit = end; - window->dictLimit = end; -} - MEM_STATIC U32 ZSTD_window_isEmpty(ZSTD_window_t const window) { return window.dictLimit == ZSTD_WINDOW_START_INDEX && diff --git a/lib/compress/zstdmt_compress.c b/lib/compress/zstdmt_compress.c index 29b6e4b01..cbfbb2e95 100644 --- a/lib/compress/zstdmt_compress.c +++ b/lib/compress/zstdmt_compress.c @@ -620,7 +620,10 @@ static void ZSTDMT_serialState_ensureFinished(SerialState* serialState, ZSTD_pthread_cond_broadcast(&serialState->cond); ZSTD_PTHREAD_MUTEX_LOCK(&serialState->ldmWindowMutex); - ZSTD_window_clear(&serialState->ldmWindow); + ZSTD_rust_windowClear((size_t)(serialState->ldmWindow.nextSrc - + serialState->ldmWindow.base), + &serialState->ldmWindow.lowLimit, + &serialState->ldmWindow.dictLimit); ZSTD_pthread_cond_signal(&serialState->ldmWindowCond); ZSTD_pthread_mutex_unlock(&serialState->ldmWindowMutex); } diff --git a/rust/src/zstd_compress.rs b/rust/src/zstd_compress.rs index cc554cccc..a53fd4d07 100644 --- a/rust/src/zstd_compress.rs +++ b/rust/src/zstd_compress.rs @@ -859,6 +859,24 @@ pub unsafe extern "C" fn ZSTD_rust_invalidateRepCodes(rep: *mut u32) { rep.fill(0); } +/// Clear a C-owned compression window using the original `size_t`-to-`U32` +/// conversion. The caller computes the pointer difference while the private +/// `ZSTD_window_t` layout remains entirely on the C side. +#[no_mangle] +pub unsafe extern "C" fn ZSTD_rust_windowClear( + end_t: usize, + low_limit: *mut u32, + dict_limit: *mut u32, +) { + debug_assert!(!low_limit.is_null()); + debug_assert!(!dict_limit.is_null()); + let end = end_t as u32; + unsafe { + *low_limit = end; + *dict_limit = end; + } +} + #[inline] fn zeroed_state() -> ZSTD_compressedBlockState_t { /* The state contains only integer arrays and enum fields. */ @@ -2146,6 +2164,30 @@ mod tests { assert_eq!(rep, [0; ZSTD_REP_NUM]); } + #[test] + fn window_clear_writes_the_same_end_to_both_limits() { + let mut low_limit = 11u32; + let mut dict_limit = 22u32; + + unsafe { ZSTD_rust_windowClear(0x1234_5678, &mut low_limit, &mut dict_limit) }; + + assert_eq!(low_limit, 0x1234_5678); + assert_eq!(dict_limit, 0x1234_5678); + } + + #[cfg(target_pointer_width = "64")] + #[test] + fn window_clear_truncates_a_large_size_t_like_c_u32_cast() { + let mut low_limit = 11u32; + let mut dict_limit = 22u32; + let end_t = 0x1_0000_0000usize + 0x89ab_cdef; + + unsafe { ZSTD_rust_windowClear(end_t, &mut low_limit, &mut dict_limit) }; + + assert_eq!(low_limit, 0x89ab_cdef); + assert_eq!(dict_limit, 0x89ab_cdef); + } + #[test] fn sizeof_local_dict_ignores_size_without_a_buffer() { assert_eq!(sizeof_local_dict(0, 37, 11), 11);