From 66cb2545324985c880b463aa1ee4989b4c64ec17 Mon Sep 17 00:00:00 2001 From: ddidderr Date: Sat, 18 Jul 2026 15:39:37 +0200 Subject: [PATCH] feat(ldm): move limit-table scalar leaf to Rust Keep the LDM limit-table update on the Rust compression path while leaving match-state ownership in C. Previously, the C block wrapper combined pointer subtraction, match-state access, and the bounded scalar update. The wrapper now computes `curr` from `anchor - window.base`, passes `curr` and `nextToUpdate` through the narrow U32 ABI, and stores Rust's result before the existing fast-table dispatch. The Rust leaf uses explicit wrapping arithmetic to preserve the C U32 behavior: the strict `curr > nextToUpdate + 1024` threshold and the `MIN(512, ...)` clamp. Focused tests cover the threshold, one-step update, clamp, nonzero starting point, and arithmetic wraparound. Test Plan: - `cargo test zstd_ldm` -- default-feature test-binary link failed because existing dict-builder C symbols are not linked. - `cargo test --no-default-features --features compression zstd_ldm` -- passed (7 tests). - `make lib-nomt` -- passed. - `make lib-mt` -- passed. - `make -C tests test-zstream` -- passed; it emitted the existing `tests/zstreamtest.c` unterminated-string warning. - `cargo clippy`, `cargo clippy --benches`, `cargo clippy --tests`, `cargo +nightly fmt`, then the same clippy sequence -- passed. --- lib/compress/zstd_ldm.c | 13 ++++--------- rust/src/zstd_ldm.rs | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/lib/compress/zstd_ldm.c b/lib/compress/zstd_ldm.c index c7e1d558c..1c859f589 100644 --- a/lib/compress/zstd_ldm.c +++ b/lib/compress/zstd_ldm.c @@ -185,6 +185,7 @@ void ZSTD_rust_ldm_skipRawSeqStoreBytes(void* rawSeqStore, size_t nbBytes); size_t ZSTD_rust_ldm_blockCompress( void* rawSeqStore, void* blockContext, void* seqStore, U32 rep[ZSTD_REP_NUM], const void* src, size_t srcSize, U32 minMatch, int useOptimalParser); +U32 ZSTD_rust_ldm_limitTableUpdate(U32 curr, U32 nextToUpdate); const U64* ZSTD_ldm_rust_gearTable(void); void ZSTD_ldm_rust_prepareBlock(void* blockContext, const void* anchor); @@ -206,14 +207,6 @@ typedef struct { ZSTD_BlockCompressor_f blockCompressor; } ZSTD_rust_ldm_block_context; -static void ZSTD_rust_ldm_limitTableUpdate(ZSTD_MatchState_t* ms, const BYTE* anchor) -{ - U32 const curr = (U32)(anchor - ms->window.base); - if (curr > ms->nextToUpdate + 1024) { - ms->nextToUpdate = curr - MIN(512, curr - ms->nextToUpdate - 1024); - } -} - static void ZSTD_rust_ldm_fillFastTables(ZSTD_MatchState_t* ms, const BYTE* end) { switch (ms->cParams.strategy) { @@ -243,7 +236,9 @@ static void ZSTD_rust_ldm_fillFastTables(ZSTD_MatchState_t* ms, const BYTE* end) void ZSTD_ldm_rust_prepareBlock(void* blockContext, const void* anchor) { ZSTD_rust_ldm_block_context* const context = (ZSTD_rust_ldm_block_context*)blockContext; - ZSTD_rust_ldm_limitTableUpdate(context->ms, (const BYTE*)anchor); + U32 const curr = (U32)((const BYTE*)anchor - context->ms->window.base); + context->ms->nextToUpdate = ZSTD_rust_ldm_limitTableUpdate( + curr, context->ms->nextToUpdate); ZSTD_rust_ldm_fillFastTables(context->ms, (const BYTE*)anchor); } diff --git a/rust/src/zstd_ldm.rs b/rust/src/zstd_ldm.rs index 3edade89d..34a12b5d7 100644 --- a/rust/src/zstd_ldm.rs +++ b/rust/src/zstd_ldm.rs @@ -187,6 +187,16 @@ fn bounded(lower: u32, value: u32, upper: u32) -> u32 { value.max(lower).min(upper) } +/// Return the next match-table update point using the C scalar rule. +#[no_mangle] +pub extern "C" fn ZSTD_rust_ldm_limitTableUpdate(curr: u32, next_to_update: u32) -> u32 { + if curr > next_to_update.wrapping_add(1024) { + curr.wrapping_sub(512u32.min(curr.wrapping_sub(next_to_update).wrapping_sub(1024))) + } else { + next_to_update + } +} + #[inline] unsafe fn ldm_bucket(hash_table: *mut LdmEntry, hash: u32, bucket_size_log: u32) -> *mut LdmEntry { unsafe { hash_table.add((hash as usize) << bucket_size_log) } @@ -1007,6 +1017,31 @@ pub unsafe extern "C" fn ZSTD_rust_ldm_blockCompress( mod tests { use super::*; + #[test] + fn limit_table_update_keeps_threshold_strict() { + assert_eq!(ZSTD_rust_ldm_limitTableUpdate(1024, 0), 0); + } + + #[test] + fn limit_table_update_moves_one_step_past_threshold() { + assert_eq!(ZSTD_rust_ldm_limitTableUpdate(1025, 0), 1024); + } + + #[test] + fn limit_table_update_clamps_to_512() { + assert_eq!(ZSTD_rust_ldm_limitTableUpdate(2048, 0), 1536); + } + + #[test] + fn limit_table_update_preserves_nonzero_next_to_update() { + assert_eq!(ZSTD_rust_ldm_limitTableUpdate(2000, 500), 1524); + } + + #[test] + fn limit_table_update_wraps_u32_arithmetic() { + assert_eq!(ZSTD_rust_ldm_limitTableUpdate(1000, u32::MAX - 511), 512); + } + #[test] fn parameter_defaults_follow_the_c_rules() { let mut params = LdmParams {