refactor(fileio): move per-file decompression policy to Rust

Move source and destination lifecycle orchestration for single-file
 decompression into the Rust file-I/O layer.  Rust now owns async-selection,
resource attachment, destination opening, handler lifetime, metadata transfer,
close-error propagation, artefact cleanup, and successful-only source removal.
C retains filesystem/resource handles, format dispatch, diagnostics, and the
private callback implementations behind a narrow projection.

Test Plan:
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo fmt --manifest-path rust/Cargo.toml --all -- --check
- git diff --check and git diff --cached --check
- Added focused Rust callback-order, cleanup, and removal-policy tests
- Heavy Cargo, Make, native, and fuzzer verification deferred to the serial capped pass
This commit is contained in:
2026-07-20 03:02:07 +02:00
parent 73e0a5e629
commit afa2dde80d
2 changed files with 887 additions and 129 deletions
+602 -6
View File
@@ -510,6 +510,159 @@ const _: () = {
);
};
pub const FIO_RUST_DECOMPRESS_SRC_OPEN_OK: c_int = 0;
const FIO_RUST_DECOMPRESS_SRC_ASYNC_THRESHOLD: u64 = (1 << 17) * 3;
const FIO_RUST_DECOMPRESS_SRC_UNKNOWN_SIZE: u64 = u64::MAX;
pub type FIO_rust_decompress_src_open_fn =
unsafe extern "C" fn(*mut c_void, *const c_char, *mut u64, *mut c_int) -> c_int;
pub type FIO_rust_decompress_src_async_fn = unsafe extern "C" fn(*mut c_void, c_int);
pub type FIO_rust_decompress_src_attach_fn = unsafe extern "C" fn(*mut c_void);
pub type FIO_rust_decompress_src_close_fn = unsafe extern "C" fn(*mut c_void) -> c_int;
pub type FIO_rust_decompress_dst_open_fn =
unsafe extern "C" fn(*mut c_void, *const c_char, *const c_char, c_int, *mut c_int) -> c_int;
pub type FIO_rust_decompress_dst_attach_fn = unsafe extern "C" fn(*mut c_void);
pub type FIO_rust_decompress_handler_fn = unsafe extern "C" fn(*mut c_void);
pub type FIO_rust_decompress_file_fn =
unsafe extern "C" fn(*mut c_void, *const c_char, *const c_char) -> c_int;
pub type FIO_rust_decompress_dst_stat_fn = unsafe extern "C" fn(*mut c_void, c_int, *const c_char);
pub type FIO_rust_decompress_dst_close_fn = unsafe extern "C" fn(*mut c_void) -> c_int;
pub type FIO_rust_decompress_dst_remove_fn = unsafe extern "C" fn(*mut c_void, *const c_char);
pub type FIO_rust_decompress_src_remove_fn =
unsafe extern "C" fn(*mut c_void, *const c_char) -> c_int;
/// C-owned resources and private I/O handles are projected as callbacks while
/// Rust owns the per-file decompression ordering and result policy.
#[repr(C)]
pub struct FIO_rust_decompress_file_projection_t {
pub opaque: *mut c_void,
pub dst_file_name: *const c_char,
pub src_file_name: *const c_char,
pub destination_already_open: c_int,
pub test_mode: c_int,
pub source_is_stdin: c_int,
pub destination_is_stdout: c_int,
pub remove_source: c_int,
pub open_source: Option<FIO_rust_decompress_src_open_fn>,
pub set_async: Option<FIO_rust_decompress_src_async_fn>,
pub attach_source: Option<FIO_rust_decompress_src_attach_fn>,
pub detach_source: Option<FIO_rust_decompress_src_attach_fn>,
pub close_source: Option<FIO_rust_decompress_src_close_fn>,
pub open_destination: Option<FIO_rust_decompress_dst_open_fn>,
pub attach_destination: Option<FIO_rust_decompress_dst_attach_fn>,
pub add_handler: Option<FIO_rust_decompress_handler_fn>,
pub decompress: Option<FIO_rust_decompress_file_fn>,
pub clear_handler: Option<FIO_rust_decompress_handler_fn>,
pub set_fd_stat: Option<FIO_rust_decompress_dst_stat_fn>,
pub close_destination: Option<FIO_rust_decompress_dst_close_fn>,
pub utime_destination: Option<FIO_rust_decompress_handler_fn>,
pub remove_destination: Option<FIO_rust_decompress_dst_remove_fn>,
pub remove_source_file: Option<FIO_rust_decompress_src_remove_fn>,
}
const FIO_RUST_DECOMPRESS_FILE_CALLBACK_OFFSET: usize =
(3 * size_of::<usize>() + 5 * size_of::<c_int>() + size_of::<usize>() - 1)
& !(size_of::<usize>() - 1);
const _: () = {
assert!(std::mem::offset_of!(FIO_rust_decompress_file_projection_t, opaque) == 0);
assert!(
std::mem::offset_of!(FIO_rust_decompress_file_projection_t, dst_file_name)
== size_of::<usize>()
);
assert!(
std::mem::offset_of!(FIO_rust_decompress_file_projection_t, src_file_name)
== 2 * size_of::<usize>()
);
assert!(
std::mem::offset_of!(
FIO_rust_decompress_file_projection_t,
destination_already_open
) == 3 * size_of::<usize>()
);
assert!(
std::mem::offset_of!(FIO_rust_decompress_file_projection_t, test_mode)
== 3 * size_of::<usize>() + size_of::<c_int>()
);
assert!(
std::mem::offset_of!(FIO_rust_decompress_file_projection_t, source_is_stdin)
== 3 * size_of::<usize>() + 2 * size_of::<c_int>()
);
assert!(
std::mem::offset_of!(FIO_rust_decompress_file_projection_t, destination_is_stdout)
== 3 * size_of::<usize>() + 3 * size_of::<c_int>()
);
assert!(
std::mem::offset_of!(FIO_rust_decompress_file_projection_t, remove_source)
== 3 * size_of::<usize>() + 4 * size_of::<c_int>()
);
assert!(
std::mem::offset_of!(FIO_rust_decompress_file_projection_t, open_source)
== FIO_RUST_DECOMPRESS_FILE_CALLBACK_OFFSET
);
assert!(
std::mem::offset_of!(FIO_rust_decompress_file_projection_t, set_async)
== FIO_RUST_DECOMPRESS_FILE_CALLBACK_OFFSET + size_of::<usize>()
);
assert!(
std::mem::offset_of!(FIO_rust_decompress_file_projection_t, attach_source)
== FIO_RUST_DECOMPRESS_FILE_CALLBACK_OFFSET + 2 * size_of::<usize>()
);
assert!(
std::mem::offset_of!(FIO_rust_decompress_file_projection_t, detach_source)
== FIO_RUST_DECOMPRESS_FILE_CALLBACK_OFFSET + 3 * size_of::<usize>()
);
assert!(
std::mem::offset_of!(FIO_rust_decompress_file_projection_t, close_source)
== FIO_RUST_DECOMPRESS_FILE_CALLBACK_OFFSET + 4 * size_of::<usize>()
);
assert!(
std::mem::offset_of!(FIO_rust_decompress_file_projection_t, open_destination)
== FIO_RUST_DECOMPRESS_FILE_CALLBACK_OFFSET + 5 * size_of::<usize>()
);
assert!(
std::mem::offset_of!(FIO_rust_decompress_file_projection_t, attach_destination)
== FIO_RUST_DECOMPRESS_FILE_CALLBACK_OFFSET + 6 * size_of::<usize>()
);
assert!(
std::mem::offset_of!(FIO_rust_decompress_file_projection_t, add_handler)
== FIO_RUST_DECOMPRESS_FILE_CALLBACK_OFFSET + 7 * size_of::<usize>()
);
assert!(
std::mem::offset_of!(FIO_rust_decompress_file_projection_t, decompress)
== FIO_RUST_DECOMPRESS_FILE_CALLBACK_OFFSET + 8 * size_of::<usize>()
);
assert!(
std::mem::offset_of!(FIO_rust_decompress_file_projection_t, clear_handler)
== FIO_RUST_DECOMPRESS_FILE_CALLBACK_OFFSET + 9 * size_of::<usize>()
);
assert!(
std::mem::offset_of!(FIO_rust_decompress_file_projection_t, set_fd_stat)
== FIO_RUST_DECOMPRESS_FILE_CALLBACK_OFFSET + 10 * size_of::<usize>()
);
assert!(
std::mem::offset_of!(FIO_rust_decompress_file_projection_t, close_destination)
== FIO_RUST_DECOMPRESS_FILE_CALLBACK_OFFSET + 11 * size_of::<usize>()
);
assert!(
std::mem::offset_of!(FIO_rust_decompress_file_projection_t, utime_destination)
== FIO_RUST_DECOMPRESS_FILE_CALLBACK_OFFSET + 12 * size_of::<usize>()
);
assert!(
std::mem::offset_of!(FIO_rust_decompress_file_projection_t, remove_destination)
== FIO_RUST_DECOMPRESS_FILE_CALLBACK_OFFSET + 13 * size_of::<usize>()
);
assert!(
std::mem::offset_of!(FIO_rust_decompress_file_projection_t, remove_source_file)
== FIO_RUST_DECOMPRESS_FILE_CALLBACK_OFFSET + 14 * size_of::<usize>()
);
assert!(
size_of::<FIO_rust_decompress_file_projection_t>()
== FIO_RUST_DECOMPRESS_FILE_CALLBACK_OFFSET + 15 * size_of::<usize>()
);
};
pub const FIO_RUST_ZSTD_OK: c_int = 0;
pub const FIO_RUST_ZSTD_COMPRESS_ERROR: c_int = 1;
pub const FIO_RUST_ZSTD_INCOMPLETE_INPUT: c_int = 2;
@@ -2463,6 +2616,177 @@ pub unsafe extern "C" fn FIO_rust_compressFilenameSrcFile(
result
}
unsafe fn decompress_destination(
projection: &FIO_rust_decompress_file_projection_t,
source_is_regular: c_int,
) -> c_int {
let decompress = projection
.decompress
.expect("decompression callback is required");
let release_destination = projection.destination_already_open == 0 && projection.test_mode == 0;
if !release_destination {
return unsafe {
decompress(
projection.opaque,
projection.dst_file_name,
projection.src_file_name,
)
};
}
let transfer_stat = projection.source_is_stdin == 0
&& projection.destination_is_stdout == 0
&& source_is_regular != 0;
let mut destination_fd = 0;
let open_destination = projection
.open_destination
.expect("destination open callback is required");
let open_status = unsafe {
open_destination(
projection.opaque,
projection.src_file_name,
projection.dst_file_name,
c_int::from(transfer_stat),
&mut destination_fd,
)
};
if open_status != 0 {
return 1;
}
let attach_destination = projection
.attach_destination
.expect("destination attachment callback is required");
unsafe { attach_destination(projection.opaque) };
let add_handler = projection
.add_handler
.expect("destination handler callback is required");
unsafe { add_handler(projection.opaque) };
let mut result = unsafe {
decompress(
projection.opaque,
projection.dst_file_name,
projection.src_file_name,
)
};
let clear_handler = projection
.clear_handler
.expect("handler-clear callback is required");
unsafe { clear_handler(projection.opaque) };
if transfer_stat {
let set_fd_stat = projection
.set_fd_stat
.expect("destination stat callback is required");
unsafe { set_fd_stat(projection.opaque, destination_fd, projection.dst_file_name) };
}
let close_destination = projection
.close_destination
.expect("destination close callback is required");
if unsafe { close_destination(projection.opaque) } != 0 {
result = 1;
}
if transfer_stat {
let utime_destination = projection
.utime_destination
.expect("destination timestamp callback is required");
unsafe { utime_destination(projection.opaque) };
}
if result != 0 && projection.destination_is_stdout == 0 {
let remove_destination = projection
.remove_destination
.expect("destination removal callback is required");
unsafe { remove_destination(projection.opaque, projection.dst_file_name) };
}
result
}
/// Runs the source and destination policy around C-owned decompression
/// resources and format callbacks. The callback order mirrors
/// `FIO_decompressSrcFile()` and `FIO_decompressDstFile()` exactly.
#[no_mangle]
pub unsafe extern "C" fn FIO_rust_decompressFilename(
projection: *const FIO_rust_decompress_file_projection_t,
) -> c_int {
assert!(!projection.is_null());
let projection = unsafe { &*projection };
assert!(!projection.opaque.is_null());
assert!(!projection.dst_file_name.is_null());
assert!(!projection.src_file_name.is_null());
let open_source = projection
.open_source
.expect("source open callback is required");
let mut file_size = FIO_RUST_DECOMPRESS_SRC_UNKNOWN_SIZE;
let mut source_is_regular = 0;
let open_status = unsafe {
open_source(
projection.opaque,
projection.src_file_name,
&mut file_size,
&mut source_is_regular,
)
};
if open_status != FIO_RUST_DECOMPRESS_SRC_OPEN_OK {
return 1;
}
let async_mode = if file_size != FIO_RUST_DECOMPRESS_SRC_UNKNOWN_SIZE
&& file_size < FIO_RUST_DECOMPRESS_SRC_ASYNC_THRESHOLD
{
0
} else {
1
};
let set_async = projection
.set_async
.expect("async-selection callback is required");
unsafe { set_async(projection.opaque, async_mode) };
let attach_source = projection
.attach_source
.expect("source attachment callback is required");
unsafe { attach_source(projection.opaque) };
let mut result = unsafe { decompress_destination(projection, source_is_regular) };
let detach_source = projection
.detach_source
.expect("source detachment callback is required");
unsafe { detach_source(projection.opaque) };
let close_source = projection
.close_source
.expect("source close callback is required");
if unsafe { close_source(projection.opaque) } != 0 {
return 1;
}
if projection.remove_source != 0 && result == 0 && projection.source_is_stdin == 0 {
let clear_handler = projection
.clear_handler
.expect("handler-clear callback is required");
unsafe { clear_handler(projection.opaque) };
let remove_source = projection
.remove_source_file
.expect("source removal callback is required");
if unsafe { remove_source(projection.opaque, projection.src_file_name) } != 0 {
result = 1;
}
}
result
}
/// Iterate the files that share one already-open destination. The C
/// callback retains source validation, resource state, diagnostics, and
/// compression dispatch; Rust preserves the original non-short-circuiting
@@ -2540,9 +2864,9 @@ pub unsafe extern "C" fn FIO_rust_compressMultipleSeparateFilenames(
error
}
/// Iterate files that share one already-open decompression destination. The
/// C callback retains source opening, format dispatch, diagnostics, and
/// `--rm`; Rust preserves the non-short-circuiting loop and counter updates.
/// Iterate files that share one already-open decompression destination. The C
/// callback retains private source/destination operations, format dispatch,
/// and diagnostics; Rust owns per-file ordering and removal policy.
#[no_mangle]
pub unsafe extern "C" fn FIO_rust_decompressMultipleFilenames(
projection: *const FIO_rust_decompress_multiple_projection_t,
@@ -2577,9 +2901,8 @@ pub unsafe extern "C" fn FIO_rust_decompressMultipleFilenames(
}
/// Iterate files that each receive a separate destination. The C callback
/// retains destination-name construction, source opening, format dispatch,
/// diagnostics, and `--rm`; Rust preserves the non-short-circuiting loop and
/// counter updates.
/// retains destination-name construction, private I/O, format dispatch, and
/// diagnostics; Rust owns per-file ordering and removal policy.
#[no_mangle]
pub unsafe extern "C" fn FIO_rust_decompressMultipleSeparateFilenames(
projection: *const FIO_rust_decompress_multiple_separate_projection_t,
@@ -4689,6 +5012,279 @@ mod tests {
);
}
const DECOMPRESS_POLICY_OPEN_SOURCE: u8 = 1;
const DECOMPRESS_POLICY_ASYNC: u8 = 2;
const DECOMPRESS_POLICY_ATTACH_SOURCE: u8 = 3;
const DECOMPRESS_POLICY_OPEN_DESTINATION: u8 = 4;
const DECOMPRESS_POLICY_ATTACH_DESTINATION: u8 = 5;
const DECOMPRESS_POLICY_ADD_HANDLER: u8 = 6;
const DECOMPRESS_POLICY_DECOMPRESS: u8 = 7;
const DECOMPRESS_POLICY_CLEAR_HANDLER: u8 = 8;
const DECOMPRESS_POLICY_SET_FD_STAT: u8 = 9;
const DECOMPRESS_POLICY_CLOSE_DESTINATION: u8 = 10;
const DECOMPRESS_POLICY_UTIME_DESTINATION: u8 = 11;
const DECOMPRESS_POLICY_REMOVE_DESTINATION: u8 = 12;
const DECOMPRESS_POLICY_DETACH_SOURCE: u8 = 13;
const DECOMPRESS_POLICY_CLOSE_SOURCE: u8 = 14;
const DECOMPRESS_POLICY_REMOVE_SOURCE: u8 = 15;
#[derive(Default)]
struct DecompressPolicyState {
events: Vec<u8>,
async_modes: Vec<c_int>,
transfers: Vec<c_int>,
destination_fds: Vec<c_int>,
source_file_size: u64,
source_is_regular: c_int,
source_open_status: c_int,
source_close_status: c_int,
destination_open_status: c_int,
destination_close_status: c_int,
decompress_status: c_int,
remove_source_status: c_int,
}
unsafe extern "C" fn decompress_policy_open_source(
opaque: *mut c_void,
_src_file_name: *const c_char,
file_size: *mut u64,
source_is_regular: *mut c_int,
) -> c_int {
let state = unsafe { &mut *opaque.cast::<DecompressPolicyState>() };
state.events.push(DECOMPRESS_POLICY_OPEN_SOURCE);
unsafe {
*file_size = state.source_file_size;
*source_is_regular = state.source_is_regular;
}
state.source_open_status
}
unsafe extern "C" fn decompress_policy_async(opaque: *mut c_void, async_mode: c_int) {
let state = unsafe { &mut *opaque.cast::<DecompressPolicyState>() };
state.events.push(DECOMPRESS_POLICY_ASYNC);
state.async_modes.push(async_mode);
}
unsafe extern "C" fn decompress_policy_attach_source(opaque: *mut c_void) {
let state = unsafe { &mut *opaque.cast::<DecompressPolicyState>() };
state.events.push(DECOMPRESS_POLICY_ATTACH_SOURCE);
}
unsafe extern "C" fn decompress_policy_open_destination(
opaque: *mut c_void,
_src_file_name: *const c_char,
_dst_file_name: *const c_char,
transfer_stat: c_int,
dst_fd: *mut c_int,
) -> c_int {
let state = unsafe { &mut *opaque.cast::<DecompressPolicyState>() };
state.events.push(DECOMPRESS_POLICY_OPEN_DESTINATION);
state.transfers.push(transfer_stat);
unsafe { *dst_fd = 37 };
state.destination_open_status
}
unsafe extern "C" fn decompress_policy_attach_destination(opaque: *mut c_void) {
let state = unsafe { &mut *opaque.cast::<DecompressPolicyState>() };
state.events.push(DECOMPRESS_POLICY_ATTACH_DESTINATION);
}
unsafe extern "C" fn decompress_policy_add_handler(opaque: *mut c_void) {
let state = unsafe { &mut *opaque.cast::<DecompressPolicyState>() };
state.events.push(DECOMPRESS_POLICY_ADD_HANDLER);
}
unsafe extern "C" fn decompress_policy_decompress(
opaque: *mut c_void,
_dst_file_name: *const c_char,
_src_file_name: *const c_char,
) -> c_int {
let state = unsafe { &mut *opaque.cast::<DecompressPolicyState>() };
state.events.push(DECOMPRESS_POLICY_DECOMPRESS);
state.decompress_status
}
unsafe extern "C" fn decompress_policy_clear_handler(opaque: *mut c_void) {
let state = unsafe { &mut *opaque.cast::<DecompressPolicyState>() };
state.events.push(DECOMPRESS_POLICY_CLEAR_HANDLER);
}
unsafe extern "C" fn decompress_policy_set_fd_stat(
opaque: *mut c_void,
dst_fd: c_int,
_dst_file_name: *const c_char,
) {
let state = unsafe { &mut *opaque.cast::<DecompressPolicyState>() };
state.events.push(DECOMPRESS_POLICY_SET_FD_STAT);
state.destination_fds.push(dst_fd);
}
unsafe extern "C" fn decompress_policy_close_destination(opaque: *mut c_void) -> c_int {
let state = unsafe { &mut *opaque.cast::<DecompressPolicyState>() };
state.events.push(DECOMPRESS_POLICY_CLOSE_DESTINATION);
state.destination_close_status
}
unsafe extern "C" fn decompress_policy_utime_destination(opaque: *mut c_void) {
let state = unsafe { &mut *opaque.cast::<DecompressPolicyState>() };
state.events.push(DECOMPRESS_POLICY_UTIME_DESTINATION);
}
unsafe extern "C" fn decompress_policy_remove_destination(
opaque: *mut c_void,
_dst_file_name: *const c_char,
) {
let state = unsafe { &mut *opaque.cast::<DecompressPolicyState>() };
state.events.push(DECOMPRESS_POLICY_REMOVE_DESTINATION);
}
unsafe extern "C" fn decompress_policy_detach_source(opaque: *mut c_void) {
let state = unsafe { &mut *opaque.cast::<DecompressPolicyState>() };
state.events.push(DECOMPRESS_POLICY_DETACH_SOURCE);
}
unsafe extern "C" fn decompress_policy_close_source(opaque: *mut c_void) -> c_int {
let state = unsafe { &mut *opaque.cast::<DecompressPolicyState>() };
state.events.push(DECOMPRESS_POLICY_CLOSE_SOURCE);
state.source_close_status
}
unsafe extern "C" fn decompress_policy_remove_source(
opaque: *mut c_void,
_src_file_name: *const c_char,
) -> c_int {
let state = unsafe { &mut *opaque.cast::<DecompressPolicyState>() };
state.events.push(DECOMPRESS_POLICY_REMOVE_SOURCE);
state.remove_source_status
}
fn decompress_policy_projection(
state: &mut DecompressPolicyState,
destination_already_open: c_int,
test_mode: c_int,
source_is_stdin: c_int,
destination_is_stdout: c_int,
remove_source: c_int,
) -> FIO_rust_decompress_file_projection_t {
FIO_rust_decompress_file_projection_t {
opaque: (state as *mut DecompressPolicyState).cast(),
dst_file_name: c"destination".as_ptr(),
src_file_name: c"source".as_ptr(),
destination_already_open,
test_mode,
source_is_stdin,
destination_is_stdout,
remove_source,
open_source: Some(decompress_policy_open_source),
set_async: Some(decompress_policy_async),
attach_source: Some(decompress_policy_attach_source),
detach_source: Some(decompress_policy_detach_source),
close_source: Some(decompress_policy_close_source),
open_destination: Some(decompress_policy_open_destination),
attach_destination: Some(decompress_policy_attach_destination),
add_handler: Some(decompress_policy_add_handler),
decompress: Some(decompress_policy_decompress),
clear_handler: Some(decompress_policy_clear_handler),
set_fd_stat: Some(decompress_policy_set_fd_stat),
close_destination: Some(decompress_policy_close_destination),
utime_destination: Some(decompress_policy_utime_destination),
remove_destination: Some(decompress_policy_remove_destination),
remove_source_file: Some(decompress_policy_remove_source),
}
}
#[test]
fn decompress_file_policy_orders_cleanup_and_successful_source_removal() {
let mut state = DecompressPolicyState {
source_file_size: FIO_RUST_DECOMPRESS_SRC_ASYNC_THRESHOLD - 1,
source_is_regular: 1,
..DecompressPolicyState::default()
};
let projection = decompress_policy_projection(&mut state, 0, 0, 0, 0, 1);
assert_eq!(unsafe { FIO_rust_decompressFilename(&projection) }, 0);
assert_eq!(state.async_modes, vec![0]);
assert_eq!(state.transfers, vec![1]);
assert_eq!(state.destination_fds, vec![37]);
assert_eq!(
state.events,
vec![
DECOMPRESS_POLICY_OPEN_SOURCE,
DECOMPRESS_POLICY_ASYNC,
DECOMPRESS_POLICY_ATTACH_SOURCE,
DECOMPRESS_POLICY_OPEN_DESTINATION,
DECOMPRESS_POLICY_ATTACH_DESTINATION,
DECOMPRESS_POLICY_ADD_HANDLER,
DECOMPRESS_POLICY_DECOMPRESS,
DECOMPRESS_POLICY_CLEAR_HANDLER,
DECOMPRESS_POLICY_SET_FD_STAT,
DECOMPRESS_POLICY_CLOSE_DESTINATION,
DECOMPRESS_POLICY_UTIME_DESTINATION,
DECOMPRESS_POLICY_DETACH_SOURCE,
DECOMPRESS_POLICY_CLOSE_SOURCE,
DECOMPRESS_POLICY_CLEAR_HANDLER,
DECOMPRESS_POLICY_REMOVE_SOURCE,
]
);
}
#[test]
fn decompress_file_policy_removes_failed_destination_and_skips_source_removal() {
let mut state = DecompressPolicyState {
source_file_size: FIO_RUST_DECOMPRESS_SRC_ASYNC_THRESHOLD,
decompress_status: 1,
destination_close_status: 1,
..DecompressPolicyState::default()
};
let projection = decompress_policy_projection(&mut state, 0, 0, 0, 0, 1);
assert_eq!(unsafe { FIO_rust_decompressFilename(&projection) }, 1);
assert_eq!(state.async_modes, vec![1]);
assert_eq!(state.transfers, vec![0]);
assert!(!state.events.contains(&DECOMPRESS_POLICY_REMOVE_SOURCE));
assert_eq!(
state.events,
vec![
DECOMPRESS_POLICY_OPEN_SOURCE,
DECOMPRESS_POLICY_ASYNC,
DECOMPRESS_POLICY_ATTACH_SOURCE,
DECOMPRESS_POLICY_OPEN_DESTINATION,
DECOMPRESS_POLICY_ATTACH_DESTINATION,
DECOMPRESS_POLICY_ADD_HANDLER,
DECOMPRESS_POLICY_DECOMPRESS,
DECOMPRESS_POLICY_CLEAR_HANDLER,
DECOMPRESS_POLICY_CLOSE_DESTINATION,
DECOMPRESS_POLICY_REMOVE_DESTINATION,
DECOMPRESS_POLICY_DETACH_SOURCE,
DECOMPRESS_POLICY_CLOSE_SOURCE,
]
);
}
#[test]
fn decompress_file_policy_does_not_remove_source_after_close_error() {
let mut state = DecompressPolicyState {
source_file_size: FIO_RUST_DECOMPRESS_SRC_UNKNOWN_SIZE,
source_close_status: 1,
..DecompressPolicyState::default()
};
let projection = decompress_policy_projection(&mut state, 1, 0, 1, 1, 1);
assert_eq!(unsafe { FIO_rust_decompressFilename(&projection) }, 1);
assert_eq!(state.async_modes, vec![1]);
assert_eq!(
state.events,
vec![
DECOMPRESS_POLICY_OPEN_SOURCE,
DECOMPRESS_POLICY_ASYNC,
DECOMPRESS_POLICY_ATTACH_SOURCE,
DECOMPRESS_POLICY_DECOMPRESS,
DECOMPRESS_POLICY_DETACH_SOURCE,
DECOMPRESS_POLICY_CLOSE_SOURCE,
]
);
}
fn run_zstd_adapt(policy: c_int, projection: &FIO_rust_zstd_adapt_projection_t) -> c_int {
unsafe { FIO_rust_zstd_adapt(policy, projection) }
}