feat(compress): move frame-block preparation order into Rust
Move the frame-chunk match-state preparation policy into Rust while keeping window and workspace mutations behind narrow C callbacks. Rust now guarantees the original overflow-correction, dictionary-validity, maximum-distance, and nextToUpdate-clamp order; the C side retains the private ZSTD_MatchState_t layout and exact primitive operations. Test Plan: - cargo fmt --manifest-path rust/Cargo.toml -- --check - ulimit -v 41943040 && CARGO_BUILD_JOBS=1 cargo test --manifest-path rust/Cargo.toml frame_chunk --lib (3 passed) - ulimit -v 41943040 && CARGO_BUILD_JOBS=1 cargo test --manifest-path rust/Cargo.toml --lib (691 passed) - ulimit -v 41943040 && CARGO_BUILD_JOBS=1 cargo clippy --manifest-path rust/Cargo.toml --all-targets -- -D warnings - ulimit -v 41943040 && MAKEFLAGS=-j1 make -B -C programs -j1 zstd - ulimit -v 41943040 && MAKEFLAGS=-j1 make -C tests -j1 test-zstream ZSTREAM_TESTTIME=-T1s (84 tests and both short fuzz rounds passed)
This commit is contained in:
+123
-18
@@ -170,7 +170,9 @@ const ZSTD_BSS_NO_COMPRESS: c_int = 1;
|
||||
const FSE_REPEAT_CHECK: c_int = 1;
|
||||
const FSE_REPEAT_VALID: c_int = 2;
|
||||
|
||||
type FrameChunkPrepareFn = unsafe extern "C" fn(*mut c_void, *const c_void, usize);
|
||||
type FrameChunkPrepareOverflowFn = unsafe extern "C" fn(*mut c_void, *const c_void, usize);
|
||||
type FrameChunkPrepareWindowFn = unsafe extern "C" fn(*mut c_void, *const c_void, usize, c_uint);
|
||||
type FrameChunkPrepareClampFn = unsafe extern "C" fn(*mut c_void);
|
||||
type FrameChunkCompressFn =
|
||||
unsafe extern "C" fn(*mut c_void, *mut c_void, usize, *const c_void, usize, c_uint) -> usize;
|
||||
type FrameChunkChecksumFn = unsafe extern "C" fn(*mut c_void, *const c_void, usize);
|
||||
@@ -493,6 +495,39 @@ pub unsafe extern "C" fn ZSTD_rust_referenceExternalSequences(
|
||||
}
|
||||
}
|
||||
|
||||
/// C-owned operations used by Rust's frame-chunk block-preparation policy.
|
||||
///
|
||||
/// The callback context and private window/workspace layout remain in C; Rust
|
||||
/// controls the order in which these operations run.
|
||||
#[repr(C)]
|
||||
pub struct ZSTD_rust_frameChunkPrepareState {
|
||||
callback_context: *mut c_void,
|
||||
max_dist: c_uint,
|
||||
correct_overflow: FrameChunkPrepareOverflowFn,
|
||||
check_dict_validity: FrameChunkPrepareWindowFn,
|
||||
enforce_max_dist: FrameChunkPrepareWindowFn,
|
||||
clamp_next_to_update: FrameChunkPrepareClampFn,
|
||||
}
|
||||
|
||||
const _: () = {
|
||||
assert!(offset_of!(ZSTD_rust_frameChunkPrepareState, callback_context) == 0);
|
||||
assert!(offset_of!(ZSTD_rust_frameChunkPrepareState, max_dist) == size_of::<usize>());
|
||||
assert!(
|
||||
offset_of!(ZSTD_rust_frameChunkPrepareState, correct_overflow) == 2 * size_of::<usize>()
|
||||
);
|
||||
assert!(
|
||||
offset_of!(ZSTD_rust_frameChunkPrepareState, check_dict_validity) == 3 * size_of::<usize>()
|
||||
);
|
||||
assert!(
|
||||
offset_of!(ZSTD_rust_frameChunkPrepareState, enforce_max_dist) == 4 * size_of::<usize>()
|
||||
);
|
||||
assert!(
|
||||
offset_of!(ZSTD_rust_frameChunkPrepareState, clamp_next_to_update)
|
||||
== 5 * size_of::<usize>()
|
||||
);
|
||||
assert!(size_of::<ZSTD_rust_frameChunkPrepareState>() == 6 * size_of::<usize>());
|
||||
};
|
||||
|
||||
/// Explicit projection of the state used by `ZSTD_compress_frameChunk`.
|
||||
///
|
||||
/// The Rust side owns the per-frame block loop and its savings/dispatch
|
||||
@@ -515,7 +550,7 @@ pub struct ZSTD_rust_frameChunkState {
|
||||
block_splitter_enabled: c_int,
|
||||
checksum_flag: c_int,
|
||||
ending_stage: c_int,
|
||||
prepare_block: FrameChunkPrepareFn,
|
||||
prepare_state: *const ZSTD_rust_frameChunkPrepareState,
|
||||
compress_target: FrameChunkCompressFn,
|
||||
compress_split: FrameChunkCompressFn,
|
||||
compress_internal: FrameChunkCompressFn,
|
||||
@@ -556,7 +591,7 @@ const _: () = {
|
||||
== 7 * size_of::<usize>() + size_of::<c_longlong>() + 5 * size_of::<c_int>()
|
||||
);
|
||||
assert!(
|
||||
offset_of!(ZSTD_rust_frameChunkState, prepare_block)
|
||||
offset_of!(ZSTD_rust_frameChunkState, prepare_state)
|
||||
== 7 * size_of::<usize>() + size_of::<c_longlong>() + 6 * size_of::<c_int>()
|
||||
);
|
||||
assert!(
|
||||
@@ -567,9 +602,22 @@ const _: () = {
|
||||
|
||||
/// Rust implementation of `ZSTD_compress_frameChunk`.
|
||||
///
|
||||
/// C still prepares match-state windows and invokes the selected block body
|
||||
/// through callbacks. Rust owns the block-size heuristic call, output
|
||||
/// framing, savings accounting, checksum sequencing, and frame-state update.
|
||||
/// C still owns the private match-state/window operations and invokes the
|
||||
/// selected block body through callbacks. Rust owns preparation ordering, the
|
||||
/// block-size heuristic call, output framing, savings accounting, checksum
|
||||
/// sequencing, and frame-state update.
|
||||
#[inline]
|
||||
unsafe fn prepare_frame_chunk_block(
|
||||
state: &ZSTD_rust_frameChunkPrepareState,
|
||||
src: *const c_void,
|
||||
block_size: usize,
|
||||
) {
|
||||
unsafe { (state.correct_overflow)(state.callback_context, src, block_size) };
|
||||
unsafe { (state.check_dict_validity)(state.callback_context, src, block_size, state.max_dist) };
|
||||
unsafe { (state.enforce_max_dist)(state.callback_context, src, block_size, state.max_dist) };
|
||||
unsafe { (state.clamp_next_to_update)(state.callback_context) };
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
unsafe fn compress_frame_chunk_body_with(
|
||||
state: &ZSTD_rust_frameChunkState,
|
||||
@@ -579,7 +627,7 @@ unsafe fn compress_frame_chunk_body_with(
|
||||
src_size: usize,
|
||||
last_frame_chunk: c_uint,
|
||||
) -> usize {
|
||||
if state.is_first_block.is_null() || state.stage.is_null() {
|
||||
if state.is_first_block.is_null() || state.stage.is_null() || state.prepare_state.is_null() {
|
||||
return ERROR(ZstdErrorCode::Generic);
|
||||
}
|
||||
|
||||
@@ -622,7 +670,7 @@ unsafe fn compress_frame_chunk_body_with(
|
||||
return ERROR(ZstdErrorCode::DstSizeTooSmall);
|
||||
}
|
||||
|
||||
unsafe { (state.prepare_block)(state.callback_context, ip.cast(), block_size) };
|
||||
unsafe { prepare_frame_chunk_block(&*state.prepare_state, ip.cast(), block_size) };
|
||||
|
||||
let c_size = if state.use_target_c_block_size != 0 {
|
||||
unsafe {
|
||||
@@ -9484,6 +9532,10 @@ mod tests {
|
||||
struct FrameChunkTestContext {
|
||||
prepare_calls: usize,
|
||||
prepared_sizes: [usize; 4],
|
||||
prepare_order: [u8; 16],
|
||||
prepare_order_len: usize,
|
||||
prepare_max_dist: c_uint,
|
||||
prepare_state: Option<ZSTD_rust_frameChunkPrepareState>,
|
||||
target_calls: usize,
|
||||
split_calls: usize,
|
||||
internal_calls: usize,
|
||||
@@ -9499,16 +9551,50 @@ mod tests {
|
||||
unsafe { &mut *context.cast::<FrameChunkTestContext>() }
|
||||
}
|
||||
|
||||
unsafe extern "C" fn frame_chunk_test_prepare(
|
||||
unsafe fn frame_chunk_test_record_prepare(context: *mut c_void, step: u8, block_size: usize) {
|
||||
let context = unsafe { frame_chunk_test_context(context) };
|
||||
if context.prepare_order_len < context.prepare_order.len() {
|
||||
context.prepare_order[context.prepare_order_len] = step;
|
||||
}
|
||||
context.prepare_order_len += 1;
|
||||
if step == 1 {
|
||||
if context.prepare_calls < context.prepared_sizes.len() {
|
||||
context.prepared_sizes[context.prepare_calls] = block_size;
|
||||
}
|
||||
context.prepare_calls += 1;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn frame_chunk_test_correct_overflow(
|
||||
context: *mut c_void,
|
||||
_src: *const c_void,
|
||||
block_size: usize,
|
||||
) {
|
||||
let context = unsafe { frame_chunk_test_context(context) };
|
||||
if context.prepare_calls < context.prepared_sizes.len() {
|
||||
context.prepared_sizes[context.prepare_calls] = block_size;
|
||||
}
|
||||
context.prepare_calls += 1;
|
||||
unsafe { frame_chunk_test_record_prepare(context, 1, block_size) };
|
||||
}
|
||||
|
||||
unsafe extern "C" fn frame_chunk_test_check_dict_validity(
|
||||
context: *mut c_void,
|
||||
_src: *const c_void,
|
||||
block_size: usize,
|
||||
max_dist: c_uint,
|
||||
) {
|
||||
unsafe { frame_chunk_test_record_prepare(context, 2, block_size) };
|
||||
unsafe { frame_chunk_test_context(context) }.prepare_max_dist = max_dist;
|
||||
}
|
||||
|
||||
unsafe extern "C" fn frame_chunk_test_enforce_max_dist(
|
||||
context: *mut c_void,
|
||||
_src: *const c_void,
|
||||
block_size: usize,
|
||||
max_dist: c_uint,
|
||||
) {
|
||||
unsafe { frame_chunk_test_record_prepare(context, 3, block_size) };
|
||||
unsafe { frame_chunk_test_context(context) }.prepare_max_dist = max_dist;
|
||||
}
|
||||
|
||||
unsafe extern "C" fn frame_chunk_test_clamp_next_to_update(context: *mut c_void) {
|
||||
unsafe { frame_chunk_test_record_prepare(context, 4, 0) };
|
||||
}
|
||||
|
||||
unsafe extern "C" fn frame_chunk_test_target(
|
||||
@@ -9577,11 +9663,28 @@ mod tests {
|
||||
block_splitter_enabled: c_int,
|
||||
checksum_flag: c_int,
|
||||
) -> ZSTD_rust_frameChunkState {
|
||||
let context = context as *mut FrameChunkTestContext as *mut c_void;
|
||||
let context_ptr = context as *mut FrameChunkTestContext;
|
||||
let callback_context = context_ptr.cast::<c_void>();
|
||||
unsafe {
|
||||
(*context_ptr).prepare_state = Some(ZSTD_rust_frameChunkPrepareState {
|
||||
callback_context,
|
||||
max_dist: 64,
|
||||
correct_overflow: frame_chunk_test_correct_overflow,
|
||||
check_dict_validity: frame_chunk_test_check_dict_validity,
|
||||
enforce_max_dist: frame_chunk_test_enforce_max_dist,
|
||||
clamp_next_to_update: frame_chunk_test_clamp_next_to_update,
|
||||
});
|
||||
}
|
||||
let prepare_state = unsafe {
|
||||
(*context_ptr)
|
||||
.prepare_state
|
||||
.as_ref()
|
||||
.map_or(ptr::null(), |state| state as *const _)
|
||||
};
|
||||
ZSTD_rust_frameChunkState {
|
||||
callback_context: context,
|
||||
callback_context,
|
||||
tmp_workspace: ptr::null_mut(),
|
||||
checksum_state: context,
|
||||
checksum_state: callback_context,
|
||||
is_first_block,
|
||||
stage,
|
||||
tmp_wksp_size: 0,
|
||||
@@ -9593,7 +9696,7 @@ mod tests {
|
||||
block_splitter_enabled,
|
||||
checksum_flag,
|
||||
ending_stage: 77,
|
||||
prepare_block: frame_chunk_test_prepare,
|
||||
prepare_state,
|
||||
compress_target: frame_chunk_test_target,
|
||||
compress_split: frame_chunk_test_split,
|
||||
compress_internal: frame_chunk_test_internal,
|
||||
@@ -9627,6 +9730,8 @@ mod tests {
|
||||
assert_eq!(result, 10);
|
||||
assert_eq!(context.prepare_calls, 2);
|
||||
assert_eq!(context.prepared_sizes[..2], [4, 4]);
|
||||
assert_eq!(context.prepare_order[..8], [1, 2, 3, 4, 1, 2, 3, 4]);
|
||||
assert_eq!(context.prepare_max_dist, 64);
|
||||
assert_eq!(context.internal_calls, 2);
|
||||
assert_eq!(context.last_blocks[..2], [0, 1]);
|
||||
assert_eq!(context.checksum_calls, 1);
|
||||
|
||||
Reference in New Issue
Block a user