From 027ec50b165d2df05abebb380c0a4684f1288354 Mon Sep 17 00:00:00 2001 From: ddidderr Date: Sat, 18 Jul 2026 10:35:09 +0200 Subject: [PATCH] feat(compress): move end-flush input policy to Rust Keep ZSTD_CStream context access and caller-specific streaming behavior in C, while moving the pure stable-versus-buffered input-buffer policy to a scalar Rust ABI helper. The helper returns the public repr(C) buffer layout, preserves stable-mode source, size, and position values including NULL sources, and returns a zeroed buffer for every other mode. 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` -- 274 passed - `make -B -C lib -j2 lib` -- passed - `make -C tests test-rust-lib-smoke` -- passed - `make -B -C tests -j2 fuzzer` and `tests/fuzzer -s4560 -t56 -i57 -v` -- passed - `make -C tests -j2 test-zstream` -- 84 named tests and 7,400 plus 8,519 randomized cases passed - `git diff --check` and `git diff --cached --check` -- passed --- lib/compress/zstd_compress.c | 11 +++++--- rust/src/zstd_compress.rs | 52 +++++++++++++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 4746b3056..3b96358ab 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -90,6 +90,10 @@ size_t ZSTD_rust_CStreamInSize(void); size_t ZSTD_rust_CStreamOutSize(void); size_t ZSTD_rust_sizeofLocalDict(int dictBufferPresent, size_t dictSize, size_t cdictSize); +ZSTD_inBuffer ZSTD_rust_inBufferForEndFlush(int inBufferMode, + const void* expectedSrc, + size_t expectedSize, + size_t expectedPos); /* Context-free compression-parameter selection and sizing leaves live in * Rust (rust/src/zstd_compress_params.rs). This file retains @@ -5615,9 +5619,10 @@ ZSTD_compressSequencesAndLiterals(ZSTD_CCtx* cctx, static ZSTD_inBuffer inBuffer_forEndFlush(const ZSTD_CStream* zcs) { - const ZSTD_inBuffer nullInput = { NULL, 0, 0 }; - const int stableInput = (zcs->appliedParams.inBufferMode == ZSTD_bm_stable); - return stableInput ? zcs->expectedInBuffer : nullInput; + return ZSTD_rust_inBufferForEndFlush(zcs->appliedParams.inBufferMode, + zcs->expectedInBuffer.src, + zcs->expectedInBuffer.size, + zcs->expectedInBuffer.pos); } /*! ZSTD_flushStream() : diff --git a/rust/src/zstd_compress.rs b/rust/src/zstd_compress.rs index cf7dbfbf8..df4b3deb9 100644 --- a/rust/src/zstd_compress.rs +++ b/rust/src/zstd_compress.rs @@ -159,7 +159,6 @@ pub extern "C" fn ZSTD_rust_targetCBlockSizeAction( ) as c_int } -#[cfg(not(test))] #[repr(C)] pub struct ZSTD_inBuffer { src: *const c_void, @@ -175,6 +174,28 @@ pub struct ZSTD_outBuffer { pos: usize, } +#[no_mangle] +pub extern "C" fn ZSTD_rust_inBufferForEndFlush( + in_buffer_mode: c_int, + expected_src: *const c_void, + expected_size: usize, + expected_pos: usize, +) -> ZSTD_inBuffer { + if in_buffer_mode == ZSTD_BM_STABLE { + ZSTD_inBuffer { + src: expected_src, + size: expected_size, + pos: expected_pos, + } + } else { + ZSTD_inBuffer { + src: ptr::null(), + size: 0, + pos: 0, + } + } +} + /* HUF_WORKSPACE_SIZE + (MaxSeq + 2) * sizeof(unsigned), rounded up. The * superblock leaf also accepts the larger pre-split workspace, so a fixed * 16 KiB buffer is sufficient for this first non-splitting path on both @@ -1147,6 +1168,35 @@ mod tests { ); } + #[test] + fn in_buffer_for_end_flush_returns_stable_expected_buffer() { + let expected_src = b"input".as_ptr().cast::(); + let result = ZSTD_rust_inBufferForEndFlush(ZSTD_BM_STABLE, expected_src, 37, 11); + + assert_eq!(result.src, expected_src); + assert_eq!(result.size, 37); + assert_eq!(result.pos, 11); + + let null_result = ZSTD_rust_inBufferForEndFlush(ZSTD_BM_STABLE, ptr::null(), 37, 11); + + assert!(null_result.src.is_null()); + assert_eq!(null_result.size, 37); + assert_eq!(null_result.pos, 11); + } + + #[test] + fn in_buffer_for_end_flush_clears_buffered_and_other_modes() { + let expected_src = b"input".as_ptr().cast::(); + + for mode in [ZSTD_BM_BUFFERED, 42] { + let result = ZSTD_rust_inBufferForEndFlush(mode, expected_src, 37, 11); + + assert!(result.src.is_null()); + assert_eq!(result.size, 0); + assert_eq!(result.pos, 0); + } + } + #[test] fn invalidate_rep_codes_clears_all_entries() { let mut rep = [11u32, 22, 33];