feat(cli): move list formatting to Rust
Move the per-file and multi-file --list row formatters out of programs/fileio.c. C still owns filesystem access, frame analysis, private fileInfo_t storage, and exact status diagnostics; Rust now formats the rows into byte buffers and sends them through a synchronous output callback. This keeps filenames byte-preserving and prevents the private C record from crossing the ABI while preserving the original display thresholds, human-readable size policy, unavailable-content spacing, checksum order, and total-row behavior. Replace the old display callbacks with one size-delimited output callback shared by the single-file and multi-file projections. Add explicit Rust ABI layout checks, formatter fixtures for normal/unavailable/verbose/checksum/total output, and callback-order assertions for the policy boundary. Test Plan: - 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 (208 passed) - ulimit -v 41943040; make -j1 - ulimit -v 41943040; make -j1 -C tests test (all original tests completed successfully) - git diff --check
This commit is contained in:
+319
-67
@@ -11,6 +11,7 @@
|
||||
//! unspecified are not zero-filled here either.
|
||||
|
||||
use std::ffi::{c_void, CStr};
|
||||
use std::fmt::Write as _;
|
||||
use std::io::{self, Write};
|
||||
use std::mem::{offset_of, size_of};
|
||||
use std::os::raw::{c_char, c_int, c_uint};
|
||||
@@ -256,7 +257,7 @@ pub struct FIO_fileInfo_t {
|
||||
}
|
||||
|
||||
/// The subset of C's private `fileInfo_t` needed by the `--list` aggregate
|
||||
/// row. C keeps opening, parsing, and formatting each file; Rust owns the
|
||||
/// row. C keeps opening and parsing each file; Rust owns formatting and the
|
||||
/// per-file and multi-file policy around those callbacks.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
@@ -890,27 +891,25 @@ pub type FIO_listMultipleFilesListFileFn = unsafe extern "C" fn(
|
||||
c_int,
|
||||
*mut FIO_listFileInfoProjection_t,
|
||||
) -> c_int;
|
||||
pub type FIO_listMultipleFilesDisplayTotalFn =
|
||||
unsafe extern "C" fn(*mut c_void, *const FIO_listFileInfoProjection_t);
|
||||
|
||||
pub type FIO_listFileGetInfoFn =
|
||||
unsafe extern "C" fn(*mut c_void, *const c_char, *mut FIO_fileInfo_t) -> c_int;
|
||||
pub type FIO_listFileDisplayStatusFn =
|
||||
unsafe extern "C" fn(*mut c_void, c_int, *const c_char, c_int);
|
||||
pub type FIO_listFileDisplayInfoFn =
|
||||
unsafe extern "C" fn(*mut c_void, *const c_char, *const FIO_fileInfo_t, c_int);
|
||||
pub type FIO_listFileWriteOutputFn = unsafe extern "C" fn(*mut c_void, *const c_char, usize);
|
||||
|
||||
/// Callbacks for the C-owned `--list` file operation.
|
||||
///
|
||||
/// The callbacks keep stat/open, frame analysis, exact status diagnostics, and
|
||||
/// row formatting in C. Rust only orders those operations and publishes the
|
||||
/// small aggregate projection consumed by the surrounding multi-file policy.
|
||||
/// The callbacks keep stat/open, frame analysis, and exact status diagnostics
|
||||
/// in C. Rust owns row formatting and writes the resulting bytes through the
|
||||
/// synchronous output callback, then publishes the small aggregate projection
|
||||
/// consumed by the surrounding multi-file policy.
|
||||
#[repr(C)]
|
||||
pub struct FIO_listFileCallbacks_t {
|
||||
opaque: *mut c_void,
|
||||
getInfo: Option<FIO_listFileGetInfoFn>,
|
||||
displayStatus: Option<FIO_listFileDisplayStatusFn>,
|
||||
displayInfo: Option<FIO_listFileDisplayInfoFn>,
|
||||
writeOutput: Option<FIO_listFileWriteOutputFn>,
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -927,8 +926,8 @@ fn list_file_status_action(status: c_int) -> c_int {
|
||||
|
||||
/// Map the C-owned `InfoError` result to the scalar `--list` action.
|
||||
///
|
||||
/// C retains the exact diagnostics, file opening/parsing, metadata formatting,
|
||||
/// and private `fileInfo_t` layout. Rust owns only this status policy.
|
||||
/// C retains the exact diagnostics, file opening/parsing, and private
|
||||
/// `fileInfo_t` layout. Rust owns the status policy and metadata formatting.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn FIO_rust_listFileStatusAction(status: c_int) -> c_int {
|
||||
list_file_status_action(status)
|
||||
@@ -947,10 +946,163 @@ fn project_list_file_info(info: &FIO_fileInfo_t) -> FIO_listFileInfoProjection_t
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn append_fmt(output: &mut Vec<u8>, args: std::fmt::Arguments<'_>) {
|
||||
let mut text = String::new();
|
||||
text.write_fmt(args)
|
||||
.expect("formatting into String cannot fail");
|
||||
output.extend_from_slice(text.as_bytes());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn append_padded_bytes(output: &mut Vec<u8>, value: &[u8], width: usize) {
|
||||
output.extend(std::iter::repeat_n(b' ', width.saturating_sub(value.len())));
|
||||
output.extend_from_slice(value);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn append_integer<T: std::fmt::Display>(output: &mut Vec<u8>, value: T, width: usize) {
|
||||
let mut text = String::new();
|
||||
write!(&mut text, "{value}").expect("formatting into String cannot fail");
|
||||
append_padded_bytes(output, text.as_bytes(), width);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn append_human_size(output: &mut Vec<u8>, size: u64, width: usize) {
|
||||
let size = crate::util::UTIL_makeHumanReadableSize(size);
|
||||
let mut number = String::new();
|
||||
write!(
|
||||
&mut number,
|
||||
"{:.*}",
|
||||
size.precision.max(0) as usize,
|
||||
size.value
|
||||
)
|
||||
.expect("formatting into String cannot fail");
|
||||
append_padded_bytes(output, number.as_bytes(), width);
|
||||
let suffix = unsafe { CStr::from_ptr(size.suffix).to_bytes() };
|
||||
append_padded_bytes(output, suffix, if width == 0 { 0 } else { 4 });
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn append_c_string(output: &mut Vec<u8>, value: *const c_char) {
|
||||
output.extend_from_slice(unsafe { CStr::from_ptr(value).to_bytes() });
|
||||
}
|
||||
|
||||
fn format_list_file_info(
|
||||
input_name: *const c_char,
|
||||
info: &FIO_fileInfo_t,
|
||||
display_level: c_int,
|
||||
) -> Vec<u8> {
|
||||
let total_frames = info.numSkippableFrames.wrapping_add(info.numActualFrames);
|
||||
let ratio = if info.compressedSize == 0 {
|
||||
0.0
|
||||
} else {
|
||||
info.decompressedSize as f64 / info.compressedSize as f64
|
||||
};
|
||||
let check = if info.usesCheck != 0 {
|
||||
b"XXH64".as_slice()
|
||||
} else {
|
||||
b"None".as_slice()
|
||||
};
|
||||
let mut output = Vec::new();
|
||||
|
||||
if display_level <= 2 {
|
||||
append_integer(&mut output, total_frames, 6);
|
||||
output.extend_from_slice(b" ");
|
||||
append_integer(&mut output, info.numSkippableFrames, 5);
|
||||
output.extend_from_slice(b" ");
|
||||
append_human_size(&mut output, info.compressedSize, 6);
|
||||
if info.decompUnavailable == 0 {
|
||||
output.extend_from_slice(b" ");
|
||||
append_human_size(&mut output, info.decompressedSize, 8);
|
||||
output.extend_from_slice(b" ");
|
||||
append_fmt(&mut output, format_args!("{:>5.3}", ratio));
|
||||
output.extend_from_slice(b" ");
|
||||
} else {
|
||||
output.extend_from_slice(b" ");
|
||||
}
|
||||
append_padded_bytes(&mut output, check, 5);
|
||||
output.extend_from_slice(b" ");
|
||||
append_c_string(&mut output, input_name);
|
||||
output.push(b'\n');
|
||||
return output;
|
||||
}
|
||||
|
||||
append_c_string(&mut output, input_name);
|
||||
output.extend_from_slice(b" \n# Zstandard Frames: ");
|
||||
append_integer(&mut output, info.numActualFrames, 0);
|
||||
output.push(b'\n');
|
||||
if info.numSkippableFrames != 0 {
|
||||
output.extend_from_slice(b"# Skippable Frames: ");
|
||||
append_integer(&mut output, info.numSkippableFrames, 0);
|
||||
output.push(b'\n');
|
||||
}
|
||||
output.extend_from_slice(b"DictID: ");
|
||||
append_integer(&mut output, info.dictID, 0);
|
||||
output.extend_from_slice(b"\nWindow Size: ");
|
||||
append_human_size(&mut output, info.windowSize, 0);
|
||||
append_fmt(&mut output, format_args!(" ({} B)\n", info.windowSize));
|
||||
output.extend_from_slice(b"Compressed Size: ");
|
||||
append_human_size(&mut output, info.compressedSize, 0);
|
||||
append_fmt(&mut output, format_args!(" ({} B)\n", info.compressedSize));
|
||||
if info.decompUnavailable == 0 {
|
||||
output.extend_from_slice(b"Decompressed Size: ");
|
||||
append_human_size(&mut output, info.decompressedSize, 0);
|
||||
append_fmt(
|
||||
&mut output,
|
||||
format_args!(" ({} B)\nRatio: {:.4}\n", info.decompressedSize, ratio),
|
||||
);
|
||||
}
|
||||
output.extend_from_slice(b"Check: ");
|
||||
output.extend_from_slice(check);
|
||||
if info.usesCheck != 0 && info.numActualFrames == 1 {
|
||||
output.push(b' ');
|
||||
for byte in info.checksum.iter().rev() {
|
||||
append_fmt(&mut output, format_args!("{byte:02x}"));
|
||||
}
|
||||
}
|
||||
output.extend_from_slice(b"\n\n");
|
||||
output
|
||||
}
|
||||
|
||||
fn format_list_total(total: &FIO_listFileInfoProjection_t) -> Vec<u8> {
|
||||
let total_frames = total.numSkippableFrames.wrapping_add(total.numActualFrames);
|
||||
let ratio = if total.compressedSize == 0 {
|
||||
0.0
|
||||
} else {
|
||||
total.decompressedSize as f64 / total.compressedSize as f64
|
||||
};
|
||||
let check = if total.usesCheck != 0 {
|
||||
b"XXH64".as_slice()
|
||||
} else {
|
||||
b"".as_slice()
|
||||
};
|
||||
let mut output =
|
||||
b"----------------------------------------------------------------- \n".to_vec();
|
||||
append_integer(&mut output, total_frames, 6);
|
||||
output.extend_from_slice(b" ");
|
||||
append_integer(&mut output, total.numSkippableFrames, 5);
|
||||
output.extend_from_slice(b" ");
|
||||
append_human_size(&mut output, total.compressedSize, 6);
|
||||
if total.decompUnavailable == 0 {
|
||||
output.extend_from_slice(b" ");
|
||||
append_human_size(&mut output, total.decompressedSize, 8);
|
||||
output.extend_from_slice(b" ");
|
||||
append_fmt(&mut output, format_args!("{:>5.3}", ratio));
|
||||
output.extend_from_slice(b" ");
|
||||
} else {
|
||||
output.extend_from_slice(b" ");
|
||||
}
|
||||
append_padded_bytes(&mut output, check, 5);
|
||||
output.extend_from_slice(b" ");
|
||||
append_fmt(&mut output, format_args!("{} files\n", total.nbFiles));
|
||||
output
|
||||
}
|
||||
|
||||
/// Run the policy and ordering around one C-owned `--list` file operation.
|
||||
///
|
||||
/// C retains the stat/open and frame-analysis callback, all user-visible
|
||||
/// status diagnostics, and the full metadata row formatter. Rust preserves
|
||||
/// C retains the stat/open and frame-analysis callback and all user-visible
|
||||
/// status diagnostics. Rust owns the metadata row formatter. Rust preserves
|
||||
/// the original status gates: fatal statuses are reported and return `1`, a
|
||||
/// frame error reports a diagnostic but still formats and publishes its
|
||||
/// partial metadata, and success formats and publishes normally.
|
||||
@@ -972,9 +1124,9 @@ pub unsafe extern "C" fn FIO_rust_listFile(
|
||||
let display_status = callbacks
|
||||
.displayStatus
|
||||
.expect("--list status-display callback is required");
|
||||
let display_info = callbacks
|
||||
.displayInfo
|
||||
.expect("--list info-display callback is required");
|
||||
let write_output = callbacks
|
||||
.writeOutput
|
||||
.expect("--list output callback is required");
|
||||
|
||||
let mut info = unsafe { std::mem::zeroed::<FIO_fileInfo_t>() };
|
||||
let status = unsafe { get_info(callbacks.opaque, input_name, &mut info) };
|
||||
@@ -996,12 +1148,26 @@ pub unsafe extern "C" fn FIO_rust_listFile(
|
||||
| FIO_RUST_LIST_FILE_ACTION_FILE_ERROR
|
||||
| FIO_RUST_LIST_FILE_ACTION_TRUNCATED_INPUT => 1,
|
||||
FIO_RUST_LIST_FILE_ACTION_SUCCESS | FIO_RUST_LIST_FILE_ACTION_FRAME_ERROR => {
|
||||
unsafe { display_info(callbacks.opaque, input_name, &info, display_level) };
|
||||
let rendered = format_list_file_info(input_name, &info, display_level);
|
||||
unsafe {
|
||||
write_output(
|
||||
callbacks.opaque,
|
||||
rendered.as_ptr().cast::<c_char>(),
|
||||
rendered.len(),
|
||||
)
|
||||
};
|
||||
unsafe { output.write(project_list_file_info(&info)) };
|
||||
status
|
||||
}
|
||||
FIO_RUST_LIST_FILE_ACTION_INVALID => {
|
||||
unsafe { display_info(callbacks.opaque, input_name, &info, display_level) };
|
||||
let rendered = format_list_file_info(input_name, &info, display_level);
|
||||
unsafe {
|
||||
write_output(
|
||||
callbacks.opaque,
|
||||
rendered.as_ptr().cast::<c_char>(),
|
||||
rendered.len(),
|
||||
)
|
||||
};
|
||||
unsafe { output.write(project_list_file_info(&info)) };
|
||||
debug_assert!(
|
||||
action == FIO_RUST_LIST_FILE_ACTION_SUCCESS
|
||||
@@ -1070,7 +1236,7 @@ pub struct FIO_listMultipleFilesCallbacks_t {
|
||||
displayNoFilesError: Option<FIO_listMultipleFilesDisplayFn>,
|
||||
displayHeader: Option<FIO_listMultipleFilesDisplayFn>,
|
||||
listFile: Option<FIO_listMultipleFilesListFileFn>,
|
||||
displayTotal: Option<FIO_listMultipleFilesDisplayTotalFn>,
|
||||
writeOutput: Option<FIO_listFileWriteOutputFn>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "decompression")]
|
||||
@@ -2645,8 +2811,9 @@ fn add_list_file_info(
|
||||
/// Run the policy surrounding C's `--list` file operation.
|
||||
///
|
||||
/// Rust owns validation, iteration, error aggregation, and the total-row
|
||||
/// decision. The callbacks retain C's private file-info operations and all
|
||||
/// user-visible formatting so the private `fileInfo_t` never crosses the ABI.
|
||||
/// decision. The callbacks retain C's private file-info operations and exact
|
||||
/// status diagnostics; Rust owns all row formatting so the private
|
||||
/// `fileInfo_t` never crosses the ABI.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn FIO_rust_listMultipleFiles(
|
||||
numFiles: c_uint,
|
||||
@@ -2714,10 +2881,17 @@ pub unsafe extern "C" fn FIO_rust_listMultipleFiles(
|
||||
}
|
||||
|
||||
if numFiles > 1 && displayLevel <= 2 {
|
||||
let display_total = callbacks
|
||||
.displayTotal
|
||||
.expect("--list total callback is required");
|
||||
unsafe { display_total(callbacks.opaque, &total) };
|
||||
let write_output = callbacks
|
||||
.writeOutput
|
||||
.expect("--list output callback is required");
|
||||
let rendered = format_list_total(&total);
|
||||
unsafe {
|
||||
write_output(
|
||||
callbacks.opaque,
|
||||
rendered.as_ptr().cast::<c_char>(),
|
||||
rendered.len(),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
error
|
||||
@@ -3863,7 +4037,7 @@ mod tests {
|
||||
infos: Vec<FIO_listFileInfoProjection_t>,
|
||||
list_calls: usize,
|
||||
display_levels: Vec<c_int>,
|
||||
total: Option<FIO_listFileInfoProjection_t>,
|
||||
output: Vec<u8>,
|
||||
}
|
||||
|
||||
unsafe extern "C" fn list_multiple_is_stdin(
|
||||
@@ -3906,13 +4080,16 @@ mod tests {
|
||||
state.statuses[index]
|
||||
}
|
||||
|
||||
unsafe extern "C" fn list_multiple_display_total(
|
||||
unsafe extern "C" fn list_multiple_write_output(
|
||||
opaque: *mut c_void,
|
||||
total: *const FIO_listFileInfoProjection_t,
|
||||
data: *const c_char,
|
||||
size: usize,
|
||||
) {
|
||||
let state = unsafe { &mut *opaque.cast::<ListMultipleFilesTestState>() };
|
||||
state.events.push("total");
|
||||
state.total = Some(unsafe { *total });
|
||||
state.events.push("write-output");
|
||||
state
|
||||
.output
|
||||
.extend_from_slice(unsafe { std::slice::from_raw_parts(data.cast::<u8>(), size) });
|
||||
}
|
||||
|
||||
fn list_multiple_callbacks(
|
||||
@@ -3925,7 +4102,7 @@ mod tests {
|
||||
displayNoFilesError: Some(list_multiple_display_no_files_error),
|
||||
displayHeader: Some(list_multiple_display_header),
|
||||
listFile: Some(list_multiple_list_file),
|
||||
displayTotal: Some(list_multiple_display_total),
|
||||
writeOutput: Some(list_multiple_write_output),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3935,7 +4112,7 @@ mod tests {
|
||||
info: FIO_fileInfo_t,
|
||||
events: Vec<&'static str>,
|
||||
status_actions: Vec<(c_int, c_int)>,
|
||||
displayed: Option<(FIO_fileInfo_t, c_int)>,
|
||||
output: Vec<u8>,
|
||||
}
|
||||
|
||||
unsafe extern "C" fn list_file_get_info(
|
||||
@@ -3960,15 +4137,16 @@ mod tests {
|
||||
state.status_actions.push((action, display_level));
|
||||
}
|
||||
|
||||
unsafe extern "C" fn list_file_display_info(
|
||||
unsafe extern "C" fn list_file_write_output(
|
||||
opaque: *mut c_void,
|
||||
_file_name: *const c_char,
|
||||
info: *const FIO_fileInfo_t,
|
||||
display_level: c_int,
|
||||
data: *const c_char,
|
||||
size: usize,
|
||||
) {
|
||||
let state = unsafe { &mut *opaque.cast::<ListFileTestState>() };
|
||||
state.events.push("display-info");
|
||||
state.displayed = Some((unsafe { *info }, display_level));
|
||||
state.events.push("write-output");
|
||||
state
|
||||
.output
|
||||
.extend_from_slice(unsafe { std::slice::from_raw_parts(data.cast::<u8>(), size) });
|
||||
}
|
||||
|
||||
fn list_file_callbacks(state: &mut ListFileTestState) -> FIO_listFileCallbacks_t {
|
||||
@@ -3976,7 +4154,7 @@ mod tests {
|
||||
opaque: (state as *mut ListFileTestState).cast(),
|
||||
getInfo: Some(list_file_get_info),
|
||||
displayStatus: Some(list_file_display_status),
|
||||
displayInfo: Some(list_file_display_info),
|
||||
writeOutput: Some(list_file_write_output),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4698,7 +4876,7 @@ mod tests {
|
||||
assert_eq!(status, 1);
|
||||
assert_eq!(state.events, vec!["stdin", "stdin", "stdin-error"]);
|
||||
assert_eq!(state.list_calls, 0);
|
||||
assert!(state.total.is_none());
|
||||
assert!(state.output.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -4711,7 +4889,7 @@ mod tests {
|
||||
assert_eq!(status, 1);
|
||||
assert_eq!(state.events, vec!["no-files"]);
|
||||
assert_eq!(state.list_calls, 0);
|
||||
assert!(state.total.is_none());
|
||||
assert!(state.output.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -4763,20 +4941,21 @@ mod tests {
|
||||
assert_eq!(state.display_levels, vec![2, 2, 2]);
|
||||
assert_eq!(
|
||||
state.events,
|
||||
vec!["stdin", "stdin", "stdin", "header", "list", "list", "list", "total"]
|
||||
);
|
||||
assert_eq!(
|
||||
state.total,
|
||||
Some(FIO_listFileInfoProjection_t {
|
||||
decompressedSize: 30,
|
||||
compressedSize: 10,
|
||||
numActualFrames: 4,
|
||||
numSkippableFrames: 2,
|
||||
decompUnavailable: 0,
|
||||
usesCheck: 1,
|
||||
nbFiles: 2,
|
||||
})
|
||||
vec![
|
||||
"stdin",
|
||||
"stdin",
|
||||
"stdin",
|
||||
"header",
|
||||
"list",
|
||||
"list",
|
||||
"list",
|
||||
"write-output"
|
||||
]
|
||||
);
|
||||
assert!(state
|
||||
.output
|
||||
.starts_with(b"----------------------------------------------------------------- \n"));
|
||||
assert!(state.output.ends_with(b"2 files\n"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -4804,7 +4983,7 @@ mod tests {
|
||||
|
||||
assert_eq!(status, 0);
|
||||
assert_eq!(state.events, vec!["stdin", "stdin", "list", "list"]);
|
||||
assert!(state.total.is_none());
|
||||
assert!(state.output.is_empty());
|
||||
|
||||
let single_name = [CString::new("single.zst").unwrap()];
|
||||
let single_table = [single_name[0].as_ptr()];
|
||||
@@ -4819,7 +4998,83 @@ mod tests {
|
||||
|
||||
assert_eq!(status, 0);
|
||||
assert_eq!(state.events, vec!["stdin", "header", "list"]);
|
||||
assert!(state.total.is_none());
|
||||
assert!(state.output.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_formatters_match_original_column_and_verbose_output() {
|
||||
let file_name = CString::new("input.zst").unwrap();
|
||||
let info = FIO_fileInfo_t {
|
||||
decompressedSize: 100,
|
||||
compressedSize: 40,
|
||||
windowSize: 32,
|
||||
numActualFrames: 2,
|
||||
numSkippableFrames: 1,
|
||||
decompUnavailable: 0,
|
||||
usesCheck: 1,
|
||||
checksum: [1, 2, 3, 4],
|
||||
nbFiles: 1,
|
||||
dictID: 7,
|
||||
};
|
||||
let normal_expected = [
|
||||
b" 3 ".as_slice(),
|
||||
b" 1 ".as_slice(),
|
||||
b" 40 B".as_slice(),
|
||||
b" ".as_slice(),
|
||||
b" 100 B".as_slice(),
|
||||
b" 2.500 XXH64 input.zst\n".as_slice(),
|
||||
]
|
||||
.concat();
|
||||
assert_eq!(
|
||||
format_list_file_info(file_name.as_ptr(), &info, 2),
|
||||
normal_expected
|
||||
);
|
||||
|
||||
let unavailable = FIO_fileInfo_t {
|
||||
numActualFrames: 0,
|
||||
decompUnavailable: 1,
|
||||
usesCheck: 0,
|
||||
..info
|
||||
};
|
||||
let mut unavailable_expected = b" 1 ".to_vec();
|
||||
unavailable_expected.extend_from_slice(b" 1 ");
|
||||
unavailable_expected.extend_from_slice(b" 40 B");
|
||||
unavailable_expected.extend(std::iter::repeat_n(b' ', 23));
|
||||
unavailable_expected.extend_from_slice(b" None input.zst\n");
|
||||
assert_eq!(
|
||||
format_list_file_info(file_name.as_ptr(), &unavailable, 2),
|
||||
unavailable_expected
|
||||
);
|
||||
|
||||
let verbose_info = FIO_fileInfo_t {
|
||||
numActualFrames: 1,
|
||||
..info
|
||||
};
|
||||
assert_eq!(
|
||||
format_list_file_info(file_name.as_ptr(), &verbose_info, 3),
|
||||
b"input.zst \n# Zstandard Frames: 1\n# Skippable Frames: 1\nDictID: 7\nWindow Size: 32 B (32 B)\nCompressed Size: 40 B (40 B)\nDecompressed Size: 100 B (100 B)\nRatio: 2.5000\nCheck: XXH64 04030201\n\n"
|
||||
);
|
||||
|
||||
let total = FIO_listFileInfoProjection_t {
|
||||
decompressedSize: 30,
|
||||
compressedSize: 10,
|
||||
numActualFrames: 4,
|
||||
numSkippableFrames: 2,
|
||||
decompUnavailable: 0,
|
||||
usesCheck: 1,
|
||||
nbFiles: 2,
|
||||
};
|
||||
let total_expected = [
|
||||
b"----------------------------------------------------------------- \n".as_slice(),
|
||||
b" 6 ".as_slice(),
|
||||
b" 2 ".as_slice(),
|
||||
b" 10 B".as_slice(),
|
||||
b" ".as_slice(),
|
||||
b" 30 B".as_slice(),
|
||||
b" 3.000 XXH64 2 files\n".as_slice(),
|
||||
]
|
||||
.concat();
|
||||
assert_eq!(format_list_total(&total), total_expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -4861,14 +5116,11 @@ mod tests {
|
||||
size_of::<Option<FIO_listMultipleFilesListFileFn>>(),
|
||||
callback
|
||||
);
|
||||
assert_eq!(
|
||||
size_of::<Option<FIO_listMultipleFilesDisplayTotalFn>>(),
|
||||
callback
|
||||
);
|
||||
assert_eq!(size_of::<Option<FIO_listFileWriteOutputFn>>(), callback);
|
||||
assert_eq!(offset_of!(FIO_listMultipleFilesCallbacks_t, opaque), 0);
|
||||
assert_eq!(offset_of!(FIO_listMultipleFilesCallbacks_t, isStdin), word);
|
||||
assert_eq!(
|
||||
offset_of!(FIO_listMultipleFilesCallbacks_t, displayTotal),
|
||||
offset_of!(FIO_listMultipleFilesCallbacks_t, writeOutput),
|
||||
word + 5 * callback
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -4953,9 +5205,9 @@ mod tests {
|
||||
state.events,
|
||||
if publishes_info {
|
||||
if expected_action == FIO_RUST_LIST_FILE_ACTION_FRAME_ERROR {
|
||||
vec!["get-info", "display-status", "display-info"]
|
||||
vec!["get-info", "display-status", "write-output"]
|
||||
} else {
|
||||
vec!["get-info", "display-info"]
|
||||
vec!["get-info", "write-output"]
|
||||
}
|
||||
} else {
|
||||
vec!["get-info", "display-status"]
|
||||
@@ -4972,9 +5224,9 @@ mod tests {
|
||||
"status {status} diagnostic action"
|
||||
);
|
||||
assert_eq!(
|
||||
state.displayed,
|
||||
publishes_info.then_some((info, display_level)),
|
||||
"status {status} display gate"
|
||||
!state.output.is_empty(),
|
||||
publishes_info,
|
||||
"status {status} output gate"
|
||||
);
|
||||
assert_eq!(
|
||||
output,
|
||||
@@ -5000,7 +5252,7 @@ mod tests {
|
||||
word + callback
|
||||
);
|
||||
assert_eq!(
|
||||
offset_of!(FIO_listFileCallbacks_t, displayInfo),
|
||||
offset_of!(FIO_listFileCallbacks_t, writeOutput),
|
||||
word + 2 * callback
|
||||
);
|
||||
assert_eq!(size_of::<FIO_listFileCallbacks_t>(), word + 3 * callback);
|
||||
|
||||
Reference in New Issue
Block a user