refactor(cli): make decompression finish policy explicit in Rust

Extract the mixed-format decompression finish status mapping into a pure Rust policy helper and keep the existing callback side effects, diagnostics, and return values in the ABI wrapper. The explicit enum makes the success, pass-through, report, and impossible-status branches auditable and testable without widening the private fileio boundary.

Test Plan: ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo clippy --manifest-path rust/Cargo.toml --all-targets -- -D warnings; cargo +nightly fmt --manifest-path rust/Cargo.toml --all -- --check; make -j1; ./tests/rustLibSmoke; make -j1 -C tests test (all shell tests, large streaming tests, native tester, fuzzer phases, and zstream tester passed).
This commit is contained in:
2026-07-20 18:25:37 +02:00
parent 701bc899ad
commit 6caba856d4
+86 -13
View File
@@ -5218,6 +5218,31 @@ pub unsafe extern "C" fn FIO_rust_decompressFrames(
/// successful-file finalization. C keeps the diagnostic and accounting /// successful-file finalization. C keeps the diagnostic and accounting
/// callbacks because they touch `DISPLAY*` state and the private `FIO_ctx_t` /// callbacks because they touch `DISPLAY*` state and the private `FIO_ctx_t`
/// layout; Rust owns the result policy and decides which callback is legal. /// layout; Rust owns the result policy and decides which callback is legal.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum DecompressFinishAction {
Finish,
PassThrough,
ReportStatus,
Invalid,
}
fn classify_decompress_finish_status(status: c_int) -> DecompressFinishAction {
match status {
FIO_RUST_DECOMPRESS_OK => DecompressFinishAction::Finish,
FIO_RUST_DECOMPRESS_PASS_THROUGH => DecompressFinishAction::PassThrough,
FIO_RUST_DECOMPRESS_EMPTY_INPUT
| FIO_RUST_DECOMPRESS_SHORT_INPUT
| FIO_RUST_DECOMPRESS_GZIP_UNSUPPORTED
| FIO_RUST_DECOMPRESS_LZMA_UNSUPPORTED
| FIO_RUST_DECOMPRESS_LZ4_UNSUPPORTED
| FIO_RUST_DECOMPRESS_FRAME_ERROR
| FIO_RUST_DECOMPRESS_UNSUPPORTED_FORMAT
| FIO_RUST_DECOMPRESS_PASS_THROUGH_ERROR
| FIO_RUST_DECOMPRESS_ZSTD_UNSUPPORTED => DecompressFinishAction::ReportStatus,
_ => DecompressFinishAction::Invalid,
}
}
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn FIO_rust_finishDecompressFrames( pub unsafe extern "C" fn FIO_rust_finishDecompressFrames(
status: c_int, status: c_int,
@@ -5229,29 +5254,21 @@ pub unsafe extern "C" fn FIO_rust_finishDecompressFrames(
assert!(!callbacks.is_null()); assert!(!callbacks.is_null());
let callbacks = unsafe { &*callbacks }; let callbacks = unsafe { &*callbacks };
match status { match classify_decompress_finish_status(status) {
FIO_RUST_DECOMPRESS_OK => { DecompressFinishAction::Finish => {
if let Some(finish) = callbacks.finish { if let Some(finish) = callbacks.finish {
unsafe { finish(callbacks.opaque, src_file_name, decoded_size) }; unsafe { finish(callbacks.opaque, src_file_name, decoded_size) };
} }
0 0
} }
FIO_RUST_DECOMPRESS_PASS_THROUGH => 0, DecompressFinishAction::PassThrough => 0,
FIO_RUST_DECOMPRESS_EMPTY_INPUT DecompressFinishAction::ReportStatus => {
| FIO_RUST_DECOMPRESS_SHORT_INPUT
| FIO_RUST_DECOMPRESS_GZIP_UNSUPPORTED
| FIO_RUST_DECOMPRESS_LZMA_UNSUPPORTED
| FIO_RUST_DECOMPRESS_LZ4_UNSUPPORTED
| FIO_RUST_DECOMPRESS_FRAME_ERROR
| FIO_RUST_DECOMPRESS_UNSUPPORTED_FORMAT
| FIO_RUST_DECOMPRESS_PASS_THROUGH_ERROR
| FIO_RUST_DECOMPRESS_ZSTD_UNSUPPORTED => {
if let Some(report_status) = callbacks.report_status { if let Some(report_status) = callbacks.report_status {
unsafe { report_status(callbacks.opaque, status, src_file_name) }; unsafe { report_status(callbacks.opaque, status, src_file_name) };
} }
1 1
} }
_ => { DecompressFinishAction::Invalid => {
unreachable!("unknown Rust decompression status: {status}"); unreachable!("unknown Rust decompression status: {status}");
} }
} }
@@ -9208,6 +9225,62 @@ mod tests {
state.finished_sizes.push(decoded_size); state.finished_sizes.push(decoded_size);
} }
#[test]
fn classifies_decompression_finish_statuses() {
for (status, expected_action) in [
(FIO_RUST_DECOMPRESS_OK, DecompressFinishAction::Finish),
(
FIO_RUST_DECOMPRESS_PASS_THROUGH,
DecompressFinishAction::PassThrough,
),
(
FIO_RUST_DECOMPRESS_EMPTY_INPUT,
DecompressFinishAction::ReportStatus,
),
(
FIO_RUST_DECOMPRESS_SHORT_INPUT,
DecompressFinishAction::ReportStatus,
),
(
FIO_RUST_DECOMPRESS_GZIP_UNSUPPORTED,
DecompressFinishAction::ReportStatus,
),
(
FIO_RUST_DECOMPRESS_LZMA_UNSUPPORTED,
DecompressFinishAction::ReportStatus,
),
(
FIO_RUST_DECOMPRESS_LZ4_UNSUPPORTED,
DecompressFinishAction::ReportStatus,
),
(
FIO_RUST_DECOMPRESS_FRAME_ERROR,
DecompressFinishAction::ReportStatus,
),
(
FIO_RUST_DECOMPRESS_UNSUPPORTED_FORMAT,
DecompressFinishAction::ReportStatus,
),
(
FIO_RUST_DECOMPRESS_PASS_THROUGH_ERROR,
DecompressFinishAction::ReportStatus,
),
(
FIO_RUST_DECOMPRESS_ZSTD_UNSUPPORTED,
DecompressFinishAction::ReportStatus,
),
] {
assert_eq!(classify_decompress_finish_status(status), expected_action);
}
for status in [-1, 99] {
assert_eq!(
classify_decompress_finish_status(status),
DecompressFinishAction::Invalid
);
}
}
#[test] #[test]
fn maps_decompression_status_and_finishes_only_success() { fn maps_decompression_status_and_finishes_only_success() {
let mut state = DecompressResultState { let mut state = DecompressResultState {