refactor(fileio): move zstd window diagnostic policy to Rust

Project the zstd window-too-large diagnostic inputs into Rust so Rust owns
error filtering and the concrete-versus-unsupported guidance choice. C keeps
frame-header parsing, window-log extraction, and the exact display text behind
a callback, preserving the existing diagnostics and fallback behavior.

All heavy verification was run serially with a 40 GiB virtual-memory cap and
one build job.

Test Plan:
- git diff --cached --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 (790 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 (184 passed)
- ulimit -v 41943040; cargo +nightly fmt --manifest-path rust/Cargo.toml --all -- --check
- ulimit -v 41943040; make -j1 (clean after C90 declaration cleanup)
- ulimit -v 41943040; make -j1 -C tests test (passed; the later cleanup only moved a declaration before statements)
This commit is contained in:
2026-07-20 08:21:27 +02:00
parent 69425cb8a4
commit 675c3fc307
2 changed files with 345 additions and 20 deletions
+242
View File
@@ -223,6 +223,127 @@ pub unsafe extern "C" fn FIO_rust_freeDResources(state: *const FIO_rust_freeDRes
}
}
#[cfg(feature = "decompression")]
// `ZSTD_error_frameParameter_windowTooLarge` from zstd_errors.h.
const ZSTD_ERROR_FRAME_PARAMETER_WINDOW_TOO_LARGE: c_int = 16;
#[cfg(feature = "decompression")]
const FIO_RUST_ZSTD_ERROR_HELP_WINDOW_SIZE: c_int = 0;
#[cfg(feature = "decompression")]
const FIO_RUST_ZSTD_ERROR_HELP_WINDOW_GUIDANCE: c_int = 1;
#[cfg(feature = "decompression")]
const FIO_RUST_ZSTD_ERROR_HELP_UNSUPPORTED_WINDOW_LOG: c_int = 2;
#[cfg(feature = "decompression")]
type FIO_rust_zstdErrorHelpDisplayFn =
unsafe extern "C" fn(*mut c_void, c_int, *const c_char, u64, c_uint, c_uint, c_uint);
/// Scalar projection for the zstd window-size error diagnostic.
///
/// C keeps frame-header decoding, window-log extraction, and output formatting
/// in C. Rust owns the error filter and selects the concrete or unsupported
/// diagnostic through the display callback.
#[cfg(feature = "decompression")]
#[repr(C)]
pub struct FIO_rust_zstdErrorHelpState {
callback_context: *mut c_void,
src_file_name: *const c_char,
window_size: u64,
header_error: usize,
window_log: c_uint,
mem_limit: c_uint,
error_code: c_int,
display: Option<FIO_rust_zstdErrorHelpDisplayFn>,
}
#[cfg(feature = "decompression")]
const _: () = {
let callback_offset = (offset_of!(FIO_rust_zstdErrorHelpState, error_code)
+ size_of::<c_int>())
.div_ceil(size_of::<usize>())
* size_of::<usize>();
assert!(offset_of!(FIO_rust_zstdErrorHelpState, callback_context) == 0);
assert!(offset_of!(FIO_rust_zstdErrorHelpState, src_file_name) == size_of::<usize>());
assert!(offset_of!(FIO_rust_zstdErrorHelpState, window_size) == 2 * size_of::<usize>());
assert!(
offset_of!(FIO_rust_zstdErrorHelpState, header_error)
== offset_of!(FIO_rust_zstdErrorHelpState, window_size) + size_of::<u64>()
);
assert!(
offset_of!(FIO_rust_zstdErrorHelpState, window_log)
== offset_of!(FIO_rust_zstdErrorHelpState, header_error) + size_of::<usize>()
);
assert!(
offset_of!(FIO_rust_zstdErrorHelpState, mem_limit)
== offset_of!(FIO_rust_zstdErrorHelpState, window_log) + size_of::<c_uint>()
);
assert!(
offset_of!(FIO_rust_zstdErrorHelpState, error_code)
== offset_of!(FIO_rust_zstdErrorHelpState, mem_limit) + size_of::<c_uint>()
);
assert!(size_of::<Option<FIO_rust_zstdErrorHelpDisplayFn>>() == size_of::<usize>());
assert!(offset_of!(FIO_rust_zstdErrorHelpState, display) == callback_offset);
assert!(size_of::<FIO_rust_zstdErrorHelpState>() == callback_offset + size_of::<usize>());
};
#[cfg(feature = "decompression")]
#[no_mangle]
pub unsafe extern "C" fn FIO_rust_zstdErrorHelp(state: *const FIO_rust_zstdErrorHelpState) {
let Some(state) = (unsafe { state.as_ref() }) else {
return;
};
if state.error_code != ZSTD_ERROR_FRAME_PARAMETER_WINDOW_TOO_LARGE {
return;
}
let Some(display) = state.display else {
return;
};
if state.header_error == 0 {
assert!(state.mem_limit > 0);
unsafe {
display(
state.callback_context,
FIO_RUST_ZSTD_ERROR_HELP_WINDOW_SIZE,
state.src_file_name,
state.window_size,
state.window_log,
0,
state.mem_limit,
);
}
if state.window_log <= ZSTD_WINDOWLOG_MAX {
let window_mb = ((state.window_size >> 20)
+ u64::from((state.window_size & ((1u64 << 20) - 1)) != 0))
as c_uint;
assert!(state.window_size < (1u64 << 52));
unsafe {
display(
state.callback_context,
FIO_RUST_ZSTD_ERROR_HELP_WINDOW_GUIDANCE,
state.src_file_name,
state.window_size,
state.window_log,
window_mb,
state.mem_limit,
);
}
return;
}
}
unsafe {
display(
state.callback_context,
FIO_RUST_ZSTD_ERROR_HELP_UNSUPPORTED_WINDOW_LOG,
state.src_file_name,
state.window_size,
state.window_log,
0,
state.mem_limit,
);
}
}
pub type FIO_createCResourcesSetParameterFn =
unsafe extern "C" fn(*mut c_void, c_int, c_int) -> usize;
pub type FIO_createCResourcesIsErrorFn = unsafe extern "C" fn(usize) -> c_int;
@@ -2280,6 +2401,127 @@ mod tests {
assert!(context.events.is_empty());
}
#[cfg(feature = "decompression")]
#[derive(Default)]
struct ZstdErrorHelpTestState {
events: Vec<(c_int, u64, c_uint, c_uint, c_uint)>,
}
#[cfg(feature = "decompression")]
unsafe extern "C" fn zstd_error_help_display_test(
context: *mut c_void,
diagnostic: c_int,
_src_file_name: *const c_char,
window_size: u64,
window_log: c_uint,
window_mb: c_uint,
mem_limit: c_uint,
) {
let state = unsafe { &mut *context.cast::<ZstdErrorHelpTestState>() };
state
.events
.push((diagnostic, window_size, window_log, window_mb, mem_limit));
}
#[cfg(feature = "decompression")]
fn zstd_error_help_test_state(
context: &mut ZstdErrorHelpTestState,
error_code: c_int,
header_error: usize,
window_size: u64,
window_log: c_uint,
mem_limit: c_uint,
) -> FIO_rust_zstdErrorHelpState {
FIO_rust_zstdErrorHelpState {
callback_context: context as *mut ZstdErrorHelpTestState as *mut c_void,
src_file_name: ptr::null(),
window_size,
header_error,
window_log,
mem_limit,
error_code,
display: Some(zstd_error_help_display_test),
}
}
#[cfg(feature = "decompression")]
#[test]
fn zstd_error_help_ignores_irrelevant_errors() {
let mut context = ZstdErrorHelpTestState::default();
let state = zstd_error_help_test_state(&mut context, 0, 0, 8u64 << 20, 23, 64);
unsafe { FIO_rust_zstdErrorHelp(&state) };
assert!(context.events.is_empty());
}
#[cfg(feature = "decompression")]
#[test]
fn zstd_error_help_selects_concrete_window_guidance() {
let mut context = ZstdErrorHelpTestState::default();
let state = zstd_error_help_test_state(
&mut context,
ZSTD_ERROR_FRAME_PARAMETER_WINDOW_TOO_LARGE,
0,
9u64 << 20,
24,
64,
);
unsafe { FIO_rust_zstdErrorHelp(&state) };
assert_eq!(
context.events,
vec![
(FIO_RUST_ZSTD_ERROR_HELP_WINDOW_SIZE, 9u64 << 20, 24, 0, 64,),
(
FIO_RUST_ZSTD_ERROR_HELP_WINDOW_GUIDANCE,
9u64 << 20,
24,
9,
64,
),
]
);
}
#[cfg(feature = "decompression")]
#[test]
fn zstd_error_help_selects_oversized_window_log_guidance() {
let mut context = ZstdErrorHelpTestState::default();
let window_log = ZSTD_WINDOWLOG_MAX + 1;
let state = zstd_error_help_test_state(
&mut context,
ZSTD_ERROR_FRAME_PARAMETER_WINDOW_TOO_LARGE,
0,
1u64 << window_log,
window_log,
64,
);
unsafe { FIO_rust_zstdErrorHelp(&state) };
assert_eq!(
context.events,
vec![
(
FIO_RUST_ZSTD_ERROR_HELP_WINDOW_SIZE,
1u64 << window_log,
window_log,
0,
64,
),
(
FIO_RUST_ZSTD_ERROR_HELP_UNSUPPORTED_WINDOW_LOG,
1u64 << window_log,
window_log,
0,
64,
),
]
);
}
#[derive(Default)]
struct CreateCResourcesTestState {
events: Vec<(c_int, c_int)>,