diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 960a9f8ef..cd33bd24a 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -350,6 +350,30 @@ typedef char ZSTD_rust_reset_cctx_state_layout[ == 6 * sizeof(void*) && sizeof(ZSTD_rust_resetCCtxState) == 7 * sizeof(void*)) ? 1 : -1]; +typedef size_t (*ZSTD_rust_copyCCtxInternal_f)( + void* context, const void* srcCCtx, + const ZSTD_frameParameters* fParams, + U64 pledgedSrcSize, int zbuff); +typedef struct { + void* callbackContext; + const void* srcCCtx; + const ZSTD_frameParameters* fParams; + const U64* pledgedSrcSize; + const int* zbuff; + ZSTD_rust_copyCCtxInternal_f copyInternal; +} ZSTD_rust_copyCCtxState; +size_t ZSTD_rust_copyCCtx(const ZSTD_rust_copyCCtxState* state); +typedef char ZSTD_rust_copy_cctx_state_layout[ + (offsetof(ZSTD_rust_copyCCtxState, callbackContext) == 0 + && offsetof(ZSTD_rust_copyCCtxState, srcCCtx) == sizeof(void*) + && offsetof(ZSTD_rust_copyCCtxState, fParams) == 2 * sizeof(void*) + && offsetof(ZSTD_rust_copyCCtxState, pledgedSrcSize) + == 3 * sizeof(void*) + && offsetof(ZSTD_rust_copyCCtxState, zbuff) == 4 * sizeof(void*) + && offsetof(ZSTD_rust_copyCCtxState, copyInternal) + == 5 * sizeof(void*) + && sizeof(ZSTD_rust_copyCCtxState) == 6 * sizeof(void*)) + ? 1 : -1]; ZSTD_frameProgression ZSTD_rust_frameProgression(U64 consumedSrcSize, size_t buffered, U64 producedCSize); @@ -3495,6 +3519,16 @@ static size_t ZSTD_copyCCtx_internal(ZSTD_CCtx* dstCCtx, return 0; } +static size_t ZSTD_rust_copyCCtx_internal_callback( + void* context, const void* srcCCtx, + const ZSTD_frameParameters* fParams, + U64 pledgedSrcSize, int zbuff) +{ + return ZSTD_copyCCtx_internal( + (ZSTD_CCtx*)context, (const ZSTD_CCtx*)srcCCtx, + *fParams, pledgedSrcSize, (ZSTD_buffered_policy_e)zbuff); +} + /*! ZSTD_copyCCtx() : * Duplicate an existing context `srcCCtx` into another one `dstCCtx`. * Only works during stage ZSTDcs_init (i.e. after creation, but before first call to ZSTD_compressContinue()). @@ -3502,15 +3536,18 @@ static size_t ZSTD_copyCCtx_internal(ZSTD_CCtx* dstCCtx, * @return : 0, or an error code */ size_t ZSTD_copyCCtx(ZSTD_CCtx* dstCCtx, const ZSTD_CCtx* srcCCtx, unsigned long long pledgedSrcSize) { - ZSTD_frameParameters fParams = { 1 /*content*/, 0 /*checksum*/, 0 /*noDictID*/ }; - ZSTD_buffered_policy_e const zbuff = srcCCtx->bufferedPolicy; + ZSTD_rust_copyCCtxState state; + ZSTD_frameParameters const fParams = { 1 /*content*/, 0 /*checksum*/, 0 /*noDictID*/ }; + U64 const pledgedSrcSizeValue = pledgedSrcSize; + int const zbuffValue = (int)srcCCtx->bufferedPolicy; ZSTD_STATIC_ASSERT((U32)ZSTDb_buffered==1); - if (pledgedSrcSize==0) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN; - fParams.contentSizeFlag = (pledgedSrcSize != ZSTD_CONTENTSIZE_UNKNOWN); - - return ZSTD_copyCCtx_internal(dstCCtx, srcCCtx, - fParams, pledgedSrcSize, - zbuff); + state.callbackContext = dstCCtx; + state.srcCCtx = srcCCtx; + state.fParams = &fParams; + state.pledgedSrcSize = &pledgedSrcSizeValue; + state.zbuff = &zbuffValue; + state.copyInternal = ZSTD_rust_copyCCtx_internal_callback; + return ZSTD_rust_copyCCtx(&state); } diff --git a/rust/src/zstd_compress.rs b/rust/src/zstd_compress.rs index fe7405ca1..14086042f 100644 --- a/rust/src/zstd_compress.rs +++ b/rust/src/zstd_compress.rs @@ -1397,6 +1397,77 @@ pub unsafe extern "C" fn ZSTD_rust_resetCCtx( 0 } +type CopyCCtxInternalFn = unsafe extern "C" fn( + *mut c_void, + *const c_void, + *const ZSTD_frameParameters, + u64, + c_int, +) -> usize; + +/// Explicit projection for the public `ZSTD_copyCCtx` wrapper. +/// +/// Rust owns zero-to-unknown pledged-size normalization and frame-parameter +/// construction. C retains the private table and workspace copy operation. +#[repr(C)] +pub struct ZSTD_rust_copyCCtxState { + callback_context: *mut c_void, + src_cctx: *const c_void, + f_params: *const ZSTD_frameParameters, + pledged_src_size: *const u64, + zbuff: *const c_int, + copy_internal: Option, +} + +const _: () = { + assert!(size_of::() == size_of::()); + assert!(offset_of!(ZSTD_rust_copyCCtxState, callback_context) == 0); + assert!(offset_of!(ZSTD_rust_copyCCtxState, src_cctx) == size_of::()); + assert!(offset_of!(ZSTD_rust_copyCCtxState, f_params) == 2 * size_of::()); + assert!(offset_of!(ZSTD_rust_copyCCtxState, pledged_src_size) == 3 * size_of::()); + assert!(offset_of!(ZSTD_rust_copyCCtxState, zbuff) == 4 * size_of::()); + assert!(offset_of!(ZSTD_rust_copyCCtxState, copy_internal) == 5 * size_of::()); + assert!(size_of::() == size_of::<[usize; 6]>()); +}; + +/// Normalize the public copy request before invoking the private C operation. +#[no_mangle] +pub unsafe extern "C" fn ZSTD_rust_copyCCtx(state: *const ZSTD_rust_copyCCtxState) -> usize { + if state.is_null() { + return ERROR(ZstdErrorCode::Generic); + } + let state = unsafe { &*state }; + let Some(copy_internal) = state.copy_internal else { + return ERROR(ZstdErrorCode::Generic); + }; + if state.callback_context.is_null() + || state.src_cctx.is_null() + || state.f_params.is_null() + || state.pledged_src_size.is_null() + || state.zbuff.is_null() + { + return ERROR(ZstdErrorCode::Generic); + } + + let pledged_src_size = unsafe { *state.pledged_src_size }; + let pledged_src_size = if pledged_src_size == 0 { + ZSTD_CONTENTSIZE_UNKNOWN + } else { + pledged_src_size + }; + let mut f_params = unsafe { *state.f_params }; + f_params.contentSizeFlag = c_int::from(pledged_src_size != ZSTD_CONTENTSIZE_UNKNOWN); + unsafe { + copy_internal( + state.callback_context, + state.src_cctx, + &f_params, + pledged_src_size, + *state.zbuff, + ) + } +} + type ResetCStreamResetFn = unsafe extern "C" fn(*mut c_void) -> usize; type ResetCStreamSetPledgedSrcSizeFn = unsafe extern "C" fn(*mut c_void, u64) -> usize; @@ -10195,6 +10266,106 @@ mod tests { assert!(context.events.is_empty()); } + #[derive(Default)] + struct CopyCCtxTestContext { + events: Vec<&'static str>, + src_cctx: *const c_void, + frame_params: ZSTD_frameParameters, + pledged_src_size: u64, + zbuff: c_int, + result: usize, + } + + unsafe extern "C" fn copy_cctx_test_internal( + context: *mut c_void, + src_cctx: *const c_void, + f_params: *const ZSTD_frameParameters, + pledged_src_size: u64, + zbuff: c_int, + ) -> usize { + let context = unsafe { &mut *context.cast::() }; + context.events.push("copy"); + context.src_cctx = src_cctx; + context.frame_params = unsafe { *f_params }; + context.pledged_src_size = pledged_src_size; + context.zbuff = zbuff; + context.result + } + + fn copy_cctx_test_state( + context: &mut CopyCCtxTestContext, + src_cctx: *const c_void, + f_params: &ZSTD_frameParameters, + pledged_src_size: &u64, + zbuff: &c_int, + ) -> ZSTD_rust_copyCCtxState { + ZSTD_rust_copyCCtxState { + callback_context: (context as *mut CopyCCtxTestContext).cast(), + src_cctx, + f_params, + pledged_src_size, + zbuff, + copy_internal: Some(copy_cctx_test_internal), + } + } + + #[test] + fn copy_cctx_converts_zero_pledge_to_unknown_and_clears_content_flag() { + let mut context = CopyCCtxTestContext::default(); + let f_params = ZSTD_frameParameters { + contentSizeFlag: 1, + checksumFlag: 1, + noDictIDFlag: 1, + }; + let pledged_src_size = 0; + let zbuff = 1; + let src_cctx = 0x4000usize as *const c_void; + let state = + copy_cctx_test_state(&mut context, src_cctx, &f_params, &pledged_src_size, &zbuff); + + let result = unsafe { ZSTD_rust_copyCCtx(&state) }; + + assert_eq!(result, 0); + assert_eq!(context.events, ["copy"]); + assert_eq!(context.src_cctx, src_cctx); + assert_eq!(context.pledged_src_size, ZSTD_CONTENTSIZE_UNKNOWN); + assert_eq!(context.zbuff, zbuff); + assert_eq!(context.frame_params.contentSizeFlag, 0); + assert_eq!(context.frame_params.checksumFlag, 1); + assert_eq!(context.frame_params.noDictIDFlag, 1); + } + + #[test] + fn copy_cctx_forwards_nonzero_pledge_and_propagates_internal_error() { + let mut context = CopyCCtxTestContext { + result: ERROR(ZstdErrorCode::MemoryAllocation), + ..CopyCCtxTestContext::default() + }; + let f_params = ZSTD_frameParameters { + contentSizeFlag: 0, + checksumFlag: 1, + noDictIDFlag: 1, + }; + let pledged_src_size = 123; + let zbuff = 7; + let state = copy_cctx_test_state( + &mut context, + 0x4000usize as *const c_void, + &f_params, + &pledged_src_size, + &zbuff, + ); + + let result = unsafe { ZSTD_rust_copyCCtx(&state) }; + + assert_eq!(result, context.result); + assert_eq!(context.events, ["copy"]); + assert_eq!(context.pledged_src_size, pledged_src_size); + assert_eq!(context.frame_params.contentSizeFlag, 1); + assert_eq!(context.frame_params.checksumFlag, 1); + assert_eq!(context.frame_params.noDictIDFlag, 1); + } + #[derive(Default)] struct ResetCStreamTestContext { events: Vec<&'static str>,