refactor(cli): move decompression status action policy to Rust

The decompression callback in fileio used to classify result statuses and
select its display action with a C switch. That left scalar result policy in
the C frontend even though Rust already owns the decompression dispatch and
result classification, and it coupled the C callback to a diagnostic enum.

Rust now exposes an ABI-checked status-action classifier. It keeps the
original action ordering and preserves silent handling for statuses that have
no display diagnostic, while the C callback retains the exact diagnostic
strings, source-name formatting, and display operation. Invalid inputs map to
a silent fallback action so the public callback remains behavior-compatible.
Focused Rust tests cover every status class, invalid values, and the exported
ABI result.

Test Plan:
- `git diff --cached --check` -- passed
- `rustfmt --edition 2021 --check rust/src/fileio_asyncio.rs` -- passed
- Full capped Rust/native verification remains pending until the parallel
  compression and decompression seams are integrated.
This commit is contained in:
2026-07-21 06:50:25 +02:00
parent c075a7b2d9
commit bc3c3b1ac6
2 changed files with 86 additions and 56 deletions
+20 -15
View File
@@ -472,14 +472,17 @@ enum {
FIO_RUST_DECOMPRESS_PASS_THROUGH_ERROR = 9, FIO_RUST_DECOMPRESS_PASS_THROUGH_ERROR = 9,
FIO_RUST_DECOMPRESS_ZSTD_UNSUPPORTED = 10 FIO_RUST_DECOMPRESS_ZSTD_UNSUPPORTED = 10
}; };
/* Rust owns the decompression status-to-action policy. C keeps the exact
* diagnostic strings and display callback around the resulting action. */
enum { enum {
FIO_RUST_DECOMPRESS_DIAGNOSTIC_NONE = 0, FIO_RUST_DECOMPRESS_ACTION_NOOP = 0,
FIO_RUST_DECOMPRESS_DIAGNOSTIC_EMPTY_INPUT = 1, FIO_RUST_DECOMPRESS_ACTION_EMPTY_INPUT = 1,
FIO_RUST_DECOMPRESS_DIAGNOSTIC_SHORT_INPUT = 2, FIO_RUST_DECOMPRESS_ACTION_SHORT_INPUT = 2,
FIO_RUST_DECOMPRESS_DIAGNOSTIC_GZIP_UNSUPPORTED = 3, FIO_RUST_DECOMPRESS_ACTION_GZIP_UNSUPPORTED = 3,
FIO_RUST_DECOMPRESS_DIAGNOSTIC_LZMA_UNSUPPORTED = 4, FIO_RUST_DECOMPRESS_ACTION_LZMA_UNSUPPORTED = 4,
FIO_RUST_DECOMPRESS_DIAGNOSTIC_LZ4_UNSUPPORTED = 5, FIO_RUST_DECOMPRESS_ACTION_LZ4_UNSUPPORTED = 5,
FIO_RUST_DECOMPRESS_DIAGNOSTIC_UNSUPPORTED_FORMAT = 6 FIO_RUST_DECOMPRESS_ACTION_UNSUPPORTED_FORMAT = 6,
FIO_RUST_DECOMPRESS_ACTION_INVALID = 7
}; };
typedef int (*FIO_rust_decompress_frame_fn)(void* opaque, typedef int (*FIO_rust_decompress_frame_fn)(void* opaque,
const char* srcFileName, const char* srcFileName,
@@ -513,7 +516,7 @@ int FIO_rust_finishDecompressFrames(int status,
const char* srcFileName, const char* srcFileName,
U64 decodedSize, U64 decodedSize,
const FIO_rust_decompress_callbacks_t* callbacks); const FIO_rust_decompress_callbacks_t* callbacks);
int FIO_rust_decompressStatusDiagnostic(int status); int FIO_rust_decompressStatusAction(int status);
int FIO_rust_decompressPassThroughPolicy(int passThrough, int FIO_rust_decompressPassThroughPolicy(int passThrough,
int overwrite, int overwrite,
int destinationIsStdout); int destinationIsStdout);
@@ -4233,25 +4236,27 @@ static void FIO_rust_decompressStatusCallback(void* opaque,
const char* srcFileName) const char* srcFileName)
{ {
(void)opaque; (void)opaque;
switch (FIO_rust_decompressStatusDiagnostic(status)) { switch (FIO_rust_decompressStatusAction(status)) {
case FIO_RUST_DECOMPRESS_DIAGNOSTIC_EMPTY_INPUT: case FIO_RUST_DECOMPRESS_ACTION_EMPTY_INPUT:
DISPLAYLEVEL(1, "zstd: %s: unexpected end of file \n", srcFileName); DISPLAYLEVEL(1, "zstd: %s: unexpected end of file \n", srcFileName);
break; break;
case FIO_RUST_DECOMPRESS_DIAGNOSTIC_SHORT_INPUT: case FIO_RUST_DECOMPRESS_ACTION_SHORT_INPUT:
DISPLAYLEVEL(1, "zstd: %s: unknown header \n", srcFileName); DISPLAYLEVEL(1, "zstd: %s: unknown header \n", srcFileName);
break; break;
case FIO_RUST_DECOMPRESS_DIAGNOSTIC_GZIP_UNSUPPORTED: case FIO_RUST_DECOMPRESS_ACTION_GZIP_UNSUPPORTED:
DISPLAYLEVEL(1, "zstd: %s: gzip file cannot be uncompressed (zstd compiled without HAVE_ZLIB) -- ignored \n", srcFileName); DISPLAYLEVEL(1, "zstd: %s: gzip file cannot be uncompressed (zstd compiled without HAVE_ZLIB) -- ignored \n", srcFileName);
break; break;
case FIO_RUST_DECOMPRESS_DIAGNOSTIC_LZMA_UNSUPPORTED: case FIO_RUST_DECOMPRESS_ACTION_LZMA_UNSUPPORTED:
DISPLAYLEVEL(1, "zstd: %s: xz/lzma file cannot be uncompressed (zstd compiled without HAVE_LZMA) -- ignored \n", srcFileName); DISPLAYLEVEL(1, "zstd: %s: xz/lzma file cannot be uncompressed (zstd compiled without HAVE_LZMA) -- ignored \n", srcFileName);
break; break;
case FIO_RUST_DECOMPRESS_DIAGNOSTIC_LZ4_UNSUPPORTED: case FIO_RUST_DECOMPRESS_ACTION_LZ4_UNSUPPORTED:
DISPLAYLEVEL(1, "zstd: %s: lz4 file cannot be uncompressed (zstd compiled without HAVE_LZ4) -- ignored \n", srcFileName); DISPLAYLEVEL(1, "zstd: %s: lz4 file cannot be uncompressed (zstd compiled without HAVE_LZ4) -- ignored \n", srcFileName);
break; break;
case FIO_RUST_DECOMPRESS_DIAGNOSTIC_UNSUPPORTED_FORMAT: case FIO_RUST_DECOMPRESS_ACTION_UNSUPPORTED_FORMAT:
DISPLAYLEVEL(1, "zstd: %s: unsupported format \n", srcFileName); DISPLAYLEVEL(1, "zstd: %s: unsupported format \n", srcFileName);
break; break;
case FIO_RUST_DECOMPRESS_ACTION_NOOP:
case FIO_RUST_DECOMPRESS_ACTION_INVALID:
default: default:
break; break;
} }
+66 -41
View File
@@ -56,13 +56,14 @@ pub const FIO_RUST_DECOMPRESS_UNSUPPORTED_FORMAT: c_int = 8;
pub const FIO_RUST_DECOMPRESS_PASS_THROUGH_ERROR: c_int = 9; pub const FIO_RUST_DECOMPRESS_PASS_THROUGH_ERROR: c_int = 9;
pub const FIO_RUST_DECOMPRESS_ZSTD_UNSUPPORTED: c_int = 10; pub const FIO_RUST_DECOMPRESS_ZSTD_UNSUPPORTED: c_int = 10;
pub const FIO_RUST_DECOMPRESS_DIAGNOSTIC_NONE: c_int = 0; pub const FIO_RUST_DECOMPRESS_ACTION_NOOP: c_int = 0;
pub const FIO_RUST_DECOMPRESS_DIAGNOSTIC_EMPTY_INPUT: c_int = 1; pub const FIO_RUST_DECOMPRESS_ACTION_EMPTY_INPUT: c_int = 1;
pub const FIO_RUST_DECOMPRESS_DIAGNOSTIC_SHORT_INPUT: c_int = 2; pub const FIO_RUST_DECOMPRESS_ACTION_SHORT_INPUT: c_int = 2;
pub const FIO_RUST_DECOMPRESS_DIAGNOSTIC_GZIP_UNSUPPORTED: c_int = 3; pub const FIO_RUST_DECOMPRESS_ACTION_GZIP_UNSUPPORTED: c_int = 3;
pub const FIO_RUST_DECOMPRESS_DIAGNOSTIC_LZMA_UNSUPPORTED: c_int = 4; pub const FIO_RUST_DECOMPRESS_ACTION_LZMA_UNSUPPORTED: c_int = 4;
pub const FIO_RUST_DECOMPRESS_DIAGNOSTIC_LZ4_UNSUPPORTED: c_int = 5; pub const FIO_RUST_DECOMPRESS_ACTION_LZ4_UNSUPPORTED: c_int = 5;
pub const FIO_RUST_DECOMPRESS_DIAGNOSTIC_UNSUPPORTED_FORMAT: c_int = 6; pub const FIO_RUST_DECOMPRESS_ACTION_UNSUPPORTED_FORMAT: c_int = 6;
pub const FIO_RUST_DECOMPRESS_ACTION_INVALID: c_int = 7;
/// Resolves the legacy automatic pass-through mode without touching the /// Resolves the legacy automatic pass-through mode without touching the
/// destination name or any C-owned file state. Explicit values are returned /// destination name or any C-owned file state. Explicit values are returned
@@ -89,22 +90,32 @@ pub extern "C" fn FIO_rust_decompressPassThroughPolicy(
decompress_pass_through_policy(pass_through, overwrite, destination_is_stdout) decompress_pass_through_policy(pass_through, overwrite, destination_is_stdout)
} }
/// Maps decompression results to the diagnostics that the C CLI displays. /// Maps decompression results to the scalar actions consumed by the C CLI.
/// Statuses without a legacy diagnostic deliberately map to `NONE`, retaining /// Rust owns this status policy; C retains the exact diagnostics, source-name
/// the previous C callback's silent default case. /// handling, and display operations for the returned action.
#[no_mangle] #[inline]
pub extern "C" fn FIO_rust_decompressStatusDiagnostic(status: c_int) -> c_int { fn decompress_status_action(status: c_int) -> c_int {
match status { match status {
FIO_RUST_DECOMPRESS_EMPTY_INPUT => FIO_RUST_DECOMPRESS_DIAGNOSTIC_EMPTY_INPUT, FIO_RUST_DECOMPRESS_EMPTY_INPUT => FIO_RUST_DECOMPRESS_ACTION_EMPTY_INPUT,
FIO_RUST_DECOMPRESS_SHORT_INPUT => FIO_RUST_DECOMPRESS_DIAGNOSTIC_SHORT_INPUT, FIO_RUST_DECOMPRESS_SHORT_INPUT => FIO_RUST_DECOMPRESS_ACTION_SHORT_INPUT,
FIO_RUST_DECOMPRESS_GZIP_UNSUPPORTED => FIO_RUST_DECOMPRESS_DIAGNOSTIC_GZIP_UNSUPPORTED, FIO_RUST_DECOMPRESS_GZIP_UNSUPPORTED => FIO_RUST_DECOMPRESS_ACTION_GZIP_UNSUPPORTED,
FIO_RUST_DECOMPRESS_LZMA_UNSUPPORTED => FIO_RUST_DECOMPRESS_DIAGNOSTIC_LZMA_UNSUPPORTED, FIO_RUST_DECOMPRESS_LZMA_UNSUPPORTED => FIO_RUST_DECOMPRESS_ACTION_LZMA_UNSUPPORTED,
FIO_RUST_DECOMPRESS_LZ4_UNSUPPORTED => FIO_RUST_DECOMPRESS_DIAGNOSTIC_LZ4_UNSUPPORTED, FIO_RUST_DECOMPRESS_LZ4_UNSUPPORTED => FIO_RUST_DECOMPRESS_ACTION_LZ4_UNSUPPORTED,
FIO_RUST_DECOMPRESS_UNSUPPORTED_FORMAT => FIO_RUST_DECOMPRESS_DIAGNOSTIC_UNSUPPORTED_FORMAT, FIO_RUST_DECOMPRESS_UNSUPPORTED_FORMAT => FIO_RUST_DECOMPRESS_ACTION_UNSUPPORTED_FORMAT,
_ => FIO_RUST_DECOMPRESS_DIAGNOSTIC_NONE, FIO_RUST_DECOMPRESS_OK
| FIO_RUST_DECOMPRESS_PASS_THROUGH
| FIO_RUST_DECOMPRESS_FRAME_ERROR
| FIO_RUST_DECOMPRESS_PASS_THROUGH_ERROR
| FIO_RUST_DECOMPRESS_ZSTD_UNSUPPORTED => FIO_RUST_DECOMPRESS_ACTION_NOOP,
_ => FIO_RUST_DECOMPRESS_ACTION_INVALID,
} }
} }
#[no_mangle]
pub extern "C" fn FIO_rust_decompressStatusAction(status: c_int) -> c_int {
decompress_status_action(status)
}
type FIO_rust_frame_progress_fn = Option<unsafe extern "C" fn(*mut c_void, *const c_char, u64)>; type FIO_rust_frame_progress_fn = Option<unsafe extern "C" fn(*mut c_void, *const c_char, u64)>;
pub type FIO_rust_decompress_frame_fn = pub type FIO_rust_decompress_frame_fn =
unsafe extern "C" fn(*mut c_void, *const c_char, u64, *mut u64, *mut usize, c_int) -> c_int; unsafe extern "C" fn(*mut c_void, *const c_char, u64, *mut u64, *mut usize, c_int) -> c_int;
@@ -5344,48 +5355,62 @@ mod tests {
} }
#[test] #[test]
fn decompression_status_diagnostic_preserves_cli_mapping() { fn decompression_status_action_preserves_cli_mapping_and_abi() {
for (status, diagnostic) in [ for (status, action) in [
(FIO_RUST_DECOMPRESS_OK, FIO_RUST_DECOMPRESS_ACTION_NOOP),
(
FIO_RUST_DECOMPRESS_PASS_THROUGH,
FIO_RUST_DECOMPRESS_ACTION_NOOP,
),
( (
FIO_RUST_DECOMPRESS_EMPTY_INPUT, FIO_RUST_DECOMPRESS_EMPTY_INPUT,
FIO_RUST_DECOMPRESS_DIAGNOSTIC_EMPTY_INPUT, FIO_RUST_DECOMPRESS_ACTION_EMPTY_INPUT,
), ),
( (
FIO_RUST_DECOMPRESS_SHORT_INPUT, FIO_RUST_DECOMPRESS_SHORT_INPUT,
FIO_RUST_DECOMPRESS_DIAGNOSTIC_SHORT_INPUT, FIO_RUST_DECOMPRESS_ACTION_SHORT_INPUT,
), ),
( (
FIO_RUST_DECOMPRESS_GZIP_UNSUPPORTED, FIO_RUST_DECOMPRESS_GZIP_UNSUPPORTED,
FIO_RUST_DECOMPRESS_DIAGNOSTIC_GZIP_UNSUPPORTED, FIO_RUST_DECOMPRESS_ACTION_GZIP_UNSUPPORTED,
), ),
( (
FIO_RUST_DECOMPRESS_LZMA_UNSUPPORTED, FIO_RUST_DECOMPRESS_LZMA_UNSUPPORTED,
FIO_RUST_DECOMPRESS_DIAGNOSTIC_LZMA_UNSUPPORTED, FIO_RUST_DECOMPRESS_ACTION_LZMA_UNSUPPORTED,
), ),
( (
FIO_RUST_DECOMPRESS_LZ4_UNSUPPORTED, FIO_RUST_DECOMPRESS_LZ4_UNSUPPORTED,
FIO_RUST_DECOMPRESS_DIAGNOSTIC_LZ4_UNSUPPORTED, FIO_RUST_DECOMPRESS_ACTION_LZ4_UNSUPPORTED,
), ),
( (
FIO_RUST_DECOMPRESS_UNSUPPORTED_FORMAT, FIO_RUST_DECOMPRESS_UNSUPPORTED_FORMAT,
FIO_RUST_DECOMPRESS_DIAGNOSTIC_UNSUPPORTED_FORMAT, FIO_RUST_DECOMPRESS_ACTION_UNSUPPORTED_FORMAT,
), ),
] { (
assert_eq!(FIO_rust_decompressStatusDiagnostic(status), diagnostic); FIO_RUST_DECOMPRESS_FRAME_ERROR,
} FIO_RUST_DECOMPRESS_ACTION_NOOP,
),
for status in [ (
FIO_RUST_DECOMPRESS_OK, FIO_RUST_DECOMPRESS_PASS_THROUGH_ERROR,
FIO_RUST_DECOMPRESS_PASS_THROUGH, FIO_RUST_DECOMPRESS_ACTION_NOOP,
FIO_RUST_DECOMPRESS_FRAME_ERROR, ),
FIO_RUST_DECOMPRESS_PASS_THROUGH_ERROR, (
FIO_RUST_DECOMPRESS_ZSTD_UNSUPPORTED, FIO_RUST_DECOMPRESS_ZSTD_UNSUPPORTED,
-1, FIO_RUST_DECOMPRESS_ACTION_NOOP,
99, ),
(-1, FIO_RUST_DECOMPRESS_ACTION_INVALID),
(11, FIO_RUST_DECOMPRESS_ACTION_INVALID),
(c_int::MAX, FIO_RUST_DECOMPRESS_ACTION_INVALID),
] { ] {
assert_eq!( assert_eq!(
FIO_rust_decompressStatusDiagnostic(status), decompress_status_action(status),
FIO_RUST_DECOMPRESS_DIAGNOSTIC_NONE action,
"unexpected action for decompression status {status}"
);
assert_eq!(
FIO_rust_decompressStatusAction(status),
action,
"C ABI disagrees for decompression status {status}"
); );
} }
} }