refactor(compress): move dictionary teardown ordering to Rust
The compression context still owns private dictionary storage and CDict objects, but the teardown order is observable because each step can invoke an allocator or clear state used by later steps. Keep those storage operations as C callbacks while moving the sequencing policy into the Rust compression module. The C bridge now contains only callback adapters and compile-time ABI layout checks, and the Rust unit test pins the original five-step order. Test Plan: - `cargo clippy --manifest-path rust/Cargo.toml --all-targets -- -D warnings` -- passed - `cargo test --manifest-path rust/Cargo.toml --all-targets` -- 771 passed - `cargo clippy --manifest-path rust/cli/Cargo.toml --all-targets -- -D warnings` -- passed - `make -j1` under `ulimit -v 41943040` -- passed - `make -j1 -C tests test` under `ulimit -v 41943040` -- passed
This commit is contained in:
@@ -2263,6 +2263,51 @@ pub unsafe extern "C" fn ZSTD_rust_freeCCtx(state: *const ZSTD_rust_freeCCtxStat
|
||||
0
|
||||
}
|
||||
|
||||
type ClearAllDictsCallbackFn = unsafe extern "C" fn(*mut c_void);
|
||||
|
||||
/// Compose the dictionary teardown operations while leaving the private
|
||||
/// ZSTD_CCtx fields behind C callbacks.
|
||||
#[repr(C)]
|
||||
pub struct ZSTD_rust_clearAllDictsState {
|
||||
callback_context: *mut c_void,
|
||||
free_local_dict_buffer: ClearAllDictsCallbackFn,
|
||||
free_local_cdict: ClearAllDictsCallbackFn,
|
||||
clear_local_dict: ClearAllDictsCallbackFn,
|
||||
clear_prefix_dict: ClearAllDictsCallbackFn,
|
||||
clear_cdict: ClearAllDictsCallbackFn,
|
||||
}
|
||||
|
||||
const _: () = {
|
||||
assert!(size_of::<ClearAllDictsCallbackFn>() == size_of::<usize>());
|
||||
assert!(offset_of!(ZSTD_rust_clearAllDictsState, callback_context) == 0);
|
||||
assert!(offset_of!(ZSTD_rust_clearAllDictsState, free_local_dict_buffer) == size_of::<usize>());
|
||||
assert!(offset_of!(ZSTD_rust_clearAllDictsState, free_local_cdict) == 2 * size_of::<usize>());
|
||||
assert!(offset_of!(ZSTD_rust_clearAllDictsState, clear_local_dict) == 3 * size_of::<usize>());
|
||||
assert!(offset_of!(ZSTD_rust_clearAllDictsState, clear_prefix_dict) == 4 * size_of::<usize>());
|
||||
assert!(offset_of!(ZSTD_rust_clearAllDictsState, clear_cdict) == 5 * size_of::<usize>());
|
||||
assert!(size_of::<ZSTD_rust_clearAllDictsState>() == 6 * size_of::<usize>());
|
||||
};
|
||||
|
||||
/// Preserve the original local-dictionary, prefix-dictionary, and attached
|
||||
/// CDict teardown order while keeping their private storage C-owned.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn ZSTD_rust_clearAllDicts(state: *const ZSTD_rust_clearAllDictsState) {
|
||||
if state.is_null() {
|
||||
return;
|
||||
}
|
||||
let state = unsafe { &*state };
|
||||
if state.callback_context.is_null() {
|
||||
return;
|
||||
}
|
||||
unsafe {
|
||||
(state.free_local_dict_buffer)(state.callback_context);
|
||||
(state.free_local_cdict)(state.callback_context);
|
||||
(state.clear_local_dict)(state.callback_context);
|
||||
(state.clear_prefix_dict)(state.callback_context);
|
||||
(state.clear_cdict)(state.callback_context);
|
||||
}
|
||||
}
|
||||
|
||||
type ResetCCtxClearAllDictsFn = unsafe extern "C" fn(*mut c_void);
|
||||
type ResetCCtxResetParamsFn = unsafe extern "C" fn(*mut c_void) -> usize;
|
||||
|
||||
@@ -15606,6 +15651,79 @@ mod tests {
|
||||
assert_eq!(non_contiguous_state.lowLimit, 14);
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct ClearAllDictsTestContext {
|
||||
events: Vec<&'static str>,
|
||||
}
|
||||
|
||||
unsafe fn clear_all_dicts_test_context(
|
||||
context: *mut c_void,
|
||||
) -> &'static mut ClearAllDictsTestContext {
|
||||
unsafe { &mut *context.cast::<ClearAllDictsTestContext>() }
|
||||
}
|
||||
|
||||
unsafe extern "C" fn clear_all_dicts_test_free_local_dict_buffer(context: *mut c_void) {
|
||||
unsafe { clear_all_dicts_test_context(context) }
|
||||
.events
|
||||
.push("free-local-buffer");
|
||||
}
|
||||
|
||||
unsafe extern "C" fn clear_all_dicts_test_free_local_cdict(context: *mut c_void) {
|
||||
unsafe { clear_all_dicts_test_context(context) }
|
||||
.events
|
||||
.push("free-local-cdict");
|
||||
}
|
||||
|
||||
unsafe extern "C" fn clear_all_dicts_test_clear_local_dict(context: *mut c_void) {
|
||||
unsafe { clear_all_dicts_test_context(context) }
|
||||
.events
|
||||
.push("clear-local");
|
||||
}
|
||||
|
||||
unsafe extern "C" fn clear_all_dicts_test_clear_prefix_dict(context: *mut c_void) {
|
||||
unsafe { clear_all_dicts_test_context(context) }
|
||||
.events
|
||||
.push("clear-prefix");
|
||||
}
|
||||
|
||||
unsafe extern "C" fn clear_all_dicts_test_clear_cdict(context: *mut c_void) {
|
||||
unsafe { clear_all_dicts_test_context(context) }
|
||||
.events
|
||||
.push("clear-cdict");
|
||||
}
|
||||
|
||||
fn clear_all_dicts_test_state(
|
||||
context: &mut ClearAllDictsTestContext,
|
||||
) -> ZSTD_rust_clearAllDictsState {
|
||||
ZSTD_rust_clearAllDictsState {
|
||||
callback_context: (context as *mut ClearAllDictsTestContext).cast(),
|
||||
free_local_dict_buffer: clear_all_dicts_test_free_local_dict_buffer,
|
||||
free_local_cdict: clear_all_dicts_test_free_local_cdict,
|
||||
clear_local_dict: clear_all_dicts_test_clear_local_dict,
|
||||
clear_prefix_dict: clear_all_dicts_test_clear_prefix_dict,
|
||||
clear_cdict: clear_all_dicts_test_clear_cdict,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clear_all_dicts_preserves_dictionary_teardown_order() {
|
||||
let mut context = ClearAllDictsTestContext::default();
|
||||
let state = clear_all_dicts_test_state(&mut context);
|
||||
|
||||
unsafe { ZSTD_rust_clearAllDicts(&state) };
|
||||
|
||||
assert_eq!(
|
||||
context.events,
|
||||
[
|
||||
"free-local-buffer",
|
||||
"free-local-cdict",
|
||||
"clear-local",
|
||||
"clear-prefix",
|
||||
"clear-cdict"
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct ResetCCtxTestContext {
|
||||
events: Vec<&'static str>,
|
||||
|
||||
Reference in New Issue
Block a user