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:
2026-07-19 16:32:00 +02:00
parent c892684b34
commit 463ceeb86f
2 changed files with 188 additions and 5 deletions
+46 -5
View File
@@ -344,6 +344,27 @@ typedef char ZSTD_rust_create_cctx_state_layout[
== 4 * sizeof(void*)
&& sizeof(ZSTD_rust_createCCtxState) == 5 * sizeof(void*))
? 1 : -1];
typedef void* (*ZSTD_rust_initStaticCCtxInit_f)(void* context);
typedef struct {
void* callbackContext;
void* workspace;
size_t workspaceSize;
size_t cctxSize;
ZSTD_rust_initStaticCCtxInit_f init;
} ZSTD_rust_initStaticCCtxState;
void* ZSTD_rust_initStaticCCtx(const ZSTD_rust_initStaticCCtxState* state);
typedef char ZSTD_rust_init_static_cctx_state_layout[
(offsetof(ZSTD_rust_initStaticCCtxState, callbackContext) == 0
&& offsetof(ZSTD_rust_initStaticCCtxState, workspace)
== sizeof(void*)
&& offsetof(ZSTD_rust_initStaticCCtxState, workspaceSize)
== 2 * sizeof(void*)
&& offsetof(ZSTD_rust_initStaticCCtxState, cctxSize)
== 3 * sizeof(void*)
&& offsetof(ZSTD_rust_initStaticCCtxState, init)
== 4 * sizeof(void*)
&& sizeof(ZSTD_rust_initStaticCCtxState) == 5 * sizeof(void*))
? 1 : -1];
typedef void* (*ZSTD_rust_createCDictCreate_f)(
void* context, const void* dict, size_t dictSize,
int dictLoadMethod, int dictContentType,
@@ -2168,20 +2189,26 @@ ZSTD_CCtx* ZSTD_createCCtx_advanced(ZSTD_customMem customMem)
return (ZSTD_CCtx*)ZSTD_rust_createCCtx(&state);
}
ZSTD_CCtx* ZSTD_initStaticCCtx(void* workspace, size_t workspaceSize)
typedef struct {
void* workspace;
size_t workspaceSize;
} ZSTD_rust_initStaticCCtxContext;
static void* ZSTD_rust_initStaticCCtx_init(void* opaque)
{
ZSTD_rust_initStaticCCtxContext* const context =
(ZSTD_rust_initStaticCCtxContext*)opaque;
ZSTD_cwksp ws;
ZSTD_CCtx* cctx;
if (workspaceSize <= sizeof(ZSTD_CCtx)) return NULL; /* minimum size */
if ((size_t)workspace & 7) return NULL; /* must be 8-aligned */
ZSTD_cwksp_init(&ws, workspace, workspaceSize, ZSTD_cwksp_static_alloc);
ZSTD_cwksp_init(&ws, context->workspace, context->workspaceSize,
ZSTD_cwksp_static_alloc);
cctx = (ZSTD_CCtx*)ZSTD_cwksp_reserve_object(&ws, sizeof(ZSTD_CCtx));
if (cctx == NULL) return NULL;
ZSTD_memset(cctx, 0, sizeof(ZSTD_CCtx));
ZSTD_cwksp_move(&cctx->workspace, &ws);
cctx->staticSize = workspaceSize;
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;
@@ -2193,6 +2220,20 @@ ZSTD_CCtx* ZSTD_initStaticCCtx(void* workspace, size_t workspaceSize)
return cctx;
}
ZSTD_CCtx* ZSTD_initStaticCCtx(void* workspace, size_t workspaceSize)
{
ZSTD_rust_initStaticCCtxContext context;
ZSTD_rust_initStaticCCtxState state;
context.workspace = workspace;
context.workspaceSize = workspaceSize;
state.callbackContext = &context;
state.workspace = workspace;
state.workspaceSize = workspaceSize;
state.cctxSize = sizeof(ZSTD_CCtx);
state.init = ZSTD_rust_initStaticCCtx_init;
return (ZSTD_CCtx*)ZSTD_rust_initStaticCCtx(&state);
}
/**
* Clears and frees all of the dictionaries in the CCtx.
*/
+142
View File
@@ -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>,