feat(compress): move continue window updates into Rust

Project the match-state and optional LDM window fields directly through the
compressContinue ABI so Rust can reuse the existing window-update leaf.  Keep
forceNonContiguous and nextToUpdate updates visible to the following C
callbacks, preserve the match-before-LDM ordering, and remove the C window
update callback while retaining context-sensitive overflow and block calls.

Test Plan:
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo fmt --all -- --check
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo test --lib zstd_compress::tests::compress_continue -- --nocapture
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo clippy --all-targets -- -D warnings
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo test
- ulimit -v 41943040; make -j1
- ulimit -v 41943040; make -j1 -C tests test-zstream ZSTREAM_TESTTIME=-T2s
- ulimit -v 41943040; make -j1 -C tests test-fuzzer FUZZERTEST=-T3s FUZZER_FLAGS=--no-big-tests
This commit is contained in:
2026-07-20 00:30:47 +02:00
parent cce9254703
commit 0a75273a50
2 changed files with 261 additions and 70 deletions
+200 -33
View File
@@ -795,17 +795,59 @@ type CompressContinueWindowFn = unsafe extern "C" fn(*mut c_void, *const c_void,
type CompressContinueBlockFn =
unsafe extern "C" fn(*mut c_void, *mut c_void, usize, *const c_void, usize, c_uint) -> usize;
/// Pointer projection for one C-owned `ZSTD_window_t` and the match-state
/// fields which follow its update. Each field points directly at the C
/// storage so later callbacks observe the updated window immediately.
#[repr(C)]
#[derive(Default)]
struct ZSTD_rust_compressContinueWindowProjection {
next_src: *mut *const c_void,
base: *mut *const c_void,
dict_base: *mut *const c_void,
dict_limit: *mut c_uint,
low_limit: *mut c_uint,
force_non_contiguous: *mut c_int,
next_to_update: *mut c_uint,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_compressContinueWindowProjection, next_src) == 0);
assert!(offset_of!(ZSTD_rust_compressContinueWindowProjection, base) == size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_compressContinueWindowProjection, dict_base) == 2 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressContinueWindowProjection, dict_limit)
== 3 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressContinueWindowProjection, low_limit) == 4 * size_of::<usize>()
);
assert!(
offset_of!(
ZSTD_rust_compressContinueWindowProjection,
force_non_contiguous
) == 5 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressContinueWindowProjection, next_to_update)
== 6 * size_of::<usize>()
);
assert!(size_of::<ZSTD_rust_compressContinueWindowProjection>() == 7 * size_of::<usize>());
};
/// Explicit projection for the high-level continue and deprecated block APIs.
///
/// Rust owns stage transitions, frame-header sequencing, input progression,
/// and dispatch between the already-migrated frame-chunk/block bodies. The
/// frame-header parameters are projected as scalars, while the opaque callback
/// context remains in C, where callbacks update the private match-state
/// windows and invoke the C-owned context-sensitive operations.
/// frame-header parameters are projected as scalars. The opaque callback
/// context remains in C for overflow correction and context-sensitive block
/// operations; window advancement is projected directly into Rust.
#[repr(C)]
pub struct ZSTD_rust_compressContinueState {
callback_context: *mut c_void,
update_window: CompressContinueWindowFn,
window_state: *const ZSTD_rust_compressContinueWindowProjection,
ldm_window_state: *const ZSTD_rust_compressContinueWindowProjection,
correct_overflow: CompressContinueWindowFn,
compress_frame_chunk: CompressContinueBlockFn,
compress_block: CompressContinueBlockFn,
@@ -821,36 +863,40 @@ pub struct ZSTD_rust_compressContinueState {
format: c_int,
window_log: c_uint,
dict_id: c_uint,
ldm_enabled: c_int,
}
const COMPRESS_CONTINUE_SCALARS_OFFSET: usize =
size_of::<[usize; 8]>() + size_of::<u64>() + size_of::<usize>();
size_of::<[usize; 9]>() + size_of::<u64>() + size_of::<usize>();
const _: () = {
assert!(offset_of!(ZSTD_rust_compressContinueState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_compressContinueState, update_window) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressContinueState, window_state) == size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_compressContinueState, correct_overflow) == 2 * size_of::<usize>()
offset_of!(ZSTD_rust_compressContinueState, ldm_window_state) == 2 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressContinueState, compress_frame_chunk) == 3 * size_of::<usize>()
offset_of!(ZSTD_rust_compressContinueState, correct_overflow) == 3 * size_of::<usize>()
);
assert!(offset_of!(ZSTD_rust_compressContinueState, compress_block) == 4 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressContinueState, stage) == 5 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_compressContinueState, consumed_src_size) == 6 * size_of::<usize>()
offset_of!(ZSTD_rust_compressContinueState, compress_frame_chunk) == 4 * size_of::<usize>()
);
assert!(offset_of!(ZSTD_rust_compressContinueState, compress_block) == 5 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressContinueState, stage) == 6 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_compressContinueState, consumed_src_size) == 7 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressContinueState, produced_c_size)
== 7 * (usize::BITS as usize / 8)
== 8 * (usize::BITS as usize / 8)
);
assert!(
offset_of!(ZSTD_rust_compressContinueState, pledged_src_size_plus_one)
== size_of::<[usize; 8]>()
== size_of::<[usize; 9]>()
);
assert!(
offset_of!(ZSTD_rust_compressContinueState, block_size_max)
== size_of::<[usize; 8]>() + size_of::<u64>()
== size_of::<[usize; 9]>() + size_of::<u64>()
);
assert!(
offset_of!(ZSTD_rust_compressContinueState, check_block_size)
@@ -880,9 +926,13 @@ const _: () = {
offset_of!(ZSTD_rust_compressContinueState, dict_id)
== COMPRESS_CONTINUE_SCALARS_OFFSET + size_of::<[c_int; 5]>() + size_of::<c_uint>()
);
assert!(
offset_of!(ZSTD_rust_compressContinueState, ldm_enabled)
== COMPRESS_CONTINUE_SCALARS_OFFSET + size_of::<[c_int; 5]>() + 2 * size_of::<c_uint>()
);
assert!(
size_of::<ZSTD_rust_compressContinueState>()
== if size_of::<usize>() == 8 { 112 } else { 72 }
== if size_of::<usize>() == 8 { 120 } else { 80 }
);
};
@@ -890,6 +940,56 @@ const ZSTD_COMPRESSION_STAGE_CREATED: c_int = 0;
const ZSTD_COMPRESSION_STAGE_INIT: c_int = 1;
const ZSTD_COMPRESSION_STAGE_ONGOING: c_int = 2;
unsafe fn compress_continue_update_window(
projection: *const ZSTD_rust_compressContinueWindowProjection,
src: *const c_void,
src_size: usize,
force_non_contiguous: c_int,
update_match_state: bool,
) -> Result<(), usize> {
if projection.is_null() {
return Err(ERROR(ZstdErrorCode::Generic));
}
let projection = unsafe { &*projection };
if projection.next_src.is_null()
|| projection.base.is_null()
|| projection.dict_base.is_null()
|| projection.dict_limit.is_null()
|| projection.low_limit.is_null()
{
return Err(ERROR(ZstdErrorCode::Generic));
}
if update_match_state
&& (projection.force_non_contiguous.is_null() || projection.next_to_update.is_null())
{
return Err(ERROR(ZstdErrorCode::Generic));
}
let mut window_state = ZSTD_rust_windowUpdateState {
nextSrc: unsafe { *projection.next_src },
base: unsafe { *projection.base },
dictBase: unsafe { *projection.dict_base },
dictLimit: unsafe { *projection.dict_limit },
lowLimit: unsafe { *projection.low_limit },
};
let contiguous =
unsafe { ZSTD_rust_windowUpdate(&mut window_state, src, src_size, force_non_contiguous) };
unsafe {
*projection.next_src = window_state.nextSrc;
*projection.base = window_state.base;
*projection.dict_base = window_state.dictBase;
*projection.dict_limit = window_state.dictLimit;
*projection.low_limit = window_state.lowLimit;
}
if update_match_state && contiguous == 0 {
unsafe {
*projection.force_non_contiguous = 0;
*projection.next_to_update = window_state.dictLimit;
}
}
Ok(())
}
#[allow(clippy::too_many_arguments)]
unsafe fn compress_continue_body_with(
state: &ZSTD_rust_compressContinueState,
@@ -947,7 +1047,32 @@ unsafe fn compress_continue_body_with(
return frame_header_size;
}
unsafe { (state.update_window)(state.callback_context, src, src_size) };
if state.window_state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let match_window = unsafe { &*state.window_state };
if match_window.force_non_contiguous.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let force_non_contiguous = unsafe { *match_window.force_non_contiguous };
if let Err(result) = unsafe {
compress_continue_update_window(
state.window_state,
src,
src_size,
force_non_contiguous,
true,
)
} {
return result;
}
if state.ldm_enabled != 0 {
if let Err(result) = unsafe {
compress_continue_update_window(state.ldm_window_state, src, src_size, 0, false)
} {
return result;
}
}
if frame == 0 {
unsafe { (state.correct_overflow)(state.callback_context, src, src_size) };
}
@@ -7610,7 +7735,7 @@ const HASH_READ_SIZE: u32 = 8;
/// Pointer arithmetic is intentionally performed as wrapping address math to
/// mirror the original C implementation's pointer-overflow contract.
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct ZSTD_rust_windowUpdateState {
pub nextSrc: *const c_void,
pub base: *const c_void,
@@ -11026,13 +11151,18 @@ mod tests {
#[derive(Default)]
struct CompressContinueTestContext {
update_window_calls: usize,
overflow_calls: usize,
frame_calls: usize,
block_calls: usize,
last_frame_chunk: c_uint,
frame_result: usize,
block_result: usize,
window: ZSTD_rust_windowUpdateState,
ldm_window: ZSTD_rust_windowUpdateState,
force_non_contiguous: c_int,
next_to_update: c_uint,
window_projection: ZSTD_rust_compressContinueWindowProjection,
ldm_window_projection: ZSTD_rust_compressContinueWindowProjection,
}
unsafe fn compress_continue_test_context(
@@ -11041,15 +11171,6 @@ mod tests {
unsafe { &mut *context.cast::<CompressContinueTestContext>() }
}
unsafe extern "C" fn compress_continue_test_window(
context: *mut c_void,
_src: *const c_void,
_src_size: usize,
) {
let context = unsafe { compress_continue_test_context(context) };
context.update_window_calls += 1;
}
unsafe extern "C" fn compress_continue_test_overflow(
context: *mut c_void,
_src: *const c_void,
@@ -11095,9 +11216,40 @@ mod tests {
block_size_max: usize,
check_block_size: c_int,
) -> ZSTD_rust_compressContinueState {
let base = WINDOW_INIT_SENTINEL.as_ptr().cast::<c_void>();
let next_src = unsafe { base.cast::<u8>().add(2).cast::<c_void>() };
context.window = ZSTD_rust_windowUpdateState {
nextSrc: next_src,
base,
dictBase: base,
dictLimit: ZSTD_WINDOW_START_INDEX,
lowLimit: ZSTD_WINDOW_START_INDEX,
};
context.ldm_window = context.window;
context.force_non_contiguous = 0;
context.next_to_update = ZSTD_WINDOW_START_INDEX;
context.window_projection = ZSTD_rust_compressContinueWindowProjection {
next_src: ptr::addr_of_mut!(context.window.nextSrc),
base: ptr::addr_of_mut!(context.window.base),
dict_base: ptr::addr_of_mut!(context.window.dictBase),
dict_limit: ptr::addr_of_mut!(context.window.dictLimit),
low_limit: ptr::addr_of_mut!(context.window.lowLimit),
force_non_contiguous: ptr::addr_of_mut!(context.force_non_contiguous),
next_to_update: ptr::addr_of_mut!(context.next_to_update),
};
context.ldm_window_projection = ZSTD_rust_compressContinueWindowProjection {
next_src: ptr::addr_of_mut!(context.ldm_window.nextSrc),
base: ptr::addr_of_mut!(context.ldm_window.base),
dict_base: ptr::addr_of_mut!(context.ldm_window.dictBase),
dict_limit: ptr::addr_of_mut!(context.ldm_window.dictLimit),
low_limit: ptr::addr_of_mut!(context.ldm_window.lowLimit),
force_non_contiguous: ptr::null_mut(),
next_to_update: ptr::null_mut(),
};
ZSTD_rust_compressContinueState {
callback_context: (context as *mut CompressContinueTestContext).cast(),
update_window: compress_continue_test_window,
window_state: &context.window_projection,
ldm_window_state: &context.ldm_window_projection,
correct_overflow: compress_continue_test_overflow,
compress_frame_chunk: compress_continue_test_frame,
compress_block: compress_continue_test_block,
@@ -11113,6 +11265,7 @@ mod tests {
format: 0,
window_log: 10,
dict_id: 0,
ldm_enabled: 1,
}
}
@@ -11153,7 +11306,12 @@ mod tests {
assert_eq!(stage, ZSTD_COMPRESSION_STAGE_ONGOING);
assert_eq!(consumed, 12);
assert_eq!(produced, 23);
assert_eq!(context.update_window_calls, 1);
assert_eq!(context.window.nextSrc, unsafe {
source.as_ptr().add(source.len()).cast()
});
assert_eq!(context.ldm_window.nextSrc, context.window.nextSrc);
assert_eq!(context.force_non_contiguous, 0);
assert_eq!(context.next_to_update, context.window.dictLimit);
assert_eq!(context.overflow_calls, 0);
assert_eq!(context.frame_calls, 1);
assert_eq!(context.block_calls, 0);
@@ -11192,7 +11350,6 @@ mod tests {
assert_eq!(result, 6);
assert_eq!(stage, ZSTD_COMPRESSION_STAGE_ONGOING);
assert_eq!((consumed, produced), (7, 11));
assert_eq!(context.update_window_calls, 0);
assert_eq!(context.frame_calls, 0);
assert_eq!(&output[..6], &[0x28, 0xb5, 0x2f, 0xfd, 0x00, 0x00]);
assert_eq!(&output[6..], &[0xa5; 12]);
@@ -11215,6 +11372,8 @@ mod tests {
);
let source = [0x22u8; 5];
let mut output = [0xa5u8; 16];
let initial_window = context.window;
let initial_ldm_window = context.ldm_window;
let oversized = unsafe {
ZSTD_rust_compressContinue(
@@ -11230,7 +11389,8 @@ mod tests {
assert_eq!(oversized, ERROR(ZstdErrorCode::SrcSizeWrong));
assert_eq!(stage, ZSTD_COMPRESSION_STAGE_INIT);
assert_eq!((consumed, produced), (4, 9));
assert_eq!(context.update_window_calls, 0);
assert_eq!(context.window, initial_window);
assert_eq!(context.ldm_window, initial_ldm_window);
assert_eq!(context.block_calls, 0);
state.check_block_size = 0;
@@ -11248,7 +11408,8 @@ mod tests {
};
assert_eq!(created, ERROR(ZstdErrorCode::StageWrong));
assert_eq!(stage, ZSTD_COMPRESSION_STAGE_CREATED);
assert_eq!(context.update_window_calls, 0);
assert_eq!(context.window, initial_window);
assert_eq!(context.ldm_window, initial_ldm_window);
assert_eq!(context.block_calls, 0);
}
@@ -11290,7 +11451,10 @@ mod tests {
(stage, consumed, produced),
(ZSTD_COMPRESSION_STAGE_ONGOING, 4, 9)
);
assert_eq!(context.update_window_calls, 1);
assert_eq!(context.window.nextSrc, unsafe {
source.as_ptr().add(source.len()).cast()
});
assert_eq!(context.ldm_window.nextSrc, context.window.nextSrc);
assert_eq!(context.overflow_calls, 1);
assert_eq!(context.block_calls, 1);
}
@@ -11330,6 +11494,9 @@ mod tests {
assert_eq!(result, ERROR(ZstdErrorCode::SrcSizeWrong));
assert_eq!((consumed, produced), (6, 3));
assert_eq!(context.window.nextSrc, unsafe {
source.as_ptr().add(source.len()).cast()
});
assert_eq!(context.block_calls, 1);
}