feat(compress): move static CDict gating into Rust
Move public ZSTD_initStaticCDict alignment, null, workspace-size, and callback dispatch into Rust. Keep C static workspace construction and dictionary initialization behind a private callback. Test Plan: - ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo test --manifest-path rust/Cargo.toml --release init_static_cdict -- --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:
@@ -387,6 +387,27 @@ typedef char ZSTD_rust_free_cdict_state_layout[
|
|||||||
== 3 * sizeof(void*)
|
== 3 * sizeof(void*)
|
||||||
&& sizeof(ZSTD_rust_freeCDictState) == 4 * sizeof(void*))
|
&& sizeof(ZSTD_rust_freeCDictState) == 4 * sizeof(void*))
|
||||||
? 1 : -1];
|
? 1 : -1];
|
||||||
|
typedef void* (*ZSTD_rust_initStaticCDictInit_f)(void* context);
|
||||||
|
typedef struct {
|
||||||
|
void* callbackContext;
|
||||||
|
void* workspace;
|
||||||
|
size_t workspaceSize;
|
||||||
|
size_t neededSize;
|
||||||
|
ZSTD_rust_initStaticCDictInit_f init;
|
||||||
|
} ZSTD_rust_initStaticCDictState;
|
||||||
|
void* ZSTD_rust_initStaticCDict(const ZSTD_rust_initStaticCDictState* state);
|
||||||
|
typedef char ZSTD_rust_init_static_cdict_state_layout[
|
||||||
|
(offsetof(ZSTD_rust_initStaticCDictState, callbackContext) == 0
|
||||||
|
&& offsetof(ZSTD_rust_initStaticCDictState, workspace)
|
||||||
|
== sizeof(void*)
|
||||||
|
&& offsetof(ZSTD_rust_initStaticCDictState, workspaceSize)
|
||||||
|
== 2 * sizeof(void*)
|
||||||
|
&& offsetof(ZSTD_rust_initStaticCDictState, neededSize)
|
||||||
|
== 3 * sizeof(void*)
|
||||||
|
&& offsetof(ZSTD_rust_initStaticCDictState, init)
|
||||||
|
== 4 * sizeof(void*)
|
||||||
|
&& sizeof(ZSTD_rust_initStaticCDictState) == 5 * sizeof(void*))
|
||||||
|
? 1 : -1];
|
||||||
typedef void (*ZSTD_rust_resetCCtxClearAllDicts_f)(void* context);
|
typedef void (*ZSTD_rust_resetCCtxClearAllDicts_f)(void* context);
|
||||||
typedef size_t (*ZSTD_rust_resetCCtxResetParams_f)(void* context);
|
typedef size_t (*ZSTD_rust_resetCCtxResetParams_f)(void* context);
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -5602,6 +5623,47 @@ size_t ZSTD_freeCDict(ZSTD_CDict* cdict)
|
|||||||
* Note : there is no corresponding "free" function.
|
* Note : there is no corresponding "free" function.
|
||||||
* Since workspace was allocated externally, it must be freed externally.
|
* Since workspace was allocated externally, it must be freed externally.
|
||||||
*/
|
*/
|
||||||
|
typedef struct {
|
||||||
|
void* workspace;
|
||||||
|
size_t workspaceSize;
|
||||||
|
const void* dict;
|
||||||
|
size_t dictSize;
|
||||||
|
ZSTD_dictLoadMethod_e dictLoadMethod;
|
||||||
|
ZSTD_dictContentType_e dictContentType;
|
||||||
|
ZSTD_compressionParameters cParams;
|
||||||
|
ZSTD_ParamSwitch_e useRowMatchFinder;
|
||||||
|
} ZSTD_rust_initStaticCDictContext;
|
||||||
|
|
||||||
|
static void* ZSTD_rust_initStaticCDict_init(void* opaque)
|
||||||
|
{
|
||||||
|
ZSTD_rust_initStaticCDictContext* const context =
|
||||||
|
(ZSTD_rust_initStaticCDictContext*)opaque;
|
||||||
|
ZSTD_cwksp ws;
|
||||||
|
ZSTD_CDict* cdict;
|
||||||
|
ZSTD_CCtx_params params;
|
||||||
|
|
||||||
|
ZSTD_cwksp_init(&ws, context->workspace, context->workspaceSize,
|
||||||
|
ZSTD_cwksp_static_alloc);
|
||||||
|
cdict = (ZSTD_CDict*)ZSTD_cwksp_reserve_object(&ws, sizeof(ZSTD_CDict));
|
||||||
|
if (cdict == NULL) return NULL;
|
||||||
|
ZSTD_cwksp_move(&cdict->workspace, &ws);
|
||||||
|
|
||||||
|
ZSTD_CCtxParams_init(¶ms, 0);
|
||||||
|
params.cParams = context->cParams;
|
||||||
|
params.useRowMatchFinder = context->useRowMatchFinder;
|
||||||
|
cdict->useRowMatchFinder = context->useRowMatchFinder;
|
||||||
|
cdict->compressionLevel = ZSTD_NO_CLEVEL;
|
||||||
|
|
||||||
|
if (ZSTD_isError( ZSTD_initCDict_internal(cdict,
|
||||||
|
context->dict, context->dictSize,
|
||||||
|
context->dictLoadMethod,
|
||||||
|
context->dictContentType,
|
||||||
|
params) ))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return cdict;
|
||||||
|
}
|
||||||
|
|
||||||
const ZSTD_CDict* ZSTD_initStaticCDict(
|
const ZSTD_CDict* ZSTD_initStaticCDict(
|
||||||
void* workspace, size_t workspaceSize,
|
void* workspace, size_t workspaceSize,
|
||||||
const void* dict, size_t dictSize,
|
const void* dict, size_t dictSize,
|
||||||
@@ -5617,35 +5679,24 @@ const ZSTD_CDict* ZSTD_initStaticCDict(
|
|||||||
: ZSTD_cwksp_alloc_size(ZSTD_cwksp_align(dictSize, sizeof(void*))))
|
: ZSTD_cwksp_alloc_size(ZSTD_cwksp_align(dictSize, sizeof(void*))))
|
||||||
+ ZSTD_cwksp_alloc_size(HUF_WORKSPACE_SIZE)
|
+ ZSTD_cwksp_alloc_size(HUF_WORKSPACE_SIZE)
|
||||||
+ matchStateSize;
|
+ matchStateSize;
|
||||||
ZSTD_CDict* cdict;
|
ZSTD_rust_initStaticCDictContext context;
|
||||||
ZSTD_CCtx_params params;
|
ZSTD_rust_initStaticCDictState state;
|
||||||
|
|
||||||
DEBUGLOG(4, "ZSTD_initStaticCDict (dictSize==%u)", (unsigned)dictSize);
|
DEBUGLOG(4, "ZSTD_initStaticCDict (dictSize==%u)", (unsigned)dictSize);
|
||||||
if ((size_t)workspace & 7) return NULL; /* 8-aligned */
|
context.workspace = workspace;
|
||||||
|
context.workspaceSize = workspaceSize;
|
||||||
{
|
context.dict = dict;
|
||||||
ZSTD_cwksp ws;
|
context.dictSize = dictSize;
|
||||||
ZSTD_cwksp_init(&ws, workspace, workspaceSize, ZSTD_cwksp_static_alloc);
|
context.dictLoadMethod = dictLoadMethod;
|
||||||
cdict = (ZSTD_CDict*)ZSTD_cwksp_reserve_object(&ws, sizeof(ZSTD_CDict));
|
context.dictContentType = dictContentType;
|
||||||
if (cdict == NULL) return NULL;
|
context.cParams = cParams;
|
||||||
ZSTD_cwksp_move(&cdict->workspace, &ws);
|
context.useRowMatchFinder = useRowMatchFinder;
|
||||||
}
|
state.callbackContext = &context;
|
||||||
|
state.workspace = workspace;
|
||||||
if (workspaceSize < neededSize) return NULL;
|
state.workspaceSize = workspaceSize;
|
||||||
|
state.neededSize = neededSize;
|
||||||
ZSTD_CCtxParams_init(¶ms, 0);
|
state.init = ZSTD_rust_initStaticCDict_init;
|
||||||
params.cParams = cParams;
|
return (const ZSTD_CDict*)ZSTD_rust_initStaticCDict(&state);
|
||||||
params.useRowMatchFinder = useRowMatchFinder;
|
|
||||||
cdict->useRowMatchFinder = useRowMatchFinder;
|
|
||||||
cdict->compressionLevel = ZSTD_NO_CLEVEL;
|
|
||||||
|
|
||||||
if (ZSTD_isError( ZSTD_initCDict_internal(cdict,
|
|
||||||
dict, dictSize,
|
|
||||||
dictLoadMethod, dictContentType,
|
|
||||||
params) ))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return cdict;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ZSTD_compressionParameters ZSTD_getCParamsFromCDict(const ZSTD_CDict* cdict)
|
ZSTD_compressionParameters ZSTD_getCParamsFromCDict(const ZSTD_CDict* cdict)
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ const ZSTD_MAGIC_DICTIONARY: u32 = 0xEC30_A437;
|
|||||||
const ZSTD_DCT_AUTO: c_int = 0;
|
const ZSTD_DCT_AUTO: c_int = 0;
|
||||||
const ZSTD_DCT_RAW_CONTENT: c_int = 1;
|
const ZSTD_DCT_RAW_CONTENT: c_int = 1;
|
||||||
const ZSTD_DCT_FULL_DICT: c_int = 2;
|
const ZSTD_DCT_FULL_DICT: c_int = 2;
|
||||||
|
const ZSTD_STATIC_WORKSPACE_ALIGNMENT: usize = 8;
|
||||||
|
|
||||||
/// C keeps match-state/content insertion private because it depends on the
|
/// C keeps match-state/content insertion private because it depends on the
|
||||||
/// configuration-sensitive `ZSTD_MatchState_t`, `ldmState_t`, workspace, and
|
/// configuration-sensitive `ZSTD_MatchState_t`, `ldmState_t`, workspace, and
|
||||||
@@ -479,6 +480,50 @@ pub unsafe extern "C" fn ZSTD_rust_freeCDict(state: *const ZSTD_rust_freeCDictSt
|
|||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type InitStaticCDictFn = unsafe extern "C" fn(*mut c_void) -> *mut c_void;
|
||||||
|
|
||||||
|
/// Explicit projection for the public `ZSTD_initStaticCDict` wrapper.
|
||||||
|
///
|
||||||
|
/// Rust owns workspace pointer/alignment validation, minimum-size validation,
|
||||||
|
/// and callback ordering. C retains the private static-workspace layout and
|
||||||
|
/// dictionary initialization behind one callback.
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct ZSTD_rust_initStaticCDictState {
|
||||||
|
callback_context: *mut c_void,
|
||||||
|
workspace: *mut c_void,
|
||||||
|
workspace_size: usize,
|
||||||
|
needed_size: usize,
|
||||||
|
init: InitStaticCDictFn,
|
||||||
|
}
|
||||||
|
|
||||||
|
const _: () = {
|
||||||
|
assert!(offset_of!(ZSTD_rust_initStaticCDictState, callback_context) == 0);
|
||||||
|
assert!(offset_of!(ZSTD_rust_initStaticCDictState, workspace) == size_of::<usize>());
|
||||||
|
assert!(offset_of!(ZSTD_rust_initStaticCDictState, workspace_size) == 2 * size_of::<usize>());
|
||||||
|
assert!(offset_of!(ZSTD_rust_initStaticCDictState, needed_size) == 3 * size_of::<usize>());
|
||||||
|
assert!(offset_of!(ZSTD_rust_initStaticCDictState, init) == 4 * size_of::<usize>());
|
||||||
|
assert!(size_of::<ZSTD_rust_initStaticCDictState>() == size_of::<[usize; 5]>());
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Validate static CDict workspace ownership before entering the C leaf.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn ZSTD_rust_initStaticCDict(
|
||||||
|
state: *const ZSTD_rust_initStaticCDictState,
|
||||||
|
) -> *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.needed_size
|
||||||
|
{
|
||||||
|
return ptr::null_mut();
|
||||||
|
}
|
||||||
|
unsafe { (state.init)(state.callback_context) }
|
||||||
|
}
|
||||||
|
|
||||||
type CompressBeginUsingDictBeginFn =
|
type CompressBeginUsingDictBeginFn =
|
||||||
unsafe extern "C" fn(*mut c_void, *const c_void, usize, *const c_void, u64) -> usize;
|
unsafe extern "C" fn(*mut c_void, *const c_void, usize, *const c_void, u64) -> usize;
|
||||||
|
|
||||||
@@ -2939,6 +2984,103 @@ mod tests {
|
|||||||
assert_eq!(result, 0);
|
assert_eq!(result, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct InitStaticCDictProbe {
|
||||||
|
events: Vec<&'static str>,
|
||||||
|
result: *mut c_void,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn init_static_cdict_test_init(context: *mut c_void) -> *mut c_void {
|
||||||
|
let probe = unsafe { &mut *context.cast::<InitStaticCDictProbe>() };
|
||||||
|
probe.events.push("init");
|
||||||
|
probe.result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_static_cdict_test_state(
|
||||||
|
probe: &mut InitStaticCDictProbe,
|
||||||
|
workspace: *mut c_void,
|
||||||
|
workspace_size: usize,
|
||||||
|
needed_size: usize,
|
||||||
|
) -> ZSTD_rust_initStaticCDictState {
|
||||||
|
ZSTD_rust_initStaticCDictState {
|
||||||
|
callback_context: (probe as *mut InitStaticCDictProbe).cast(),
|
||||||
|
workspace,
|
||||||
|
workspace_size,
|
||||||
|
needed_size,
|
||||||
|
init: init_static_cdict_test_init,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn init_static_cdict_rejects_an_unaligned_workspace_before_callback() {
|
||||||
|
let mut probe = InitStaticCDictProbe {
|
||||||
|
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_cdict_test_state(&mut probe, unaligned_workspace, usize::MAX, 0);
|
||||||
|
|
||||||
|
let result = unsafe { ZSTD_rust_initStaticCDict(&state) };
|
||||||
|
|
||||||
|
assert!(result.is_null());
|
||||||
|
assert!(probe.events.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn init_static_cdict_rejects_insufficient_workspace_before_callback() {
|
||||||
|
let mut probe = InitStaticCDictProbe {
|
||||||
|
result: ptr::dangling_mut(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let mut workspace = [0usize; 1];
|
||||||
|
let state = init_static_cdict_test_state(
|
||||||
|
&mut probe,
|
||||||
|
workspace.as_mut_ptr().cast(),
|
||||||
|
size_of_val(&workspace),
|
||||||
|
size_of_val(&workspace) + 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
let result = unsafe { ZSTD_rust_initStaticCDict(&state) };
|
||||||
|
|
||||||
|
assert!(result.is_null());
|
||||||
|
assert!(probe.events.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn init_static_cdict_calls_the_initializer_after_workspace_validation() {
|
||||||
|
let mut probe = InitStaticCDictProbe {
|
||||||
|
result: ptr::dangling_mut(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let mut workspace = [0usize; 1];
|
||||||
|
let state = init_static_cdict_test_state(
|
||||||
|
&mut probe,
|
||||||
|
workspace.as_mut_ptr().cast(),
|
||||||
|
size_of_val(&workspace),
|
||||||
|
size_of_val(&workspace),
|
||||||
|
);
|
||||||
|
|
||||||
|
let result = unsafe { ZSTD_rust_initStaticCDict(&state) };
|
||||||
|
|
||||||
|
assert_eq!(result, probe.result);
|
||||||
|
assert_eq!(probe.events, ["init"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn init_static_cdict_rejects_a_null_workspace() {
|
||||||
|
let mut probe = InitStaticCDictProbe {
|
||||||
|
result: ptr::dangling_mut(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let state = init_static_cdict_test_state(&mut probe, ptr::null_mut(), usize::MAX, 0);
|
||||||
|
|
||||||
|
let result = unsafe { ZSTD_rust_initStaticCDict(&state) };
|
||||||
|
|
||||||
|
assert!(result.is_null());
|
||||||
|
assert!(probe.events.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct CompressBeginUsingDictProbe {
|
struct CompressBeginUsingDictProbe {
|
||||||
events: Vec<&'static str>,
|
events: Vec<&'static str>,
|
||||||
|
|||||||
Reference in New Issue
Block a user