refactor(mt): move stream reset ordering to Rust

Make Rust own the MT stream reset sequence through a narrow callback
projection. C retains the private buffer, job-ID, frame-flag, progress, and
sentinel mutations, preserving the original order without exposing private
layouts across the ABI.

Test Plan:
- rustfmt --edition 2021 --check rust/src/zstdmt_compress.rs
- git diff --check
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo clippy --manifest-path rust/Cargo.toml --all-targets -- -D warnings
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo test --manifest-path rust/Cargo.toml --all-targets (786 passed)
- 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 (179 passed)
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo +nightly fmt --manifest-path rust/Cargo.toml -- --check
- ulimit -v 41943040; make -j1
- ulimit -v 41943040; make -j1 -C tests test (all tests completed successfully)
This commit is contained in:
2026-07-20 07:31:23 +02:00
parent dc3c9c6c11
commit 429694a250
2 changed files with 214 additions and 10 deletions
+151
View File
@@ -791,12 +791,74 @@ pub type ZSTDMT_initSetBufferSizeFn = unsafe extern "C" fn(*mut c_void, usize);
pub type ZSTDMT_initResizeRoundBufferFn = unsafe extern "C" fn(*mut c_void, usize) -> usize;
pub type ZSTDMT_initResetStreamFn = unsafe extern "C" fn(*mut c_void);
pub type ZSTDMT_initSerialResetFn = unsafe extern "C" fn(*mut c_void, usize) -> usize;
type ZSTDMT_resetStreamCallbackFn = unsafe extern "C" fn(*mut c_void);
pub type ZSTDMT_serialResetVoidFn = unsafe extern "C" fn(*mut c_void);
pub type ZSTDMT_serialResetSetNbSeqFn = unsafe extern "C" fn(*mut c_void, usize);
pub type ZSTDMT_serialResetResizeFn = unsafe extern "C" fn(*mut c_void) -> c_int;
pub type ZSTDMT_serialStateVoidFn = unsafe extern "C" fn(*mut c_void);
pub type ZSTDMT_serialStateInitFn = unsafe extern "C" fn(*mut c_void) -> c_int;
/// Callback projection for the MT stream reset. C retains the private buffer,
/// job, flag, and progress fields; Rust owns the order in which each group is
/// reset.
#[repr(C)]
pub struct ZSTDMT_RustResetStreamState {
callback_context: *mut c_void,
reset_round_buffer: Option<ZSTDMT_resetStreamCallbackFn>,
reset_input_buffer: Option<ZSTDMT_resetStreamCallbackFn>,
reset_job_ids: Option<ZSTDMT_resetStreamCallbackFn>,
reset_frame_flags: Option<ZSTDMT_resetStreamCallbackFn>,
reset_progress: Option<ZSTDMT_resetStreamCallbackFn>,
}
const _: () = {
assert!(size_of::<ZSTDMT_resetStreamCallbackFn>() == size_of::<usize>());
assert!(offset_of!(ZSTDMT_RustResetStreamState, callback_context) == 0);
assert!(offset_of!(ZSTDMT_RustResetStreamState, reset_round_buffer) == size_of::<usize>());
assert!(offset_of!(ZSTDMT_RustResetStreamState, reset_input_buffer) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTDMT_RustResetStreamState, reset_job_ids) == 3 * size_of::<usize>());
assert!(offset_of!(ZSTDMT_RustResetStreamState, reset_frame_flags) == 4 * size_of::<usize>());
assert!(offset_of!(ZSTDMT_RustResetStreamState, reset_progress) == 5 * size_of::<usize>());
assert!(size_of::<ZSTDMT_RustResetStreamState>() == 6 * size_of::<usize>());
};
/// Reset the MT stream state in the same order as the original C function.
/// Each callback keeps the corresponding private C layout and sentinel values
/// out of the Rust ABI.
#[no_mangle]
pub unsafe extern "C" fn ZSTDMT_rust_resetStream(state: *const ZSTDMT_RustResetStreamState) {
let Some(state) = (unsafe { state.as_ref() }) else {
return;
};
if state.callback_context.is_null() {
return;
}
let (
Some(reset_round_buffer),
Some(reset_input_buffer),
Some(reset_job_ids),
Some(reset_frame_flags),
Some(reset_progress),
) = (
state.reset_round_buffer,
state.reset_input_buffer,
state.reset_job_ids,
state.reset_frame_flags,
state.reset_progress,
)
else {
return;
};
unsafe {
reset_round_buffer(state.callback_context);
reset_input_buffer(state.callback_context);
reset_job_ids(state.callback_context);
reset_frame_flags(state.callback_context);
reset_progress(state.callback_context);
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct ZSTDMT_flushPublicationResult {
@@ -5345,6 +5407,95 @@ mod tests {
}
}
#[derive(Default)]
struct ResetStreamTestContext {
events: Vec<&'static str>,
}
unsafe extern "C" fn reset_stream_test_round_buffer(context: *mut c_void) {
unsafe {
(*context.cast::<ResetStreamTestContext>())
.events
.push("round");
}
}
unsafe extern "C" fn reset_stream_test_input_buffer(context: *mut c_void) {
unsafe {
(*context.cast::<ResetStreamTestContext>())
.events
.push("input");
}
}
unsafe extern "C" fn reset_stream_test_job_ids(context: *mut c_void) {
unsafe {
(*context.cast::<ResetStreamTestContext>())
.events
.push("jobs");
}
}
unsafe extern "C" fn reset_stream_test_frame_flags(context: *mut c_void) {
unsafe {
(*context.cast::<ResetStreamTestContext>())
.events
.push("flags");
}
}
unsafe extern "C" fn reset_stream_test_progress(context: *mut c_void) {
unsafe {
(*context.cast::<ResetStreamTestContext>())
.events
.push("progress");
}
}
fn reset_stream_test_state(context: *mut c_void) -> ZSTDMT_RustResetStreamState {
ZSTDMT_RustResetStreamState {
callback_context: context,
reset_round_buffer: Some(reset_stream_test_round_buffer),
reset_input_buffer: Some(reset_stream_test_input_buffer),
reset_job_ids: Some(reset_stream_test_job_ids),
reset_frame_flags: Some(reset_stream_test_frame_flags),
reset_progress: Some(reset_stream_test_progress),
}
}
#[test]
fn reset_stream_preserves_callback_order() {
let mut context = ResetStreamTestContext::default();
let state = reset_stream_test_state((&mut context as *mut ResetStreamTestContext).cast());
unsafe { ZSTDMT_rust_resetStream(&state) };
assert_eq!(
context.events,
vec!["round", "input", "jobs", "flags", "progress"]
);
}
#[test]
fn reset_stream_ignores_null_or_incomplete_state() {
let mut context = ResetStreamTestContext::default();
let state = ZSTDMT_RustResetStreamState {
callback_context: (&mut context as *mut ResetStreamTestContext).cast(),
reset_round_buffer: None,
reset_input_buffer: Some(reset_stream_test_input_buffer),
reset_job_ids: Some(reset_stream_test_job_ids),
reset_frame_flags: Some(reset_stream_test_frame_flags),
reset_progress: Some(reset_stream_test_progress),
};
unsafe {
ZSTDMT_rust_resetStream(&state);
ZSTDMT_rust_resetStream(ptr::null());
}
assert!(context.events.is_empty());
}
#[derive(Default)]
struct SerialResetTestContext {
events: Vec<&'static str>,