From 6d4917e18786169e968fee0f34efc412cdb64a3c Mon Sep 17 00:00:00 2001 From: ddidderr Date: Sat, 18 Jul 2026 11:49:47 +0200 Subject: [PATCH] feat(compress): move workspace size aggregation to Rust Keep all context-sensitive workspace sizing in C, including intermediate allocation calculations, match-state and LDM sizing, error propagation, and diagnostics. Move only the final nine-component neededSpace aggregation behind a scalar Rust ABI helper so its size_t wrapping behavior is explicit and independently tested. Test Plan: - `cargo clippy --manifest-path rust/Cargo.toml --no-default-features --features compression` -- passed before and after formatting. - The same command with `--benches` and `--tests` -- passed before and after formatting. - `cargo +nightly fmt --manifest-path rust/Cargo.toml` -- passed. - `cargo test --manifest-path rust/Cargo.toml --no-default-features --features compression zstd_compress` -- 186 passed. - `make -B -C lib -j2 lib` -- passed. - `make -C tests test-rust-lib-smoke` -- passed. - `tests/fuzzer -s4560 -t56 -i57 -v` -- passed. - `make -C tests -j2 test-zstream` -- 84 named tests and 6,536 plus 7,961 randomized cases passed; the existing unterminated-string warning remains. - `git diff --cached --check` -- passed. --- lib/compress/zstd_compress.c | 22 ++++----- rust/src/zstd_compress.rs | 87 ++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 10 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index c82705fa3..f0063b65b 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -92,6 +92,15 @@ size_t ZSTD_rust_sizeofLocalDict(int dictBufferPresent, size_t dictSize, size_t cdictSize); size_t ZSTD_rust_sizeofCCtx(size_t objectSize, size_t workspaceSize, size_t localDictSize, size_t mtctxSize); +size_t ZSTD_rust_estimateWorkspaceSize(size_t cctxSpace, + size_t tmpWorkSpace, + size_t blockStateSpace, + size_t ldmSpace, + size_t ldmSeqSpace, + size_t matchStateSize, + size_t tokenSpace, + size_t bufferSpace, + size_t externalSeqSpace); ZSTD_inBuffer ZSTD_rust_inBufferForEndFlush(int inBufferMode, const void* expectedSrc, size_t expectedSize, @@ -1284,16 +1293,9 @@ static size_t ZSTD_estimateCCtxSize_usingCCtxParams_internal( ? ZSTD_cwksp_aligned64_alloc_size(maxNbExternalSeq * sizeof(ZSTD_Sequence)) : 0; - size_t const neededSpace = - cctxSpace + - tmpWorkSpace + - blockStateSpace + - ldmSpace + - ldmSeqSpace + - matchStateSize + - tokenSpace + - bufferSpace + - externalSeqSpace; + size_t const neededSpace = ZSTD_rust_estimateWorkspaceSize( + cctxSpace, tmpWorkSpace, blockStateSpace, ldmSpace, ldmSeqSpace, + matchStateSize, tokenSpace, bufferSpace, externalSeqSpace); DEBUGLOG(5, "estimate workspace : %u", (U32)neededSpace); return neededSpace; diff --git a/rust/src/zstd_compress.rs b/rust/src/zstd_compress.rs index bf179e099..6ea0884c4 100644 --- a/rust/src/zstd_compress.rs +++ b/rust/src/zstd_compress.rs @@ -455,6 +455,55 @@ pub extern "C" fn ZSTD_rust_sizeofCCtx( sizeof_cctx(object_size, workspace_size, local_dict_size, mtctx_size) } +#[inline] +fn estimate_workspace_size( + cctx_space: usize, + tmp_work_space: usize, + block_state_space: usize, + ldm_space: usize, + ldm_seq_space: usize, + match_state_size: usize, + token_space: usize, + buffer_space: usize, + external_seq_space: usize, +) -> usize { + cctx_space + .wrapping_add(tmp_work_space) + .wrapping_add(block_state_space) + .wrapping_add(ldm_space) + .wrapping_add(ldm_seq_space) + .wrapping_add(match_state_size) + .wrapping_add(token_space) + .wrapping_add(buffer_space) + .wrapping_add(external_seq_space) +} + +/// Aggregate workspace-size components with C `size_t` wrapping semantics. +#[no_mangle] +pub extern "C" fn ZSTD_rust_estimateWorkspaceSize( + cctx_space: usize, + tmp_work_space: usize, + block_state_space: usize, + ldm_space: usize, + ldm_seq_space: usize, + match_state_size: usize, + token_space: usize, + buffer_space: usize, + external_seq_space: usize, +) -> usize { + estimate_workspace_size( + cctx_space, + tmp_work_space, + block_state_space, + ldm_space, + ldm_seq_space, + match_state_size, + token_space, + buffer_space, + external_seq_space, + ) +} + #[inline] fn reduce_table_internal(table: &mut [u32], reducer_value: u32, preserve_mark: bool) { debug_assert_eq!(table.len() % ZSTD_ROWSIZE, 0); @@ -1452,6 +1501,44 @@ mod tests { assert_eq!(ZSTD_rust_sizeofCCtx(usize::MAX, 1, 2, 3), 5); } + #[test] + fn estimate_workspace_size_handles_zero_components() { + assert_eq!(estimate_workspace_size(0, 0, 0, 0, 0, 0, 0, 0, 0), 0); + assert_eq!( + ZSTD_rust_estimateWorkspaceSize(0, 0, 0, 0, 0, 0, 0, 0, 0), + 0 + ); + } + + #[test] + fn estimate_workspace_size_adds_components_in_order() { + assert_eq!(estimate_workspace_size(1, 2, 3, 4, 5, 6, 7, 8, 9), 45); + assert_eq!( + ZSTD_rust_estimateWorkspaceSize(1, 2, 3, 4, 5, 6, 7, 8, 9), + 45 + ); + } + + #[test] + fn estimate_workspace_size_wraps_at_multiple_operand_positions() { + assert_eq!( + estimate_workspace_size(usize::MAX, 1, 0, 0, 0, 0, 0, 0, 0), + 0 + ); + assert_eq!( + estimate_workspace_size(0, usize::MAX, 1, 0, 0, 0, 0, 0, 0), + 0 + ); + assert_eq!( + estimate_workspace_size(0, 0, 0, 0, 0, usize::MAX, 1, 0, 0), + 0 + ); + assert_eq!( + ZSTD_rust_estimateWorkspaceSize(0, 0, 0, 0, 0, 0, 0, usize::MAX, 1), + 0 + ); + } + #[test] fn public_one_shot_abi_is_c_compatible() { let entry: unsafe extern "C" fn(*mut c_void, usize, *const c_void, usize, c_int) -> usize =