diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 537c60b4f..83fb932e7 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1392,6 +1392,24 @@ typedef char ZSTD_rust_compress_using_cdict_state_layout[ == 3 * sizeof(void*) && sizeof(ZSTD_rust_compressUsingCDictState) == 4 * sizeof(void*)) ? 1 : -1]; +typedef size_t (*ZSTD_rust_compressBeginUsingCDictPublicBegin_f)( + void* context, const void* cdict, + const ZSTD_frameParameters* fParams, U64 pledgedSrcSize); +typedef struct { + void* callbackContext; + const void* cdict; + ZSTD_rust_compressBeginUsingCDictPublicBegin_f begin; +} ZSTD_rust_compressBeginUsingCDictPublicState; +size_t ZSTD_rust_compressBeginUsingCDictPublic( + const ZSTD_rust_compressBeginUsingCDictPublicState* state); +typedef char ZSTD_rust_compress_begin_using_cdict_public_state_layout[ + (offsetof(ZSTD_rust_compressBeginUsingCDictPublicState, callbackContext) == 0 + && offsetof(ZSTD_rust_compressBeginUsingCDictPublicState, cdict) + == sizeof(void*) + && offsetof(ZSTD_rust_compressBeginUsingCDictPublicState, begin) + == 2 * sizeof(void*) + && sizeof(ZSTD_rust_compressBeginUsingCDictPublicState) == 3 * sizeof(void*)) + ? 1 : -1]; typedef void (*ZSTD_rust_compressBeginUsingDictInitParams_f)( void* cctxParams, const ZSTD_parameters* params, int compressionLevel); typedef size_t (*ZSTD_rust_compressBeginUsingDictBegin_f)( @@ -5591,6 +5609,15 @@ static size_t ZSTD_compressBegin_usingCDict_internal( return ZSTD_rust_compressBeginUsingCDict(&state); } +static size_t ZSTD_rust_compressBeginUsingCDictPublic_begin( + void* context, const void* cdict, + const ZSTD_frameParameters* fParams, U64 pledgedSrcSize) +{ + return ZSTD_compressBegin_usingCDict_internal( + (ZSTD_CCtx*)context, (const ZSTD_CDict*)cdict, + *fParams, pledgedSrcSize); +} + /* ZSTD_compressBegin_usingCDict_advanced() : * This function is DEPRECATED. @@ -5606,8 +5633,11 @@ size_t ZSTD_compressBegin_usingCDict_advanced( * cdict must be != NULL */ size_t ZSTD_compressBegin_usingCDict_deprecated(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict) { - ZSTD_frameParameters const fParams = { 0 /*content*/, 0 /*checksum*/, 0 /*noDictID*/ }; - return ZSTD_compressBegin_usingCDict_internal(cctx, cdict, fParams, ZSTD_CONTENTSIZE_UNKNOWN); + ZSTD_rust_compressBeginUsingCDictPublicState state; + state.callbackContext = cctx; + state.cdict = cdict; + state.begin = ZSTD_rust_compressBeginUsingCDictPublic_begin; + return ZSTD_rust_compressBeginUsingCDictPublic(&state); } size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict) diff --git a/rust/src/zstd_compress_dictionary.rs b/rust/src/zstd_compress_dictionary.rs index fca4d6e21..934b9a021 100644 --- a/rust/src/zstd_compress_dictionary.rs +++ b/rust/src/zstd_compress_dictionary.rs @@ -299,6 +299,65 @@ pub unsafe extern "C" fn ZSTD_rust_compressUsingCDict( unsafe { (state.end)(state.callback_context, dst, dst_capacity, src, src_size) } } +type CompressBeginUsingCDictPublicBeginFn = + unsafe extern "C" fn(*mut c_void, *const c_void, *const ZSTD_frameParameters, u64) -> usize; + +/// Explicit projection for the public `ZSTD_compressBegin_usingCDict` wrapper. +/// +/// Rust owns the fixed frame policy and unknown-source pledge. C retains the +/// private CDict begin path behind a callback. +#[repr(C)] +pub struct ZSTD_rust_compressBeginUsingCDictPublicState { + callback_context: *mut c_void, + cdict: *const c_void, + begin: CompressBeginUsingCDictPublicBeginFn, +} + +const _: () = { + assert!( + offset_of!( + ZSTD_rust_compressBeginUsingCDictPublicState, + callback_context + ) == 0 + ); + assert!(offset_of!(ZSTD_rust_compressBeginUsingCDictPublicState, cdict) == size_of::()); + assert!( + offset_of!(ZSTD_rust_compressBeginUsingCDictPublicState, begin) == 2 * size_of::() + ); + assert!(size_of::() == size_of::<[usize; 3]>()); +}; + +/// Start a CDict frame with the legacy fixed frame policy. +#[no_mangle] +pub unsafe extern "C" fn ZSTD_rust_compressBeginUsingCDictPublic( + state: *const ZSTD_rust_compressBeginUsingCDictPublicState, +) -> usize { + if state.is_null() { + return ERROR(ZstdErrorCode::Generic); + } + let state = unsafe { &*state }; + if state.cdict.is_null() { + return ERROR(ZstdErrorCode::DictionaryWrong); + } + if state.callback_context.is_null() { + return ERROR(ZstdErrorCode::Generic); + } + + let f_params = ZSTD_frameParameters { + contentSizeFlag: 0, + checksumFlag: 0, + noDictIDFlag: 0, + }; + unsafe { + (state.begin)( + state.callback_context, + state.cdict, + &f_params, + ZSTD_CONTENTSIZE_UNKNOWN, + ) + } +} + type CompressBeginUsingDictBeginFn = unsafe extern "C" fn(*mut c_void, *const c_void, usize, *const c_void, u64) -> usize; @@ -2554,6 +2613,48 @@ mod tests { assert_eq!(probe.events, ["begin"]); } + fn compress_begin_using_cdict_public_test_state( + probe: &mut CompressUsingCDictProbe, + cdict: *const c_void, + ) -> ZSTD_rust_compressBeginUsingCDictPublicState { + ZSTD_rust_compressBeginUsingCDictPublicState { + callback_context: (probe as *mut CompressUsingCDictProbe).cast(), + cdict, + begin: compress_using_cdict_test_begin, + } + } + + #[test] + fn compress_begin_using_cdict_public_preserves_fixed_frame_policy() { + let mut probe = CompressUsingCDictProbe { + begin_result: 43, + ..Default::default() + }; + let cdict = 0x5000usize as *const c_void; + let state = compress_begin_using_cdict_public_test_state(&mut probe, cdict); + + let result = unsafe { ZSTD_rust_compressBeginUsingCDictPublic(&state) }; + + assert_eq!(result, probe.begin_result); + assert_eq!(probe.events, ["begin"]); + assert_eq!(probe.cdict, cdict); + assert_eq!(probe.frame_params.contentSizeFlag, 0); + assert_eq!(probe.frame_params.checksumFlag, 0); + assert_eq!(probe.frame_params.noDictIDFlag, 0); + assert_eq!(probe.pledged_src_size, ZSTD_CONTENTSIZE_UNKNOWN); + } + + #[test] + fn compress_begin_using_cdict_public_rejects_null_dictionary_before_callback() { + let mut probe = CompressUsingCDictProbe::default(); + let state = compress_begin_using_cdict_public_test_state(&mut probe, ptr::null()); + + let result = unsafe { ZSTD_rust_compressBeginUsingCDictPublic(&state) }; + + assert_eq!(result, ERROR(ZstdErrorCode::DictionaryWrong)); + assert!(probe.events.is_empty()); + } + #[derive(Default)] struct CompressBeginUsingDictProbe { events: Vec<&'static str>,