feat(cli): move decompression resource orchestration to Rust
Move the decompression resource creation order into the Rust CLI policy layer: dictionary stat and patch-memory preparation, decoder allocation, window/checksum configuration, dictionary reset/attachment, and asynchronous pool creation now run through one Rust-owned sequence. Keep dRess_t, stat_t, dictionary buffers, decoder context, pools, and exact allocation errors in C callbacks. Preserve the original patch-from and dictionary-reference behavior while adding ABI layout assertions, scalar policy coverage, and error short-circuit tests for the new boundary. Test Plan: - ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo +nightly fmt --manifest-path rust/cli/Cargo.toml --all - ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo clippy --manifest-path rust/cli/Cargo.toml --all-targets -- -D warnings - ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo test --manifest-path rust/cli/Cargo.toml --all-targets (207 passed) - ulimit -v 41943040; CARGO_BUILD_JOBS=1 make -j1
This commit is contained in:
@@ -358,6 +358,99 @@ pub unsafe extern "C" fn FIO_rust_freeDResources(state: *const FIO_rust_freeDRes
|
||||
}
|
||||
}
|
||||
|
||||
pub type FIO_createDResourcesPrepareDictionaryFn =
|
||||
unsafe extern "C" fn(*mut c_void, *mut FIO_prefs_t, *const c_char);
|
||||
pub type FIO_createDResourcesCreateDctxFn = unsafe extern "C" fn(*mut c_void);
|
||||
pub type FIO_createDResourcesSetMaxWindowSizeFn =
|
||||
unsafe extern "C" fn(*mut c_void, c_uint) -> usize;
|
||||
pub type FIO_createDResourcesSetParameterFn = unsafe extern "C" fn(*mut c_void, c_int) -> usize;
|
||||
pub type FIO_createDResourcesLoadDictionaryFn =
|
||||
unsafe extern "C" fn(*mut c_void, *mut FIO_prefs_t, *const c_char) -> usize;
|
||||
pub type FIO_createDResourcesCreatePoolFn = unsafe extern "C" fn(*mut c_void, *const FIO_prefs_t);
|
||||
pub type FIO_createDResourcesIsErrorFn = unsafe extern "C" fn(usize) -> c_int;
|
||||
|
||||
/// Decompression-resource initialization projection.
|
||||
///
|
||||
/// Rust owns the stable creation order and error short-circuiting. C keeps
|
||||
/// the `dRess_t`, decoder context, dictionary/stat storage, and asynchronous
|
||||
/// pools private behind callbacks which preserve the original diagnostics and
|
||||
/// resource operations.
|
||||
#[repr(C)]
|
||||
pub struct FIO_rust_createDResourcesState {
|
||||
callback_context: *mut c_void,
|
||||
prefs: *mut FIO_prefs_t,
|
||||
dict_file_name: *const c_char,
|
||||
prepare_dictionary: FIO_createDResourcesPrepareDictionaryFn,
|
||||
create_dctx: FIO_createDResourcesCreateDctxFn,
|
||||
set_max_window_size: FIO_createDResourcesSetMaxWindowSizeFn,
|
||||
set_parameter: FIO_createDResourcesSetParameterFn,
|
||||
load_dictionary: FIO_createDResourcesLoadDictionaryFn,
|
||||
create_write_pool: FIO_createDResourcesCreatePoolFn,
|
||||
create_read_pool: FIO_createDResourcesCreatePoolFn,
|
||||
is_error: FIO_createDResourcesIsErrorFn,
|
||||
}
|
||||
|
||||
const _: () = {
|
||||
let word = size_of::<usize>();
|
||||
assert!(offset_of!(FIO_rust_createDResourcesState, callback_context) == 0);
|
||||
assert!(offset_of!(FIO_rust_createDResourcesState, prefs) == word);
|
||||
assert!(offset_of!(FIO_rust_createDResourcesState, dict_file_name) == 2 * word);
|
||||
assert!(offset_of!(FIO_rust_createDResourcesState, prepare_dictionary) == 3 * word);
|
||||
assert!(offset_of!(FIO_rust_createDResourcesState, create_dctx) == 4 * word);
|
||||
assert!(offset_of!(FIO_rust_createDResourcesState, set_max_window_size) == 5 * word);
|
||||
assert!(offset_of!(FIO_rust_createDResourcesState, set_parameter) == 6 * word);
|
||||
assert!(offset_of!(FIO_rust_createDResourcesState, load_dictionary) == 7 * word);
|
||||
assert!(offset_of!(FIO_rust_createDResourcesState, create_write_pool) == 8 * word);
|
||||
assert!(offset_of!(FIO_rust_createDResourcesState, create_read_pool) == 9 * word);
|
||||
assert!(offset_of!(FIO_rust_createDResourcesState, is_error) == 10 * word);
|
||||
assert!(size_of::<FIO_rust_createDResourcesState>() == 11 * word);
|
||||
};
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn FIO_rust_createDResources(
|
||||
state: *const FIO_rust_createDResourcesState,
|
||||
) -> usize {
|
||||
let Some(state) = (unsafe { state.as_ref() }) else {
|
||||
return usize::MAX;
|
||||
};
|
||||
if state.prefs.is_null() {
|
||||
return usize::MAX;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
(state.prepare_dictionary)(state.callback_context, state.prefs, state.dict_file_name);
|
||||
(state.create_dctx)(state.callback_context);
|
||||
}
|
||||
|
||||
// The preparation callback may adjust prefs->memLimit for patch mode, so
|
||||
// read the scalar inputs only after that callback has completed.
|
||||
let prefs = unsafe { &*state.prefs };
|
||||
let result = unsafe { (state.set_max_window_size)(state.callback_context, prefs.memLimit) };
|
||||
if unsafe { (state.is_error)(result) } != 0 {
|
||||
return result;
|
||||
}
|
||||
|
||||
let result = unsafe {
|
||||
(state.set_parameter)(state.callback_context, c_int::from(prefs.checksumFlag == 0))
|
||||
};
|
||||
if unsafe { (state.is_error)(result) } != 0 {
|
||||
return result;
|
||||
}
|
||||
|
||||
let result = unsafe {
|
||||
(state.load_dictionary)(state.callback_context, state.prefs, state.dict_file_name)
|
||||
};
|
||||
if unsafe { (state.is_error)(result) } != 0 {
|
||||
return result;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
(state.create_write_pool)(state.callback_context, state.prefs);
|
||||
(state.create_read_pool)(state.callback_context, state.prefs);
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
#[cfg(feature = "decompression")]
|
||||
// `ZSTD_error_frameParameter_windowTooLarge` from zstd_errors.h.
|
||||
const ZSTD_ERROR_FRAME_PARAMETER_WINDOW_TOO_LARGE: c_int = 16;
|
||||
@@ -3189,6 +3282,164 @@ mod tests {
|
||||
assert!(context.events.is_empty());
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct CreateDResourcesTestState {
|
||||
lifecycle: Vec<&'static str>,
|
||||
max_window_size: Option<c_uint>,
|
||||
ignore_checksum: Option<c_int>,
|
||||
fail_at: Option<&'static str>,
|
||||
}
|
||||
|
||||
unsafe extern "C" fn create_d_resources_prepare_dictionary_test(
|
||||
context: *mut c_void,
|
||||
_prefs: *mut FIO_prefs_t,
|
||||
_dict_file_name: *const c_char,
|
||||
) {
|
||||
unsafe {
|
||||
(*context.cast::<CreateDResourcesTestState>())
|
||||
.lifecycle
|
||||
.push("prepare-dictionary");
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn create_d_resources_create_dctx_test(context: *mut c_void) {
|
||||
unsafe {
|
||||
(*context.cast::<CreateDResourcesTestState>())
|
||||
.lifecycle
|
||||
.push("create-dctx");
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn create_d_resources_set_max_window_size_test(
|
||||
context: *mut c_void,
|
||||
value: c_uint,
|
||||
) -> usize {
|
||||
let state = unsafe { &mut *context.cast::<CreateDResourcesTestState>() };
|
||||
state.lifecycle.push("set-max-window");
|
||||
state.max_window_size = Some(value);
|
||||
usize::from(state.fail_at == Some("set-max-window")) * usize::MAX
|
||||
}
|
||||
|
||||
unsafe extern "C" fn create_d_resources_set_parameter_test(
|
||||
context: *mut c_void,
|
||||
value: c_int,
|
||||
) -> usize {
|
||||
let state = unsafe { &mut *context.cast::<CreateDResourcesTestState>() };
|
||||
state.lifecycle.push("set-parameter");
|
||||
state.ignore_checksum = Some(value);
|
||||
usize::from(state.fail_at == Some("set-parameter")) * usize::MAX
|
||||
}
|
||||
|
||||
unsafe extern "C" fn create_d_resources_load_dictionary_test(
|
||||
context: *mut c_void,
|
||||
_prefs: *mut FIO_prefs_t,
|
||||
_dict_file_name: *const c_char,
|
||||
) -> usize {
|
||||
let state = unsafe { &mut *context.cast::<CreateDResourcesTestState>() };
|
||||
state.lifecycle.push("load-dictionary");
|
||||
usize::from(state.fail_at == Some("load-dictionary")) * usize::MAX
|
||||
}
|
||||
|
||||
unsafe extern "C" fn create_d_resources_create_write_pool_test(
|
||||
context: *mut c_void,
|
||||
_prefs: *const FIO_prefs_t,
|
||||
) {
|
||||
unsafe {
|
||||
(*context.cast::<CreateDResourcesTestState>())
|
||||
.lifecycle
|
||||
.push("create-write-pool");
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn create_d_resources_create_read_pool_test(
|
||||
context: *mut c_void,
|
||||
_prefs: *const FIO_prefs_t,
|
||||
) {
|
||||
unsafe {
|
||||
(*context.cast::<CreateDResourcesTestState>())
|
||||
.lifecycle
|
||||
.push("create-read-pool");
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn create_d_resources_is_error_test(result: usize) -> c_int {
|
||||
c_int::from(result == usize::MAX)
|
||||
}
|
||||
|
||||
fn create_d_resources_test_prefs() -> FIO_prefs_t {
|
||||
let mut prefs: FIO_prefs_t = unsafe { std::mem::zeroed() };
|
||||
prefs.memLimit = 64;
|
||||
prefs.checksumFlag = 1;
|
||||
prefs
|
||||
}
|
||||
|
||||
fn create_d_resources_test_state(
|
||||
prefs: &FIO_prefs_t,
|
||||
context: &mut CreateDResourcesTestState,
|
||||
) -> FIO_rust_createDResourcesState {
|
||||
FIO_rust_createDResourcesState {
|
||||
callback_context: context as *mut CreateDResourcesTestState as *mut c_void,
|
||||
prefs: prefs as *const FIO_prefs_t as *mut FIO_prefs_t,
|
||||
dict_file_name: ptr::null(),
|
||||
prepare_dictionary: create_d_resources_prepare_dictionary_test,
|
||||
create_dctx: create_d_resources_create_dctx_test,
|
||||
set_max_window_size: create_d_resources_set_max_window_size_test,
|
||||
set_parameter: create_d_resources_set_parameter_test,
|
||||
load_dictionary: create_d_resources_load_dictionary_test,
|
||||
create_write_pool: create_d_resources_create_write_pool_test,
|
||||
create_read_pool: create_d_resources_create_read_pool_test,
|
||||
is_error: create_d_resources_is_error_test,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_d_resources_preserves_creation_order_and_scalar_policy() {
|
||||
let prefs = create_d_resources_test_prefs();
|
||||
let mut context = CreateDResourcesTestState::default();
|
||||
let state = create_d_resources_test_state(&prefs, &mut context);
|
||||
|
||||
let result = unsafe { FIO_rust_createDResources(&state) };
|
||||
|
||||
assert_eq!(result, 0);
|
||||
assert_eq!(context.max_window_size, Some(64));
|
||||
assert_eq!(context.ignore_checksum, Some(0));
|
||||
assert_eq!(
|
||||
context.lifecycle,
|
||||
vec![
|
||||
"prepare-dictionary",
|
||||
"create-dctx",
|
||||
"set-max-window",
|
||||
"set-parameter",
|
||||
"load-dictionary",
|
||||
"create-write-pool",
|
||||
"create-read-pool",
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_d_resources_short_circuits_on_decoder_parameter_error() {
|
||||
let prefs = create_d_resources_test_prefs();
|
||||
let mut context = CreateDResourcesTestState {
|
||||
fail_at: Some("set-parameter"),
|
||||
..CreateDResourcesTestState::default()
|
||||
};
|
||||
let state = create_d_resources_test_state(&prefs, &mut context);
|
||||
|
||||
let result = unsafe { FIO_rust_createDResources(&state) };
|
||||
|
||||
assert_eq!(result, usize::MAX);
|
||||
assert_eq!(
|
||||
context.lifecycle,
|
||||
vec![
|
||||
"prepare-dictionary",
|
||||
"create-dctx",
|
||||
"set-max-window",
|
||||
"set-parameter",
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "decompression")]
|
||||
#[derive(Default)]
|
||||
struct ZstdErrorHelpTestState {
|
||||
|
||||
Reference in New Issue
Block a user