feat(cli): move multi-file summary policy to Rust
The multi-file compression and decompression entry points already delegate file iteration and resource-sensitive work to Rust, but each still kept the final summary decision and progress-to-summary ordering in C. Move that scalar policy into a Rust ABI function. The C side now supplies only exact legacy display callbacks and unchanged format strings, while Rust receives plain counters and a summary kind without exposing FIO, cRess, dRess, or codec layouts. Focused unit tests cover both summary modes, callback order, argument forwarding, and sessions that must not display a summary. Test Plan: - `git diff --check -- programs/fileio.c rust/src/fileio_asyncio.rs` -- passed - `git diff --cached --check` -- passed - `rustfmt --check --edition 2021 rust/src/fileio_asyncio.rs` -- parsed the file; reports pre-existing formatting differences elsewhere in the file, so no unrelated formatting was applied - Cargo, make, and runtime tests were intentionally not run per worker scope
This commit is contained in:
@@ -1598,6 +1598,73 @@ struct FIO_rust_compression_context_t {
|
||||
totalBytesOutput: usize,
|
||||
}
|
||||
|
||||
pub const FIO_RUST_MULTIPLE_SUMMARY_DECOMPRESSION: c_int = 0;
|
||||
pub const FIO_RUST_MULTIPLE_SUMMARY_COMPRESSION: c_int = 1;
|
||||
|
||||
type FIO_rust_multiple_summary_progress_fn = unsafe extern "C" fn(*mut c_void);
|
||||
type FIO_rust_compress_multiple_summary_fn =
|
||||
unsafe extern "C" fn(*mut c_void, c_int, u64, u64);
|
||||
type FIO_rust_decompress_multiple_summary_fn = unsafe extern "C" fn(*mut c_void, c_int, u64);
|
||||
|
||||
/// C supplies only display sinks. The multi-file counters are passed as
|
||||
/// scalars so the summary policy never depends on private FIO/cRess/dRess
|
||||
/// layouts or external codec state.
|
||||
#[repr(C)]
|
||||
pub struct FIO_rust_multiple_summary_callbacks_t {
|
||||
pub opaque: *mut c_void,
|
||||
pub display_progress: Option<FIO_rust_multiple_summary_progress_fn>,
|
||||
pub display_compression: Option<FIO_rust_compress_multiple_summary_fn>,
|
||||
pub display_decompression: Option<FIO_rust_decompress_multiple_summary_fn>,
|
||||
}
|
||||
|
||||
/// Runs the final multi-file summary in the same position and order as the
|
||||
/// original C callers: clear progress first, then select the compression or
|
||||
/// decompression summary sink. C retains only the exact display formatting
|
||||
/// callbacks, preserving diagnostics and the CLI ABI.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn FIO_rust_displayMultipleFileSummary(
|
||||
nb_files_total: c_int,
|
||||
nb_files_processed: c_int,
|
||||
total_bytes_input: u64,
|
||||
total_bytes_output: u64,
|
||||
summary_kind: c_int,
|
||||
callbacks: *const FIO_rust_multiple_summary_callbacks_t,
|
||||
) {
|
||||
if nb_files_total <= 1 || nb_files_processed < 1 {
|
||||
return;
|
||||
}
|
||||
|
||||
assert!(!callbacks.is_null());
|
||||
let callbacks = unsafe { &*callbacks };
|
||||
let display_progress = callbacks
|
||||
.display_progress
|
||||
.expect("multi-file summary progress callback is required");
|
||||
unsafe { display_progress(callbacks.opaque) };
|
||||
|
||||
match summary_kind {
|
||||
FIO_RUST_MULTIPLE_SUMMARY_COMPRESSION => {
|
||||
let display = callbacks
|
||||
.display_compression
|
||||
.expect("compression summary callback is required");
|
||||
unsafe {
|
||||
display(
|
||||
callbacks.opaque,
|
||||
nb_files_processed,
|
||||
total_bytes_input,
|
||||
total_bytes_output,
|
||||
)
|
||||
};
|
||||
}
|
||||
FIO_RUST_MULTIPLE_SUMMARY_DECOMPRESSION => {
|
||||
let display = callbacks
|
||||
.display_decompression
|
||||
.expect("decompression summary callback is required");
|
||||
unsafe { display(callbacks.opaque, nb_files_processed, total_bytes_output) };
|
||||
}
|
||||
_ => unreachable!("invalid multi-file summary kind"),
|
||||
}
|
||||
}
|
||||
|
||||
type PoolFunction = unsafe extern "C" fn(*mut c_void);
|
||||
|
||||
#[cfg(not(test))]
|
||||
@@ -7328,6 +7395,119 @@ mod tests {
|
||||
statuses: Vec<c_int>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct MultipleSummaryState {
|
||||
events: Vec<&'static str>,
|
||||
compression: Option<(c_int, u64, u64)>,
|
||||
decompression: Option<(c_int, u64)>,
|
||||
}
|
||||
|
||||
unsafe extern "C" fn record_multiple_summary_progress(opaque: *mut c_void) {
|
||||
let state = unsafe { &mut *opaque.cast::<MultipleSummaryState>() };
|
||||
state.events.push("progress");
|
||||
}
|
||||
|
||||
unsafe extern "C" fn record_multiple_compression_summary(
|
||||
opaque: *mut c_void,
|
||||
nb_files_processed: c_int,
|
||||
total_bytes_input: u64,
|
||||
total_bytes_output: u64,
|
||||
) {
|
||||
let state = unsafe { &mut *opaque.cast::<MultipleSummaryState>() };
|
||||
state.events.push("compression");
|
||||
state.compression = Some((
|
||||
nb_files_processed,
|
||||
total_bytes_input,
|
||||
total_bytes_output,
|
||||
));
|
||||
}
|
||||
|
||||
unsafe extern "C" fn record_multiple_decompression_summary(
|
||||
opaque: *mut c_void,
|
||||
nb_files_processed: c_int,
|
||||
total_bytes_output: u64,
|
||||
) {
|
||||
let state = unsafe { &mut *opaque.cast::<MultipleSummaryState>() };
|
||||
state.events.push("decompression");
|
||||
state.decompression = Some((nb_files_processed, total_bytes_output));
|
||||
}
|
||||
|
||||
fn multiple_summary_callbacks(
|
||||
state: &mut MultipleSummaryState,
|
||||
) -> FIO_rust_multiple_summary_callbacks_t {
|
||||
FIO_rust_multiple_summary_callbacks_t {
|
||||
opaque: (state as *mut MultipleSummaryState).cast(),
|
||||
display_progress: Some(record_multiple_summary_progress),
|
||||
display_compression: Some(record_multiple_compression_summary),
|
||||
display_decompression: Some(record_multiple_decompression_summary),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_file_summary_dispatches_compression_after_progress() {
|
||||
let mut state = MultipleSummaryState::default();
|
||||
let callbacks = multiple_summary_callbacks(&mut state);
|
||||
|
||||
unsafe {
|
||||
FIO_rust_displayMultipleFileSummary(
|
||||
3,
|
||||
2,
|
||||
4096,
|
||||
1024,
|
||||
FIO_RUST_MULTIPLE_SUMMARY_COMPRESSION,
|
||||
&callbacks,
|
||||
)
|
||||
};
|
||||
|
||||
assert_eq!(state.events, ["progress", "compression"]);
|
||||
assert_eq!(state.compression, Some((2, 4096, 1024)));
|
||||
assert_eq!(state.decompression, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_file_summary_dispatches_decompression_and_skips_empty_sessions() {
|
||||
let mut state = MultipleSummaryState::default();
|
||||
let callbacks = multiple_summary_callbacks(&mut state);
|
||||
|
||||
unsafe {
|
||||
FIO_rust_displayMultipleFileSummary(
|
||||
4,
|
||||
3,
|
||||
0,
|
||||
777,
|
||||
FIO_RUST_MULTIPLE_SUMMARY_DECOMPRESSION,
|
||||
&callbacks,
|
||||
)
|
||||
};
|
||||
assert_eq!(state.events, ["progress", "decompression"]);
|
||||
assert_eq!(state.decompression, Some((3, 777)));
|
||||
|
||||
state.events.clear();
|
||||
state.decompression = None;
|
||||
unsafe {
|
||||
FIO_rust_displayMultipleFileSummary(
|
||||
1,
|
||||
3,
|
||||
10,
|
||||
20,
|
||||
FIO_RUST_MULTIPLE_SUMMARY_DECOMPRESSION,
|
||||
&callbacks,
|
||||
)
|
||||
};
|
||||
unsafe {
|
||||
FIO_rust_displayMultipleFileSummary(
|
||||
4,
|
||||
0,
|
||||
10,
|
||||
20,
|
||||
FIO_RUST_MULTIPLE_SUMMARY_DECOMPRESSION,
|
||||
&callbacks,
|
||||
)
|
||||
};
|
||||
assert!(state.events.is_empty());
|
||||
assert_eq!(state.decompression, None);
|
||||
}
|
||||
|
||||
unsafe extern "C" fn record_multiple_compression_file(
|
||||
opaque: *mut c_void,
|
||||
destination: *const c_char,
|
||||
|
||||
Reference in New Issue
Block a user