feat(compress): project split discovery into Rust

Replace the frame-chunk split-discovery callback with an explicit projection of the sequence stores, block-state pointer slots, entropy metadata, parameters, and workspace required by the Rust estimator. Rust now invokes the existing split estimator directly before the Rust-owned partition loop, while C retains only private block-split storage and projection assembly.

Keep the block-state pointer slots live through estimation instead of capturing entropy-table addresses during frame preparation. Sequence-store work can change which compressed-block state is active, and following the slots preserves the original C behavior for dictionary-backed streaming compression.

Test Plan:
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo fmt --manifest-path rust/Cargo.toml --all -- --check
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo test --manifest-path rust/Cargo.toml --lib
- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo clippy --manifest-path rust/Cargo.toml --all-targets -- -D warnings
- 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 02:07:57 +02:00
parent 59b3c7021b
commit 93eaef18b8
2 changed files with 200 additions and 63 deletions
+107 -24
View File
@@ -21,6 +21,7 @@ use crate::xxhash::XXH64_reset;
use crate::xxhash::{XXH64_digest, XXH64_state_t, XXH64_update};
#[cfg(not(test))]
use crate::zstd_compress_api::ZSTD_compressBound;
use crate::zstd_compress_block_split::ZSTD_rust_deriveBlockSplits;
use crate::zstd_compress_frame::{
write_raw_block, ZSTD_rust_noCompressBlock, ZSTD_rust_rleCompressBlock,
ZSTD_rust_writeBlockHeader, ZSTD_rust_writeEpilogue, ZSTD_rust_writeFrameHeader,
@@ -42,14 +43,15 @@ use crate::zstd_compress_params_api::{
use crate::zstd_compress_sequences::SeqDef;
use crate::zstd_compress_stats::{
update_rep, SeqCollector, SeqStore_t, ZSTD_Sequence, ZSTD_SequencePosition,
ZSTD_compressedBlockState_t, ZSTD_entropyCTables_t, ZSTD_rust_confirmRepcodesAndEntropyTables,
ZSTD_rust_convertSequencesNoRepcodes, ZSTD_rust_copyBlockSequences,
ZSTD_rust_countSeqStoreLiteralsBytes, ZSTD_rust_countSeqStoreMatchBytes,
ZSTD_rust_deriveSeqStoreChunk, ZSTD_rust_determineBlockSize, ZSTD_rust_entropyCompressSeqStore,
ZSTD_rust_entropyCompressSeqStore_internal, ZSTD_rust_fastSequenceLengthSum,
ZSTD_rust_finalizeOffBase, ZSTD_rust_get1BlockSummary, ZSTD_rust_isRLE, ZSTD_rust_maybeRLE,
ZSTD_rust_postProcessSequenceProducerResult, ZSTD_rust_resetCompressedBlockState,
ZSTD_rust_resetSeqStore, ZSTD_rust_seqStore_resolveOffCodes, ZSTD_rust_storeLastLiterals,
ZSTD_compressedBlockState_t, ZSTD_entropyCTablesMetadata_t, ZSTD_entropyCTables_t,
ZSTD_rust_confirmRepcodesAndEntropyTables, ZSTD_rust_convertSequencesNoRepcodes,
ZSTD_rust_copyBlockSequences, ZSTD_rust_countSeqStoreLiteralsBytes,
ZSTD_rust_countSeqStoreMatchBytes, ZSTD_rust_deriveSeqStoreChunk, ZSTD_rust_determineBlockSize,
ZSTD_rust_entropyCompressSeqStore, ZSTD_rust_entropyCompressSeqStore_internal,
ZSTD_rust_fastSequenceLengthSum, ZSTD_rust_finalizeOffBase, ZSTD_rust_get1BlockSummary,
ZSTD_rust_isRLE, ZSTD_rust_maybeRLE, ZSTD_rust_postProcessSequenceProducerResult,
ZSTD_rust_resetCompressedBlockState, ZSTD_rust_resetSeqStore,
ZSTD_rust_seqStore_resolveOffCodes, ZSTD_rust_storeLastLiterals,
ZSTD_rust_transferSequencesNoDelim, ZSTD_rust_transferSequencesWBlockDelim,
ZSTD_rust_validateSeqStore, ZSTD_LLT_LITERAL_LENGTH, ZSTD_LLT_MATCH_LENGTH,
};
@@ -180,7 +182,6 @@ type FrameChunkPrepareOverflowFn = unsafe extern "C" fn(*mut c_void, *const c_vo
type FrameChunkPrepareWindowFn = unsafe extern "C" fn(*mut c_void, *const c_void, usize, c_uint);
type FrameChunkCompressFn =
unsafe extern "C" fn(*mut c_void, *mut c_void, usize, *const c_void, usize, c_uint) -> usize;
type FrameChunkDeriveSplitsFn = unsafe extern "C" fn(*mut c_void, *mut u32, c_uint) -> usize;
type BuildSeqStoreSkipFn = unsafe extern "C" fn(*mut c_void, usize);
type BuildSeqStorePrepareFn = unsafe extern "C" fn(*mut c_void, *const c_void, usize);
@@ -560,6 +561,62 @@ const _: () = {
assert!(size_of::<ZSTD_rust_compressContinueBlockState>() == 2 * size_of::<usize>());
};
/// Explicit projection of the state used by the block-split estimator.
///
/// C retains the private block-split context and owns projection assembly;
/// Rust owns the estimator implementation and receives only its required
/// sequence stores, entropy tables, parameters, and workspace.
#[repr(C)]
pub struct ZSTD_rust_deriveBlockSplitsState {
original_seq_store: *const SeqStore_t,
full_seq_store_chunk: *mut SeqStore_t,
first_half_seq_store: *mut SeqStore_t,
second_half_seq_store: *mut SeqStore_t,
prev_c_block: *mut *mut ZSTD_compressedBlockState_t,
next_c_block: *mut *mut ZSTD_compressedBlockState_t,
entropy_metadata: *mut ZSTD_entropyCTablesMetadata_t,
workspace: *mut c_void,
workspace_size: usize,
strategy: c_int,
disable_literal_compression: c_int,
}
const _: () = {
assert!(offset_of!(ZSTD_rust_deriveBlockSplitsState, original_seq_store) == 0);
assert!(
offset_of!(ZSTD_rust_deriveBlockSplitsState, full_seq_store_chunk) == size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_deriveBlockSplitsState, first_half_seq_store)
== 2 * size_of::<usize>()
);
assert!(
offset_of!(ZSTD_rust_deriveBlockSplitsState, second_half_seq_store)
== 3 * size_of::<usize>()
);
assert!(offset_of!(ZSTD_rust_deriveBlockSplitsState, prev_c_block) == 4 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_deriveBlockSplitsState, next_c_block) == 5 * size_of::<usize>());
assert!(
offset_of!(ZSTD_rust_deriveBlockSplitsState, entropy_metadata) == 6 * size_of::<usize>()
);
assert!(offset_of!(ZSTD_rust_deriveBlockSplitsState, workspace) == 7 * size_of::<usize>());
assert!(offset_of!(ZSTD_rust_deriveBlockSplitsState, workspace_size) == usize::BITS as usize);
assert!(
offset_of!(ZSTD_rust_deriveBlockSplitsState, strategy)
== usize::BITS as usize + size_of::<usize>()
);
assert!(
offset_of!(
ZSTD_rust_deriveBlockSplitsState,
disable_literal_compression
) == usize::BITS as usize + size_of::<usize>() + size_of::<c_int>()
);
assert!(
size_of::<ZSTD_rust_deriveBlockSplitsState>()
== usize::BITS as usize + size_of::<usize>() + 2 * size_of::<c_int>()
);
};
/// Explicit projection of the state used by `ZSTD_compress_frameChunk`.
///
/// The Rust side owns the per-frame block loop and its savings/dispatch
@@ -589,7 +646,7 @@ pub struct ZSTD_rust_frameChunkState {
compress_internal_state: *const ZSTD_rust_compressContinueBlockState,
compress_target_state: *const ZSTD_rust_targetCBlockSizeState,
compress_split_state: *const ZSTD_rust_splitBlockState,
derive_block_splits: Option<FrameChunkDeriveSplitsFn>,
derive_block_splits_state: *const ZSTD_rust_deriveBlockSplitsState,
}
const _: () = {
@@ -642,7 +699,7 @@ const _: () = {
== 13 * size_of::<usize>() + size_of::<c_longlong>() + 6 * size_of::<c_int>()
);
assert!(
offset_of!(ZSTD_rust_frameChunkState, derive_block_splits)
offset_of!(ZSTD_rust_frameChunkState, derive_block_splits_state)
== 14 * size_of::<usize>() + size_of::<c_longlong>() + 6 * size_of::<c_int>()
);
assert!(
@@ -820,14 +877,40 @@ unsafe fn compress_frame_chunk_body_with(
if sequence_count < 0 {
return ERROR(ZstdErrorCode::Generic);
}
let Some(derive_block_splits) = state.derive_block_splits else {
if state.derive_block_splits_state.is_null() {
return ERROR(ZstdErrorCode::Generic);
};
}
let derive_state = unsafe { &*state.derive_block_splits_state };
if derive_state.original_seq_store.is_null()
|| derive_state.full_seq_store_chunk.is_null()
|| derive_state.first_half_seq_store.is_null()
|| derive_state.second_half_seq_store.is_null()
|| derive_state.prev_c_block.is_null()
|| derive_state.next_c_block.is_null()
|| derive_state.entropy_metadata.is_null()
{
return ERROR(ZstdErrorCode::Generic);
}
let prev_c_block = unsafe { *derive_state.prev_c_block };
let next_c_block = unsafe { *derive_state.next_c_block };
if prev_c_block.is_null() || next_c_block.is_null() {
return ERROR(ZstdErrorCode::Generic);
}
let result = unsafe {
derive_block_splits(
state.callback_context,
ZSTD_rust_deriveBlockSplits(
split_state.partitions.cast_mut(),
sequence_count as c_uint,
sequence_count as u32,
derive_state.original_seq_store,
derive_state.full_seq_store_chunk,
derive_state.first_half_seq_store,
derive_state.second_half_seq_store,
ptr::addr_of!((*prev_c_block).entropy),
ptr::addr_of_mut!((*next_c_block).entropy),
derive_state.strategy,
derive_state.disable_literal_compression,
derive_state.entropy_metadata,
derive_state.workspace,
derive_state.workspace_size,
)
};
if ERR_isError(result) {
@@ -5304,9 +5387,9 @@ fn split_single_block_state(
/// Rust implementation of the post-split partition loop.
///
/// C retains `ZSTD_deriveBlockSplits()` and the private `ZSTD_CCtx`; Rust
/// receives only the sequence-store views and block-emission state needed to
/// reproduce the original loop.
/// C retains the private `ZSTD_CCtx` and block-split storage; Rust receives
/// projected estimator inputs alongside the sequence-store views and owns the
/// partition accounting, repcode simulation, and block emission.
#[allow(clippy::too_many_arguments)]
unsafe fn compress_block_split_body_with(
state: &ZSTD_rust_splitBlockState,
@@ -10535,7 +10618,7 @@ mod tests {
compress_internal_state: ptr::null(),
compress_target_state: ptr::null(),
compress_split_state: ptr::null(),
derive_block_splits: None,
derive_block_splits_state: ptr::null(),
});
let frame_chunk_state = context
.frame_chunk_state
@@ -11414,7 +11497,7 @@ mod tests {
compress_internal_state: ptr::null(),
compress_target_state: ptr::null(),
compress_split_state: ptr::null(),
derive_block_splits: None,
derive_block_splits_state: ptr::null(),
}
}
@@ -11757,7 +11840,7 @@ mod tests {
compress_internal_state: ptr::null(),
compress_target_state: ptr::null(),
compress_split_state: ptr::null(),
derive_block_splits: None,
derive_block_splits_state: ptr::null(),
});
let frame_chunk_state = context
.frame_chunk_state
@@ -12246,7 +12329,7 @@ mod tests {
compress_internal_state: ptr::null(),
compress_target_state: ptr::null(),
compress_split_state: ptr::null(),
derive_block_splits: None,
derive_block_splits_state: ptr::null(),
});
let continue_frame_chunk_state = context
.continue_frame_chunk_state
@@ -12274,7 +12357,7 @@ mod tests {
compress_internal_state: ptr::null(),
compress_target_state: ptr::null(),
compress_split_state: ptr::null(),
derive_block_splits: None,
derive_block_splits_state: ptr::null(),
});
let end_frame_chunk_state = context
.end_frame_chunk_state