diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index c2c2acc57..5e0e1d024 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -363,13 +363,29 @@ typedef char ZSTD_rust_init_cctx_state_layout[ && 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 void (*ZSTD_rust_initStaticCCtxCreateWorkspace_f)(void* context); +typedef void* (*ZSTD_rust_initStaticCCtxReserveObject_f)(void* context); +typedef void (*ZSTD_rust_initStaticCCtxZero_f)(void* context, void* cctx); +typedef void (*ZSTD_rust_initStaticCCtxMoveWorkspace_f)( + void* context, void* cctx); +typedef int (*ZSTD_rust_initStaticCCtxCheckAvailable_f)(void* context); +typedef void* (*ZSTD_rust_initStaticCCtxReserveBlockState_f)( + void* context, int blockState); +typedef void* (*ZSTD_rust_initStaticCCtxReserveTmpWorkspace_f)(void* context); +typedef void (*ZSTD_rust_initStaticCCtxSetBmi2_f)(void* context); typedef struct { void* callbackContext; void* workspace; size_t workspaceSize; size_t cctxSize; - ZSTD_rust_initStaticCCtxInit_f init; + ZSTD_rust_initStaticCCtxCreateWorkspace_f createWorkspace; + ZSTD_rust_initStaticCCtxReserveObject_f reserveObject; + ZSTD_rust_initStaticCCtxZero_f zero; + ZSTD_rust_initStaticCCtxMoveWorkspace_f moveWorkspace; + ZSTD_rust_initStaticCCtxCheckAvailable_f checkAvailable; + ZSTD_rust_initStaticCCtxReserveBlockState_f reserveBlockState; + ZSTD_rust_initStaticCCtxReserveTmpWorkspace_f reserveTmpWorkspace; + ZSTD_rust_initStaticCCtxSetBmi2_f setBmi2; } ZSTD_rust_initStaticCCtxState; void* ZSTD_rust_initStaticCCtx(const ZSTD_rust_initStaticCCtxState* state); typedef char ZSTD_rust_init_static_cctx_state_layout[ @@ -380,9 +396,23 @@ typedef char ZSTD_rust_init_static_cctx_state_layout[ == 2 * sizeof(void*) && offsetof(ZSTD_rust_initStaticCCtxState, cctxSize) == 3 * sizeof(void*) - && offsetof(ZSTD_rust_initStaticCCtxState, init) + && offsetof(ZSTD_rust_initStaticCCtxState, createWorkspace) == 4 * sizeof(void*) - && sizeof(ZSTD_rust_initStaticCCtxState) == 5 * sizeof(void*)) + && offsetof(ZSTD_rust_initStaticCCtxState, reserveObject) + == 5 * sizeof(void*) + && offsetof(ZSTD_rust_initStaticCCtxState, zero) + == 6 * sizeof(void*) + && offsetof(ZSTD_rust_initStaticCCtxState, moveWorkspace) + == 7 * sizeof(void*) + && offsetof(ZSTD_rust_initStaticCCtxState, checkAvailable) + == 8 * sizeof(void*) + && offsetof(ZSTD_rust_initStaticCCtxState, reserveBlockState) + == 9 * sizeof(void*) + && offsetof(ZSTD_rust_initStaticCCtxState, reserveTmpWorkspace) + == 10 * sizeof(void*) + && offsetof(ZSTD_rust_initStaticCCtxState, setBmi2) + == 11 * sizeof(void*) + && sizeof(ZSTD_rust_initStaticCCtxState) == 12 * sizeof(void*)) ? 1 : -1]; typedef void* (*ZSTD_rust_createCDictCreate_f)( void* context, const void* dict, size_t dictSize, @@ -3347,32 +3377,93 @@ ZSTD_CCtx* ZSTD_createCCtx_advanced(ZSTD_customMem customMem) typedef struct { void* workspace; size_t workspaceSize; + ZSTD_cwksp workspaceState; + ZSTD_CCtx* cctx; } ZSTD_rust_initStaticCCtxContext; -static void* ZSTD_rust_initStaticCCtx_init(void* opaque) +static void ZSTD_rust_initStaticCCtx_createWorkspace(void* opaque) { ZSTD_rust_initStaticCCtxContext* const context = (ZSTD_rust_initStaticCCtxContext*)opaque; - ZSTD_cwksp ws; - ZSTD_CCtx* cctx; - - ZSTD_cwksp_init(&ws, context->workspace, context->workspaceSize, + ZSTD_cwksp_init(&context->workspaceState, context->workspace, + context->workspaceSize, ZSTD_cwksp_static_alloc); - cctx = (ZSTD_CCtx*)ZSTD_cwksp_reserve_object(&ws, sizeof(ZSTD_CCtx)); - if (cctx == NULL) return NULL; +} +static void* ZSTD_rust_initStaticCCtx_reserveObject(void* opaque) +{ + ZSTD_rust_initStaticCCtxContext* const context = + (ZSTD_rust_initStaticCCtxContext*)opaque; + context->cctx = (ZSTD_CCtx*)ZSTD_cwksp_reserve_object( + &context->workspaceState, sizeof(ZSTD_CCtx)); + return context->cctx; +} + +static void ZSTD_rust_initStaticCCtx_zero(void* opaque, void* cctx) +{ + (void)opaque; ZSTD_memset(cctx, 0, sizeof(ZSTD_CCtx)); - ZSTD_cwksp_move(&cctx->workspace, &ws); - cctx->staticSize = context->workspaceSize; +} - /* statically sized space. tmpWorkspace never moves (but prev/next block swap places) */ - if (!ZSTD_cwksp_check_available(&cctx->workspace, TMP_WORKSPACE_SIZE + 2 * sizeof(ZSTD_compressedBlockState_t))) return NULL; - cctx->blockState.prevCBlock = (ZSTD_compressedBlockState_t*)ZSTD_cwksp_reserve_object(&cctx->workspace, sizeof(ZSTD_compressedBlockState_t)); - cctx->blockState.nextCBlock = (ZSTD_compressedBlockState_t*)ZSTD_cwksp_reserve_object(&cctx->workspace, sizeof(ZSTD_compressedBlockState_t)); - cctx->tmpWorkspace = ZSTD_cwksp_reserve_object(&cctx->workspace, TMP_WORKSPACE_SIZE); - cctx->tmpWkspSize = TMP_WORKSPACE_SIZE; - cctx->bmi2 = ZSTD_cpuid_bmi2(ZSTD_cpuid()); - return cctx; +static void ZSTD_rust_initStaticCCtx_moveWorkspace(void* opaque, void* cctx) +{ + ZSTD_rust_initStaticCCtxContext* const context = + (ZSTD_rust_initStaticCCtxContext*)opaque; + ZSTD_CCtx* const contextCCtx = (ZSTD_CCtx*)cctx; + ZSTD_cwksp_move(&contextCCtx->workspace, &context->workspaceState); + contextCCtx->staticSize = context->workspaceSize; +} + +static int ZSTD_rust_initStaticCCtx_checkAvailable(void* opaque) +{ + ZSTD_rust_initStaticCCtxContext const* const context = + (const ZSTD_rust_initStaticCCtxContext*)opaque; + return ZSTD_cwksp_check_available( + &context->cctx->workspace, + TMP_WORKSPACE_SIZE + 2 * sizeof(ZSTD_compressedBlockState_t)); +} + +enum { + ZSTD_RUST_INIT_STATIC_CCTX_PREV_CBLOCK = 0, + ZSTD_RUST_INIT_STATIC_CCTX_NEXT_CBLOCK = 1 +}; + +static void* ZSTD_rust_initStaticCCtx_reserveBlockState( + void* opaque, int blockState) +{ + ZSTD_rust_initStaticCCtxContext* const context = + (ZSTD_rust_initStaticCCtxContext*)opaque; + void* const block = ZSTD_cwksp_reserve_object( + &context->cctx->workspace, + sizeof(ZSTD_compressedBlockState_t)); + if (blockState == ZSTD_RUST_INIT_STATIC_CCTX_PREV_CBLOCK) { + context->cctx->blockState.prevCBlock = + (ZSTD_compressedBlockState_t*)block; + } else if (blockState == ZSTD_RUST_INIT_STATIC_CCTX_NEXT_CBLOCK) { + context->cctx->blockState.nextCBlock = + (ZSTD_compressedBlockState_t*)block; + } else { + assert(0); + return NULL; + } + return block; +} + +static void* ZSTD_rust_initStaticCCtx_reserveTmpWorkspace(void* opaque) +{ + ZSTD_rust_initStaticCCtxContext* const context = + (ZSTD_rust_initStaticCCtxContext*)opaque; + context->cctx->tmpWorkspace = ZSTD_cwksp_reserve_object( + &context->cctx->workspace, TMP_WORKSPACE_SIZE); + context->cctx->tmpWkspSize = TMP_WORKSPACE_SIZE; + return context->cctx->tmpWorkspace; +} + +static void ZSTD_rust_initStaticCCtx_setBmi2(void* opaque) +{ + ZSTD_rust_initStaticCCtxContext const* const context = + (const ZSTD_rust_initStaticCCtxContext*)opaque; + context->cctx->bmi2 = ZSTD_cpuid_bmi2(ZSTD_cpuid()); } ZSTD_CCtx* ZSTD_initStaticCCtx(void* workspace, size_t workspaceSize) @@ -3385,10 +3476,20 @@ ZSTD_CCtx* ZSTD_initStaticCCtx(void* workspace, size_t workspaceSize) state.workspace = workspace; state.workspaceSize = workspaceSize; state.cctxSize = sizeof(ZSTD_CCtx); - state.init = ZSTD_rust_initStaticCCtx_init; + state.createWorkspace = ZSTD_rust_initStaticCCtx_createWorkspace; + state.reserveObject = ZSTD_rust_initStaticCCtx_reserveObject; + state.zero = ZSTD_rust_initStaticCCtx_zero; + state.moveWorkspace = ZSTD_rust_initStaticCCtx_moveWorkspace; + state.checkAvailable = ZSTD_rust_initStaticCCtx_checkAvailable; + state.reserveBlockState = ZSTD_rust_initStaticCCtx_reserveBlockState; + state.reserveTmpWorkspace = ZSTD_rust_initStaticCCtx_reserveTmpWorkspace; + state.setBmi2 = ZSTD_rust_initStaticCCtx_setBmi2; return (ZSTD_CCtx*)ZSTD_rust_initStaticCCtx(&state); } +/* Keep the private workspace and CCtx layout operations above in C. Rust + * owns only the public checks, construction order, and failure policy. */ + /** * Clears and frees all of the dictionaries in the CCtx. */ diff --git a/rust/src/zstd_compress.rs b/rust/src/zstd_compress.rs index 0c83967a5..42599eced 100644 --- a/rust/src/zstd_compress.rs +++ b/rust/src/zstd_compress.rs @@ -2351,33 +2351,68 @@ pub unsafe extern "C" fn ZSTD_rust_createCDictAdvancedWrapper( } } -type InitStaticCCtxFn = unsafe extern "C" fn(*mut c_void) -> *mut c_void; +type InitStaticCCtxCreateWorkspaceFn = unsafe extern "C" fn(*mut c_void); +type InitStaticCCtxReserveObjectFn = unsafe extern "C" fn(*mut c_void) -> *mut c_void; +type InitStaticCCtxZeroFn = unsafe extern "C" fn(*mut c_void, *mut c_void); +type InitStaticCCtxMoveWorkspaceFn = unsafe extern "C" fn(*mut c_void, *mut c_void); +type InitStaticCCtxCheckAvailableFn = unsafe extern "C" fn(*mut c_void) -> c_int; +type InitStaticCCtxReserveBlockStateFn = unsafe extern "C" fn(*mut c_void, c_int) -> *mut c_void; +type InitStaticCCtxReserveTmpWorkspaceFn = unsafe extern "C" fn(*mut c_void) -> *mut c_void; +type InitStaticCCtxSetBmi2Fn = unsafe extern "C" fn(*mut c_void); const ZSTD_STATIC_WORKSPACE_ALIGNMENT: usize = 8; +const INIT_STATIC_CCTX_PREV_CBLOCK: c_int = 0; +const INIT_STATIC_CCTX_NEXT_CBLOCK: c_int = 1; /// Explicit projection for the public `ZSTD_initStaticCCtx` wrapper. /// /// Rust owns workspace pointer/alignment validation, minimum-size validation, -/// and callback ordering. C retains the private static-workspace layout and -/// context initialization behind one callback. +/// construction ordering, and callback failure policy. C retains the private +/// static-workspace and CCtx layout operations behind narrow callbacks. #[repr(C)] pub struct ZSTD_rust_initStaticCCtxState { callback_context: *mut c_void, workspace: *mut c_void, workspace_size: usize, cctx_size: usize, - init: InitStaticCCtxFn, + create_workspace: InitStaticCCtxCreateWorkspaceFn, + reserve_object: InitStaticCCtxReserveObjectFn, + zero: InitStaticCCtxZeroFn, + move_workspace: InitStaticCCtxMoveWorkspaceFn, + check_available: InitStaticCCtxCheckAvailableFn, + reserve_block_state: InitStaticCCtxReserveBlockStateFn, + reserve_tmp_workspace: InitStaticCCtxReserveTmpWorkspaceFn, + set_bmi2: InitStaticCCtxSetBmi2Fn, } const _: () = { + assert!(size_of::() == size_of::()); + assert!(size_of::() == size_of::()); + assert!(size_of::() == size_of::()); + assert!(size_of::() == size_of::()); + assert!(size_of::() == size_of::()); + assert!(size_of::() == size_of::()); + assert!(size_of::() == size_of::()); + assert!(size_of::() == size_of::()); assert!(offset_of!(ZSTD_rust_initStaticCCtxState, callback_context) == 0); assert!(offset_of!(ZSTD_rust_initStaticCCtxState, workspace) == size_of::()); assert!(offset_of!(ZSTD_rust_initStaticCCtxState, workspace_size) == 2 * size_of::()); assert!(offset_of!(ZSTD_rust_initStaticCCtxState, cctx_size) == 3 * size_of::()); - assert!(offset_of!(ZSTD_rust_initStaticCCtxState, init) == 4 * size_of::()); - assert!(size_of::() == size_of::<[usize; 5]>()); + assert!(offset_of!(ZSTD_rust_initStaticCCtxState, create_workspace) == 4 * size_of::()); + assert!(offset_of!(ZSTD_rust_initStaticCCtxState, reserve_object) == 5 * size_of::()); + assert!(offset_of!(ZSTD_rust_initStaticCCtxState, zero) == 6 * size_of::()); + assert!(offset_of!(ZSTD_rust_initStaticCCtxState, move_workspace) == 7 * size_of::()); + assert!(offset_of!(ZSTD_rust_initStaticCCtxState, check_available) == 8 * size_of::()); + assert!( + offset_of!(ZSTD_rust_initStaticCCtxState, reserve_block_state) == 9 * size_of::() + ); + assert!( + offset_of!(ZSTD_rust_initStaticCCtxState, reserve_tmp_workspace) == 10 * size_of::() + ); + assert!(offset_of!(ZSTD_rust_initStaticCCtxState, set_bmi2) == 11 * size_of::()); + assert!(size_of::() == size_of::<[usize; 12]>()); }; -/// Validate static CCtx workspace ownership before entering the C leaf. +/// Validate and sequence static CCtx construction before entering C leaves. #[no_mangle] pub unsafe extern "C" fn ZSTD_rust_initStaticCCtx( state: *const ZSTD_rust_initStaticCCtxState, @@ -2393,7 +2428,33 @@ pub unsafe extern "C" fn ZSTD_rust_initStaticCCtx( { return ptr::null_mut(); } - unsafe { (state.init)(state.callback_context) } + unsafe { + (state.create_workspace)(state.callback_context); + let cctx = (state.reserve_object)(state.callback_context); + if cctx.is_null() { + return ptr::null_mut(); + } + (state.zero)(state.callback_context, cctx); + (state.move_workspace)(state.callback_context, cctx); + if (state.check_available)(state.callback_context) == 0 { + return ptr::null_mut(); + } + if (state.reserve_block_state)(state.callback_context, INIT_STATIC_CCTX_PREV_CBLOCK) + .is_null() + { + return ptr::null_mut(); + } + if (state.reserve_block_state)(state.callback_context, INIT_STATIC_CCTX_NEXT_CBLOCK) + .is_null() + { + return ptr::null_mut(); + } + if (state.reserve_tmp_workspace)(state.callback_context).is_null() { + return ptr::null_mut(); + } + (state.set_bmi2)(state.callback_context); + cctx + } } type FreeCCtxContentFn = unsafe extern "C" fn(*mut c_void); @@ -18444,13 +18505,84 @@ mod tests { #[derive(Default)] struct InitStaticCCtxTestContext { events: Vec<&'static str>, - result: *mut c_void, + reserved_cctx: *mut c_void, + check_available_result: c_int, + reserve_block_state_failure: Option, + reserve_block_state_count: usize, + reserve_tmp_workspace_result: *mut c_void, } - unsafe extern "C" fn init_static_cctx_test_init(context: *mut c_void) -> *mut c_void { + unsafe fn init_static_cctx_test_context( + context: *mut c_void, + ) -> &'static mut InitStaticCCtxTestContext { + unsafe { &mut *context.cast::() } + } + + unsafe extern "C" fn init_static_cctx_test_create_workspace(context: *mut c_void) { + unsafe { init_static_cctx_test_context(context) } + .events + .push("create-workspace"); + } + + unsafe extern "C" fn init_static_cctx_test_reserve_object(context: *mut c_void) -> *mut c_void { + let context = unsafe { init_static_cctx_test_context(context) }; + context.events.push("reserve-object"); + context.reserved_cctx + } + + unsafe extern "C" fn init_static_cctx_test_zero(context: *mut c_void, _cctx: *mut c_void) { + unsafe { init_static_cctx_test_context(context) } + .events + .push("zero"); + } + + unsafe extern "C" fn init_static_cctx_test_move_workspace( + context: *mut c_void, + _cctx: *mut c_void, + ) { + unsafe { init_static_cctx_test_context(context) } + .events + .push("move-workspace"); + } + + unsafe extern "C" fn init_static_cctx_test_check_available(context: *mut c_void) -> c_int { + let context = unsafe { init_static_cctx_test_context(context) }; + context.events.push("check-available"); + context.check_available_result + } + + unsafe extern "C" fn init_static_cctx_test_reserve_block_state( + context: *mut c_void, + block_state: c_int, + ) -> *mut c_void { let context = unsafe { &mut *context.cast::() }; - context.events.push("init"); - context.result + context.events.push(match block_state { + INIT_STATIC_CCTX_PREV_CBLOCK => "reserve-prev-block-state", + INIT_STATIC_CCTX_NEXT_CBLOCK => "reserve-next-block-state", + _ => "reserve-unknown-block-state", + }); + let result = + if context.reserve_block_state_failure == Some(context.reserve_block_state_count) { + ptr::null_mut() + } else { + ptr::dangling_mut() + }; + context.reserve_block_state_count += 1; + result + } + + unsafe extern "C" fn init_static_cctx_test_reserve_tmp_workspace( + context: *mut c_void, + ) -> *mut c_void { + let context = unsafe { init_static_cctx_test_context(context) }; + context.events.push("reserve-tmp-workspace"); + context.reserve_tmp_workspace_result + } + + unsafe extern "C" fn init_static_cctx_test_set_bmi2(context: *mut c_void) { + unsafe { init_static_cctx_test_context(context) } + .events + .push("set-bmi2"); } fn init_static_cctx_test_state( @@ -18464,50 +18596,23 @@ mod tests { workspace, workspace_size, cctx_size, - init: init_static_cctx_test_init, + create_workspace: init_static_cctx_test_create_workspace, + reserve_object: init_static_cctx_test_reserve_object, + zero: init_static_cctx_test_zero, + move_workspace: init_static_cctx_test_move_workspace, + check_available: init_static_cctx_test_check_available, + reserve_block_state: init_static_cctx_test_reserve_block_state, + reserve_tmp_workspace: init_static_cctx_test_reserve_tmp_workspace, + set_bmi2: init_static_cctx_test_set_bmi2, } } #[test] - fn init_static_cctx_rejects_an_unaligned_workspace_before_callback() { + fn init_static_cctx_runs_private_construction_steps_in_order() { let mut context = InitStaticCCtxTestContext { - result: ptr::dangling_mut(), - ..Default::default() - }; - let mut workspace = [0usize; 2]; - let unaligned_workspace = workspace.as_mut_ptr().cast::().wrapping_add(1).cast(); - let state = init_static_cctx_test_state(&mut context, unaligned_workspace, usize::MAX, 0); - - let result = unsafe { ZSTD_rust_initStaticCCtx(&state) }; - - assert!(result.is_null()); - assert!(context.events.is_empty()); - } - - #[test] - fn init_static_cctx_rejects_a_small_workspace_before_callback() { - let mut context = InitStaticCCtxTestContext { - result: ptr::dangling_mut(), - ..Default::default() - }; - let mut workspace = [0usize; 1]; - let state = init_static_cctx_test_state( - &mut context, - workspace.as_mut_ptr().cast(), - size_of_val(&workspace), - size_of_val(&workspace), - ); - - let result = unsafe { ZSTD_rust_initStaticCCtx(&state) }; - - assert!(result.is_null()); - assert!(context.events.is_empty()); - } - - #[test] - fn init_static_cctx_calls_the_initializer_after_workspace_validation() { - let mut context = InitStaticCCtxTestContext { - result: ptr::dangling_mut(), + reserved_cctx: ptr::dangling_mut(), + check_available_result: 1, + reserve_tmp_workspace_result: ptr::dangling_mut(), ..Default::default() }; let mut workspace = [0usize; 1]; @@ -18520,14 +18625,155 @@ mod tests { let result = unsafe { ZSTD_rust_initStaticCCtx(&state) }; - assert_eq!(result, context.result); - assert_eq!(context.events, ["init"]); + assert_eq!(result, context.reserved_cctx); + assert_eq!( + context.events, + [ + "create-workspace", + "reserve-object", + "zero", + "move-workspace", + "check-available", + "reserve-prev-block-state", + "reserve-next-block-state", + "reserve-tmp-workspace", + "set-bmi2", + ] + ); + } + + #[test] + fn init_static_cctx_stops_after_cctx_reservation_failure() { + let mut context = InitStaticCCtxTestContext { + check_available_result: 1, + reserve_tmp_workspace_result: ptr::dangling_mut(), + ..Default::default() + }; + let mut workspace = [0usize; 1]; + let state = init_static_cctx_test_state( + &mut context, + workspace.as_mut_ptr().cast(), + size_of_val(&workspace), + size_of_val(&workspace), + ); + + let result = unsafe { ZSTD_rust_initStaticCCtx(&state) }; + + assert!(result.is_null()); + assert_eq!(context.events, ["create-workspace", "reserve-object"]); + } + + #[test] + fn init_static_cctx_stops_after_workspace_capacity_failure() { + let mut context = InitStaticCCtxTestContext { + reserved_cctx: ptr::dangling_mut(), + reserve_tmp_workspace_result: ptr::dangling_mut(), + ..Default::default() + }; + let mut workspace = [0usize; 1]; + let state = init_static_cctx_test_state( + &mut context, + workspace.as_mut_ptr().cast(), + size_of_val(&workspace), + size_of_val(&workspace) - 1, + ); + + let result = unsafe { ZSTD_rust_initStaticCCtx(&state) }; + + assert!(result.is_null()); + assert_eq!( + context.events, + [ + "create-workspace", + "reserve-object", + "zero", + "move-workspace", + "check-available", + ] + ); + } + + #[test] + fn init_static_cctx_stops_after_block_state_reservation_failure() { + let mut context = InitStaticCCtxTestContext { + reserved_cctx: ptr::dangling_mut(), + check_available_result: 1, + reserve_block_state_failure: Some(1), + reserve_tmp_workspace_result: ptr::dangling_mut(), + ..Default::default() + }; + let mut workspace = [0usize; 1]; + let state = init_static_cctx_test_state( + &mut context, + workspace.as_mut_ptr().cast(), + size_of_val(&workspace), + size_of_val(&workspace) - 1, + ); + + let result = unsafe { ZSTD_rust_initStaticCCtx(&state) }; + + assert!(result.is_null()); + assert_eq!( + context.events, + [ + "create-workspace", + "reserve-object", + "zero", + "move-workspace", + "check-available", + "reserve-prev-block-state", + "reserve-next-block-state", + ] + ); + } + + #[test] + fn init_static_cctx_stops_after_tmp_workspace_reservation_failure() { + let mut context = InitStaticCCtxTestContext { + reserved_cctx: ptr::dangling_mut(), + check_available_result: 1, + reserve_tmp_workspace_result: ptr::null_mut(), + ..Default::default() + }; + let mut workspace = [0usize; 1]; + let state = init_static_cctx_test_state( + &mut context, + workspace.as_mut_ptr().cast(), + size_of_val(&workspace), + size_of_val(&workspace) - 1, + ); + + let result = unsafe { ZSTD_rust_initStaticCCtx(&state) }; + + assert!(result.is_null()); + assert_eq!( + context.events, + [ + "create-workspace", + "reserve-object", + "zero", + "move-workspace", + "check-available", + "reserve-prev-block-state", + "reserve-next-block-state", + "reserve-tmp-workspace", + ] + ); + } + + #[test] + fn init_static_cctx_rejects_a_null_state() { + let result = unsafe { ZSTD_rust_initStaticCCtx(ptr::null()) }; + + assert!(result.is_null()); } #[test] fn init_static_cctx_rejects_a_null_workspace() { let mut context = InitStaticCCtxTestContext { - result: ptr::dangling_mut(), + reserved_cctx: ptr::dangling_mut(), + check_available_result: 1, + reserve_tmp_workspace_result: ptr::dangling_mut(), ..Default::default() }; let state = init_static_cctx_test_state(&mut context, ptr::null_mut(), usize::MAX, 0); @@ -18538,6 +18784,36 @@ mod tests { assert!(context.events.is_empty()); } + #[test] + fn init_static_cctx_rejects_an_unaligned_workspace_before_callback() { + let mut context = InitStaticCCtxTestContext::default(); + let mut workspace = [0usize; 2]; + let unaligned_workspace = workspace.as_mut_ptr().cast::().wrapping_add(1).cast(); + let state = init_static_cctx_test_state(&mut context, unaligned_workspace, usize::MAX, 0); + + let result = unsafe { ZSTD_rust_initStaticCCtx(&state) }; + + assert!(result.is_null()); + assert!(context.events.is_empty()); + } + + #[test] + fn init_static_cctx_rejects_a_small_workspace_before_callback() { + let mut context = InitStaticCCtxTestContext::default(); + let mut workspace = [0usize; 1]; + let state = init_static_cctx_test_state( + &mut context, + workspace.as_mut_ptr().cast(), + size_of_val(&workspace), + size_of_val(&workspace), + ); + + let result = unsafe { ZSTD_rust_initStaticCCtx(&state) }; + + assert!(result.is_null()); + assert!(context.events.is_empty()); + } + #[derive(Default)] struct FreeCCtxTestContext { events: Vec<&'static str>,