refactor(fileio): move zstd stream callback into Rust
The fileio zstd projection used to route its stream callback through a C implementation that constructed the public input and output buffer views, queried pending output, called the streaming codec, and copied four scalar results back to the Rust-owned loop. That left the central codec operation in the CLI C translation unit even though the surrounding stream loop was already in Rust. Move that callback into Rust while keeping the C CCtx opaque across the boundary. Rust now builds the public ZSTD_inBuffer and ZSTD_outBuffer views, invokes ZSTD_toFlushNow and ZSTD_compressStream2, publishes the original positions and result, and preserves the existing success diagnostic through a small C display callback. The C projection passes the real CCtx as codecOpaque and retains the adaptive iteration context and all private CLI state. The buffer structs are made public within the Rust crate so this seam can reuse the existing C-compatible definitions without duplicating them. Test Plan: - `cargo +nightly fmt --manifest-path rust/Cargo.toml --all` -- passed - `cc -fsyntax-only -Werror=incompatible-pointer-types -Ilib -Ilib/common -Ilib/compress -Ilib/decompress -Ilib/dict -Ilib/legacy programs/fileio.c` -- passed - `git diff --check` and `git diff --cached --check` -- passed - Full capped native and Rust verification remains to be run after this seam
This commit is contained in:
+219
-3
@@ -26,6 +26,19 @@ use std::ptr;
|
||||
use std::sync::{Arc, Condvar, Mutex};
|
||||
use std::thread::{self, JoinHandle};
|
||||
|
||||
use crate::zstd_compress::{ZSTD_inBuffer, ZSTD_outBuffer};
|
||||
|
||||
#[cfg(not(test))]
|
||||
unsafe extern "C" {
|
||||
fn ZSTD_toFlushNow(cctx: *mut c_void) -> usize;
|
||||
fn FIO_rust_zstd_compressStreamDisplay(
|
||||
directive: c_int,
|
||||
input_pos: usize,
|
||||
input_size: usize,
|
||||
output_produced: usize,
|
||||
);
|
||||
}
|
||||
|
||||
const MAX_IO_JOBS: usize = 10;
|
||||
const IO_QUEUE_SIZE: usize = MAX_IO_JOBS - 2;
|
||||
const SPARSE_SEGMENT_SIZE: usize = 32 * 1024;
|
||||
@@ -3318,10 +3331,106 @@ pub unsafe extern "C" fn FIO_rust_zstd_adapt(
|
||||
zstd_adapt_policy(policy, projection)
|
||||
}
|
||||
|
||||
type FIO_rust_zstd_to_flush_now_fn = unsafe extern "C" fn(*mut c_void) -> usize;
|
||||
type FIO_rust_zstd_compress_stream2_fn =
|
||||
unsafe extern "C" fn(*mut c_void, *mut ZSTD_outBuffer, *mut ZSTD_inBuffer, c_int) -> usize;
|
||||
type FIO_rust_zstd_compress_display_fn = unsafe extern "C" fn(c_int, usize, usize, usize);
|
||||
|
||||
/// Build the public stream-buffer views, call the Rust compressor, and publish
|
||||
/// the same scalar results as the former C-only callback. The codec context
|
||||
/// stays opaque; only the public buffer ABI crosses into the compressor.
|
||||
unsafe fn fio_zstd_compress_stream_with(
|
||||
cctx: *mut c_void,
|
||||
directive: c_int,
|
||||
input: *const u8,
|
||||
input_size: usize,
|
||||
input_pos: usize,
|
||||
output: *mut u8,
|
||||
output_size: usize,
|
||||
input_pos_after: *mut usize,
|
||||
output_produced: *mut usize,
|
||||
to_flush_now: *mut usize,
|
||||
zstd_result: *mut usize,
|
||||
to_flush_now_fn: FIO_rust_zstd_to_flush_now_fn,
|
||||
compress_stream2_fn: FIO_rust_zstd_compress_stream2_fn,
|
||||
display_fn: Option<FIO_rust_zstd_compress_display_fn>,
|
||||
) -> c_int {
|
||||
let mut input_view = ZSTD_inBuffer {
|
||||
src: input.cast::<c_void>(),
|
||||
size: input_size,
|
||||
pos: input_pos,
|
||||
};
|
||||
let mut output_view = ZSTD_outBuffer {
|
||||
dst: output.cast::<c_void>(),
|
||||
size: output_size,
|
||||
pos: 0,
|
||||
};
|
||||
|
||||
let pending_flush = unsafe { to_flush_now_fn(cctx) };
|
||||
let result = unsafe { compress_stream2_fn(cctx, &mut output_view, &mut input_view, directive) };
|
||||
|
||||
unsafe {
|
||||
*input_pos_after = input_view.pos;
|
||||
*output_produced = output_view.pos;
|
||||
*to_flush_now = pending_flush;
|
||||
*zstd_result = result;
|
||||
}
|
||||
|
||||
if !crate::errors::ERR_isError(result) {
|
||||
if let Some(display) = display_fn {
|
||||
unsafe {
|
||||
display(directive, input_view.pos, input_view.size, output_view.pos);
|
||||
}
|
||||
}
|
||||
0
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
/// The codec callback used by the zstd file-I/O projection. C retains only
|
||||
/// the iteration callback's private adaptive/diagnostic context; this callback
|
||||
/// receives the opaque `ZSTD_CCtx` directly and delegates to Rust's public
|
||||
/// `ZSTD_compressStream2` implementation.
|
||||
#[cfg(not(test))]
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn FIO_rust_zstd_compressStream(
|
||||
cctx: *mut c_void,
|
||||
_src_file_name: *const c_char,
|
||||
directive: c_int,
|
||||
input: *const u8,
|
||||
input_size: usize,
|
||||
input_pos: usize,
|
||||
output: *mut u8,
|
||||
output_size: usize,
|
||||
input_pos_after: *mut usize,
|
||||
output_produced: *mut usize,
|
||||
to_flush_now: *mut usize,
|
||||
zstd_result: *mut usize,
|
||||
) -> c_int {
|
||||
unsafe {
|
||||
fio_zstd_compress_stream_with(
|
||||
cctx,
|
||||
directive,
|
||||
input,
|
||||
input_size,
|
||||
input_pos,
|
||||
output,
|
||||
output_size,
|
||||
input_pos_after,
|
||||
output_produced,
|
||||
to_flush_now,
|
||||
zstd_result,
|
||||
ZSTD_toFlushNow,
|
||||
crate::zstd_compress::ZSTD_compressStream2,
|
||||
Some(FIO_rust_zstd_compressStreamDisplay),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Compresses one zstd frame through the C-owned zstd context and adaptive
|
||||
/// policy. Rust owns the stream loop and exact pool accounting; C callbacks
|
||||
/// retain `ZSTD_compressStream2()`, adaptive diagnostics, and all private CLI
|
||||
/// state.
|
||||
/// policy. Rust owns the stream loop and codec call; C callbacks retain the
|
||||
/// adaptive diagnostics and all private CLI state.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn FIO_rust_compressZstdFrame(
|
||||
projection: *const FIO_rust_zstd_compress_projection_t,
|
||||
@@ -6796,6 +6905,113 @@ mod tests {
|
||||
assert_eq!(context.nbFilesProcessed, 4);
|
||||
}
|
||||
|
||||
struct ZstdCodecCallbackTestState {
|
||||
pending_flush: usize,
|
||||
codec_result: usize,
|
||||
observed_input: Option<(usize, usize, usize)>,
|
||||
observed_output: Option<(usize, usize, usize)>,
|
||||
observed_directive: c_int,
|
||||
}
|
||||
|
||||
unsafe extern "C" fn zstd_test_to_flush_now(opaque: *mut c_void) -> usize {
|
||||
let state = unsafe { &mut *opaque.cast::<ZstdCodecCallbackTestState>() };
|
||||
state.pending_flush
|
||||
}
|
||||
|
||||
unsafe extern "C" fn zstd_test_compress_stream2(
|
||||
opaque: *mut c_void,
|
||||
output: *mut ZSTD_outBuffer,
|
||||
input: *mut ZSTD_inBuffer,
|
||||
directive: c_int,
|
||||
) -> usize {
|
||||
let state = unsafe { &mut *opaque.cast::<ZstdCodecCallbackTestState>() };
|
||||
let input = unsafe { &mut *input };
|
||||
let output = unsafe { &mut *output };
|
||||
state.observed_input = Some((input.src as usize, input.size, input.pos));
|
||||
state.observed_output = Some((output.dst as usize, output.size, output.pos));
|
||||
state.observed_directive = directive;
|
||||
input.pos = input.size;
|
||||
output.pos = 3;
|
||||
state.codec_result
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zstd_codec_callback_builds_public_views_and_preserves_results() {
|
||||
let input = b"abcdef";
|
||||
let mut output = [0_u8; 8];
|
||||
let mut state = ZstdCodecCallbackTestState {
|
||||
pending_flush: 19,
|
||||
codec_result: 0,
|
||||
observed_input: None,
|
||||
observed_output: None,
|
||||
observed_directive: -1,
|
||||
};
|
||||
let mut input_pos_after = 0;
|
||||
let mut output_produced = 0;
|
||||
let mut to_flush_now = 0;
|
||||
let mut zstd_result = 0;
|
||||
let cctx = (&mut state as *mut ZstdCodecCallbackTestState).cast::<c_void>();
|
||||
|
||||
assert_eq!(
|
||||
unsafe {
|
||||
fio_zstd_compress_stream_with(
|
||||
cctx,
|
||||
2,
|
||||
input.as_ptr(),
|
||||
input.len(),
|
||||
1,
|
||||
output.as_mut_ptr(),
|
||||
output.len(),
|
||||
&mut input_pos_after,
|
||||
&mut output_produced,
|
||||
&mut to_flush_now,
|
||||
&mut zstd_result,
|
||||
zstd_test_to_flush_now,
|
||||
zstd_test_compress_stream2,
|
||||
None,
|
||||
)
|
||||
},
|
||||
0
|
||||
);
|
||||
assert_eq!(
|
||||
state.observed_input,
|
||||
Some((input.as_ptr() as usize, input.len(), 1))
|
||||
);
|
||||
assert_eq!(
|
||||
state.observed_output,
|
||||
Some((output.as_mut_ptr() as usize, output.len(), 0))
|
||||
);
|
||||
assert_eq!(state.observed_directive, 2);
|
||||
assert_eq!(input_pos_after, input.len());
|
||||
assert_eq!(output_produced, 3);
|
||||
assert_eq!(to_flush_now, 19);
|
||||
assert_eq!(zstd_result, 0);
|
||||
|
||||
state.codec_result = usize::MAX;
|
||||
assert_eq!(
|
||||
unsafe {
|
||||
fio_zstd_compress_stream_with(
|
||||
cctx,
|
||||
0,
|
||||
input.as_ptr(),
|
||||
input.len(),
|
||||
0,
|
||||
output.as_mut_ptr(),
|
||||
output.len(),
|
||||
&mut input_pos_after,
|
||||
&mut output_produced,
|
||||
&mut to_flush_now,
|
||||
&mut zstd_result,
|
||||
zstd_test_to_flush_now,
|
||||
zstd_test_compress_stream2,
|
||||
None,
|
||||
)
|
||||
},
|
||||
1
|
||||
);
|
||||
assert_eq!(zstd_result, usize::MAX);
|
||||
}
|
||||
|
||||
struct ZstdProjectionState {
|
||||
input: [u8; 5],
|
||||
input_pos: usize,
|
||||
|
||||
@@ -6282,16 +6282,16 @@ pub extern "C" fn ZSTD_rust_targetCBlockSizeAction(
|
||||
|
||||
#[repr(C)]
|
||||
pub struct ZSTD_inBuffer {
|
||||
src: *const c_void,
|
||||
size: usize,
|
||||
pos: usize,
|
||||
pub src: *const c_void,
|
||||
pub size: usize,
|
||||
pub pos: usize,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct ZSTD_outBuffer {
|
||||
dst: *mut c_void,
|
||||
size: usize,
|
||||
pos: usize,
|
||||
pub dst: *mut c_void,
|
||||
pub size: usize,
|
||||
pub pos: usize,
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
||||
Reference in New Issue
Block a user