From ad1ec27212576cec46edd745c8fe0b1cfa9fc24a Mon Sep 17 00:00:00 2001 From: ddidderr Date: Sun, 19 Jul 2026 12:29:14 +0200 Subject: [PATCH] feat(compress): move dictionary stream init policy into Rust Move ZSTD_initCStream_usingDict's public initialization policy into the Rust projection. Rust now preserves reset, compression-level, and dictionary-load ordering while C retains the private context and dictionary callbacks. Test Plan: - ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo test --manifest-path rust/Cargo.toml init_cstream_using_dict -- --test-threads=1 - ulimit -v 41943040; make -B -C programs -j1 zstd - git diff --cached --check --- lib/compress/zstd_compress.c | 46 +++++++++--- rust/src/zstd_compress.rs | 131 +++++++++++++++++++++++++++++++++++ 2 files changed, 168 insertions(+), 9 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index c1afcf28a..b4522ef1f 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -148,6 +148,25 @@ typedef char ZSTD_rust_init_cstream_state_layout[ && offsetof(ZSTD_rust_initCStreamState, setLevel) == 3 * sizeof(void*) && sizeof(ZSTD_rust_initCStreamState) == 4 * sizeof(void*)) ? 1 : -1]; +typedef size_t (*ZSTD_rust_initCStreamUsingDictLoadDictionary_f)( + void* context, const void* dict, size_t dictSize); +typedef struct { + void* callbackContext; + ZSTD_rust_initCStreamUsingCDictAdvancedReset_f resetSession; + ZSTD_rust_initCStreamSrcSizeSetLevel_f setLevel; + ZSTD_rust_initCStreamUsingDictLoadDictionary_f loadDictionary; +} ZSTD_rust_initCStreamUsingDictState; +size_t ZSTD_rust_initCStreamUsingDict( + const ZSTD_rust_initCStreamUsingDictState* state, + const void* dict, size_t dictSize, int compressionLevel); +typedef char ZSTD_rust_init_cstream_using_dict_state_layout[ + (offsetof(ZSTD_rust_initCStreamUsingDictState, callbackContext) == 0 + && offsetof(ZSTD_rust_initCStreamUsingDictState, resetSession) == sizeof(void*) + && offsetof(ZSTD_rust_initCStreamUsingDictState, setLevel) == 2 * sizeof(void*) + && offsetof(ZSTD_rust_initCStreamUsingDictState, loadDictionary) + == 3 * sizeof(void*) + && sizeof(ZSTD_rust_initCStreamUsingDictState) == 4 * sizeof(void*)) + ? 1 : -1]; ZSTD_frameProgression ZSTD_rust_frameProgression(U64 consumedSrcSize, size_t buffered, U64 producedCSize); @@ -4991,15 +5010,6 @@ size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs, return 0; } -size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel) -{ - DEBUGLOG(4, "ZSTD_initCStream_usingDict"); - FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) , ""); - FORWARD_IF_ERROR( ZSTD_CCtx_setParameter(zcs, ZSTD_c_compressionLevel, compressionLevel) , ""); - FORWARD_IF_ERROR( ZSTD_CCtx_loadDictionary(zcs, dict, dictSize) , ""); - return 0; -} - static size_t ZSTD_rust_initCStreamSrcSize_setLevel( void* context, int compressionLevel) { @@ -5007,6 +5017,24 @@ static size_t ZSTD_rust_initCStreamSrcSize_setLevel( (ZSTD_CCtx*)context, ZSTD_c_compressionLevel, compressionLevel); } +static size_t ZSTD_rust_initCStreamUsingDict_loadDictionary( + void* context, const void* dict, size_t dictSize) +{ + return ZSTD_CCtx_loadDictionary((ZSTD_CCtx*)context, dict, dictSize); +} + +size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs, const void* dict, size_t dictSize, int compressionLevel) +{ + ZSTD_rust_initCStreamUsingDictState state; + DEBUGLOG(4, "ZSTD_initCStream_usingDict"); + state.callbackContext = zcs; + state.resetSession = ZSTD_rust_initCStreamUsingCDictAdvanced_resetSession; + state.setLevel = ZSTD_rust_initCStreamSrcSize_setLevel; + state.loadDictionary = ZSTD_rust_initCStreamUsingDict_loadDictionary; + return ZSTD_rust_initCStreamUsingDict( + &state, dict, dictSize, compressionLevel); +} + size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs, int compressionLevel, unsigned long long pss) { ZSTD_rust_initCStreamSrcSizeState state; diff --git a/rust/src/zstd_compress.rs b/rust/src/zstd_compress.rs index 9ed9d0072..cf48d57f4 100644 --- a/rust/src/zstd_compress.rs +++ b/rust/src/zstd_compress.rs @@ -1086,6 +1086,55 @@ pub unsafe extern "C" fn ZSTD_rust_initCStream( 0 } +type InitCStreamUsingDictLoadDictionaryFn = + unsafe extern "C" fn(*mut c_void, *const c_void, usize) -> usize; + +/// Explicit projection for `ZSTD_initCStream_usingDict`. +#[repr(C)] +pub struct ZSTD_rust_initCStreamUsingDictState { + callback_context: *mut c_void, + reset_session: InitCStreamUsingCDictAdvancedResetFn, + set_level: InitCStreamSrcSizeSetLevelFn, + load_dictionary: InitCStreamUsingDictLoadDictionaryFn, +} + +const _: () = { + assert!(offset_of!(ZSTD_rust_initCStreamUsingDictState, callback_context) == 0); + assert!(offset_of!(ZSTD_rust_initCStreamUsingDictState, reset_session) == size_of::()); + assert!(offset_of!(ZSTD_rust_initCStreamUsingDictState, set_level) == 2 * size_of::()); + assert!( + offset_of!(ZSTD_rust_initCStreamUsingDictState, load_dictionary) == 3 * size_of::() + ); + assert!(size_of::() == 4 * size_of::()); +}; + +#[no_mangle] +pub unsafe extern "C" fn ZSTD_rust_initCStreamUsingDict( + state: *const ZSTD_rust_initCStreamUsingDictState, + dict: *const c_void, + dict_size: usize, + compression_level: c_int, +) -> usize { + if state.is_null() { + return ERROR(ZstdErrorCode::Generic); + } + let state = unsafe { &*state }; + + let result = unsafe { (state.reset_session)(state.callback_context) }; + 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.load_dictionary)(state.callback_context, dict, dict_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; @@ -8936,11 +8985,13 @@ mod tests { reset_result: usize, pledged_result: usize, set_level_result: usize, + load_dict_result: usize, ref_result: usize, pledged_src_size: u64, compression_level: c_int, frame_params: [c_uint; 3], cdict: *const c_void, + dict_size: usize, } unsafe fn init_cstream_using_cdict_advanced_test_context( @@ -8977,6 +9028,18 @@ mod tests { context.set_level_result } + unsafe extern "C" fn init_cstream_using_dict_test_load_dictionary( + context: *mut c_void, + dict: *const c_void, + dict_size: usize, + ) -> usize { + let context = unsafe { init_cstream_using_cdict_advanced_test_context(context) }; + context.events.push("load-dict"); + context.cdict = dict; + context.dict_size = dict_size; + context.load_dict_result + } + unsafe extern "C" fn init_cstream_using_cdict_advanced_test_set_frame_params( context: *mut c_void, content_size_flag: c_uint, @@ -9270,6 +9333,74 @@ mod tests { assert_eq!(context.events, ["reset", "ref-cdict", "level"]); } + fn init_cstream_using_dict_test_state( + context: &mut InitCStreamUsingCDictAdvancedTestContext, + ) -> ZSTD_rust_initCStreamUsingDictState { + ZSTD_rust_initCStreamUsingDictState { + callback_context: (context as *mut InitCStreamUsingCDictAdvancedTestContext).cast(), + reset_session: init_cstream_using_cdict_advanced_test_reset, + set_level: init_cstream_src_size_test_set_level, + load_dictionary: init_cstream_using_dict_test_load_dictionary, + } + } + + #[test] + fn init_cstream_using_dict_preserves_order_and_dictionary_arguments() { + let dict = ptr::dangling::(); + let mut context = InitCStreamUsingCDictAdvancedTestContext::default(); + let state = init_cstream_using_dict_test_state(&mut context); + + let result = unsafe { ZSTD_rust_initCStreamUsingDict(&state, dict, 123, -3) }; + + assert_eq!(result, 0); + assert_eq!(context.events, ["reset", "level", "load-dict"]); + assert_eq!(context.compression_level, -3); + assert_eq!(context.cdict, dict); + assert_eq!(context.dict_size, 123); + } + + #[test] + fn init_cstream_using_dict_stops_after_reset_error() { + let mut context = InitCStreamUsingCDictAdvancedTestContext { + reset_result: ERROR(ZstdErrorCode::MemoryAllocation), + ..InitCStreamUsingCDictAdvancedTestContext::default() + }; + let state = init_cstream_using_dict_test_state(&mut context); + + let result = unsafe { ZSTD_rust_initCStreamUsingDict(&state, ptr::null(), 0, 4) }; + + assert_eq!(result, ERROR(ZstdErrorCode::MemoryAllocation)); + assert_eq!(context.events, ["reset"]); + } + + #[test] + fn init_cstream_using_dict_stops_after_level_error() { + let mut context = InitCStreamUsingCDictAdvancedTestContext { + set_level_result: ERROR(ZstdErrorCode::ParameterOutOfBound), + ..InitCStreamUsingCDictAdvancedTestContext::default() + }; + let state = init_cstream_using_dict_test_state(&mut context); + + let result = unsafe { ZSTD_rust_initCStreamUsingDict(&state, ptr::null(), 0, 4) }; + + assert_eq!(result, ERROR(ZstdErrorCode::ParameterOutOfBound)); + assert_eq!(context.events, ["reset", "level"]); + } + + #[test] + fn init_cstream_using_dict_propagates_dictionary_error_last() { + let mut context = InitCStreamUsingCDictAdvancedTestContext { + load_dict_result: ERROR(ZstdErrorCode::DictionaryCreationFailed), + ..InitCStreamUsingCDictAdvancedTestContext::default() + }; + let state = init_cstream_using_dict_test_state(&mut context); + + let result = unsafe { ZSTD_rust_initCStreamUsingDict(&state, ptr::null(), 0, 4) }; + + assert_eq!(result, ERROR(ZstdErrorCode::DictionaryCreationFailed)); + assert_eq!(context.events, ["reset", "level", "load-dict"]); + } + #[test] fn pledged_src_size_writes_the_init_stage_value_plus_one() { let mut pledged_src_size_plus_one = 0;