From 1aab4489432644c1eead6d0f4342ff6618d5fbbf Mon Sep 17 00:00:00 2001 From: ddidderr Date: Sun, 19 Jul 2026 12:13:17 +0200 Subject: [PATCH] feat(compress): move src-size stream init policy into Rust Move ZSTD_initCStream_srcSize's public initialization policy into the Rust projection. Rust now normalizes the pledged source size and preserves the reset, dictionary-clear, compression-level, and pledge ordering while C retains the private context callbacks. Test Plan: - ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo test --manifest-path rust/Cargo.toml init_cstream_src_size -- --test-threads=1 - ulimit -v 41943040; make -B -C programs -j1 zstd - git diff --cached --check --- lib/compress/zstd_compress.c | 41 ++++++++-- rust/src/zstd_compress.rs | 153 +++++++++++++++++++++++++++++++++++ 2 files changed, 188 insertions(+), 6 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index ca8dcc7aa..674f2f60d 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -112,6 +112,27 @@ typedef char ZSTD_rust_init_cstream_using_cdict_state_layout[ && offsetof(ZSTD_rust_initCStreamUsingCDictState, refCDict) == 2 * sizeof(void*) && sizeof(ZSTD_rust_initCStreamUsingCDictState) == 3 * sizeof(void*)) ? 1 : -1]; +typedef size_t (*ZSTD_rust_initCStreamSrcSizeSetLevel_f)( + void* context, int compressionLevel); +typedef struct { + void* callbackContext; + ZSTD_rust_initCStreamUsingCDictAdvancedReset_f resetSession; + ZSTD_rust_initCStreamUsingCDictAdvancedRefCDict_f refCDict; + ZSTD_rust_initCStreamSrcSizeSetLevel_f setLevel; + ZSTD_rust_initCStreamUsingCDictAdvancedSetPledgedSrcSize_f setPledgedSrcSize; +} ZSTD_rust_initCStreamSrcSizeState; +size_t ZSTD_rust_initCStreamSrcSize( + const ZSTD_rust_initCStreamSrcSizeState* state, + unsigned long long pss, int compressionLevel); +typedef char ZSTD_rust_init_cstream_src_size_state_layout[ + (offsetof(ZSTD_rust_initCStreamSrcSizeState, callbackContext) == 0 + && offsetof(ZSTD_rust_initCStreamSrcSizeState, resetSession) == sizeof(void*) + && offsetof(ZSTD_rust_initCStreamSrcSizeState, refCDict) == 2 * sizeof(void*) + && offsetof(ZSTD_rust_initCStreamSrcSizeState, setLevel) == 3 * sizeof(void*) + && offsetof(ZSTD_rust_initCStreamSrcSizeState, setPledgedSrcSize) + == 4 * sizeof(void*) + && sizeof(ZSTD_rust_initCStreamSrcSizeState) == 5 * sizeof(void*)) + ? 1 : -1]; ZSTD_frameProgression ZSTD_rust_frameProgression(U64 consumedSrcSize, size_t buffered, U64 producedCSize); @@ -4964,19 +4985,27 @@ size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t di return 0; } +static size_t ZSTD_rust_initCStreamSrcSize_setLevel( + void* context, int compressionLevel) +{ + return ZSTD_CCtx_setParameter( + (ZSTD_CCtx*)context, ZSTD_c_compressionLevel, compressionLevel); +} + size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pss) { + ZSTD_rust_initCStreamSrcSizeState state; /* temporary : 0 interpreted as "unknown" during transition period. * Users willing to specify "unknown" **must** use ZSTD_CONTENTSIZE_UNKNOWN. * 0 will be interpreted as "empty" in the future. */ - U64 const pledgedSrcSize = (pss==0) ? ZSTD_CONTENTSIZE_UNKNOWN : pss; DEBUGLOG(4, "ZSTD_initCStream_srcSize"); - FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) , ""); - FORWARD_IF_ERROR( ZSTD_CCtx_refCDict(zcs, NULL) , ""); - FORWARD_IF_ERROR( ZSTD_CCtx_setParameter(zcs, ZSTD_c_compressionLevel, compressionLevel) , ""); - FORWARD_IF_ERROR( ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize) , ""); - return 0; + state.callbackContext = zcs; + state.resetSession = ZSTD_rust_initCStreamUsingCDictAdvanced_resetSession; + state.refCDict = ZSTD_rust_initCStreamUsingCDictAdvanced_refCDict; + state.setLevel = ZSTD_rust_initCStreamSrcSize_setLevel; + state.setPledgedSrcSize = ZSTD_rust_initCStreamUsingCDictAdvanced_setPledgedSrcSize; + return ZSTD_rust_initCStreamSrcSize(&state, pss, compressionLevel); } size_t ZSTD_initCStream(ZSTD_CStream* zcs, int compressionLevel) diff --git a/rust/src/zstd_compress.rs b/rust/src/zstd_compress.rs index a9db8c68f..e4600d89e 100644 --- a/rust/src/zstd_compress.rs +++ b/rust/src/zstd_compress.rs @@ -985,6 +985,65 @@ pub unsafe extern "C" fn ZSTD_rust_initCStreamUsingCDict( 0 } +type InitCStreamSrcSizeSetLevelFn = unsafe extern "C" fn(*mut c_void, c_int) -> usize; + +/// Explicit projection for `ZSTD_initCStream_srcSize`. +#[repr(C)] +pub struct ZSTD_rust_initCStreamSrcSizeState { + callback_context: *mut c_void, + reset_session: InitCStreamUsingCDictAdvancedResetFn, + ref_cdict: InitCStreamUsingCDictAdvancedRefCDictFn, + set_level: InitCStreamSrcSizeSetLevelFn, + set_pledged_src_size: InitCStreamUsingCDictAdvancedSetPledgedSrcSizeFn, +} + +const _: () = { + assert!(offset_of!(ZSTD_rust_initCStreamSrcSizeState, callback_context) == 0); + assert!(offset_of!(ZSTD_rust_initCStreamSrcSizeState, reset_session) == size_of::()); + assert!(offset_of!(ZSTD_rust_initCStreamSrcSizeState, ref_cdict) == 2 * size_of::()); + assert!(offset_of!(ZSTD_rust_initCStreamSrcSizeState, set_level) == 3 * size_of::()); + assert!( + offset_of!(ZSTD_rust_initCStreamSrcSizeState, set_pledged_src_size) + == 4 * size_of::() + ); + assert!(size_of::() == 5 * size_of::()); +}; + +#[no_mangle] +pub unsafe extern "C" fn ZSTD_rust_initCStreamSrcSize( + state: *const ZSTD_rust_initCStreamSrcSizeState, + pss: u64, + compression_level: c_int, +) -> usize { + if state.is_null() { + return ERROR(ZstdErrorCode::Generic); + } + let state = unsafe { &*state }; + let pledged_src_size = if pss == 0 { + ZSTD_CONTENTSIZE_UNKNOWN + } else { + pss + }; + + let result = unsafe { (state.reset_session)(state.callback_context) }; + if ERR_isError(result) { + return result; + } + let result = unsafe { (state.ref_cdict)(state.callback_context, ptr::null()) }; + if ERR_isError(result) { + return result; + } + let result = unsafe { (state.set_level)(state.callback_context, compression_level) }; + if ERR_isError(result) { + return result; + } + let result = unsafe { (state.set_pledged_src_size)(state.callback_context, pledged_src_size) }; + if ERR_isError(result) { + return result; + } + 0 +} + type CompressStreamBlockFn = unsafe extern "C" fn(*mut c_void, *mut c_void, usize, *const c_void, usize) -> usize; type CompressStreamResetFn = unsafe extern "C" fn(*mut c_void) -> usize; @@ -8834,8 +8893,10 @@ mod tests { events: Vec<&'static str>, reset_result: usize, pledged_result: usize, + set_level_result: usize, ref_result: usize, pledged_src_size: u64, + compression_level: c_int, frame_params: [c_uint; 3], cdict: *const c_void, } @@ -8864,6 +8925,16 @@ mod tests { context.pledged_result } + unsafe extern "C" fn init_cstream_src_size_test_set_level( + context: *mut c_void, + compression_level: c_int, + ) -> usize { + let context = unsafe { init_cstream_using_cdict_advanced_test_context(context) }; + context.events.push("level"); + context.compression_level = compression_level; + context.set_level_result + } + unsafe extern "C" fn init_cstream_using_cdict_advanced_test_set_frame_params( context: *mut c_void, content_size_flag: c_uint, @@ -9009,6 +9080,88 @@ mod tests { assert_eq!(context.events, ["reset", "ref-cdict"]); } + fn init_cstream_src_size_test_state( + context: &mut InitCStreamUsingCDictAdvancedTestContext, + ) -> ZSTD_rust_initCStreamSrcSizeState { + ZSTD_rust_initCStreamSrcSizeState { + callback_context: (context as *mut InitCStreamUsingCDictAdvancedTestContext).cast(), + reset_session: init_cstream_using_cdict_advanced_test_reset, + ref_cdict: init_cstream_using_cdict_advanced_test_ref_cdict, + set_level: init_cstream_src_size_test_set_level, + set_pledged_src_size: init_cstream_using_cdict_advanced_test_set_pledged, + } + } + + #[test] + fn init_cstream_src_size_preserves_order_and_normalizes_unknown_pledge() { + let mut context = InitCStreamUsingCDictAdvancedTestContext::default(); + let state = init_cstream_src_size_test_state(&mut context); + + let result = unsafe { ZSTD_rust_initCStreamSrcSize(&state, 0, -3) }; + + assert_eq!(result, 0); + assert_eq!(context.events, ["reset", "ref-cdict", "level", "pledged"]); + assert_eq!(context.compression_level, -3); + assert_eq!(context.pledged_src_size, ZSTD_CONTENTSIZE_UNKNOWN); + assert_eq!(context.cdict, ptr::null()); + } + + #[test] + fn init_cstream_src_size_stops_after_reset_error() { + let mut context = InitCStreamUsingCDictAdvancedTestContext { + reset_result: ERROR(ZstdErrorCode::MemoryAllocation), + ..InitCStreamUsingCDictAdvancedTestContext::default() + }; + let state = init_cstream_src_size_test_state(&mut context); + + let result = unsafe { ZSTD_rust_initCStreamSrcSize(&state, 77, 4) }; + + assert_eq!(result, ERROR(ZstdErrorCode::MemoryAllocation)); + assert_eq!(context.events, ["reset"]); + } + + #[test] + fn init_cstream_src_size_stops_after_cdict_clear_error() { + let mut context = InitCStreamUsingCDictAdvancedTestContext { + ref_result: ERROR(ZstdErrorCode::DictionaryCreationFailed), + ..InitCStreamUsingCDictAdvancedTestContext::default() + }; + let state = init_cstream_src_size_test_state(&mut context); + + let result = unsafe { ZSTD_rust_initCStreamSrcSize(&state, 77, 4) }; + + assert_eq!(result, ERROR(ZstdErrorCode::DictionaryCreationFailed)); + assert_eq!(context.events, ["reset", "ref-cdict"]); + } + + #[test] + fn init_cstream_src_size_stops_after_level_error() { + let mut context = InitCStreamUsingCDictAdvancedTestContext { + set_level_result: ERROR(ZstdErrorCode::ParameterOutOfBound), + ..InitCStreamUsingCDictAdvancedTestContext::default() + }; + let state = init_cstream_src_size_test_state(&mut context); + + let result = unsafe { ZSTD_rust_initCStreamSrcSize(&state, 77, 4) }; + + assert_eq!(result, ERROR(ZstdErrorCode::ParameterOutOfBound)); + assert_eq!(context.events, ["reset", "ref-cdict", "level"]); + } + + #[test] + fn init_cstream_src_size_propagates_pledge_error_last() { + let mut context = InitCStreamUsingCDictAdvancedTestContext { + pledged_result: ERROR(ZstdErrorCode::StageWrong), + ..InitCStreamUsingCDictAdvancedTestContext::default() + }; + let state = init_cstream_src_size_test_state(&mut context); + + let result = unsafe { ZSTD_rust_initCStreamSrcSize(&state, 77, 4) }; + + assert_eq!(result, ERROR(ZstdErrorCode::StageWrong)); + assert_eq!(context.events, ["reset", "ref-cdict", "level", "pledged"]); + } + #[test] fn pledged_src_size_writes_the_init_stage_value_plus_one() { let mut pledged_src_size_plus_one = 0;