feat(mt): move frame checksum finalization to Rust

The MT flush path already delegated checksum serialization to Rust but still
computed XXH64_digest in C. Pass the live C-owned XXH64 state through the
checked projection and let Rust finalize the digest before the existing
little-endian write. C retains serial state storage and job/flush mutation.

Test Plan:
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo check --tests (rust)
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo clippy --tests -- -A clippy::manual-bits -D warnings (rust)
- ulimit -v 41943040; make -j1
- ulimit -v 41943040; make -j1 -C tests test
- Focused cargo test compiles but remains link-blocked by existing C-owned decompression-view symbols.
This commit is contained in:
2026-07-22 00:13:58 +02:00
parent b72c51216b
commit e3d1f7057d
2 changed files with 47 additions and 19 deletions
+3 -5
View File
@@ -466,7 +466,7 @@ typedef struct {
void* dst; void* dst;
size_t dstCapacity; size_t dstCapacity;
size_t cSize; size_t cSize;
U32 checksum; const XXH64_state_t* checksumState;
} ZSTDMT_RustFrameChecksumProjection; } ZSTDMT_RustFrameChecksumProjection;
typedef char ZSTDMT_frame_checksum_projection_layout[ typedef char ZSTDMT_frame_checksum_projection_layout[
(offsetof(ZSTDMT_RustFrameChecksumProjection, dst) == 0 (offsetof(ZSTDMT_RustFrameChecksumProjection, dst) == 0
@@ -474,9 +474,8 @@ typedef char ZSTDMT_frame_checksum_projection_layout[
== sizeof(void*) == sizeof(void*)
&& offsetof(ZSTDMT_RustFrameChecksumProjection, cSize) && offsetof(ZSTDMT_RustFrameChecksumProjection, cSize)
== 2 * sizeof(void*) == 2 * sizeof(void*)
&& offsetof(ZSTDMT_RustFrameChecksumProjection, checksum) && offsetof(ZSTDMT_RustFrameChecksumProjection, checksumState)
== 3 * sizeof(void*) == 3 * sizeof(void*)
&& sizeof(U32) == 4
&& sizeof(ZSTDMT_RustFrameChecksumProjection) && sizeof(ZSTDMT_RustFrameChecksumProjection)
== 4 * sizeof(void*)) == 4 * sizeof(void*))
? 1 : -1]; ? 1 : -1];
@@ -3440,12 +3439,11 @@ static void ZSTDMT_addFrameChecksum(void* opaque, unsigned jobID,
{ {
ZSTDMT_CCtx* const mtctx = (ZSTDMT_CCtx*)opaque; ZSTDMT_CCtx* const mtctx = (ZSTDMT_CCtx*)opaque;
ZSTDMT_jobDescription* const job = &mtctx->jobs[jobID]; ZSTDMT_jobDescription* const job = &mtctx->jobs[jobID];
U32 const checksum = (U32)XXH64_digest(&mtctx->serial.xxhState);
ZSTDMT_RustFrameChecksumProjection const checksumProjection = { ZSTDMT_RustFrameChecksumProjection const checksumProjection = {
job->dstBuff.start, job->dstBuff.start,
job->dstBuff.capacity, job->dstBuff.capacity,
job->cSize, job->cSize,
checksum &mtctx->serial.xxhState
}; };
size_t const cSize = ZSTDMT_rust_writeFrameChecksum(&checksumProjection); size_t const cSize = ZSTDMT_rust_writeFrameChecksum(&checksumProjection);
+44 -14
View File
@@ -23,7 +23,7 @@ use std::sync::Mutex;
use crate::bits::ZSTD_highbit32; use crate::bits::ZSTD_highbit32;
use crate::errors::{ERR_isError, ZstdErrorCode, ERROR}; use crate::errors::{ERR_isError, ZstdErrorCode, ERROR};
use crate::mem::MEM_writeLE32; use crate::mem::MEM_writeLE32;
use crate::xxhash::{XXH64_state_t, XXH64_update}; use crate::xxhash::{XXH64_digest, XXH64_state_t, XXH64_update};
use crate::zstd_compress::{ZSTD_frameProgression, ZSTD_rust_invalidateRepCodes}; use crate::zstd_compress::{ZSTD_frameProgression, ZSTD_rust_invalidateRepCodes};
use crate::zstd_compress_frame::ZSTD_rust_writeFrameHeader; use crate::zstd_compress_frame::ZSTD_rust_writeFrameHeader;
use crate::zstd_compress_params::{ use crate::zstd_compress_params::{
@@ -1893,23 +1893,22 @@ pub struct ZSTDMT_flushJobProjection {
} }
/// Narrow projection for serializing the C-owned frame checksum. C retains /// Narrow projection for serializing the C-owned frame checksum. C retains
/// the serial XXH64 state and computes the checksum; Rust owns only the /// the serial XXH64 state; Rust owns digest finalization, the deterministic
/// deterministic little-endian write and destination policy. /// little-endian write, and destination policy.
#[repr(C)] #[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct ZSTDMT_frameChecksumProjection { pub struct ZSTDMT_frameChecksumProjection {
pub dst: *mut c_void, pub dst: *mut c_void,
pub dstCapacity: usize, pub dstCapacity: usize,
pub cSize: usize, pub cSize: usize,
pub checksum: c_uint, pub checksumState: *const XXH64_state_t,
} }
const _: () = { const _: () = {
assert!(offset_of!(ZSTDMT_frameChecksumProjection, dst) == 0); assert!(offset_of!(ZSTDMT_frameChecksumProjection, dst) == 0);
assert!(offset_of!(ZSTDMT_frameChecksumProjection, dstCapacity) == size_of::<usize>()); assert!(offset_of!(ZSTDMT_frameChecksumProjection, dstCapacity) == size_of::<usize>());
assert!(offset_of!(ZSTDMT_frameChecksumProjection, cSize) == 2 * size_of::<usize>()); assert!(offset_of!(ZSTDMT_frameChecksumProjection, cSize) == 2 * size_of::<usize>());
assert!(offset_of!(ZSTDMT_frameChecksumProjection, checksum) == 3 * size_of::<usize>()); assert!(offset_of!(ZSTDMT_frameChecksumProjection, checksumState) == 3 * size_of::<usize>());
assert!(size_of::<c_uint>() == 4);
assert!(size_of::<ZSTDMT_frameChecksumProjection>() == 4 * size_of::<usize>()); assert!(size_of::<ZSTDMT_frameChecksumProjection>() == 4 * size_of::<usize>());
}; };
@@ -1921,11 +1920,15 @@ fn write_frame_checksum(projection: &ZSTDMT_frameChecksumProjection) -> usize {
{ {
return ERROR(ZstdErrorCode::DstSizeTooSmall); return ERROR(ZstdErrorCode::DstSizeTooSmall);
} }
if projection.checksumState.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let checksum = unsafe { XXH64_digest(projection.checksumState) as c_uint };
unsafe { unsafe {
MEM_writeLE32( MEM_writeLE32(
projection.dst.cast::<u8>().add(projection.cSize).cast(), projection.dst.cast::<u8>().add(projection.cSize).cast(),
projection.checksum, checksum,
); );
} }
projection.cSize + 4 projection.cSize + 4
@@ -5966,7 +5969,7 @@ pub unsafe extern "C" fn ZSTDMT_rust_cctx_pool_release(pool: *mut RustCCtxPool,
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::xxhash::{XXH64, XXH64_digest, XXH64_reset}; use crate::xxhash::{XXH64_digest, XXH64_reset, XXH64_update, XXH64};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::{cell::RefCell, rc::Rc}; use std::{cell::RefCell, rc::Rc};
@@ -9321,46 +9324,73 @@ mod tests {
#[test] #[test]
fn frame_checksum_is_serialized_little_endian() { fn frame_checksum_is_serialized_little_endian() {
let mut dst = [0xa5u8; 8]; let mut dst = [0xa5u8; 8];
let input = b"mt frame checksum";
let mut checksum_state = unsafe { MaybeUninit::<XXH64_state_t>::zeroed().assume_init() };
unsafe {
assert_eq!(XXH64_reset(&mut checksum_state, 0), 0);
assert_eq!(
XXH64_update(&mut checksum_state, input.as_ptr().cast(), input.len()),
0
);
}
let checksum = unsafe { XXH64(input.as_ptr().cast(), input.len(), 0) as u32 };
let projection = ZSTDMT_frameChecksumProjection { let projection = ZSTDMT_frameChecksumProjection {
dst: dst.as_mut_ptr().cast(), dst: dst.as_mut_ptr().cast(),
dstCapacity: dst.len(), dstCapacity: dst.len(),
cSize: 4, cSize: 4,
checksum: 0x7856_3412, checksumState: &checksum_state,
}; };
assert_eq!(write_frame_checksum(&projection), 8); assert_eq!(write_frame_checksum(&projection), 8);
assert_eq!(&dst[..4], &[0xa5; 4]); assert_eq!(&dst[..4], &[0xa5; 4]);
assert_eq!(&dst[4..], &[0x12, 0x34, 0x56, 0x78]); assert_eq!(&dst[4..], &checksum.to_le_bytes());
} }
#[test] #[test]
fn frame_checksum_accepts_exact_destination_capacity() { fn frame_checksum_accepts_exact_destination_capacity() {
let mut dst = [0xa5u8; 6]; let mut dst = [0xa5u8; 6];
let input = b"exact capacity";
let mut checksum_state = unsafe { MaybeUninit::<XXH64_state_t>::zeroed().assume_init() };
unsafe {
assert_eq!(XXH64_reset(&mut checksum_state, 0), 0);
assert_eq!(
XXH64_update(&mut checksum_state, input.as_ptr().cast(), input.len()),
0
);
}
let projection = ZSTDMT_frameChecksumProjection { let projection = ZSTDMT_frameChecksumProjection {
dst: dst.as_mut_ptr().cast(), dst: dst.as_mut_ptr().cast(),
dstCapacity: dst.len(), dstCapacity: dst.len(),
cSize: 2, cSize: 2,
checksum: 0x1122_3344, checksumState: &checksum_state,
}; };
assert_eq!(write_frame_checksum(&projection), dst.len()); assert_eq!(write_frame_checksum(&projection), dst.len());
assert_eq!(&dst, &[0xa5, 0xa5, 0x44, 0x33, 0x22, 0x11]); assert_eq!(&dst[..2], &[0xa5, 0xa5]);
assert_eq!(
&dst[2..],
&(unsafe { XXH64(input.as_ptr().cast(), input.len(), 0) as u32 }).to_le_bytes()
);
} }
#[test] #[test]
fn frame_checksum_rejects_too_small_or_null_destination() { fn frame_checksum_rejects_too_small_or_null_destination() {
let mut dst = [0xa5u8; 8]; let mut dst = [0xa5u8; 8];
let mut checksum_state = unsafe { MaybeUninit::<XXH64_state_t>::zeroed().assume_init() };
unsafe {
assert_eq!(XXH64_reset(&mut checksum_state, 0), 0);
}
let too_small = ZSTDMT_frameChecksumProjection { let too_small = ZSTDMT_frameChecksumProjection {
dst: dst.as_mut_ptr().cast(), dst: dst.as_mut_ptr().cast(),
dstCapacity: 7, dstCapacity: 7,
cSize: 4, cSize: 4,
checksum: 0x0102_0304, checksumState: &checksum_state,
}; };
let null_destination = ZSTDMT_frameChecksumProjection { let null_destination = ZSTDMT_frameChecksumProjection {
dst: ptr::null_mut(), dst: ptr::null_mut(),
dstCapacity: 4, dstCapacity: 4,
cSize: 0, cSize: 0,
checksum: 0x0102_0304, checksumState: &checksum_state,
}; };
assert_eq!( assert_eq!(