feat(compress): move frame header projection into Rust

Remove the C frame-header callback from the Rust-owned compressContinue
orchestration.  Project the applied frame parameters and dictionary ID as
scalars, so Rust can call the existing header serializer directly while
preserving pledged-size subtraction, stage transitions, and output accounting.
Route the sequence API's remaining header call directly to the same Rust leaf
and delete the redundant C wrapper.

Test Plan:
- 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:06:44 +02:00
parent 048979aff6
commit 534d7c9264
3 changed files with 122 additions and 89 deletions
+46 -43
View File
@@ -878,11 +878,8 @@ typedef char ZSTD_rust_frame_chunk_state_layout[
? 1 : -1];
/* The high-level continue/block entry points are Rust-owned. This projection
* carries only mutable scalar state and C callbacks; the private CCtx and
* match-state layout never crosses the ABI. */
typedef size_t (*ZSTD_rust_compressContinueHeader_f)(void* context,
void* dst,
size_t dstCapacity);
* carries frame-header scalars, mutable state, and C callbacks; the private
* CCtx and match-state layout never crosses the ABI. */
typedef void (*ZSTD_rust_compressContinueWindow_f)(void* context,
const void* src,
size_t srcSize);
@@ -894,7 +891,6 @@ typedef size_t (*ZSTD_rust_compressContinueBlock_f)(void* context,
U32 lastFrameChunk);
typedef struct {
void* callbackContext;
ZSTD_rust_compressContinueHeader_f writeFrameHeader;
ZSTD_rust_compressContinueWindow_f updateWindow;
ZSTD_rust_compressContinueWindow_f correctOverflow;
ZSTD_rust_compressContinueBlock_f compressFrameChunk;
@@ -905,6 +901,12 @@ typedef struct {
U64 pledgedSrcSizePlusOne;
size_t blockSizeMax;
int checkBlockSize;
int noDictIDFlag;
int checksumFlag;
int contentSizeFlag;
int format;
U32 windowLog;
U32 dictID;
} ZSTD_rust_compressContinueState;
size_t ZSTD_rust_compressContinue(
const ZSTD_rust_compressContinueState* state,
@@ -913,21 +915,32 @@ size_t ZSTD_rust_compressContinue(
U32 frame, U32 lastFrameChunk);
typedef char ZSTD_rust_compress_continue_state_layout[
(offsetof(ZSTD_rust_compressContinueState, callbackContext) == 0
&& offsetof(ZSTD_rust_compressContinueState, writeFrameHeader) == sizeof(void*)
&& offsetof(ZSTD_rust_compressContinueState, updateWindow) == 2 * sizeof(void*)
&& offsetof(ZSTD_rust_compressContinueState, correctOverflow) == 3 * sizeof(void*)
&& offsetof(ZSTD_rust_compressContinueState, compressFrameChunk) == 4 * sizeof(void*)
&& offsetof(ZSTD_rust_compressContinueState, compressBlock) == 5 * sizeof(void*)
&& offsetof(ZSTD_rust_compressContinueState, stage) == 6 * sizeof(void*)
&& offsetof(ZSTD_rust_compressContinueState, consumedSrcSize) == 7 * sizeof(void*)
&& offsetof(ZSTD_rust_compressContinueState, producedCSize) == 8 * sizeof(void*)
&& offsetof(ZSTD_rust_compressContinueState, pledgedSrcSizePlusOne) == 9 * sizeof(void*)
&& offsetof(ZSTD_rust_compressContinueState, updateWindow) == sizeof(void*)
&& offsetof(ZSTD_rust_compressContinueState, correctOverflow) == 2 * sizeof(void*)
&& offsetof(ZSTD_rust_compressContinueState, compressFrameChunk) == 3 * sizeof(void*)
&& offsetof(ZSTD_rust_compressContinueState, compressBlock) == 4 * sizeof(void*)
&& offsetof(ZSTD_rust_compressContinueState, stage) == 5 * sizeof(void*)
&& offsetof(ZSTD_rust_compressContinueState, consumedSrcSize) == 6 * sizeof(void*)
&& offsetof(ZSTD_rust_compressContinueState, producedCSize) == 7 * sizeof(void*)
&& offsetof(ZSTD_rust_compressContinueState, pledgedSrcSizePlusOne) == 8 * sizeof(void*)
&& offsetof(ZSTD_rust_compressContinueState, blockSizeMax)
== 9 * sizeof(void*) + sizeof(U64)
== 8 * sizeof(void*) + sizeof(U64)
&& offsetof(ZSTD_rust_compressContinueState, checkBlockSize)
== 9 * sizeof(void*) + sizeof(U64) + sizeof(size_t)
== 8 * sizeof(void*) + sizeof(U64) + sizeof(size_t)
&& offsetof(ZSTD_rust_compressContinueState, noDictIDFlag)
== 8 * sizeof(void*) + sizeof(U64) + sizeof(size_t) + sizeof(int)
&& offsetof(ZSTD_rust_compressContinueState, checksumFlag)
== 8 * sizeof(void*) + sizeof(U64) + sizeof(size_t) + 2 * sizeof(int)
&& offsetof(ZSTD_rust_compressContinueState, contentSizeFlag)
== 8 * sizeof(void*) + sizeof(U64) + sizeof(size_t) + 3 * sizeof(int)
&& offsetof(ZSTD_rust_compressContinueState, format)
== 8 * sizeof(void*) + sizeof(U64) + sizeof(size_t) + 4 * sizeof(int)
&& offsetof(ZSTD_rust_compressContinueState, windowLog)
== 8 * sizeof(void*) + sizeof(U64) + sizeof(size_t) + 5 * sizeof(int)
&& offsetof(ZSTD_rust_compressContinueState, dictID)
== 8 * sizeof(void*) + sizeof(U64) + sizeof(size_t) + 5 * sizeof(int) + sizeof(U32)
&& sizeof(ZSTD_rust_compressContinueState)
== (sizeof(void*) == 8 ? 96 : 52))
== (sizeof(void*) == 8 ? 112 : 72))
? 1 : -1];
/* Rust owns the end-of-frame orchestration. The callbacks retain the
@@ -5600,28 +5613,6 @@ static size_t ZSTD_compress_frameChunk(ZSTD_CCtx* cctx,
}
static size_t ZSTD_writeFrameHeader(void* dst, size_t dstCapacity,
const ZSTD_CCtx_params* params,
U64 pledgedSrcSize, U32 dictID)
{
return ZSTD_rust_writeFrameHeader(dst, dstCapacity,
params->fParams.noDictIDFlag,
params->fParams.checksumFlag,
params->fParams.contentSizeFlag,
(int)params->format,
params->cParams.windowLog,
pledgedSrcSize, dictID);
}
static size_t ZSTD_rust_compressContinue_writeFrameHeader(
void* context, void* dst, size_t dstCapacity)
{
ZSTD_CCtx* const cctx = (ZSTD_CCtx*)context;
return ZSTD_writeFrameHeader(
dst, dstCapacity, &cctx->appliedParams,
cctx->pledgedSrcSizePlusOne - 1, cctx->dictID);
}
static void ZSTD_rust_compressContinue_updateWindow(
void* context, const void* src, size_t srcSize)
{
@@ -5676,7 +5667,6 @@ static size_t ZSTD_compressContinue_dispatch(
{
ZSTD_rust_compressContinueState state;
state.callbackContext = cctx;
state.writeFrameHeader = ZSTD_rust_compressContinue_writeFrameHeader;
state.updateWindow = ZSTD_rust_compressContinue_updateWindow;
state.correctOverflow = ZSTD_rust_compressContinue_correctOverflow;
state.compressFrameChunk = ZSTD_rust_compressContinue_frameChunk;
@@ -5687,6 +5677,12 @@ static size_t ZSTD_compressContinue_dispatch(
state.pledgedSrcSizePlusOne = cctx->pledgedSrcSizePlusOne;
state.blockSizeMax = blockSizeMax;
state.checkBlockSize = checkBlockSize;
state.noDictIDFlag = cctx->appliedParams.fParams.noDictIDFlag;
state.checksumFlag = cctx->appliedParams.fParams.checksumFlag;
state.contentSizeFlag = cctx->appliedParams.fParams.contentSizeFlag;
state.format = (int)cctx->appliedParams.format;
state.windowLog = cctx->appliedParams.cParams.windowLog;
state.dictID = cctx->dictID;
return ZSTD_rust_compressContinue(
&state, dst, dstCapacity, src, srcSize, frame, lastFrameChunk);
}
@@ -8030,8 +8026,15 @@ static size_t ZSTD_rust_sequenceApi_writeFrameHeader(
void* context, void* dst, size_t dstCapacity, size_t pledgedSrcSize)
{
ZSTD_CCtx const* const cctx = (ZSTD_CCtx const*)context;
return ZSTD_writeFrameHeader(dst, dstCapacity, &cctx->appliedParams,
pledgedSrcSize, cctx->dictID);
return ZSTD_rust_writeFrameHeader(
dst, dstCapacity,
cctx->appliedParams.fParams.noDictIDFlag,
cctx->appliedParams.fParams.checksumFlag,
cctx->appliedParams.fParams.contentSizeFlag,
(int)cctx->appliedParams.format,
cctx->appliedParams.cParams.windowLog,
pledgedSrcSize,
cctx->dictID);
}
static void ZSTD_rust_sequenceApi_updateChecksum(
+73 -43
View File
@@ -790,7 +790,6 @@ pub unsafe extern "C" fn ZSTD_rust_compressFrameChunk(
}
}
type CompressContinueHeaderFn = unsafe extern "C" fn(*mut c_void, *mut c_void, usize) -> usize;
type CompressContinueWindowFn = unsafe extern "C" fn(*mut c_void, *const c_void, usize);
type CompressContinueBlockFn =
unsafe extern "C" fn(*mut c_void, *mut c_void, usize, *const c_void, usize, c_uint) -> usize;
@@ -799,12 +798,12 @@ type CompressContinueBlockFn =
///
/// Rust owns stage transitions, frame-header sequencing, input progression,
/// and dispatch between the already-migrated frame-chunk/block bodies. 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, while the opaque callback
/// context remains in C, where callbacks update the private match-state
/// windows and invoke the C-owned context-sensitive operations.
#[repr(C)]
pub struct ZSTD_rust_compressContinueState {
callback_context: *mut c_void,
write_frame_header: CompressContinueHeaderFn,
update_window: CompressContinueWindowFn,
correct_overflow: CompressContinueWindowFn,
compress_frame_chunk: CompressContinueBlockFn,
@@ -815,42 +814,74 @@ pub struct ZSTD_rust_compressContinueState {
pledged_src_size_plus_one: u64,
block_size_max: usize,
check_block_size: c_int,
no_dict_id_flag: c_int,
checksum_flag: c_int,
content_size_flag: c_int,
format: c_int,
window_log: c_uint,
dict_id: c_uint,
}
const COMPRESS_CONTINUE_SCALARS_OFFSET: usize =
size_of::<[usize; 8]>() + size_of::<u64>() + size_of::<usize>();
const _: () = {
assert!(offset_of!(ZSTD_rust_compressContinueState, callback_context) == 0);
assert!(offset_of!(ZSTD_rust_compressContinueState, write_frame_header) == size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressContinueState, update_window) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_compressContinueState, update_window) == size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_compressContinueState, correct_overflow) == 3 * size_of::<usize>()
offset_of!(ZSTD_rust_compressContinueState, correct_overflow) == 2 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressContinueState, compress_frame_chunk) == 4 * size_of::<usize>()
offset_of!(ZSTD_rust_compressContinueState, compress_frame_chunk) == 3 * 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, 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) == 7 * size_of::<usize>()
offset_of!(ZSTD_rust_compressContinueState, consumed_src_size) == 6 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_compressContinueState, produced_c_size)
== 8 * (usize::BITS as usize / 8)
== 7 * (usize::BITS as usize / 8)
);
assert!(
offset_of!(ZSTD_rust_compressContinueState, pledged_src_size_plus_one)
== 9 * size_of::<usize>()
== size_of::<[usize; 8]>()
);
assert!(
offset_of!(ZSTD_rust_compressContinueState, block_size_max)
== 9 * size_of::<usize>() + size_of::<u64>()
== size_of::<[usize; 8]>() + size_of::<u64>()
);
assert!(
offset_of!(ZSTD_rust_compressContinueState, check_block_size)
== 9 * size_of::<usize>() + size_of::<u64>() + size_of::<usize>()
== COMPRESS_CONTINUE_SCALARS_OFFSET
);
assert!(
offset_of!(ZSTD_rust_compressContinueState, no_dict_id_flag)
== COMPRESS_CONTINUE_SCALARS_OFFSET + size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_compressContinueState, checksum_flag)
== COMPRESS_CONTINUE_SCALARS_OFFSET + size_of::<[c_int; 2]>()
);
assert!(
offset_of!(ZSTD_rust_compressContinueState, content_size_flag)
== COMPRESS_CONTINUE_SCALARS_OFFSET + size_of::<[c_int; 3]>()
);
assert!(
offset_of!(ZSTD_rust_compressContinueState, format)
== COMPRESS_CONTINUE_SCALARS_OFFSET + size_of::<[c_int; 4]>()
);
assert!(
offset_of!(ZSTD_rust_compressContinueState, window_log)
== COMPRESS_CONTINUE_SCALARS_OFFSET + size_of::<[c_int; 5]>()
);
assert!(
offset_of!(ZSTD_rust_compressContinueState, dict_id)
== COMPRESS_CONTINUE_SCALARS_OFFSET + size_of::<[c_int; 5]>() + size_of::<c_uint>()
);
assert!(
size_of::<ZSTD_rust_compressContinueState>()
== if size_of::<usize>() == 8 { 96 } else { 52 }
== if size_of::<usize>() == 8 { 112 } else { 72 }
);
};
@@ -885,8 +916,19 @@ unsafe fn compress_continue_body_with(
let mut frame_header_size = 0usize;
if frame != 0 && unsafe { *state.stage } == ZSTD_COMPRESSION_STAGE_INIT {
frame_header_size =
unsafe { (state.write_frame_header)(state.callback_context, dst, dst_capacity) };
frame_header_size = unsafe {
ZSTD_rust_writeFrameHeader(
dst,
dst_capacity,
state.no_dict_id_flag,
state.checksum_flag,
state.content_size_flag,
state.format,
state.window_log,
state.pledged_src_size_plus_one.wrapping_sub(1),
state.dict_id,
)
};
if ERR_isError(frame_header_size) {
return frame_header_size;
}
@@ -10964,13 +11006,11 @@ mod tests {
#[derive(Default)]
struct CompressContinueTestContext {
header_calls: usize,
update_window_calls: usize,
overflow_calls: usize,
frame_calls: usize,
block_calls: usize,
last_frame_chunk: c_uint,
header_result: usize,
frame_result: usize,
block_result: usize,
}
@@ -10981,16 +11021,6 @@ mod tests {
unsafe { &mut *context.cast::<CompressContinueTestContext>() }
}
unsafe extern "C" fn compress_continue_test_header(
context: *mut c_void,
_dst: *mut c_void,
_dst_capacity: usize,
) -> usize {
let context = unsafe { compress_continue_test_context(context) };
context.header_calls += 1;
context.header_result
}
unsafe extern "C" fn compress_continue_test_window(
context: *mut c_void,
_src: *const c_void,
@@ -11047,7 +11077,6 @@ mod tests {
) -> ZSTD_rust_compressContinueState {
ZSTD_rust_compressContinueState {
callback_context: (context as *mut CompressContinueTestContext).cast(),
write_frame_header: compress_continue_test_header,
update_window: compress_continue_test_window,
correct_overflow: compress_continue_test_overflow,
compress_frame_chunk: compress_continue_test_frame,
@@ -11058,13 +11087,18 @@ mod tests {
pledged_src_size_plus_one,
block_size_max,
check_block_size,
no_dict_id_flag: 0,
checksum_flag: 0,
content_size_flag: 0,
format: 0,
window_log: 10,
dict_id: 0,
}
}
#[test]
fn compress_continue_starts_frame_and_updates_progression() {
let mut context = CompressContinueTestContext {
header_result: 4,
frame_result: 6,
..CompressContinueTestContext::default()
};
@@ -11095,11 +11129,10 @@ mod tests {
)
};
assert_eq!(result, 10);
assert_eq!(result, 12);
assert_eq!(stage, ZSTD_COMPRESSION_STAGE_ONGOING);
assert_eq!(consumed, 12);
assert_eq!(produced, 21);
assert_eq!(context.header_calls, 1);
assert_eq!(produced, 23);
assert_eq!(context.update_window_calls, 1);
assert_eq!(context.overflow_calls, 0);
assert_eq!(context.frame_calls, 1);
@@ -11109,10 +11142,7 @@ mod tests {
#[test]
fn compress_continue_empty_frame_only_writes_header() {
let mut context = CompressContinueTestContext {
header_result: 3,
..CompressContinueTestContext::default()
};
let mut context = CompressContinueTestContext::default();
let mut stage = ZSTD_COMPRESSION_STAGE_INIT;
let mut consumed = 7;
let mut produced = 11;
@@ -11125,7 +11155,7 @@ mod tests {
16,
0,
);
let mut output = [0xa5u8; 8];
let mut output = [0xa5u8; 18];
let result = unsafe {
ZSTD_rust_compressContinue(
@@ -11139,13 +11169,13 @@ mod tests {
)
};
assert_eq!(result, 3);
assert_eq!(result, 6);
assert_eq!(stage, ZSTD_COMPRESSION_STAGE_ONGOING);
assert_eq!((consumed, produced), (7, 11));
assert_eq!(context.header_calls, 1);
assert_eq!(context.update_window_calls, 0);
assert_eq!(context.frame_calls, 0);
assert_eq!(output, [0xa5; 8]);
assert_eq!(&output[..6], &[0x28, 0xb5, 0x2f, 0xfd, 0x00, 0x00]);
assert_eq!(&output[6..], &[0xa5; 12]);
}
#[test]
+3 -3
View File
@@ -112,9 +112,9 @@ pub unsafe extern "C" fn ZSTD_rust_writeBlockHeader(
/// Rust implementation of the private `ZSTD_writeFrameHeader()` leaf.
///
/// `no_dict_id_flag`, `checksum_flag`, `content_size_flag`, `format`, and
/// `window_log` are extracted by a C wrapper from `ZSTD_CCtx_params`. The
/// wrapper preserves the existing static C function signature, avoiding any
/// dependency on the full context-parameter layout here.
/// `window_log` are extracted by C callers from `ZSTD_CCtx_params`. Keeping
/// those projections scalar avoids any dependency on the full
/// context-parameter layout here.
///
/// The caller must provide a writable buffer of at least
/// `ZSTD_FRAMEHEADERSIZE_MAX` bytes, or this returns `dstSize_tooSmall`. As