refactor(cdict): move advanced workspace sizing into Rust

The advanced-CDict path previously kept a C callback that rebuilt the
ZSTD_rustCDictSizing projection and delegated the actual arithmetic back to
Rust. Replace that callback with a pointer to the explicit sizing projection,
so Rust owns the workspace-size policy while C continues to own private
zstd_cwksp allocation, object reservation, initialization, and teardown. The
projection replaces the old callback slot, preserving every later callback
offset and the synchronous lifetime of the C sizing values.

Update both sides' layout assertions, remove the redundant C helper, and make
the lifecycle tests assert that Rust's computed size reaches allocation and
workspace creation before the existing C-owned callbacks run. Custom-memory
validation and all other CDict callbacks remain unchanged.

Test Plan:
- `cc -fsyntax-only -Werror=incompatible-pointer-types -Ilib -Ilib/common -Ilib/compress -Ilib/decompress -Ilib/dict -Ilib/legacy lib/compress/zstd_compress.c` -- passed.
- `rustfmt +nightly --edition 2021 --check rust/src/zstd_compress_dictionary.rs` -- passed.
- `git diff --check` and `git diff --cached --check` -- passed.
- Cargo build/test/clippy, Make, fuzzers, and other heavy verification were intentionally not run per request.
This commit is contained in:
2026-07-20 15:00:52 +02:00
parent c2bed1242c
commit 0e93557879
2 changed files with 136 additions and 104 deletions
+25 -29
View File
@@ -2094,6 +2094,20 @@ typedef struct {
size_t optimalTSize;
size_t asanRedzoneSize;
} ZSTD_rustCDictSizing;
typedef char zstd_rust_cdict_sizing_layout[
(offsetof(ZSTD_rustCDictSizing, cdictSize) == 0
&& offsetof(ZSTD_rustCDictSizing, hufWorkspaceSize)
== sizeof(size_t)
&& offsetof(ZSTD_rustCDictSizing, hashLog3Max)
== 2 * sizeof(size_t)
&& offsetof(ZSTD_rustCDictSizing, matchTSize)
== 3 * sizeof(size_t)
&& offsetof(ZSTD_rustCDictSizing, optimalTSize)
== 4 * sizeof(size_t)
&& offsetof(ZSTD_rustCDictSizing, asanRedzoneSize)
== 5 * sizeof(size_t)
&& sizeof(ZSTD_rustCDictSizing) == 6 * sizeof(size_t))
? 1 : -1];
size_t ZSTD_rust_params_estimateCDictSizeFromCParams(
size_t dictSize, ZSTD_compressionParameters cParams,
int dictLoadMethod, const ZSTD_rustCDictSizing* sizing);
@@ -2658,10 +2672,6 @@ typedef char ZSTD_rust_init_cdict_state_layout[
? 1 : -1];
typedef int (*ZSTD_rust_createCDictAdvancedValidateCustomMem_f)(void* context);
typedef size_t (*ZSTD_rust_createCDictAdvancedWorkspaceSize_f)(
void* context, size_t dictSize, int dictLoadMethod,
const ZSTD_compressionParameters* cParams,
int useRowMatchFinder, int enableDedicatedDictSearch);
typedef void* (*ZSTD_rust_createCDictAdvancedAllocate_f)(
void* context, size_t workspaceSize);
typedef void (*ZSTD_rust_createCDictAdvancedCreateWorkspace_f)(
@@ -2689,7 +2699,7 @@ typedef struct {
U32 exclusionMask;
U32 ldmDefaultWindowLog;
ZSTD_rust_createCDictAdvancedValidateCustomMem_f validateCustomMem;
ZSTD_rust_createCDictAdvancedWorkspaceSize_f workspaceSize;
const ZSTD_rustCDictSizing* sizing;
ZSTD_rust_createCDictAdvancedAllocate_f allocate;
ZSTD_rust_createCDictAdvancedCreateWorkspace_f createWorkspace;
ZSTD_rust_createCDictAdvancedReserveObject_f reserveObject;
@@ -2719,7 +2729,7 @@ typedef char ZSTD_rust_create_cdict_advanced_state_layout[
== 5 * sizeof(void*) + sizeof(U32)
&& offsetof(ZSTD_rust_createCDictAdvancedState, validateCustomMem)
== 5 * sizeof(void*) + 2 * sizeof(U32)
&& offsetof(ZSTD_rust_createCDictAdvancedState, workspaceSize)
&& offsetof(ZSTD_rust_createCDictAdvancedState, sizing)
== 6 * sizeof(void*) + 2 * sizeof(U32)
&& offsetof(ZSTD_rust_createCDictAdvancedState, allocate)
== 7 * sizeof(void*) + 2 * sizeof(U32)
@@ -6880,28 +6890,6 @@ static int ZSTD_rust_createCDictAdvanced_validateCustomMem(void* context)
return ((!customMem->customAlloc) ^ (!customMem->customFree)) == 0;
}
static size_t ZSTD_rust_createCDictAdvanced_workspaceSize(
void* context, size_t dictSize, int dictLoadMethod,
const ZSTD_compressionParameters* cParams,
int useRowMatchFinder, int enableDedicatedDictSearch)
{
(void)context;
if (cParams == NULL) return 0;
{ ZSTD_rustCDictSizing const sizing = {
sizeof(ZSTD_CDict),
HUF_WORKSPACE_SIZE,
ZSTD_HASHLOG3_MAX,
sizeof(ZSTD_match_t),
sizeof(ZSTD_optimal_t),
ZSTD_RUST_ASAN_REDZONE_SIZE
};
return ZSTD_rust_params_estimateCDictWorkspaceSize(
dictSize, *cParams, dictLoadMethod,
useRowMatchFinder, enableDedicatedDictSearch,
&sizing);
}
}
static void* ZSTD_rust_createCDictAdvanced_allocate(
void* context, size_t workspaceSize)
{
@@ -7035,6 +7023,14 @@ ZSTD_CDict* ZSTD_createCDict_advanced2(
ZSTD_CCtx_params cctxParams;
ZSTD_rust_createCDictAdvancedContext context;
ZSTD_rust_createCDictAdvancedState state;
ZSTD_rustCDictSizing const sizing = {
sizeof(ZSTD_CDict),
HUF_WORKSPACE_SIZE,
ZSTD_HASHLOG3_MAX,
sizeof(ZSTD_match_t),
sizeof(ZSTD_optimal_t),
ZSTD_RUST_ASAN_REDZONE_SIZE
};
DEBUGLOG(3, "ZSTD_createCDict_advanced2, dictSize=%u, mode=%u", (unsigned)dictSize, (unsigned)dictContentType);
if (originalCctxParams == NULL) return NULL;
@@ -7049,7 +7045,7 @@ ZSTD_CDict* ZSTD_createCDict_advanced2(
state.exclusionMask = ZSTD_getCParamsExclusionMask();
state.ldmDefaultWindowLog = ZSTD_LDM_DEFAULT_WINDOW_LOG;
state.validateCustomMem = ZSTD_rust_createCDictAdvanced_validateCustomMem;
state.workspaceSize = ZSTD_rust_createCDictAdvanced_workspaceSize;
state.sizing = &sizing;
state.allocate = ZSTD_rust_createCDictAdvanced_allocate;
state.createWorkspace = ZSTD_rust_createCDictAdvanced_createWorkspace;
state.reserveObject = ZSTD_rust_createCDictAdvanced_reserveObject;
+111 -75
View File
@@ -16,11 +16,12 @@ use crate::fse_compress::FSE_buildCTable_wksp;
use crate::huf_compress::HUF_readCTable;
use crate::zstd_compress::{ZSTD_rust_copyCDictTableIntoCCtx, ZSTD_rust_windowClear};
use crate::zstd_compress_params::{
ZSTD_compressionParameters, ZSTD_frameParameters, ZSTD_parameters,
ZSTD_compressionParameters, ZSTD_frameParameters, ZSTD_parameters, ZSTD_rustCDictSizing,
ZSTD_rust_params_allocateChainTable, ZSTD_rust_params_defaultCLevel,
ZSTD_rust_params_getCParams, ZSTD_rust_params_getParamsInternal,
ZSTD_rust_params_rowMatchFinderUsed, ZSTD_rust_params_shouldAttachDict,
ZSTD_CONTENTSIZE_UNKNOWN, ZSTD_RUST_CPM_CREATE_CDICT, ZSTD_RUST_CPM_NO_ATTACH_DICT,
ZSTD_rust_params_estimateCDictWorkspaceSize, ZSTD_rust_params_getCParams,
ZSTD_rust_params_getParamsInternal, ZSTD_rust_params_rowMatchFinderUsed,
ZSTD_rust_params_shouldAttachDict, ZSTD_CONTENTSIZE_UNKNOWN, ZSTD_RUST_CPM_CREATE_CDICT,
ZSTD_RUST_CPM_NO_ATTACH_DICT,
};
use crate::zstd_compress_params_api::ZSTD_CCtx_params;
use crate::zstd_compress_stats::{
@@ -825,14 +826,6 @@ pub unsafe extern "C" fn ZSTD_rust_createCDict(
}
type CreateCDictAdvancedValidateCustomMemFn = unsafe extern "C" fn(*mut c_void) -> c_int;
type CreateCDictAdvancedWorkspaceSizeFn = unsafe extern "C" fn(
*mut c_void,
usize,
c_int,
*const ZSTD_compressionParameters,
c_int,
c_int,
) -> usize;
type CreateCDictAdvancedAllocateFn = unsafe extern "C" fn(*mut c_void, usize) -> *mut c_void;
type CreateCDictAdvancedCreateWorkspaceFn = unsafe extern "C" fn(*mut c_void, *mut c_void, usize);
type CreateCDictAdvancedReserveObjectFn = unsafe extern "C" fn(*mut c_void) -> *mut c_void;
@@ -852,12 +845,12 @@ type CreateCDictAdvancedFreeWorkspaceFn = unsafe extern "C" fn(*mut c_void, *mut
/// Explicit projection for the public `ZSTD_createCDict_advanced2` wrapper.
///
/// Rust owns context-free parameter preparation, custom-memory validation,
/// allocation, and callback ordering. C retains private workspace
/// construction, CDict initialization, and teardown behind narrow callbacks.
/// The three field pointers keep the private `ZSTD_CCtx_params` layout opaque
/// here while allowing C to publish the fields selected by the Rust parameter
/// leaf.
/// 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.
/// 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)]
pub struct ZSTD_rust_createCDictAdvancedState {
callback_context: *mut c_void,
@@ -868,7 +861,7 @@ pub struct ZSTD_rust_createCDictAdvancedState {
exclusion_mask: u32,
ldm_default_window_log: u32,
validate_custom_mem: CreateCDictAdvancedValidateCustomMemFn,
workspace_size: CreateCDictAdvancedWorkspaceSizeFn,
sizing: *const ZSTD_rustCDictSizing,
allocate: CreateCDictAdvancedAllocateFn,
create_workspace: CreateCDictAdvancedCreateWorkspaceFn,
reserve_object: CreateCDictAdvancedReserveObjectFn,
@@ -879,6 +872,16 @@ pub struct ZSTD_rust_createCDictAdvancedState {
free: CreateCDictAdvancedFreeFn,
}
const _: () = {
assert!(offset_of!(ZSTD_rustCDictSizing, cdictSize) == 0);
assert!(offset_of!(ZSTD_rustCDictSizing, hufWorkspaceSize) == size_of::<usize>());
assert!(offset_of!(ZSTD_rustCDictSizing, hashLog3Max) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rustCDictSizing, matchTSize) == 3 * size_of::<usize>());
assert!(offset_of!(ZSTD_rustCDictSizing, optimalTSize) == 4 * size_of::<usize>());
assert!(offset_of!(ZSTD_rustCDictSizing, asanRedzoneSize) == 5 * size_of::<usize>());
assert!(size_of::<ZSTD_rustCDictSizing>() == 6 * size_of::<usize>());
};
const _: () = {
assert!(offset_of!(ZSTD_rust_createCDictAdvancedState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_createCDictAdvancedState, cctx_params) == size_of::<usize>());
@@ -905,7 +908,7 @@ const _: () = {
== size_of::<[usize; 5]>() + size_of::<[u32; 2]>()
);
assert!(
offset_of!(ZSTD_rust_createCDictAdvancedState, workspace_size)
offset_of!(ZSTD_rust_createCDictAdvancedState, sizing)
== size_of::<[usize; 6]>() + size_of::<[u32; 2]>()
);
assert!(
@@ -965,6 +968,7 @@ pub unsafe extern "C" fn ZSTD_rust_createCDictAdvanced(
|| state.cparams.is_null()
|| state.enable_dedicated_dict_search.is_null()
|| state.use_row_match_finder.is_null()
|| state.sizing.is_null()
{
return ptr::null_mut();
}
@@ -986,13 +990,13 @@ pub unsafe extern "C" fn ZSTD_rust_createCDictAdvanced(
}
let workspace_size = unsafe {
(state.workspace_size)(
state.callback_context,
ZSTD_rust_params_estimateCDictWorkspaceSize(
dict_size,
*state.cparams,
dict_load_method,
state.cparams,
*state.use_row_match_finder,
*state.enable_dedicated_dict_search,
state.sizing,
)
};
let workspace = unsafe { (state.allocate)(state.callback_context, workspace_size) };
@@ -4610,12 +4614,6 @@ mod tests {
struct CreateCDictAdvancedProbe {
events: Vec<&'static str>,
custom_mem_valid: c_int,
workspace_size_result: usize,
workspace_size_dict_size: usize,
workspace_size_dict_load_method: c_int,
workspace_size_cparams: *const ZSTD_compressionParameters,
workspace_size_use_row_match_finder: c_int,
workspace_size_enable_dedicated_dict_search: c_int,
allocated_workspace: *mut c_void,
allocated_workspace_size: usize,
created_workspace: *mut c_void,
@@ -4644,24 +4642,6 @@ mod tests {
probe.custom_mem_valid
}
unsafe extern "C" fn create_cdict_advanced_test_workspace_size(
context: *mut c_void,
dict_size: usize,
dict_load_method: c_int,
cparams: *const ZSTD_compressionParameters,
use_row_match_finder: c_int,
enable_dedicated_dict_search: c_int,
) -> usize {
let probe = unsafe { &mut *context.cast::<CreateCDictAdvancedProbe>() };
probe.events.push("workspace_size");
probe.workspace_size_dict_size = dict_size;
probe.workspace_size_dict_load_method = dict_load_method;
probe.workspace_size_cparams = cparams;
probe.workspace_size_use_row_match_finder = use_row_match_finder;
probe.workspace_size_enable_dedicated_dict_search = enable_dedicated_dict_search;
probe.workspace_size_result
}
unsafe extern "C" fn create_cdict_advanced_test_allocate(
context: *mut c_void,
workspace_size: usize,
@@ -4761,6 +4741,50 @@ mod tests {
storage
}
fn create_cdict_advanced_test_cparams() -> ZSTD_compressionParameters {
ZSTD_compressionParameters {
windowLog: 20,
chainLog: 19,
hashLog: 18,
searchLog: 5,
minMatch: 4,
targetLength: 16,
strategy: 3,
}
}
static CREATE_CDICT_ADVANCED_TEST_SIZING: ZSTD_rustCDictSizing = ZSTD_rustCDictSizing {
cdictSize: 64,
hufWorkspaceSize: 32,
hashLog3Max: 17,
matchTSize: 16,
optimalTSize: 32,
asanRedzoneSize: 0,
};
fn create_cdict_advanced_test_sizing() -> *const ZSTD_rustCDictSizing {
&CREATE_CDICT_ADVANCED_TEST_SIZING
}
fn create_cdict_advanced_expected_workspace_size(
dict_size: usize,
cparams: ZSTD_compressionParameters,
dict_load_method: c_int,
use_row_match_finder: c_int,
enable_dedicated_dict_search: c_int,
) -> usize {
unsafe {
ZSTD_rust_params_estimateCDictWorkspaceSize(
dict_size,
cparams,
dict_load_method,
use_row_match_finder,
enable_dedicated_dict_search,
create_cdict_advanced_test_sizing(),
)
}
}
fn create_cdict_advanced_test_state(
probe: &mut CreateCDictAdvancedProbe,
cctx_params: *mut ZSTD_CCtx_params,
@@ -4777,7 +4801,7 @@ mod tests {
exclusion_mask: 0,
ldm_default_window_log: 27,
validate_custom_mem: create_cdict_advanced_test_validate_custom_mem,
workspace_size: create_cdict_advanced_test_workspace_size,
sizing: create_cdict_advanced_test_sizing(),
allocate: create_cdict_advanced_test_allocate,
create_workspace: create_cdict_advanced_test_create_workspace,
reserve_object: create_cdict_advanced_test_reserve_object,
@@ -4793,7 +4817,6 @@ mod tests {
fn create_cdict_advanced_publishes_params_and_preserves_create_init_order() {
let mut probe = CreateCDictAdvancedProbe {
custom_mem_valid: 1,
workspace_size_result: 123,
allocated_workspace: ptr::dangling_mut(),
cdict_result: ptr::dangling_mut(),
..Default::default()
@@ -4829,13 +4852,19 @@ mod tests {
ZSTD_DCT_RAW_CONTENT,
)
};
let expected_workspace_size = create_cdict_advanced_expected_workspace_size(
dict.len(),
cparams,
ZSTD_DLM_BY_REF,
use_row_match_finder,
enable_dedicated_dict_search,
);
assert_eq!(result, probe.cdict_result);
assert_eq!(
probe.events,
[
"validate",
"workspace_size",
"allocate",
"create_workspace",
"reserve_object",
@@ -4844,20 +4873,9 @@ mod tests {
"init"
]
);
assert_eq!(probe.workspace_size_dict_size, dict.len());
assert_eq!(probe.workspace_size_dict_load_method, ZSTD_DLM_BY_REF);
assert_eq!(probe.workspace_size_cparams, &cparams);
assert_eq!(
probe.workspace_size_use_row_match_finder,
use_row_match_finder
);
assert_eq!(
probe.workspace_size_enable_dedicated_dict_search,
enable_dedicated_dict_search
);
assert_eq!(probe.allocated_workspace_size, 123);
assert_eq!(probe.allocated_workspace_size, expected_workspace_size);
assert_eq!(probe.created_workspace, probe.allocated_workspace);
assert_eq!(probe.created_workspace_size, 123);
assert_eq!(probe.created_workspace_size, expected_workspace_size);
assert_eq!(probe.reserved_cdict, probe.cdict_result);
assert_eq!(probe.moved_cdict, probe.cdict_result);
assert_eq!(probe.initialized_cdict, probe.cdict_result);
@@ -4903,12 +4921,11 @@ mod tests {
fn create_cdict_advanced_stops_after_allocation_failure() {
let mut probe = CreateCDictAdvancedProbe {
custom_mem_valid: 1,
workspace_size_result: 321,
..Default::default()
};
let mut params_storage = create_cdict_advanced_test_params();
let cctx_params = params_storage.as_mut_ptr();
let cparams = ZSTD_compressionParameters::default();
let cparams = create_cdict_advanced_test_cparams();
let enable_dedicated_dict_search = 0;
let use_row_match_finder = 0;
let state = create_cdict_advanced_test_state(
@@ -4924,8 +4941,17 @@ mod tests {
};
assert!(result.is_null());
assert_eq!(probe.events, ["validate", "workspace_size", "allocate"]);
assert_eq!(probe.allocated_workspace_size, 321);
assert_eq!(probe.events, ["validate", "allocate"]);
assert_eq!(
probe.allocated_workspace_size,
create_cdict_advanced_expected_workspace_size(
0,
cparams,
ZSTD_DLM_BY_REF,
use_row_match_finder,
enable_dedicated_dict_search,
)
);
assert!(probe.created_workspace.is_null());
assert!(probe.free_workspace.is_null());
assert!(probe.free_cdict.is_null());
@@ -4936,15 +4962,21 @@ mod tests {
let workspace = ptr::dangling_mut::<c_void>();
let mut probe = CreateCDictAdvancedProbe {
custom_mem_valid: 1,
workspace_size_result: 321,
allocated_workspace: workspace,
..Default::default()
};
let mut params_storage = create_cdict_advanced_test_params();
let cctx_params = params_storage.as_mut_ptr();
let cparams = ZSTD_compressionParameters::default();
let cparams = create_cdict_advanced_test_cparams();
let enable_dedicated_dict_search = 0;
let use_row_match_finder = 0;
let expected_workspace_size = create_cdict_advanced_expected_workspace_size(
0,
cparams,
ZSTD_DLM_BY_REF,
use_row_match_finder,
enable_dedicated_dict_search,
);
let state = create_cdict_advanced_test_state(
&mut probe,
cctx_params,
@@ -4962,7 +4994,6 @@ mod tests {
probe.events,
[
"validate",
"workspace_size",
"allocate",
"create_workspace",
"reserve_object",
@@ -4970,7 +5001,7 @@ mod tests {
]
);
assert_eq!(probe.created_workspace, workspace);
assert_eq!(probe.created_workspace_size, 321);
assert_eq!(probe.created_workspace_size, expected_workspace_size);
assert_eq!(probe.free_workspace, workspace);
assert!(probe.free_cdict.is_null());
}
@@ -4981,7 +5012,6 @@ mod tests {
let workspace = ptr::dangling_mut::<c_void>();
let mut probe = CreateCDictAdvancedProbe {
custom_mem_valid: 1,
workspace_size_result: 321,
allocated_workspace: workspace,
cdict_result: cdict,
init_result: ERROR(ZstdErrorCode::MemoryAllocation),
@@ -4989,9 +5019,16 @@ mod tests {
};
let mut params_storage = create_cdict_advanced_test_params();
let cctx_params = params_storage.as_mut_ptr();
let cparams = ZSTD_compressionParameters::default();
let cparams = create_cdict_advanced_test_cparams();
let enable_dedicated_dict_search = 0;
let use_row_match_finder = 0;
let expected_workspace_size = create_cdict_advanced_expected_workspace_size(
0,
cparams,
ZSTD_DLM_BY_REF,
use_row_match_finder,
enable_dedicated_dict_search,
);
let state = create_cdict_advanced_test_state(
&mut probe,
cctx_params,
@@ -5009,7 +5046,6 @@ mod tests {
probe.events,
[
"validate",
"workspace_size",
"allocate",
"create_workspace",
"reserve_object",
@@ -5020,7 +5056,7 @@ mod tests {
]
);
assert_eq!(probe.created_workspace, workspace);
assert_eq!(probe.created_workspace_size, 321);
assert_eq!(probe.created_workspace_size, expected_workspace_size);
assert_eq!(probe.reserved_cdict, cdict);
assert_eq!(probe.moved_cdict, cdict);
assert_eq!(probe.initialized_cdict, cdict);