feat(compress): move static CCtx gating into Rust
Move public ZSTD_initStaticCCtx null, alignment, minimum-size, and callback dispatch into Rust. Keep private workspace reservations, block-state buffers, and CPU initialization in the C callback. Test Plan: - ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo test --manifest-path rust/Cargo.toml --release init_static_cctx -- --nocapture - ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo test --manifest-path rust/Cargo.toml --release - ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo clippy --manifest-path rust/Cargo.toml --release --all-targets -- -D warnings - ulimit -v 41943040; make -B -C programs -j1 zstd - ulimit -v 41943040; make -C tests -j1 test-zstream ZSTREAM_TESTTIME=-T1s
This commit is contained in:
@@ -1324,6 +1324,51 @@ pub unsafe extern "C" fn ZSTD_rust_createCCtx(
|
||||
cctx
|
||||
}
|
||||
|
||||
type InitStaticCCtxFn = unsafe extern "C" fn(*mut c_void) -> *mut c_void;
|
||||
const ZSTD_STATIC_WORKSPACE_ALIGNMENT: usize = 8;
|
||||
|
||||
/// 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.
|
||||
#[repr(C)]
|
||||
pub struct ZSTD_rust_initStaticCCtxState {
|
||||
callback_context: *mut c_void,
|
||||
workspace: *mut c_void,
|
||||
workspace_size: usize,
|
||||
cctx_size: usize,
|
||||
init: InitStaticCCtxFn,
|
||||
}
|
||||
|
||||
const _: () = {
|
||||
assert!(offset_of!(ZSTD_rust_initStaticCCtxState, callback_context) == 0);
|
||||
assert!(offset_of!(ZSTD_rust_initStaticCCtxState, workspace) == size_of::<usize>());
|
||||
assert!(offset_of!(ZSTD_rust_initStaticCCtxState, workspace_size) == 2 * size_of::<usize>());
|
||||
assert!(offset_of!(ZSTD_rust_initStaticCCtxState, cctx_size) == 3 * size_of::<usize>());
|
||||
assert!(offset_of!(ZSTD_rust_initStaticCCtxState, init) == 4 * size_of::<usize>());
|
||||
assert!(size_of::<ZSTD_rust_initStaticCCtxState>() == size_of::<[usize; 5]>());
|
||||
};
|
||||
|
||||
/// Validate static CCtx workspace ownership before entering the C leaf.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn ZSTD_rust_initStaticCCtx(
|
||||
state: *const ZSTD_rust_initStaticCCtxState,
|
||||
) -> *mut c_void {
|
||||
if state.is_null() {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
let state = unsafe { &*state };
|
||||
if state.callback_context.is_null()
|
||||
|| state.workspace.is_null()
|
||||
|| (state.workspace as usize) & (ZSTD_STATIC_WORKSPACE_ALIGNMENT - 1) != 0
|
||||
|| state.workspace_size <= state.cctx_size
|
||||
{
|
||||
return ptr::null_mut();
|
||||
}
|
||||
unsafe { (state.init)(state.callback_context) }
|
||||
}
|
||||
|
||||
type FreeCCtxContentFn = unsafe extern "C" fn(*mut c_void);
|
||||
type FreeCCtxObjectFn = unsafe extern "C" fn(*mut c_void);
|
||||
|
||||
@@ -12045,6 +12090,103 @@ mod tests {
|
||||
assert!(context.initialized.is_null());
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct InitStaticCCtxTestContext {
|
||||
events: Vec<&'static str>,
|
||||
result: *mut c_void,
|
||||
}
|
||||
|
||||
unsafe extern "C" fn init_static_cctx_test_init(context: *mut c_void) -> *mut c_void {
|
||||
let context = unsafe { &mut *context.cast::<InitStaticCCtxTestContext>() };
|
||||
context.events.push("init");
|
||||
context.result
|
||||
}
|
||||
|
||||
fn init_static_cctx_test_state(
|
||||
context: &mut InitStaticCCtxTestContext,
|
||||
workspace: *mut c_void,
|
||||
workspace_size: usize,
|
||||
cctx_size: usize,
|
||||
) -> ZSTD_rust_initStaticCCtxState {
|
||||
ZSTD_rust_initStaticCCtxState {
|
||||
callback_context: (context as *mut InitStaticCCtxTestContext).cast(),
|
||||
workspace,
|
||||
workspace_size,
|
||||
cctx_size,
|
||||
init: init_static_cctx_test_init,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn init_static_cctx_rejects_an_unaligned_workspace_before_callback() {
|
||||
let mut context = InitStaticCCtxTestContext {
|
||||
result: ptr::dangling_mut(),
|
||||
..Default::default()
|
||||
};
|
||||
let mut workspace = [0usize; 2];
|
||||
let unaligned_workspace = workspace.as_mut_ptr().cast::<u8>().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(),
|
||||
..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_eq!(result, context.result);
|
||||
assert_eq!(context.events, ["init"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn init_static_cctx_rejects_a_null_workspace() {
|
||||
let mut context = InitStaticCCtxTestContext {
|
||||
result: ptr::dangling_mut(),
|
||||
..Default::default()
|
||||
};
|
||||
let state = init_static_cctx_test_state(&mut context, ptr::null_mut(), usize::MAX, 0);
|
||||
|
||||
let result = unsafe { ZSTD_rust_initStaticCCtx(&state) };
|
||||
|
||||
assert!(result.is_null());
|
||||
assert!(context.events.is_empty());
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct FreeCCtxTestContext {
|
||||
events: Vec<&'static str>,
|
||||
|
||||
Reference in New Issue
Block a user