From 534d7c92648866cac24c10787e99957b1bd97cad Mon Sep 17 00:00:00 2001 From: ddidderr Date: Mon, 20 Jul 2026 00:06:44 +0200 Subject: [PATCH] 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 --- lib/compress/zstd_compress.c | 89 ++++++++++++------------ rust/src/zstd_compress.rs | 116 ++++++++++++++++++++------------ rust/src/zstd_compress_frame.rs | 6 +- 3 files changed, 122 insertions(+), 89 deletions(-) diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index ce1750205..468e9634d 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -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( diff --git a/rust/src/zstd_compress.rs b/rust/src/zstd_compress.rs index 13e842008..5c7fd04c3 100644 --- a/rust/src/zstd_compress.rs +++ b/rust/src/zstd_compress.rs @@ -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::() + size_of::(); + const _: () = { assert!(offset_of!(ZSTD_rust_compressContinueState, callback_context) == 0); - assert!(offset_of!(ZSTD_rust_compressContinueState, write_frame_header) == size_of::()); - assert!(offset_of!(ZSTD_rust_compressContinueState, update_window) == 2 * size_of::()); + assert!(offset_of!(ZSTD_rust_compressContinueState, update_window) == size_of::()); assert!( - offset_of!(ZSTD_rust_compressContinueState, correct_overflow) == 3 * size_of::() + offset_of!(ZSTD_rust_compressContinueState, correct_overflow) == 2 * size_of::() ); assert!( - offset_of!(ZSTD_rust_compressContinueState, compress_frame_chunk) == 4 * size_of::() + offset_of!(ZSTD_rust_compressContinueState, compress_frame_chunk) == 3 * size_of::() ); - assert!(offset_of!(ZSTD_rust_compressContinueState, compress_block) == 5 * size_of::()); - assert!(offset_of!(ZSTD_rust_compressContinueState, stage) == 6 * size_of::()); + assert!(offset_of!(ZSTD_rust_compressContinueState, compress_block) == 4 * size_of::()); + assert!(offset_of!(ZSTD_rust_compressContinueState, stage) == 5 * size_of::()); assert!( - offset_of!(ZSTD_rust_compressContinueState, consumed_src_size) == 7 * size_of::() + offset_of!(ZSTD_rust_compressContinueState, consumed_src_size) == 6 * size_of::() ); 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::() + == size_of::<[usize; 8]>() ); assert!( offset_of!(ZSTD_rust_compressContinueState, block_size_max) - == 9 * size_of::() + size_of::() + == size_of::<[usize; 8]>() + size_of::() ); assert!( offset_of!(ZSTD_rust_compressContinueState, check_block_size) - == 9 * size_of::() + size_of::() + size_of::() + == COMPRESS_CONTINUE_SCALARS_OFFSET + ); + assert!( + offset_of!(ZSTD_rust_compressContinueState, no_dict_id_flag) + == COMPRESS_CONTINUE_SCALARS_OFFSET + size_of::() + ); + 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::() ); assert!( size_of::() - == if size_of::() == 8 { 96 } else { 52 } + == if size_of::() == 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::() } } - 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] diff --git a/rust/src/zstd_compress_frame.rs b/rust/src/zstd_compress_frame.rs index 21f3a6a92..9c5fb8afc 100644 --- a/rust/src/zstd_compress_frame.rs +++ b/rust/src/zstd_compress_frame.rs @@ -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