diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index b36d6d81f..a0434e0b8 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -340,6 +340,29 @@ typedef char ZSTD_rust_create_cctx_state_layout[ == 4 * sizeof(void*) && sizeof(ZSTD_rust_createCCtxState) == 5 * sizeof(void*)) ? 1 : -1]; +typedef void (*ZSTD_rust_initCCtxCallback_f)(void* context); +typedef void (*ZSTD_rust_initCCtxSetCustomMem_f)( + void* context, const void* customMem); +typedef size_t (*ZSTD_rust_initCCtxReset_f)(void* context); +typedef struct { + void* callbackContext; + const void* customMem; + ZSTD_rust_initCCtxCallback_f zero; + ZSTD_rust_initCCtxSetCustomMem_f setCustomMem; + ZSTD_rust_initCCtxCallback_f setBmi2; + ZSTD_rust_initCCtxReset_f reset; +} ZSTD_rust_initCCtxState; +size_t ZSTD_rust_initCCtx(const ZSTD_rust_initCCtxState* state); +typedef char ZSTD_rust_init_cctx_state_layout[ + (offsetof(ZSTD_rust_initCCtxState, callbackContext) == 0 + && offsetof(ZSTD_rust_initCCtxState, customMem) == sizeof(void*) + && offsetof(ZSTD_rust_initCCtxState, zero) == 2 * sizeof(void*) + && offsetof(ZSTD_rust_initCCtxState, setCustomMem) + == 3 * sizeof(void*) + && offsetof(ZSTD_rust_initCCtxState, setBmi2) == 4 * sizeof(void*) + && offsetof(ZSTD_rust_initCCtxState, reset) == 5 * sizeof(void*) + && sizeof(ZSTD_rust_initCCtxState) == 6 * sizeof(void*)) + ? 1 : -1]; typedef void* (*ZSTD_rust_initStaticCCtxInit_f)(void* context); typedef struct { void* callbackContext; @@ -3149,16 +3172,28 @@ ZSTD_CCtx* ZSTD_createCCtx(void) return ZSTD_createCCtx_advanced(ZSTD_defaultCMem); } -static void ZSTD_initCCtx(ZSTD_CCtx* cctx, ZSTD_customMem memManager) +static void ZSTD_initCCtx_zero(void* context) { + ZSTD_CCtx* const cctx = (ZSTD_CCtx*)context; assert(cctx != NULL); ZSTD_memset(cctx, 0, sizeof(*cctx)); - cctx->customMem = memManager; +} + +static void ZSTD_initCCtx_setCustomMem(void* context, const void* customMem) +{ + ZSTD_CCtx* const cctx = (ZSTD_CCtx*)context; + cctx->customMem = *(const ZSTD_customMem*)customMem; +} + +static void ZSTD_initCCtx_setBmi2(void* context) +{ + ZSTD_CCtx* const cctx = (ZSTD_CCtx*)context; cctx->bmi2 = ZSTD_cpuSupportsBmi2(); - { size_t const err = ZSTD_CCtx_reset(cctx, ZSTD_reset_parameters); - assert(!ZSTD_isError(err)); - (void)err; - } +} + +static size_t ZSTD_initCCtx_reset(void* context) +{ + return ZSTD_CCtx_reset((ZSTD_CCtx*)context, ZSTD_reset_parameters); } static int ZSTD_rust_createCCtx_validateCustomMem(void* context) @@ -3174,7 +3209,17 @@ static void* ZSTD_rust_createCCtx_allocate(void* context, size_t size) static void ZSTD_rust_createCCtx_init(void* context, void* cctx) { - ZSTD_initCCtx((ZSTD_CCtx*)cctx, *(const ZSTD_customMem*)context); + ZSTD_rust_initCCtxState state; + state.callbackContext = cctx; + state.customMem = context; + state.zero = ZSTD_initCCtx_zero; + state.setCustomMem = ZSTD_initCCtx_setCustomMem; + state.setBmi2 = ZSTD_initCCtx_setBmi2; + state.reset = ZSTD_initCCtx_reset; + { size_t const err = ZSTD_rust_initCCtx(&state); + assert(!ZSTD_isError(err)); + (void)err; + } } ZSTD_CCtx* ZSTD_createCCtx_advanced(ZSTD_customMem customMem) diff --git a/rust/src/zstd_compress.rs b/rust/src/zstd_compress.rs index 365174b38..faad252bd 100644 --- a/rust/src/zstd_compress.rs +++ b/rust/src/zstd_compress.rs @@ -2120,6 +2120,61 @@ pub unsafe extern "C" fn ZSTD_rust_refThreadPool( 0 } +type InitCCtxCallbackFn = unsafe extern "C" fn(*mut c_void); +type InitCCtxSetCustomMemFn = unsafe extern "C" fn(*mut c_void, *const c_void); +type InitCCtxResetFn = unsafe extern "C" fn(*mut c_void) -> usize; + +/// Explicit projection for private heap-CCtx initialization. +/// +/// Rust owns the initialization ordering while C retains the private context +/// layout, allocator publication, CPU-feature query, and reset operation +/// behind callbacks. +#[repr(C)] +pub struct ZSTD_rust_initCCtxState { + callback_context: *mut c_void, + custom_mem: *const c_void, + zero: InitCCtxCallbackFn, + set_custom_mem: InitCCtxSetCustomMemFn, + set_bmi2: InitCCtxCallbackFn, + reset: InitCCtxResetFn, +} + +const _: () = { + assert!(size_of::() == size_of::()); + assert!(size_of::() == size_of::()); + assert!(size_of::() == size_of::()); + assert!(offset_of!(ZSTD_rust_initCCtxState, callback_context) == 0); + assert!(offset_of!(ZSTD_rust_initCCtxState, custom_mem) == size_of::()); + assert!(offset_of!(ZSTD_rust_initCCtxState, zero) == 2 * size_of::()); + assert!(offset_of!(ZSTD_rust_initCCtxState, set_custom_mem) == 3 * size_of::()); + assert!(offset_of!(ZSTD_rust_initCCtxState, set_bmi2) == 4 * size_of::()); + assert!(offset_of!(ZSTD_rust_initCCtxState, reset) == 5 * size_of::()); + assert!(size_of::() == 6 * size_of::()); +}; + +/// Initialize a heap-owned C compression context in the original order. +#[no_mangle] +pub unsafe extern "C" fn ZSTD_rust_initCCtx(state: *const ZSTD_rust_initCCtxState) -> usize { + if state.is_null() { + return ERROR(ZstdErrorCode::Generic); + } + let state = unsafe { &*state }; + if state.callback_context.is_null() || state.custom_mem.is_null() { + return ERROR(ZstdErrorCode::Generic); + } + + unsafe { + (state.zero)(state.callback_context); + (state.set_custom_mem)(state.callback_context, state.custom_mem); + (state.set_bmi2)(state.callback_context); + let result = (state.reset)(state.callback_context); + if ERR_isError(result) { + return result; + } + } + 0 +} + type CreateCCtxValidateCustomMemFn = unsafe extern "C" fn(*mut c_void) -> c_int; type CreateCCtxAllocateFn = unsafe extern "C" fn(*mut c_void, usize) -> *mut c_void; type CreateCCtxInitFn = unsafe extern "C" fn(*mut c_void, *mut c_void); @@ -17745,6 +17800,85 @@ mod tests { assert!(context.initialized.is_null()); } + #[derive(Default)] + struct InitCCtxTestContext { + events: Vec<&'static str>, + reset_result: usize, + } + + unsafe fn init_cctx_test_context(context: *mut c_void) -> &'static mut InitCCtxTestContext { + unsafe { &mut *context.cast::() } + } + + unsafe extern "C" fn init_cctx_test_zero(context: *mut c_void) { + unsafe { init_cctx_test_context(context) } + .events + .push("zero"); + } + + unsafe extern "C" fn init_cctx_test_set_custom_mem( + context: *mut c_void, + _custom_mem: *const c_void, + ) { + unsafe { init_cctx_test_context(context) } + .events + .push("set-custom-mem"); + } + + unsafe extern "C" fn init_cctx_test_set_bmi2(context: *mut c_void) { + unsafe { init_cctx_test_context(context) } + .events + .push("set-bmi2"); + } + + unsafe extern "C" fn init_cctx_test_reset(context: *mut c_void) -> usize { + let context = unsafe { init_cctx_test_context(context) }; + context.events.push("reset"); + context.reset_result + } + + fn init_cctx_test_state(context: &mut InitCCtxTestContext) -> ZSTD_rust_initCCtxState { + ZSTD_rust_initCCtxState { + callback_context: (context as *mut InitCCtxTestContext).cast(), + custom_mem: ptr::dangling(), + zero: init_cctx_test_zero, + set_custom_mem: init_cctx_test_set_custom_mem, + set_bmi2: init_cctx_test_set_bmi2, + reset: init_cctx_test_reset, + } + } + + #[test] + fn init_cctx_runs_private_initialization_steps_in_order() { + let mut context = InitCCtxTestContext::default(); + let state = init_cctx_test_state(&mut context); + + let result = unsafe { ZSTD_rust_initCCtx(&state) }; + + assert_eq!(result, 0); + assert_eq!( + context.events, + ["zero", "set-custom-mem", "set-bmi2", "reset"] + ); + } + + #[test] + fn init_cctx_propagates_reset_errors_after_initialization_steps() { + let mut context = InitCCtxTestContext { + reset_result: ERROR(ZstdErrorCode::MemoryAllocation), + ..Default::default() + }; + let state = init_cctx_test_state(&mut context); + + let result = unsafe { ZSTD_rust_initCCtx(&state) }; + + assert_eq!(result, ERROR(ZstdErrorCode::MemoryAllocation)); + assert_eq!( + context.events, + ["zero", "set-custom-mem", "set-bmi2", "reset"] + ); + } + #[derive(Default)] struct InitStaticCCtxTestContext { events: Vec<&'static str>,