feat(compress): project MT frame headers into Rust

Pass the non-first multithreaded frame-header operation through an explicit
scalar projection so Rust writes the header, advances the compression stage,
and invalidates repcodes without calling the public C continue wrapper.
Retain job descriptors, pools, synchronization, and CCtx initialization in C.

Test Plan:
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo fmt --all -- --check
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo test --lib zstdmt_compress::tests::compression_job -- --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 01:24:35 +02:00
parent ad49e4466c
commit 6e9fdcf305
2 changed files with 254 additions and 31 deletions
+176 -7
View File
@@ -21,7 +21,8 @@ use std::sync::Mutex;
use crate::bits::ZSTD_highbit32;
use crate::errors::{ERR_isError, ZstdErrorCode, ERROR};
use crate::zstd_compress::ZSTD_frameProgression;
use crate::zstd_compress::{ZSTD_frameProgression, ZSTD_rust_invalidateRepCodes};
use crate::zstd_compress_frame::ZSTD_rust_writeFrameHeader;
const ZSTDMT_JOBLOG_MAX: c_uint = if mem::size_of::<usize>() == 4 { 29 } else { 30 };
const ZSTD_WINDOWLOG_MAX: c_uint = if mem::size_of::<usize>() == 4 { 30 } else { 31 };
@@ -68,13 +69,145 @@ pub struct ZSTDMT_chunkProcessResult {
/// Scalar job state used by the Rust compression-job scheduler.
///
/// The job descriptor, pools, synchronization, and codec state remain
/// private to C. Rust uses only the frame-position flags to choose the
/// sequencing and non-first-job header stages.
/// private to C. Rust uses the frame-position flags and a scalar header
/// projection to choose the non-first-job header stage.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct ZSTDMT_compressionJobProjection {
pub firstJob: c_uint,
pub lastJob: c_uint,
pub frameHeaderState: *const ZSTDMT_compressionJobFrameHeaderState,
}
/// Scalar projection for the non-first MT job frame header. The destination,
/// stage, and repcodes point at the worker-owned C storage; all header
/// serialization and repcode invalidation happen in Rust.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct ZSTDMT_compressionJobFrameHeaderState {
dst: *mut c_void,
dst_capacity: usize,
stage: *mut c_int,
no_dict_id_flag: c_int,
checksum_flag: c_int,
content_size_flag: c_int,
format: c_int,
window_log: c_uint,
pledged_src_size_plus_one: u64,
dict_id: c_uint,
rep_codes: *mut c_uint,
}
const _: () = {
assert!(offset_of!(ZSTDMT_compressionJobProjection, firstJob) == 0);
assert!(offset_of!(ZSTDMT_compressionJobProjection, lastJob) == size_of::<c_uint>());
assert!(
offset_of!(ZSTDMT_compressionJobProjection, frameHeaderState) == size_of::<[c_uint; 2]>()
);
assert!(
size_of::<ZSTDMT_compressionJobProjection>()
== size_of::<[c_uint; 2]>() + size_of::<usize>()
);
assert!(offset_of!(ZSTDMT_compressionJobFrameHeaderState, dst) == 0);
assert!(offset_of!(ZSTDMT_compressionJobFrameHeaderState, dst_capacity) == size_of::<usize>());
assert!(offset_of!(ZSTDMT_compressionJobFrameHeaderState, stage) == 2 * size_of::<usize>());
assert!(
offset_of!(ZSTDMT_compressionJobFrameHeaderState, no_dict_id_flag)
== 3 * size_of::<usize>()
);
assert!(
offset_of!(ZSTDMT_compressionJobFrameHeaderState, checksum_flag)
== 3 * size_of::<usize>() + size_of::<c_int>()
);
assert!(
offset_of!(ZSTDMT_compressionJobFrameHeaderState, content_size_flag)
== 3 * size_of::<usize>() + 2 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTDMT_compressionJobFrameHeaderState, format)
== 3 * size_of::<usize>() + 3 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTDMT_compressionJobFrameHeaderState, window_log)
== 3 * size_of::<usize>() + 4 * size_of::<c_int>()
);
assert!(
offset_of!(
ZSTDMT_compressionJobFrameHeaderState,
pledged_src_size_plus_one
) == ZSTDMT_HEADER_PLEDGED_OFFSET
);
assert!(
offset_of!(ZSTDMT_compressionJobFrameHeaderState, dict_id)
== ZSTDMT_HEADER_PLEDGED_OFFSET + size_of::<u64>()
);
assert!(
offset_of!(ZSTDMT_compressionJobFrameHeaderState, rep_codes)
== ZSTDMT_HEADER_REP_CODES_OFFSET
);
assert!(
size_of::<ZSTDMT_compressionJobFrameHeaderState>()
== if size_of::<usize>() == 8 { 72 } else { 48 }
);
};
const ZSTD_COMPRESSION_STAGE_CREATED: c_int = 0;
const ZSTD_COMPRESSION_STAGE_INIT: c_int = 1;
const ZSTD_COMPRESSION_STAGE_ONGOING: c_int = 2;
const ZSTDMT_HEADER_PLEDGED_OFFSET: usize = if size_of::<usize>() == 8 {
size_of::<[usize; 6]>()
} else {
size_of::<[usize; 8]>()
};
const ZSTDMT_HEADER_REP_CODES_OFFSET: usize = if size_of::<usize>() == 8 {
size_of::<[usize; 8]>()
} else {
size_of::<[usize; 11]>()
};
#[no_mangle]
pub unsafe extern "C" fn ZSTDMT_rust_writeFrameHeader(
state: *const ZSTDMT_compressionJobFrameHeaderState,
) -> usize {
if state.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let state = unsafe { &*state };
if state.stage.is_null() || state.rep_codes.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
if unsafe { *state.stage } == ZSTD_COMPRESSION_STAGE_CREATED {
return ERROR(ZstdErrorCode::StageWrong);
}
let header_size = if unsafe { *state.stage } == ZSTD_COMPRESSION_STAGE_INIT {
let header_size = unsafe {
ZSTD_rust_writeFrameHeader(
state.dst,
state.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(header_size) {
return header_size;
}
if header_size > state.dst_capacity {
return ERROR(ZstdErrorCode::DstSizeTooSmall);
}
unsafe { *state.stage = ZSTD_COMPRESSION_STAGE_ONGOING };
header_size
} else {
0
};
unsafe { ZSTD_rust_invalidateRepCodes(state.rep_codes) };
header_size
}
pub type ZSTDMT_compressionJobStepFn = unsafe extern "C" fn(*mut c_void) -> usize;
@@ -1116,7 +1249,6 @@ pub unsafe extern "C" fn ZSTDMT_rust_compressionJob(
generateSequences: Option<ZSTDMT_compressionJobVoidFn>,
beginJob: Option<ZSTDMT_compressionJobStepFn>,
applySequences: Option<ZSTDMT_compressionJobVoidFn>,
writeFrameHeader: Option<ZSTDMT_compressionJobStepFn>,
compressJob: Option<ZSTDMT_compressionJobCompressFn>,
traceJob: Option<ZSTDMT_compressionJobVoidFn>,
setError: Option<ZSTDMT_compressionJobErrorFn>,
@@ -1131,7 +1263,6 @@ pub unsafe extern "C" fn ZSTDMT_rust_compressionJob(
Some(generate_sequences),
Some(begin_job),
Some(apply_sequences),
Some(write_frame_header),
Some(compress_job),
Some(trace_job),
Some(set_error),
@@ -1142,7 +1273,6 @@ pub unsafe extern "C" fn ZSTDMT_rust_compressionJob(
generateSequences,
beginJob,
applySequences,
writeFrameHeader,
compressJob,
traceJob,
setError,
@@ -1159,7 +1289,7 @@ pub unsafe extern "C" fn ZSTDMT_rust_compressionJob(
|| unsafe { generate_sequences(opaque) },
|| unsafe { begin_job(opaque) },
|| unsafe { apply_sequences(opaque) },
|| unsafe { write_frame_header(opaque) },
|| unsafe { ZSTDMT_rust_writeFrameHeader(projection.frameHeaderState) },
|last_job| unsafe { compress_job(opaque, last_job) },
|| unsafe { trace_job(opaque) },
|error| unsafe { set_error(opaque, error) },
@@ -3994,6 +4124,43 @@ mod tests {
state.borrow_mut().events.push(event);
}
#[test]
fn compression_job_frame_header_projection_writes_header_and_clears_repcodes() {
let mut stage = ZSTD_COMPRESSION_STAGE_INIT;
let mut rep_codes = [11u32, 22, 33];
let mut output = [0xa5u8; 18];
let state = ZSTDMT_compressionJobFrameHeaderState {
dst: output.as_mut_ptr().cast(),
dst_capacity: output.len(),
stage: &mut stage,
no_dict_id_flag: 0,
checksum_flag: 0,
content_size_flag: 1,
format: 0,
window_log: 20,
pledged_src_size_plus_one: 1,
dict_id: 0,
rep_codes: rep_codes.as_mut_ptr(),
};
let mut created_stage = ZSTD_COMPRESSION_STAGE_CREATED;
let created_state = ZSTDMT_compressionJobFrameHeaderState {
stage: &mut created_stage,
..state
};
let created = unsafe { ZSTDMT_rust_writeFrameHeader(&created_state) };
assert_eq!(created, ERROR(ZstdErrorCode::StageWrong));
assert_eq!(created_stage, ZSTD_COMPRESSION_STAGE_CREATED);
assert_eq!(rep_codes, [11, 22, 33]);
let result = unsafe { ZSTDMT_rust_writeFrameHeader(&state) };
assert_eq!(result, 6);
assert_eq!(stage, ZSTD_COMPRESSION_STAGE_ONGOING);
assert_eq!(rep_codes, [0; 3]);
assert_eq!(&output[..result], &[0x28, 0xb5, 0x2f, 0xfd, 0x20, 0x00]);
assert_eq!(&output[result..], &[0xa5; 12]);
}
#[test]
fn compression_job_runs_first_job_stages_and_reports_final_block() {
let state = Rc::new(RefCell::new(MockCompressionJob::default()));
@@ -4010,6 +4177,7 @@ mod tests {
ZSTDMT_compressionJobProjection {
firstJob: 1,
lastJob: 1,
frameHeaderState: ptr::null(),
},
move || {
record_compression_job_event(&acquire_state, "acquire");
@@ -4250,6 +4418,7 @@ mod tests {
ZSTDMT_compressionJobProjection {
firstJob: 0,
lastJob: 1,
frameHeaderState: ptr::null(),
},
move || {
record_compression_job_event(&acquire_state, "acquire");