feat(cli): move decompression result policy into Rust
Move mixed-format decompression status mapping and successful-file finalization into Rust. C keeps the diagnostic and private FIO context callbacks, while Rust decides pass-through/error behavior and invokes final accounting only for a successful decoded file. Test Plan: - cargo test --manifest-path rust/Cargo.toml --all-targets -- --test-threads=1 - cargo test --manifest-path rust/cli/Cargo.toml --all-targets -- --test-threads=1 - cargo clippy --manifest-path rust/Cargo.toml --tests -- -D warnings - make -B -C programs -j2 zstd - make -B -C tests -j2 test-cli-tests
This commit is contained in:
+57
-40
@@ -441,6 +441,12 @@ typedef int (*FIO_rust_decompress_frame_fn)(void* opaque,
|
|||||||
size_t* errorCode,
|
size_t* errorCode,
|
||||||
int mode);
|
int mode);
|
||||||
typedef int (*FIO_rust_pass_through_fn)(void* opaque);
|
typedef int (*FIO_rust_pass_through_fn)(void* opaque);
|
||||||
|
typedef void (*FIO_rust_decompress_status_fn)(void* opaque,
|
||||||
|
int status,
|
||||||
|
const char* srcFileName);
|
||||||
|
typedef void (*FIO_rust_decompress_finish_fn)(void* opaque,
|
||||||
|
const char* srcFileName,
|
||||||
|
U64 decodedSize);
|
||||||
typedef struct {
|
typedef struct {
|
||||||
void* opaque;
|
void* opaque;
|
||||||
FIO_rust_decompress_frame_fn decode_zstd;
|
FIO_rust_decompress_frame_fn decode_zstd;
|
||||||
@@ -448,12 +454,18 @@ typedef struct {
|
|||||||
FIO_rust_decompress_frame_fn decode_lzma;
|
FIO_rust_decompress_frame_fn decode_lzma;
|
||||||
FIO_rust_decompress_frame_fn decode_lz4;
|
FIO_rust_decompress_frame_fn decode_lz4;
|
||||||
FIO_rust_pass_through_fn pass_through;
|
FIO_rust_pass_through_fn pass_through;
|
||||||
|
FIO_rust_decompress_status_fn report_status;
|
||||||
|
FIO_rust_decompress_finish_fn finish;
|
||||||
} FIO_rust_decompress_callbacks_t;
|
} FIO_rust_decompress_callbacks_t;
|
||||||
int FIO_rust_decompressFrames(ReadPoolCtx_t* readCtx,
|
int FIO_rust_decompressFrames(ReadPoolCtx_t* readCtx,
|
||||||
const char* srcFileName,
|
const char* srcFileName,
|
||||||
int passThrough,
|
int passThrough,
|
||||||
U64* decodedSize,
|
U64* decodedSize,
|
||||||
const FIO_rust_decompress_callbacks_t* callbacks);
|
const FIO_rust_decompress_callbacks_t* callbacks);
|
||||||
|
int FIO_rust_finishDecompressFrames(int status,
|
||||||
|
const char* srcFileName,
|
||||||
|
U64 decodedSize,
|
||||||
|
const FIO_rust_decompress_callbacks_t* callbacks);
|
||||||
void FIO_rust_displayCompressionParameters(const FIO_prefs_t* prefs);
|
void FIO_rust_displayCompressionParameters(const FIO_prefs_t* prefs);
|
||||||
#ifdef ZSTD_LZ4COMPRESS
|
#ifdef ZSTD_LZ4COMPRESS
|
||||||
int FIO_rust_LZ4_GetBlockSize_FromBlockId(int id);
|
int FIO_rust_LZ4_GetBlockSize_FromBlockId(int id);
|
||||||
@@ -3105,6 +3117,48 @@ static int FIO_rust_decompressPassThroughCallback(void* opaque)
|
|||||||
return FIO_passThrough(projection->ress);
|
return FIO_passThrough(projection->ress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void FIO_rust_decompressStatusCallback(void* opaque,
|
||||||
|
int status,
|
||||||
|
const char* srcFileName)
|
||||||
|
{
|
||||||
|
(void)opaque;
|
||||||
|
switch (status) {
|
||||||
|
case FIO_RUST_DECOMPRESS_EMPTY_INPUT:
|
||||||
|
DISPLAYLEVEL(1, "zstd: %s: unexpected end of file \n", srcFileName);
|
||||||
|
break;
|
||||||
|
case FIO_RUST_DECOMPRESS_SHORT_INPUT:
|
||||||
|
DISPLAYLEVEL(1, "zstd: %s: unknown header \n", srcFileName);
|
||||||
|
break;
|
||||||
|
case FIO_RUST_DECOMPRESS_GZIP_UNSUPPORTED:
|
||||||
|
DISPLAYLEVEL(1, "zstd: %s: gzip file cannot be uncompressed (zstd compiled without HAVE_ZLIB) -- ignored \n", srcFileName);
|
||||||
|
break;
|
||||||
|
case FIO_RUST_DECOMPRESS_LZMA_UNSUPPORTED:
|
||||||
|
DISPLAYLEVEL(1, "zstd: %s: xz/lzma file cannot be uncompressed (zstd compiled without HAVE_LZMA) -- ignored \n", srcFileName);
|
||||||
|
break;
|
||||||
|
case FIO_RUST_DECOMPRESS_LZ4_UNSUPPORTED:
|
||||||
|
DISPLAYLEVEL(1, "zstd: %s: lz4 file cannot be uncompressed (zstd compiled without HAVE_LZ4) -- ignored \n", srcFileName);
|
||||||
|
break;
|
||||||
|
case FIO_RUST_DECOMPRESS_UNSUPPORTED_FORMAT:
|
||||||
|
DISPLAYLEVEL(1, "zstd: %s: unsupported format \n", srcFileName);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FIO_rust_decompressFinalCallback(void* opaque,
|
||||||
|
const char* srcFileName,
|
||||||
|
U64 decodedSize)
|
||||||
|
{
|
||||||
|
FIO_rust_decompression_projection_t* const projection =
|
||||||
|
(FIO_rust_decompression_projection_t*)opaque;
|
||||||
|
projection->fCtx->totalBytesOutput += (size_t)decodedSize;
|
||||||
|
DISPLAY_PROGRESS("\r%79s\r", "");
|
||||||
|
if (FIO_shouldDisplayFileSummary(projection->fCtx))
|
||||||
|
DISPLAY_SUMMARY("%-20s: %llu bytes \n", srcFileName,
|
||||||
|
(unsigned long long)decodedSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** FIO_decompressFrames() :
|
/** FIO_decompressFrames() :
|
||||||
@@ -3148,49 +3202,12 @@ static int FIO_decompressFrames(FIO_ctx_t* const fCtx,
|
|||||||
callbacks.decode_lz4 = FIO_rust_decompressLz4FrameCallback;
|
callbacks.decode_lz4 = FIO_rust_decompressLz4FrameCallback;
|
||||||
#endif
|
#endif
|
||||||
callbacks.pass_through = FIO_rust_decompressPassThroughCallback;
|
callbacks.pass_through = FIO_rust_decompressPassThroughCallback;
|
||||||
|
callbacks.report_status = FIO_rust_decompressStatusCallback;
|
||||||
|
callbacks.finish = FIO_rust_decompressFinalCallback;
|
||||||
|
|
||||||
status = FIO_rust_decompressFrames(ress.readCtx, srcFileName, passThrough,
|
status = FIO_rust_decompressFrames(ress.readCtx, srcFileName, passThrough,
|
||||||
&filesize, &callbacks);
|
&filesize, &callbacks);
|
||||||
switch (status) {
|
return FIO_rust_finishDecompressFrames(status, srcFileName, filesize, &callbacks);
|
||||||
case FIO_RUST_DECOMPRESS_OK:
|
|
||||||
break;
|
|
||||||
case FIO_RUST_DECOMPRESS_PASS_THROUGH:
|
|
||||||
return 0;
|
|
||||||
case FIO_RUST_DECOMPRESS_EMPTY_INPUT:
|
|
||||||
DISPLAYLEVEL(1, "zstd: %s: unexpected end of file \n", srcFileName);
|
|
||||||
return 1;
|
|
||||||
case FIO_RUST_DECOMPRESS_SHORT_INPUT:
|
|
||||||
DISPLAYLEVEL(1, "zstd: %s: unknown header \n", srcFileName);
|
|
||||||
return 1;
|
|
||||||
case FIO_RUST_DECOMPRESS_GZIP_UNSUPPORTED:
|
|
||||||
DISPLAYLEVEL(1, "zstd: %s: gzip file cannot be uncompressed (zstd compiled without HAVE_ZLIB) -- ignored \n", srcFileName);
|
|
||||||
return 1;
|
|
||||||
case FIO_RUST_DECOMPRESS_LZMA_UNSUPPORTED:
|
|
||||||
DISPLAYLEVEL(1, "zstd: %s: xz/lzma file cannot be uncompressed (zstd compiled without HAVE_LZMA) -- ignored \n", srcFileName);
|
|
||||||
return 1;
|
|
||||||
case FIO_RUST_DECOMPRESS_LZ4_UNSUPPORTED:
|
|
||||||
DISPLAYLEVEL(1, "zstd: %s: lz4 file cannot be uncompressed (zstd compiled without HAVE_LZ4) -- ignored \n", srcFileName);
|
|
||||||
return 1;
|
|
||||||
case FIO_RUST_DECOMPRESS_UNSUPPORTED_FORMAT:
|
|
||||||
DISPLAYLEVEL(1, "zstd: %s: unsupported format \n", srcFileName);
|
|
||||||
return 1;
|
|
||||||
case FIO_RUST_DECOMPRESS_FRAME_ERROR:
|
|
||||||
case FIO_RUST_DECOMPRESS_PASS_THROUGH_ERROR:
|
|
||||||
case FIO_RUST_DECOMPRESS_ZSTD_UNSUPPORTED:
|
|
||||||
return 1;
|
|
||||||
default:
|
|
||||||
assert(0);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Final Status */
|
|
||||||
fCtx->totalBytesOutput += (size_t)filesize;
|
|
||||||
DISPLAY_PROGRESS("\r%79s\r", "");
|
|
||||||
if (FIO_shouldDisplayFileSummary(fCtx))
|
|
||||||
DISPLAY_SUMMARY("%-20s: %llu bytes \n", srcFileName,
|
|
||||||
(unsigned long long)filesize);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** FIO_decompressDstFile() :
|
/** FIO_decompressDstFile() :
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ type FIO_rust_frame_progress_fn = Option<unsafe extern "C" fn(*mut c_void, *cons
|
|||||||
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;
|
||||||
pub type FIO_rust_pass_through_fn = unsafe extern "C" fn(*mut c_void) -> c_int;
|
pub type FIO_rust_pass_through_fn = unsafe extern "C" fn(*mut c_void) -> c_int;
|
||||||
|
pub type FIO_rust_decompress_status_fn = unsafe extern "C" fn(*mut c_void, c_int, *const c_char);
|
||||||
|
pub type FIO_rust_decompress_finish_fn = unsafe extern "C" fn(*mut c_void, *const c_char, u64);
|
||||||
type FIO_zstd_reset_fn = unsafe extern "C" fn(*mut c_void, c_int) -> usize;
|
type FIO_zstd_reset_fn = unsafe extern "C" fn(*mut c_void, c_int) -> usize;
|
||||||
type FIO_zstd_decompress_fn = unsafe extern "C" fn(
|
type FIO_zstd_decompress_fn = unsafe extern "C" fn(
|
||||||
*mut c_void,
|
*mut c_void,
|
||||||
@@ -73,6 +75,8 @@ pub struct FIO_rust_decompress_callbacks_t {
|
|||||||
pub decode_lzma: Option<FIO_rust_decompress_frame_fn>,
|
pub decode_lzma: Option<FIO_rust_decompress_frame_fn>,
|
||||||
pub decode_lz4: Option<FIO_rust_decompress_frame_fn>,
|
pub decode_lz4: Option<FIO_rust_decompress_frame_fn>,
|
||||||
pub pass_through: Option<FIO_rust_pass_through_fn>,
|
pub pass_through: Option<FIO_rust_pass_through_fn>,
|
||||||
|
pub report_status: Option<FIO_rust_decompress_status_fn>,
|
||||||
|
pub finish: Option<FIO_rust_decompress_finish_fn>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const FIO_RUST_COMPRESS_OK: c_int = 0;
|
pub const FIO_RUST_COMPRESS_OK: c_int = 0;
|
||||||
@@ -2854,6 +2858,49 @@ pub unsafe extern "C" fn FIO_rust_decompressFrames(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Maps the mixed-format loop's result to the CLI result and performs the
|
||||||
|
/// successful-file finalization. C keeps the diagnostic and accounting
|
||||||
|
/// callbacks because they touch `DISPLAY*` state and the private `FIO_ctx_t`
|
||||||
|
/// layout; Rust owns the result policy and decides which callback is legal.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn FIO_rust_finishDecompressFrames(
|
||||||
|
status: c_int,
|
||||||
|
src_file_name: *const c_char,
|
||||||
|
decoded_size: u64,
|
||||||
|
callbacks: *const FIO_rust_decompress_callbacks_t,
|
||||||
|
) -> c_int {
|
||||||
|
assert!(!src_file_name.is_null());
|
||||||
|
assert!(!callbacks.is_null());
|
||||||
|
|
||||||
|
let callbacks = unsafe { &*callbacks };
|
||||||
|
match status {
|
||||||
|
FIO_RUST_DECOMPRESS_OK => {
|
||||||
|
if let Some(finish) = callbacks.finish {
|
||||||
|
unsafe { finish(callbacks.opaque, src_file_name, decoded_size) };
|
||||||
|
}
|
||||||
|
0
|
||||||
|
}
|
||||||
|
FIO_RUST_DECOMPRESS_PASS_THROUGH => 0,
|
||||||
|
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 => {
|
||||||
|
if let Some(report_status) = callbacks.report_status {
|
||||||
|
unsafe { report_status(callbacks.opaque, status, src_file_name) };
|
||||||
|
}
|
||||||
|
1
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
unreachable!("unknown Rust decompression status: {status}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn read_buffer_ptr(ctx: *mut ReadPoolCtx_t) -> *const u8 {
|
unsafe fn read_buffer_ptr(ctx: *mut ReadPoolCtx_t) -> *const u8 {
|
||||||
let context = ctx.cast::<u8>();
|
let context = ctx.cast::<u8>();
|
||||||
@@ -4223,6 +4270,8 @@ mod tests {
|
|||||||
decode_lzma: None,
|
decode_lzma: None,
|
||||||
decode_lz4: None,
|
decode_lz4: None,
|
||||||
pass_through: None,
|
pass_through: None,
|
||||||
|
report_status: None,
|
||||||
|
finish: None,
|
||||||
};
|
};
|
||||||
let mut decoded_size = 0;
|
let mut decoded_size = 0;
|
||||||
let status = unsafe {
|
let status = unsafe {
|
||||||
@@ -4241,6 +4290,90 @@ mod tests {
|
|||||||
unsafe { AIO_ReadPool_free(read_ctx) };
|
unsafe { AIO_ReadPool_free(read_ctx) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct DecompressResultState {
|
||||||
|
reported_statuses: Vec<c_int>,
|
||||||
|
finished_sizes: Vec<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn record_decompress_status(
|
||||||
|
opaque: *mut c_void,
|
||||||
|
status: c_int,
|
||||||
|
_src_file_name: *const c_char,
|
||||||
|
) {
|
||||||
|
let state = unsafe { &mut *opaque.cast::<DecompressResultState>() };
|
||||||
|
state.reported_statuses.push(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn record_decompress_finish(
|
||||||
|
opaque: *mut c_void,
|
||||||
|
_src_file_name: *const c_char,
|
||||||
|
decoded_size: u64,
|
||||||
|
) {
|
||||||
|
let state = unsafe { &mut *opaque.cast::<DecompressResultState>() };
|
||||||
|
state.finished_sizes.push(decoded_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn maps_decompression_status_and_finishes_only_success() {
|
||||||
|
let mut state = DecompressResultState {
|
||||||
|
reported_statuses: Vec::new(),
|
||||||
|
finished_sizes: Vec::new(),
|
||||||
|
};
|
||||||
|
let callbacks = FIO_rust_decompress_callbacks_t {
|
||||||
|
opaque: (&mut state as *mut DecompressResultState).cast(),
|
||||||
|
decode_zstd: None,
|
||||||
|
decode_gzip: None,
|
||||||
|
decode_lzma: None,
|
||||||
|
decode_lz4: None,
|
||||||
|
pass_through: None,
|
||||||
|
report_status: Some(record_decompress_status),
|
||||||
|
finish: Some(record_decompress_finish),
|
||||||
|
};
|
||||||
|
|
||||||
|
let statuses = [
|
||||||
|
(FIO_RUST_DECOMPRESS_OK, 0),
|
||||||
|
(FIO_RUST_DECOMPRESS_PASS_THROUGH, 0),
|
||||||
|
(FIO_RUST_DECOMPRESS_EMPTY_INPUT, 1),
|
||||||
|
(FIO_RUST_DECOMPRESS_SHORT_INPUT, 1),
|
||||||
|
(FIO_RUST_DECOMPRESS_GZIP_UNSUPPORTED, 1),
|
||||||
|
(FIO_RUST_DECOMPRESS_LZMA_UNSUPPORTED, 1),
|
||||||
|
(FIO_RUST_DECOMPRESS_LZ4_UNSUPPORTED, 1),
|
||||||
|
(FIO_RUST_DECOMPRESS_FRAME_ERROR, 1),
|
||||||
|
(FIO_RUST_DECOMPRESS_UNSUPPORTED_FORMAT, 1),
|
||||||
|
(FIO_RUST_DECOMPRESS_PASS_THROUGH_ERROR, 1),
|
||||||
|
(FIO_RUST_DECOMPRESS_ZSTD_UNSUPPORTED, 1),
|
||||||
|
];
|
||||||
|
for (status, expected_result) in statuses {
|
||||||
|
assert_eq!(
|
||||||
|
unsafe {
|
||||||
|
FIO_rust_finishDecompressFrames(
|
||||||
|
status,
|
||||||
|
c"decompress-result-test".as_ptr(),
|
||||||
|
123,
|
||||||
|
&callbacks,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
expected_result
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(state.finished_sizes, vec![123]);
|
||||||
|
assert_eq!(
|
||||||
|
state.reported_statuses,
|
||||||
|
vec![
|
||||||
|
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,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
struct CBase<M> {
|
struct CBase<M> {
|
||||||
thread_pool: *mut c_void,
|
thread_pool: *mut c_void,
|
||||||
|
|||||||
Reference in New Issue
Block a user