From 4c114b8e7bcbaca9e3a30a2a90873e279e12d6f6 Mon Sep 17 00:00:00 2001 From: ddidderr Date: Sat, 18 Jul 2026 13:50:36 +0200 Subject: [PATCH] feat(compress): return frame progression from Rust Complete the single-thread branch of ZSTD_getFrameProgression behind a narrow scalar ABI boundary. C still owns the public entry point, buffered input extraction, assertions, and the multithreaded dispatch; Rust now owns the six-field single-thread result construction. The Rust #[repr(C)] result uses wrapping u64 conversion for consumed input plus buffered size, mirrors produced into flushed, and zeros the MT-only fields. This replaces the earlier ingested-only helper so the ABI no longer splits one result across duplicate C and Rust construction paths. The unit tests now cover the complete result and its u64 wrapping behavior. Test Plan: - All three requested compression clippy commands passed before and after `cargo +nightly fmt --manifest-path rust/Cargo.toml`. - `cargo test --manifest-path rust/Cargo.toml --no-default-features --features compression` -- 327 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` -- passed: 84 named, 5,330, and 9,772 randomized cases. - `git diff --check` and `git diff --cached --check` -- passed. The zstream build retains the pre-existing warning at `tests/zstreamtest.c:1899` about an unterminated initializer string. --- lib/compress/zstd_compress.c | 17 ++++---- rust/src/zstd_compress.rs | 81 ++++++++++++++++++++++++------------ 2 files changed, 61 insertions(+), 37 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 238b98308..c9a85e6cc 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -48,7 +48,9 @@ int ZSTD_rust_updateFrameProgression(unsigned long long* consumedSrcSize, unsigned long long pledgedSrcSizePlusOne, size_t srcSize, size_t cSize, size_t fhSize); -U64 ZSTD_rust_frameProgressionIngested(U64 consumedSrcSize, size_t buffered); +ZSTD_frameProgression ZSTD_rust_frameProgression(U64 consumedSrcSize, + size_t buffered, + U64 producedCSize); size_t ZSTD_rust_resetCCtxForSimpleCompression(void* cctx); size_t ZSTD_rust_prepareCCtxForSimpleCompression(void* cctx, size_t srcSize, @@ -1436,19 +1438,14 @@ ZSTD_frameProgression ZSTD_getFrameProgression(const ZSTD_CCtx* cctx) return ZSTDMT_getFrameProgression(cctx->mtctx); } #endif - { ZSTD_frameProgression fp; + { size_t const buffered = (cctx->inBuff == NULL) ? 0 : cctx->inBuffPos - cctx->inToCompress; if (buffered) assert(cctx->inBuffPos >= cctx->inToCompress); assert(buffered <= ZSTD_BLOCKSIZE_MAX); - fp.ingested = ZSTD_rust_frameProgressionIngested(cctx->consumedSrcSize, buffered); - fp.consumed = cctx->consumedSrcSize; - fp.produced = cctx->producedCSize; - fp.flushed = cctx->producedCSize; /* simplified; some data might still be left within streaming output buffer */ - fp.currentJobID = 0; - fp.nbActiveWorkers = 0; - return fp; -} } + return ZSTD_rust_frameProgression(cctx->consumedSrcSize, buffered, + cctx->producedCSize); + } } /*! ZSTD_toFlushNow() * Only useful for multithreading scenarios currently (nbWorkers >= 1). diff --git a/rust/src/zstd_compress.rs b/rust/src/zstd_compress.rs index d282938af..a2f1a6e1c 100644 --- a/rust/src/zstd_compress.rs +++ b/rust/src/zstd_compress.rs @@ -29,7 +29,7 @@ use crate::zstd_compress_stats::{SeqStore_t, ZSTD_compressedBlockState_t}; use crate::zstd_compress_superblock::ZSTD_rust_compressSuperBlock; use std::ffi::c_void; use std::mem::{size_of, MaybeUninit}; -use std::os::raw::c_int; +use std::os::raw::{c_int, c_uint}; use std::ptr; #[cfg(not(test))] @@ -345,19 +345,43 @@ pub unsafe extern "C" fn ZSTD_rust_updateFrameProgression( ) as c_int } -#[inline] -fn frame_progression_ingested(consumed_src_size: u64, buffered: usize) -> u64 { - // C's usual arithmetic conversions promote size_t to U64 before wrapping. - consumed_src_size.wrapping_add(buffered as u64) +/// ABI-compatible representation of `ZSTD_frameProgression` from `zstd.h`. +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub struct ZSTD_frameProgression { + pub ingested: u64, + pub consumed: u64, + pub produced: u64, + pub flushed: u64, + pub currentJobID: c_uint, + pub nbActiveWorkers: c_uint, } -/// Calculate single-threaded frame input progression from consumed and buffered input. -#[no_mangle] -pub extern "C" fn ZSTD_rust_frameProgressionIngested( +#[inline] +fn frame_progression( consumed_src_size: u64, buffered: usize, -) -> u64 { - frame_progression_ingested(consumed_src_size, buffered) + produced_c_size: u64, +) -> ZSTD_frameProgression { + ZSTD_frameProgression { + // C's usual arithmetic conversions promote size_t to U64 before wrapping. + ingested: consumed_src_size.wrapping_add(buffered as u64), + consumed: consumed_src_size, + produced: produced_c_size, + flushed: produced_c_size, + currentJobID: 0, + nbActiveWorkers: 0, + } +} + +/// Construct single-threaded frame progression from C-owned scalar state. +#[no_mangle] +pub extern "C" fn ZSTD_rust_frameProgression( + consumed_src_size: u64, + buffered: usize, + produced_c_size: u64, +) -> ZSTD_frameProgression { + frame_progression(consumed_src_size, buffered, produced_c_size) } #[inline] @@ -1435,35 +1459,38 @@ mod tests { } #[test] - fn frame_progression_ingested_handles_zero() { - assert_eq!(frame_progression_ingested(0, 0), 0); - assert_eq!(ZSTD_rust_frameProgressionIngested(0, 0), 0); - } - - #[test] - fn frame_progression_ingested_adds_consumed_and_buffered_input() { + fn frame_progression_constructs_single_thread_values() { let consumed = 11_u64; let buffered = 37_usize; - let expected = consumed + buffered as u64; + let produced = 53_u64; + let expected = ZSTD_frameProgression { + ingested: consumed + buffered as u64, + consumed, + produced, + flushed: produced, + currentJobID: 0, + nbActiveWorkers: 0, + }; - assert_eq!(frame_progression_ingested(consumed, buffered), expected); + assert_eq!(frame_progression(consumed, buffered, produced), expected); assert_eq!( - ZSTD_rust_frameProgressionIngested(consumed, buffered), + ZSTD_rust_frameProgression(consumed, buffered, produced), expected ); } #[test] - fn frame_progression_ingested_wraps_u64() { + fn frame_progression_wraps_buffered_input_to_u64() { let consumed = u64::MAX - 3; let buffered = 8_usize; - let expected = consumed.wrapping_add(buffered as u64); + let progression = ZSTD_rust_frameProgression(consumed, buffered, 17); - assert_eq!(frame_progression_ingested(consumed, buffered), expected); - assert_eq!( - ZSTD_rust_frameProgressionIngested(consumed, buffered), - expected - ); + assert_eq!(progression.ingested, consumed.wrapping_add(buffered as u64)); + assert_eq!(progression.consumed, consumed); + assert_eq!(progression.produced, 17); + assert_eq!(progression.flushed, 17); + assert_eq!(progression.currentJobID, 0); + assert_eq!(progression.nbActiveWorkers, 0); } #[test]