feat(compress): move sequence checksums into Rust

Project the sequence API's XXH64 state directly through the Rust ABI boundary
so Rust owns checksum updates, digest ordering, and little-endian frame
checksum serialization.  Keep the C callback limited to private CCtx and
sequence-state initialization, remove the three checksum callback shims, and
update the cross-language layout assertions for the smaller state.

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::sequence_api -- --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:23:13 +02:00
parent 4a8345f596
commit cce9254703
2 changed files with 41 additions and 82 deletions
+15 -49
View File
@@ -2742,28 +2742,21 @@ typedef char ZSTD_rust_sequence_literals_state_layout[
== 6 * sizeof(void*) + 4 * sizeof(int) + 3 * sizeof(void*)) == 6 * sizeof(void*) + 4 * sizeof(int) + 3 * sizeof(void*))
? 1 : -1]; ? 1 : -1];
/* The public sequence APIs keep their CCtx initialization and checksum state /* The public sequence APIs keep their CCtx initialization in C, while Rust
* in C, while Rust owns frame-header serialization, ordering, and output * owns frame-header/checksum serialization, ordering, and output accounting.
* accounting. The two block-loop projections above are populated by the * The checksum state and two block-loop projections are populated by the
* initialization callback after the private CCtx has been initialized. */ * initialization callback after the private CCtx has been initialized. */
typedef struct ZSTD_rust_sequenceApiState_s ZSTD_rust_sequenceApiState; typedef struct ZSTD_rust_sequenceApiState_s ZSTD_rust_sequenceApiState;
typedef size_t (*ZSTD_rust_sequenceApiInit_f)( typedef size_t (*ZSTD_rust_sequenceApiInit_f)(
void* context, size_t pledgedSrcSize, void* context, size_t pledgedSrcSize,
ZSTD_rust_sequenceApiState* state); ZSTD_rust_sequenceApiState* state);
typedef void (*ZSTD_rust_sequenceApiUpdateChecksum_f)(
void* context, const void* src, size_t srcSize);
typedef U32 (*ZSTD_rust_sequenceApiDigestChecksum_f)(void* context);
typedef void (*ZSTD_rust_sequenceApiWriteChecksum_f)(
void* context, void* dst, U32 checksum);
struct ZSTD_rust_sequenceApiState_s { struct ZSTD_rust_sequenceApiState_s {
void* callbackContext; void* callbackContext;
ZSTD_rust_sequenceCompressionState* sequenceState; ZSTD_rust_sequenceCompressionState* sequenceState;
ZSTD_rust_sequenceLiteralsState* sequenceLiteralsState; ZSTD_rust_sequenceLiteralsState* sequenceLiteralsState;
ZSTD_rust_sequenceApiInit_f init; ZSTD_rust_sequenceApiInit_f init;
ZSTD_rust_sequenceApiUpdateChecksum_f updateChecksum; XXH64_state_t* checksumState;
ZSTD_rust_sequenceApiDigestChecksum_f digestChecksum;
ZSTD_rust_sequenceApiWriteChecksum_f writeChecksum;
int checksumFlag; int checksumFlag;
int blockDelimiters; int blockDelimiters;
int validateSequences; int validateSequences;
@@ -2792,30 +2785,26 @@ typedef char ZSTD_rust_sequence_api_state_layout[
== 2 * sizeof(void*) == 2 * sizeof(void*)
&& offsetof(ZSTD_rust_sequenceApiState, init) && offsetof(ZSTD_rust_sequenceApiState, init)
== 3 * sizeof(void*) == 3 * sizeof(void*)
&& offsetof(ZSTD_rust_sequenceApiState, updateChecksum) && offsetof(ZSTD_rust_sequenceApiState, checksumState)
== 4 * sizeof(void*) == 4 * sizeof(void*)
&& offsetof(ZSTD_rust_sequenceApiState, digestChecksum)
== 5 * sizeof(void*)
&& offsetof(ZSTD_rust_sequenceApiState, writeChecksum)
== 6 * sizeof(void*)
&& offsetof(ZSTD_rust_sequenceApiState, checksumFlag) && offsetof(ZSTD_rust_sequenceApiState, checksumFlag)
== 7 * sizeof(void*) == 5 * sizeof(void*)
&& offsetof(ZSTD_rust_sequenceApiState, blockDelimiters) && offsetof(ZSTD_rust_sequenceApiState, blockDelimiters)
== 7 * sizeof(void*) + sizeof(int) == 5 * sizeof(void*) + sizeof(int)
&& offsetof(ZSTD_rust_sequenceApiState, validateSequences) && offsetof(ZSTD_rust_sequenceApiState, validateSequences)
== 7 * sizeof(void*) + 2 * sizeof(int) == 5 * sizeof(void*) + 2 * sizeof(int)
&& offsetof(ZSTD_rust_sequenceApiState, noDictIDFlag) && offsetof(ZSTD_rust_sequenceApiState, noDictIDFlag)
== 7 * sizeof(void*) + 3 * sizeof(int) == 5 * sizeof(void*) + 3 * sizeof(int)
&& offsetof(ZSTD_rust_sequenceApiState, contentSizeFlag) && offsetof(ZSTD_rust_sequenceApiState, contentSizeFlag)
== 7 * sizeof(void*) + 4 * sizeof(int) == 5 * sizeof(void*) + 4 * sizeof(int)
&& offsetof(ZSTD_rust_sequenceApiState, format) && offsetof(ZSTD_rust_sequenceApiState, format)
== 7 * sizeof(void*) + 5 * sizeof(int) == 5 * sizeof(void*) + 5 * sizeof(int)
&& offsetof(ZSTD_rust_sequenceApiState, windowLog) && offsetof(ZSTD_rust_sequenceApiState, windowLog)
== 7 * sizeof(void*) + 6 * sizeof(int) == 5 * sizeof(void*) + 6 * sizeof(int)
&& offsetof(ZSTD_rust_sequenceApiState, dictID) && offsetof(ZSTD_rust_sequenceApiState, dictID)
== 7 * sizeof(void*) + 6 * sizeof(int) + sizeof(U32) == 5 * sizeof(void*) + 6 * sizeof(int) + sizeof(U32)
&& sizeof(ZSTD_rust_sequenceApiState) && sizeof(ZSTD_rust_sequenceApiState)
== (sizeof(void*) == 8 ? 88 : 60)) == (sizeof(void*) == 8 ? 72 : 52))
? 1 : -1]; ? 1 : -1];
typedef char ZSTD_rust_stats_seqdef_layout[(sizeof(SeqDef) == 8) ? 1 : -1]; typedef char ZSTD_rust_stats_seqdef_layout[(sizeof(SeqDef) == 8) ? 1 : -1];
@@ -8037,27 +8026,6 @@ static size_t ZSTD_rust_sequenceApi_init(
return 0; return 0;
} }
static void ZSTD_rust_sequenceApi_updateChecksum(
void* context, const void* src, size_t srcSize)
{
ZSTD_CCtx* const cctx = (ZSTD_CCtx*)context;
(void)XXH64_update(&cctx->xxhState, src, srcSize);
}
static U32 ZSTD_rust_sequenceApi_digestChecksum(void* context)
{
ZSTD_CCtx const* const cctx = (ZSTD_CCtx const*)context;
return (U32)XXH64_digest(&cctx->xxhState);
}
static void ZSTD_rust_sequenceApi_writeChecksum(
void* context, void* dst, U32 checksum)
{
(void)context;
DEBUGLOG(4, "Write checksum : %08X", (unsigned)checksum);
MEM_writeLE32((char*)dst, checksum);
}
static void ZSTD_rust_sequenceApi_initState( static void ZSTD_rust_sequenceApi_initState(
ZSTD_rust_sequenceApiState* state, ZSTD_rust_sequenceApiState* state,
ZSTD_CCtx* cctx, ZSTD_CCtx* cctx,
@@ -8068,9 +8036,7 @@ static void ZSTD_rust_sequenceApi_initState(
state->sequenceState = sequenceState; state->sequenceState = sequenceState;
state->sequenceLiteralsState = sequenceLiteralsState; state->sequenceLiteralsState = sequenceLiteralsState;
state->init = ZSTD_rust_sequenceApi_init; state->init = ZSTD_rust_sequenceApi_init;
state->updateChecksum = ZSTD_rust_sequenceApi_updateChecksum; state->checksumState = &cctx->xxhState;
state->digestChecksum = ZSTD_rust_sequenceApi_digestChecksum;
state->writeChecksum = ZSTD_rust_sequenceApi_writeChecksum;
state->checksumFlag = 0; state->checksumFlag = 0;
state->blockDelimiters = 0; state->blockDelimiters = 0;
state->validateSequences = 0; state->validateSequences = 0;
+26 -33
View File
@@ -15,7 +15,8 @@
use crate::common::MINMATCH; use crate::common::MINMATCH;
use crate::errors::{ERR_isError, ZstdErrorCode, ERROR}; use crate::errors::{ERR_isError, ZstdErrorCode, ERROR};
use crate::xxhash::{XXH64_digest, XXH64_state_t}; use crate::mem::MEM_writeLE32;
use crate::xxhash::{XXH64_digest, XXH64_state_t, XXH64_update};
#[cfg(not(test))] #[cfg(not(test))]
use crate::zstd_compress_api::ZSTD_compressBound; use crate::zstd_compress_api::ZSTD_compressBound;
use crate::zstd_compress_frame::{ use crate::zstd_compress_frame::{
@@ -3739,25 +3740,21 @@ const _: () = {
type SequenceApiInitFn = type SequenceApiInitFn =
unsafe extern "C" fn(*mut c_void, usize, *mut ZSTD_rust_sequenceApiState) -> usize; unsafe extern "C" fn(*mut c_void, usize, *mut ZSTD_rust_sequenceApiState) -> usize;
type SequenceApiUpdateChecksumFn = unsafe extern "C" fn(*mut c_void, *const c_void, usize);
type SequenceApiDigestChecksumFn = unsafe extern "C" fn(*mut c_void) -> c_uint;
type SequenceApiWriteChecksumFn = unsafe extern "C" fn(*mut c_void, *mut c_void, c_uint);
/// Explicit projection for the public sequence-compression API orchestration. /// Explicit projection for the public sequence-compression API orchestration.
/// ///
/// Rust owns validation ordering, frame-header/checksum sequencing, and /// Rust owns validation ordering, frame-header/checksum sequencing, and
/// output accounting. Frame-header parameters are projected as scalars; C /// output accounting. Frame-header parameters are projected as scalars; C
/// retains the private CCtx, sequence-store, block, and checksum layouts /// retains the private CCtx and sequence-store/block layouts through the
/// through the two block-state projections and callbacks. /// initialization callback and two block-state projections. The XXH64 state
/// is projected directly so checksum updates and serialization stay in Rust.
#[repr(C)] #[repr(C)]
pub struct ZSTD_rust_sequenceApiState { pub struct ZSTD_rust_sequenceApiState {
callback_context: *mut c_void, callback_context: *mut c_void,
sequence_state: *mut ZSTD_rust_sequenceCompressionState, sequence_state: *mut ZSTD_rust_sequenceCompressionState,
sequence_literals_state: *mut ZSTD_rust_sequenceLiteralsState, sequence_literals_state: *mut ZSTD_rust_sequenceLiteralsState,
init: SequenceApiInitFn, init: SequenceApiInitFn,
update_checksum: SequenceApiUpdateChecksumFn, checksum_state: *mut XXH64_state_t,
digest_checksum: SequenceApiDigestChecksumFn,
write_checksum: SequenceApiWriteChecksumFn,
checksum_flag: c_int, checksum_flag: c_int,
block_delimiters: c_int, block_delimiters: c_int,
validate_sequences: c_int, validate_sequences: c_int,
@@ -3775,40 +3772,38 @@ const _: () = {
offset_of!(ZSTD_rust_sequenceApiState, sequence_literals_state) == 2 * size_of::<usize>() offset_of!(ZSTD_rust_sequenceApiState, sequence_literals_state) == 2 * size_of::<usize>()
); );
assert!(offset_of!(ZSTD_rust_sequenceApiState, init) == 3 * size_of::<usize>()); assert!(offset_of!(ZSTD_rust_sequenceApiState, init) == 3 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_sequenceApiState, update_checksum) == 4 * size_of::<usize>()); assert!(offset_of!(ZSTD_rust_sequenceApiState, checksum_state) == 4 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_sequenceApiState, digest_checksum) == 5 * size_of::<usize>()); assert!(offset_of!(ZSTD_rust_sequenceApiState, checksum_flag) == size_of::<[usize; 5]>());
assert!(offset_of!(ZSTD_rust_sequenceApiState, write_checksum) == 6 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_sequenceApiState, checksum_flag) == size_of::<[usize; 7]>());
assert!( assert!(
offset_of!(ZSTD_rust_sequenceApiState, block_delimiters) offset_of!(ZSTD_rust_sequenceApiState, block_delimiters)
== size_of::<[usize; 7]>() + size_of::<c_int>() == size_of::<[usize; 5]>() + size_of::<c_int>()
); );
assert!( assert!(
offset_of!(ZSTD_rust_sequenceApiState, validate_sequences) offset_of!(ZSTD_rust_sequenceApiState, validate_sequences)
== size_of::<[usize; 7]>() + size_of::<[c_int; 2]>() == size_of::<[usize; 5]>() + size_of::<[c_int; 2]>()
); );
assert!( assert!(
offset_of!(ZSTD_rust_sequenceApiState, no_dict_id_flag) offset_of!(ZSTD_rust_sequenceApiState, no_dict_id_flag)
== size_of::<[usize; 7]>() + size_of::<[c_int; 3]>() == size_of::<[usize; 5]>() + size_of::<[c_int; 3]>()
); );
assert!( assert!(
offset_of!(ZSTD_rust_sequenceApiState, content_size_flag) offset_of!(ZSTD_rust_sequenceApiState, content_size_flag)
== size_of::<[usize; 7]>() + size_of::<[c_int; 4]>() == size_of::<[usize; 5]>() + size_of::<[c_int; 4]>()
); );
assert!( assert!(
offset_of!(ZSTD_rust_sequenceApiState, format) offset_of!(ZSTD_rust_sequenceApiState, format)
== size_of::<[usize; 7]>() + size_of::<[c_int; 5]>() == size_of::<[usize; 5]>() + size_of::<[c_int; 5]>()
); );
assert!( assert!(
offset_of!(ZSTD_rust_sequenceApiState, window_log) offset_of!(ZSTD_rust_sequenceApiState, window_log)
== size_of::<[usize; 7]>() + size_of::<[c_int; 6]>() == size_of::<[usize; 5]>() + size_of::<[c_int; 6]>()
); );
assert!( assert!(
offset_of!(ZSTD_rust_sequenceApiState, dict_id) offset_of!(ZSTD_rust_sequenceApiState, dict_id)
== size_of::<[usize; 7]>() + size_of::<[c_int; 6]>() + size_of::<c_uint>() == size_of::<[usize; 5]>() + size_of::<[c_int; 6]>() + size_of::<c_uint>()
); );
assert!( assert!(
size_of::<ZSTD_rust_sequenceApiState>() == if size_of::<usize>() == 8 { 88 } else { 60 } size_of::<ZSTD_rust_sequenceApiState>() == if size_of::<usize>() == 8 { 72 } else { 52 }
); );
}; };
@@ -8263,9 +8258,7 @@ unsafe fn sequence_api_prepare(
|| state.sequence_state.is_null() || state.sequence_state.is_null()
|| state.sequence_literals_state.is_null() || state.sequence_literals_state.is_null()
|| state.init as usize == 0 || state.init as usize == 0
|| state.update_checksum as usize == 0 || state.checksum_state.is_null()
|| state.digest_checksum as usize == 0
|| state.write_checksum as usize == 0
{ {
return Err(ERROR(ZstdErrorCode::Generic)); return Err(ERROR(ZstdErrorCode::Generic));
} }
@@ -8337,13 +8330,11 @@ unsafe fn sequence_api_append_frame_checksum(
/* Keep the original ordering: digest the checksum before checking the /* Keep the original ordering: digest the checksum before checking the
* remaining destination capacity. */ * remaining destination capacity. */
let checksum = unsafe { (state.digest_checksum)(state.callback_context) }; let checksum = unsafe { XXH64_digest(state.checksum_state) as c_uint };
if dst_capacity < 4 { if dst_capacity < 4 {
return ERROR(ZstdErrorCode::DstSizeTooSmall); return ERROR(ZstdErrorCode::DstSizeTooSmall);
} }
unsafe { unsafe { MEM_writeLE32(op.cast(), checksum) };
(state.write_checksum)(state.callback_context, op.cast(), checksum);
}
*c_size = c_size.wrapping_add(4); *c_size = c_size.wrapping_add(4);
0 0
} }
@@ -8351,10 +8342,10 @@ unsafe fn sequence_api_append_frame_checksum(
/// Rust-owned orchestration for `ZSTD_compressSequences`. /// Rust-owned orchestration for `ZSTD_compressSequences`.
/// ///
/// The C wrapper provides the post-initialization block-state projection and /// The C wrapper provides the post-initialization block-state projection and
/// callbacks for the private CCtx/checksum operations; frame-header fields are /// callback for private CCtx initialization; frame-header fields and the
/// projected as scalars. Rust preserves the public ordering: initialize, /// checksum state are projected directly. Rust preserves the public ordering:
/// write the frame header, update the input checksum, emit blocks, then append /// initialize, write the frame header, update the input checksum, emit blocks,
/// the frame checksum. /// then append the frame checksum.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn ZSTD_rust_compressSequences( pub unsafe extern "C" fn ZSTD_rust_compressSequences(
state: *mut ZSTD_rust_sequenceApiState, state: *mut ZSTD_rust_sequenceApiState,
@@ -8385,7 +8376,9 @@ pub unsafe extern "C" fn ZSTD_rust_compressSequences(
} }
if plan.update_input_checksum && src_size != 0 { if plan.update_input_checksum && src_size != 0 {
unsafe { (state.update_checksum)(state.callback_context, src, src_size) }; unsafe {
let _ = XXH64_update(state.checksum_state, src, src_size);
}
} }
let block_size = unsafe { let block_size = unsafe {