diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 2b897631d..81a02e763 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -921,6 +921,9 @@ size_t ZSTD_rust_compressContinue( void* dst, size_t dstCapacity, const void* src, size_t srcSize, U32 frame, U32 lastFrameChunk); +size_t ZSTD_rust_compressContinueBlock( + void* context, void* dst, size_t dstCapacity, + const void* src, size_t srcSize, U32 lastFrameChunk); typedef char ZSTD_rust_compress_continue_state_layout[ (offsetof(ZSTD_rust_compressContinueState, callbackContext) == 0 && offsetof(ZSTD_rust_compressContinueState, windowState) == sizeof(void*) @@ -5422,6 +5425,13 @@ typedef struct { const ZSTD_CCtx_params* params; } ZSTD_rust_overflowCorrectContext; +typedef struct { + const ZSTD_rust_buildSeqStoreState* buildSeqStoreState; + const ZSTD_rust_blockInternalState* blockInternalState; +} ZSTD_rust_compressContinueBlockState; +typedef char ZSTD_rust_compress_continue_block_state_layout[ + (sizeof(ZSTD_rust_compressContinueBlockState) == 2 * sizeof(void*)) ? 1 : -1]; + /* All continue entry points use this synchronous stack projection. The * private CCtx and match-state layouts remain behind the direct field * projections and context-sensitive leaf callbacks. */ @@ -5434,6 +5444,9 @@ typedef struct { ZSTD_rust_frameChunkClampState frameChunkClampState; ZSTD_rust_overflowCorrectContext overflowContext; ZSTD_rust_overflowCorrectState overflowState; + ZSTD_rust_buildSeqStoreState buildSeqStoreState; + ZSTD_rust_blockInternalState blockInternalState; + ZSTD_rust_compressContinueBlockState blockState; } ZSTD_rust_compressContinueContext; static int ZSTD_rust_overflowCorrect_need( @@ -5573,16 +5586,6 @@ static size_t ZSTD_rust_frameChunk_compressInternal( 1 /* frame */); } -static size_t ZSTD_rust_compressContinue_block( - void* context, void* dst, size_t dstCapacity, - const void* src, size_t srcSize, U32 lastFrameChunk) -{ - (void)lastFrameChunk; - return ZSTD_compressBlock_internal( - (ZSTD_CCtx*)context, dst, dstCapacity, src, srcSize, - 0 /* frame */); -} - static void ZSTD_compressContinue_prepare( ZSTD_CCtx* cctx, size_t blockSizeMax, int checkBlockSize, ZSTD_rust_compressContinueContext* context) @@ -5633,6 +5636,21 @@ static void ZSTD_compressContinue_prepare( context->frameChunkState.compressSplit = ZSTD_rust_frameChunk_compressSplit; context->frameChunkState.compressInternal = ZSTD_rust_frameChunk_compressInternal; + ZSTD_initBuildSeqStoreState(cctx, &context->buildSeqStoreState); + context->blockInternalState.seqStore = &cctx->seqStore; + context->blockInternalState.prevCBlock = &cctx->blockState.prevCBlock; + context->blockInternalState.nextCBlock = &cctx->blockState.nextCBlock; + context->blockInternalState.tmpWorkspace = cctx->tmpWorkspace; + context->blockInternalState.tmpWkspSize = cctx->tmpWkspSize; + context->blockInternalState.seqCollector = &cctx->seqCollector; + context->blockInternalState.strategy = (int)cctx->appliedParams.cParams.strategy; + context->blockInternalState.disableLiteralCompression = + ZSTD_literalsCompressionIsDisabled(&cctx->appliedParams); + context->blockInternalState.bmi2 = cctx->bmi2; + context->blockInternalState.isFirstBlock = cctx->isFirstBlock; + context->blockState.buildSeqStoreState = &context->buildSeqStoreState; + context->blockState.blockInternalState = &context->blockInternalState; + context->overflowContext.matchState = ms; context->overflowContext.workspace = &cctx->workspace; context->overflowContext.params = &cctx->appliedParams; @@ -5646,12 +5664,12 @@ static void ZSTD_compressContinue_prepare( context->overflowState.loadedDictEnd = &ms->loadedDictEnd; context->overflowState.dictMatchState = &ms->dictMatchState; - context->state.callbackContext = cctx; + context->state.callbackContext = &context->blockState; context->state.windowState = &context->windowState; context->state.ldmWindowState = &context->ldmWindowState; context->state.overflowState = &context->overflowState; context->state.frameChunkState = &context->frameChunkState; - context->state.compressBlock = ZSTD_rust_compressContinue_block; + context->state.compressBlock = ZSTD_rust_compressContinueBlock; context->state.stage = &cctx->stage; context->state.consumedSrcSize = &cctx->consumedSrcSize; context->state.producedCSize = &cctx->producedCSize; diff --git a/rust/src/zstd_compress.rs b/rust/src/zstd_compress.rs index 4d3db5baf..c79775bd7 100644 --- a/rust/src/zstd_compress.rs +++ b/rust/src/zstd_compress.rs @@ -796,6 +796,24 @@ pub unsafe extern "C" fn ZSTD_rust_compressFrameChunk( type CompressContinueBlockFn = unsafe extern "C" fn(*mut c_void, *mut c_void, usize, *const c_void, usize, c_uint) -> usize; +/// Explicit projection for the deprecated block adapter. Rust builds the +/// sequence store and then enters the already-Rust-owned block body; the +/// projected states retain only the matchfinder and CCtx storage callbacks. +#[repr(C)] +struct ZSTD_rust_compressContinueBlockState { + build_seq_store_state: *const ZSTD_rust_buildSeqStoreState, + block_internal_state: *const ZSTD_rust_blockInternalState, +} + +const _: () = { + assert!(offset_of!(ZSTD_rust_compressContinueBlockState, build_seq_store_state) == 0); + assert!( + offset_of!(ZSTD_rust_compressContinueBlockState, block_internal_state) + == size_of::() + ); + assert!(size_of::() == 2 * size_of::()); +}; + /// Pointer projection for one C-owned `ZSTD_window_t` and the match-state /// fields which follow its update. Each field points directly at the C /// storage so later callbacks observe the updated window immediately. @@ -942,6 +960,39 @@ const ZSTD_COMPRESSION_STAGE_ONGOING: c_int = 2; #[cfg(test)] const ZSTD_COMPRESSION_STAGE_ENDING: c_int = 3; +#[no_mangle] +pub unsafe extern "C" fn ZSTD_rust_compressContinueBlock( + context: *mut c_void, + dst: *mut c_void, + dst_capacity: usize, + src: *const c_void, + src_size: usize, + _last_frame_chunk: c_uint, +) -> usize { + if context.is_null() { + return ERROR(ZstdErrorCode::Generic); + } + let state = unsafe { &*context.cast::() }; + if state.build_seq_store_state.is_null() || state.block_internal_state.is_null() { + return ERROR(ZstdErrorCode::Generic); + } + let bss = unsafe { ZSTD_rust_buildSeqStore(state.build_seq_store_state, src, src_size) }; + if ERR_isError(bss) { + return bss; + } + unsafe { + ZSTD_rust_compressBlockInternalAfterBuild( + state.block_internal_state, + dst, + dst_capacity, + src, + src_size, + 0, + bss as c_int, + ) + } +} + unsafe fn compress_continue_update_window( projection: *const ZSTD_rust_compressContinueWindowProjection, src: *const c_void, @@ -4097,10 +4148,10 @@ const _: () = { /// Explicit projection of the state used by `ZSTD_compressBlock_internal`. /// -/// Sequence-store construction and the no-compress fallback remain in C. -/// Rust owns sequence collection, entropy emission, the legacy first-block -/// RLE gate, and the compressed-block state transitions after the store is -/// ready. +/// Sequence-store construction enters through an explicit build-state +/// projection. Rust owns sequence collection, entropy emission, the legacy +/// first-block RLE gate, and the compressed-block state transitions after the +/// store is ready. #[repr(C)] pub struct ZSTD_rust_blockInternalState { seq_store: *const SeqStore_t, @@ -11525,6 +11576,59 @@ mod tests { } } + #[test] + fn compress_continue_block_builds_small_block_through_projected_states() { + let mut probe = BuildSeqStoreSelectProbe::default(); + let mut seq_store = unsafe { MaybeUninit::::zeroed().assume_init() }; + let mut build_state = build_seq_store_select_test_state(&mut probe, 0, 0, 0, 0); + build_state.seq_store = &mut seq_store; + + let mut prev_block = zeroed_state(); + let mut next_block = zeroed_state(); + let mut prev_c_block = &mut prev_block as *mut _; + let mut next_c_block = &mut next_block as *mut _; + let mut seq_collector = SeqCollector { + collectSequences: 0, + seqStart: ptr::null_mut(), + seqIndex: 0, + maxSequences: 0, + }; + let block_state = ZSTD_rust_blockInternalState { + seq_store: &seq_store, + prev_c_block: &mut prev_c_block, + next_c_block: &mut next_c_block, + tmp_workspace: ptr::null_mut(), + tmp_wksp_size: 0, + seq_collector: &mut seq_collector, + strategy: 0, + disable_literal_compression: 0, + bmi2: 0, + is_first_block: 1, + }; + let state = ZSTD_rust_compressContinueBlockState { + build_seq_store_state: &build_state, + block_internal_state: &block_state, + }; + let source = [0u8; 1]; + let mut output = [0xa5u8; 8]; + + let result = unsafe { + ZSTD_rust_compressContinueBlock( + (&state as *const ZSTD_rust_compressContinueBlockState) + .cast_mut() + .cast(), + output.as_mut_ptr().cast(), + output.len(), + source.as_ptr().cast(), + source.len(), + 0, + ) + }; + + assert_eq!(result, 0); + assert_eq!(output, [0xa5; 8]); + } + #[test] fn compress_continue_starts_frame_and_updates_progression() { let mut context = CompressContinueTestContext {