refactor(dict): move advanced-CDict copy policy to Rust

Keep the opaque ZSTD_CCtx_params copy operation in C, but make Rust own the public advanced2 null-source validation and copy-before-preparation ordering. The bridge now rejects a missing source before any allocator or workspace callback, with explicit ABI assertions and focused order/null-input tests.

Test Plan: Not run in this atomic commit; the capped serial Rust, native, smoke, and original test-suite verification follows.
This commit is contained in:
2026-07-21 08:15:25 +02:00
parent f44d9726c3
commit 0800f0d4c0
2 changed files with 96 additions and 10 deletions
+18 -4
View File
@@ -2910,6 +2910,8 @@ typedef void (*ZSTD_rust_createCDictAdvancedFreeWorkspace_f)(
void* context, void* workspace);
typedef void (*ZSTD_rust_createCDictAdvancedFree_f)(
void* context, void* cdict);
typedef void (*ZSTD_rust_createCDictAdvancedCopyParams_f)(
void* context, void* destination, const void* source);
typedef struct {
void* callbackContext;
ZSTD_CCtx_params* cctxParams;
@@ -2928,6 +2930,8 @@ typedef struct {
ZSTD_rust_createCDictAdvancedInit_f init;
ZSTD_rust_createCDictAdvancedFreeWorkspace_f freeWorkspace;
ZSTD_rust_createCDictAdvancedFree_f free;
const void* originalCctxParams;
ZSTD_rust_createCDictAdvancedCopyParams_f copyParams;
} ZSTD_rust_createCDictAdvancedState;
void* ZSTD_rust_createCDictAdvanced(
const ZSTD_rust_createCDictAdvancedState* state,
@@ -2967,8 +2971,12 @@ typedef char ZSTD_rust_create_cdict_advanced_state_layout[
== 13 * sizeof(void*) + 2 * sizeof(U32)
&& offsetof(ZSTD_rust_createCDictAdvancedState, free)
== 14 * sizeof(void*) + 2 * sizeof(U32)
&& offsetof(ZSTD_rust_createCDictAdvancedState, originalCctxParams)
== 15 * sizeof(void*) + 2 * sizeof(U32)
&& offsetof(ZSTD_rust_createCDictAdvancedState, copyParams)
== 16 * sizeof(void*) + 2 * sizeof(U32)
&& sizeof(ZSTD_rust_createCDictAdvancedState)
== 15 * sizeof(void*) + 2 * sizeof(U32))
== 17 * sizeof(void*) + 2 * sizeof(U32))
? 1 : -1];
typedef void (*ZSTD_rust_createCDictAdvancedWrapperInitParams_f)(
@@ -7200,6 +7208,13 @@ static int ZSTD_rust_createCDictAdvanced_validateCustomMem(void* context)
return ((!customMem->customAlloc) ^ (!customMem->customFree)) == 0;
}
static void ZSTD_rust_createCDictAdvanced_copyParams(
void* context, void* destination, const void* source)
{
(void)context;
*(ZSTD_CCtx_params*)destination = *(const ZSTD_CCtx_params*)source;
}
static void* ZSTD_rust_createCDictAdvanced_allocate(
void* context, size_t workspaceSize)
{
@@ -7343,9 +7358,6 @@ ZSTD_CDict* ZSTD_createCDict_advanced2(
};
DEBUGLOG(3, "ZSTD_createCDict_advanced2, dictSize=%u, mode=%u", (unsigned)dictSize, (unsigned)dictContentType);
if (originalCctxParams == NULL) return NULL;
cctxParams = *originalCctxParams;
context.customMem = customMem;
state.callbackContext = &context;
state.cctxParams = &cctxParams;
@@ -7364,6 +7376,8 @@ ZSTD_CDict* ZSTD_createCDict_advanced2(
state.init = ZSTD_rust_createCDictAdvanced_init;
state.freeWorkspace = ZSTD_rust_createCDictAdvanced_freeWorkspace;
state.free = ZSTD_rust_createCDictAdvanced_free;
state.originalCctxParams = originalCctxParams;
state.copyParams = ZSTD_rust_createCDictAdvanced_copyParams;
return (ZSTD_CDict*)ZSTD_rust_createCDictAdvanced(
&state, dict, dictSize,
(int)dictLoadMethod, (int)dictContentType);
+78 -6
View File
@@ -900,13 +900,16 @@ type CreateCDictAdvancedInitFn = unsafe extern "C" fn(
) -> usize;
type CreateCDictAdvancedFreeFn = unsafe extern "C" fn(*mut c_void, *mut c_void);
type CreateCDictAdvancedFreeWorkspaceFn = unsafe extern "C" fn(*mut c_void, *mut c_void);
type CreateCDictAdvancedCopyParamsFn =
unsafe extern "C" fn(*mut c_void, *mut c_void, *const c_void);
/// Explicit projection for the public `ZSTD_createCDict_advanced2` wrapper.
///
/// Rust owns context-free parameter preparation, workspace-size policy,
/// custom-memory validation, allocation, and callback ordering. C supplies
/// the private layout-size inputs and retains workspace construction, CDict
/// initialization, and teardown behind narrow callbacks.
/// source-parameter validation/copy ordering, custom-memory validation,
/// allocation, and callback ordering. C supplies the private layout-size
/// inputs and retains workspace construction, CDict initialization, and
/// teardown behind narrow callbacks.
/// The pointer fields keep the private `ZSTD_CCtx_params` layout opaque here
/// while allowing C to publish the fields selected by the Rust parameter leaf.
#[repr(C)]
@@ -928,6 +931,8 @@ pub struct ZSTD_rust_createCDictAdvancedState {
init: CreateCDictAdvancedInitFn,
free_workspace: CreateCDictAdvancedFreeWorkspaceFn,
free: CreateCDictAdvancedFreeFn,
original_cctx_params: *const c_void,
copy_params: CreateCDictAdvancedCopyParamsFn,
}
const _: () = {
@@ -1002,9 +1007,17 @@ const _: () = {
== size_of::<[usize; 14]>() + size_of::<[u32; 2]>()
);
assert!(
size_of::<ZSTD_rust_createCDictAdvancedState>()
offset_of!(ZSTD_rust_createCDictAdvancedState, original_cctx_params)
== size_of::<[usize; 15]>() + size_of::<[u32; 2]>()
);
assert!(
offset_of!(ZSTD_rust_createCDictAdvancedState, copy_params)
== size_of::<[usize; 16]>() + size_of::<[u32; 2]>()
);
assert!(
size_of::<ZSTD_rust_createCDictAdvancedState>()
== size_of::<[usize; 17]>() + size_of::<[u32; 2]>()
);
};
/// Prepare advanced-CDict parameters and run the C-owned construction leaves
@@ -1027,10 +1040,22 @@ pub unsafe extern "C" fn ZSTD_rust_createCDictAdvanced(
|| state.enable_dedicated_dict_search.is_null()
|| state.use_row_match_finder.is_null()
|| state.sizing.is_null()
|| state.original_cctx_params.is_null()
{
return ptr::null_mut();
}
// The source is opaque here: C retains the private parameter-layout copy,
// while Rust owns rejecting a missing source and placing that copy before
// parameter preparation and the allocation/initialization callbacks.
unsafe {
(state.copy_params)(
state.callback_context,
state.cctx_params.cast(),
state.original_cctx_params,
);
}
let prepare_result = unsafe {
crate::zstd_compress_params_api::ZSTD_rust_params_prepareAdvancedCDict(
state.cctx_params,
@@ -4842,11 +4867,24 @@ mod tests {
init_dict_load_method: c_int,
init_dict_content_type: c_int,
init_cctx_params: *const ZSTD_CCtx_params,
copied_destination: *mut c_void,
copied_source: *const c_void,
free_workspace: *mut c_void,
free_cdict: *mut c_void,
init_result: usize,
}
unsafe extern "C" fn create_cdict_advanced_test_copy_params(
context: *mut c_void,
destination: *mut c_void,
source: *const c_void,
) {
let probe = unsafe { &mut *context.cast::<CreateCDictAdvancedProbe>() };
probe.events.push("copy_params");
probe.copied_destination = destination;
probe.copied_source = source;
}
unsafe extern "C" fn create_cdict_advanced_test_validate_custom_mem(
context: *mut c_void,
) -> c_int {
@@ -5023,6 +5061,8 @@ mod tests {
init: create_cdict_advanced_test_init,
free_workspace: create_cdict_advanced_test_free_workspace,
free: create_cdict_advanced_test_free,
original_cctx_params: ptr::dangling(),
copy_params: create_cdict_advanced_test_copy_params,
}
}
@@ -5077,6 +5117,7 @@ mod tests {
assert_eq!(
probe.events,
[
"copy_params",
"validate",
"allocate",
"create_workspace",
@@ -5094,6 +5135,8 @@ mod tests {
assert_eq!(probe.initialized_cdict, probe.cdict_result);
assert_eq!(probe.use_row_match_finder, use_row_match_finder);
assert_eq!(probe.init_cdict, probe.cdict_result);
assert_eq!(probe.copied_destination, cctx_params.cast());
assert_eq!(probe.copied_source, state.original_cctx_params);
assert_eq!(probe.init_dict, dict.as_ptr().cast());
assert_eq!(probe.init_dict_size, dict.len());
assert_eq!(probe.init_dict_load_method, ZSTD_DLM_BY_REF);
@@ -5124,7 +5167,7 @@ mod tests {
};
assert!(result.is_null());
assert_eq!(probe.events, ["validate"]);
assert_eq!(probe.events, ["copy_params", "validate"]);
assert!(probe.created_workspace.is_null());
assert!(probe.free_workspace.is_null());
assert!(probe.free_cdict.is_null());
@@ -5154,7 +5197,7 @@ mod tests {
};
assert!(result.is_null());
assert_eq!(probe.events, ["validate", "allocate"]);
assert_eq!(probe.events, ["copy_params", "validate", "allocate"]);
assert_eq!(
probe.allocated_workspace_size,
create_cdict_advanced_expected_workspace_size(
@@ -5170,6 +5213,33 @@ mod tests {
assert!(probe.free_cdict.is_null());
}
#[test]
fn create_cdict_advanced_rejects_missing_source_params_before_copy() {
let mut probe = CreateCDictAdvancedProbe::default();
let mut params_storage = create_cdict_advanced_test_params();
let cctx_params = params_storage.as_mut_ptr();
let cparams = ZSTD_compressionParameters::default();
let enable_dedicated_dict_search = 0;
let use_row_match_finder = 0;
let mut state = create_cdict_advanced_test_state(
&mut probe,
cctx_params,
&cparams,
&enable_dedicated_dict_search,
&use_row_match_finder,
);
state.original_cctx_params = ptr::null();
let result = unsafe {
ZSTD_rust_createCDictAdvanced(&state, ptr::null(), 0, ZSTD_DLM_BY_REF, ZSTD_DCT_AUTO)
};
assert!(result.is_null());
assert!(probe.events.is_empty());
assert!(probe.copied_destination.is_null());
assert!(probe.copied_source.is_null());
}
#[test]
fn create_cdict_advanced_frees_workspace_after_reservation_failure() {
let workspace = ptr::dangling_mut::<c_void>();
@@ -5206,6 +5276,7 @@ mod tests {
assert_eq!(
probe.events,
[
"copy_params",
"validate",
"allocate",
"create_workspace",
@@ -5258,6 +5329,7 @@ mod tests {
assert_eq!(
probe.events,
[
"copy_params",
"validate",
"allocate",
"create_workspace",