From a895506e785115bc2329ce9cb338a4eface6bee5 Mon Sep 17 00:00:00 2001 From: ddidderr Date: Sat, 18 Jul 2026 10:46:03 +0200 Subject: [PATCH] feat(compress): move C parameter assertion to Rust Keep ZSTD_assertEqualCParams as a C-local wrapper so both existing callers and surrounding control flow remain unchanged, but move its seven-field invariant to the Rust compression-parameter module. The ABI helper takes both repr(C) parameter structs by value, compares each named field explicitly with debug_assert_eq!, and remains exported in release builds so the C linkage is stable while the checks compile out like C assert under NDEBUG. Test Plan: - `cargo clippy --manifest-path rust/Cargo.toml --no-default-features --features compression` -- passed before and after formatting. - The same clippy 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_params` -- 36 passed. - `make -B -C lib -j2 lib` -- passed. - `make -C tests test-rust-lib-smoke` -- passed. - `tests/fuzzer -s4560 -t56 -i57 -v` -- passed. - `make -B -C tests -j2 test-zstream` -- 84 named and 15,504 randomized cases passed; the existing unterminated-string warning remains. - `git diff --check` and `git diff --cached --check` -- passed. --- lib/compress/zstd_compress.c | 12 ++----- rust/src/zstd_compress_params.rs | 58 ++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 9 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 3b96358ab..49c878679 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -107,6 +107,8 @@ int ZSTD_rust_params_minCLevel(void); int ZSTD_rust_params_defaultCLevel(void); ZSTD_bounds ZSTD_rust_params_getBounds(int param); size_t ZSTD_rust_params_checkCParams(ZSTD_compressionParameters cParams); +void ZSTD_rust_params_assertEqualCParams(ZSTD_compressionParameters cParams1, + ZSTD_compressionParameters cParams2); ZSTD_compressionParameters ZSTD_rust_params_clampCParams(ZSTD_compressionParameters cParams); U32 ZSTD_rust_params_cycleLog(U32 hashLog, int strategy); @@ -1442,15 +1444,7 @@ size_t ZSTD_toFlushNow(ZSTD_CCtx* cctx) static void ZSTD_assertEqualCParams(ZSTD_compressionParameters cParams1, ZSTD_compressionParameters cParams2) { - (void)cParams1; - (void)cParams2; - assert(cParams1.windowLog == cParams2.windowLog); - assert(cParams1.chainLog == cParams2.chainLog); - assert(cParams1.hashLog == cParams2.hashLog); - assert(cParams1.searchLog == cParams2.searchLog); - assert(cParams1.minMatch == cParams2.minMatch); - assert(cParams1.targetLength == cParams2.targetLength); - assert(cParams1.strategy == cParams2.strategy); + ZSTD_rust_params_assertEqualCParams(cParams1, cParams2); } void ZSTD_reset_compressedBlockState(ZSTD_compressedBlockState_t* bs) diff --git a/rust/src/zstd_compress_params.rs b/rust/src/zstd_compress_params.rs index 29aa190b8..ff789a786 100644 --- a/rust/src/zstd_compress_params.rs +++ b/rust/src/zstd_compress_params.rs @@ -773,6 +773,20 @@ fn check_cparams(cparams: ZSTD_compressionParameters) -> usize { } } +#[inline] +fn assert_equal_cparams( + cparams1: ZSTD_compressionParameters, + cparams2: ZSTD_compressionParameters, +) { + debug_assert_eq!(cparams1.windowLog, cparams2.windowLog); + debug_assert_eq!(cparams1.chainLog, cparams2.chainLog); + debug_assert_eq!(cparams1.hashLog, cparams2.hashLog); + debug_assert_eq!(cparams1.searchLog, cparams2.searchLog); + debug_assert_eq!(cparams1.minMatch, cparams2.minMatch); + debug_assert_eq!(cparams1.targetLength, cparams2.targetLength); + debug_assert_eq!(cparams1.strategy, cparams2.strategy); +} + /// Returns the highest table-backed compression level (`ZSTD_MAX_CLEVEL`). #[no_mangle] pub extern "C" fn ZSTD_rust_params_maxCLevel() -> c_int { @@ -804,6 +818,18 @@ pub extern "C" fn ZSTD_rust_params_checkCParams(cparams: ZSTD_compressionParamet check_cparams(cparams) } +/// C ABI for the debug-only equality invariant in `ZSTD_assertEqualCParams`. +/// +/// Keep the symbol exported in release builds so the C ABI remains stable; +/// `debug_assert_eq!` compiles the checks out there, matching C's `assert()`. +#[no_mangle] +pub extern "C" fn ZSTD_rust_params_assertEqualCParams( + cparams1: ZSTD_compressionParameters, + cparams2: ZSTD_compressionParameters, +) { + assert_equal_cparams(cparams1, cparams2); +} + /// Clamps the seven fields of `ZSTD_compressionParameters` to public bounds. #[no_mangle] pub extern "C" fn ZSTD_rust_params_clampCParams( @@ -1737,6 +1763,38 @@ mod tests { assert!(ERR_isError(ZSTD_rust_params_getBounds(-1).error)); } + #[test] + fn equal_cparams_pass_the_debug_invariant() { + let cparams = ZSTD_compressionParameters { + windowLog: 21, + chainLog: 18, + hashLog: 19, + searchLog: 4, + minMatch: 5, + targetLength: 16, + strategy: ZSTD_LAZY2, + }; + ZSTD_rust_params_assertEqualCParams(cparams, cparams); + } + + #[cfg(debug_assertions)] + #[test] + #[should_panic] + fn mismatched_cparams_panic_only_with_debug_assertions() { + let cparams = ZSTD_compressionParameters { + windowLog: 21, + chainLog: 18, + hashLog: 19, + searchLog: 4, + minMatch: 5, + targetLength: 16, + strategy: ZSTD_LAZY2, + }; + let mut mismatched = cparams; + mismatched.strategy = ZSTD_BTLAZY2; + assert_equal_cparams(cparams, mismatched); + } + #[test] fn params_default_frame_flags_match_c() { let cparams = select_cparams(5, ZSTD_CONTENTSIZE_UNKNOWN, 0, ZSTD_RUST_CPM_UNKNOWN);