diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index fe10f816d..116b064fd 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -1792,6 +1792,10 @@ size_t ZSTD_rust_checkBufferStability( const void* expectedInSrc, size_t expectedInPos, const void* inputSrc, size_t inputPos, size_t expectedOutBufferSize, size_t outputSize, size_t outputPos); +void ZSTD_rust_setBufferExpectations( + int inBufferMode, int outBufferMode, + ZSTD_inBuffer* expectedInBuffer, size_t* expectedOutBufferSize, + const ZSTD_outBuffer* output, const ZSTD_inBuffer* input); /* Context-free compression-parameter selection and sizing leaves live in * Rust (rust/src/zstd_compress_params.rs). This file retains @@ -7690,12 +7694,13 @@ static void ZSTD_setBufferExpectations(ZSTD_CCtx* cctx, const ZSTD_outBuffer* output, const ZSTD_inBuffer* input) { DEBUGLOG(5, "ZSTD_setBufferExpectations (for advanced stable in/out modes)"); - if (cctx->appliedParams.inBufferMode == ZSTD_bm_stable) { - cctx->expectedInBuffer = *input; - } - if (cctx->appliedParams.outBufferMode == ZSTD_bm_stable) { - cctx->expectedOutBufferSize = output->size - output->pos; - } + ZSTD_rust_setBufferExpectations( + (int)cctx->appliedParams.inBufferMode, + (int)cctx->appliedParams.outBufferMode, + &cctx->expectedInBuffer, + &cctx->expectedOutBufferSize, + output, + input); } /* Validate that the input/output buffers match the expectations set by diff --git a/rust/src/zstd_compress.rs b/rust/src/zstd_compress.rs index dfe15bcaf..e447a3658 100644 --- a/rust/src/zstd_compress.rs +++ b/rust/src/zstd_compress.rs @@ -6169,6 +6169,53 @@ pub extern "C" fn ZSTD_rust_checkBufferStability( ) } +#[inline] +unsafe fn set_buffer_expectations( + in_buffer_mode: c_int, + out_buffer_mode: c_int, + expected_in_buffer: *mut ZSTD_inBuffer, + expected_out_buffer_size: *mut usize, + output: *const ZSTD_outBuffer, + input: *const ZSTD_inBuffer, +) { + if in_buffer_mode == ZSTD_BM_STABLE { + unsafe { + *expected_in_buffer = ptr::read(input); + } + } + if out_buffer_mode == ZSTD_BM_STABLE { + let output = unsafe { &*output }; + unsafe { + *expected_out_buffer_size = output.size.wrapping_sub(output.pos); + } + } +} + +/// Update the stable input/output expectations after a compression call. +/// +/// C retains the private `ZSTD_CCtx` fields and passes only their projections; +/// Rust owns the buffer-mode policy and C-size arithmetic. +#[no_mangle] +pub unsafe extern "C" fn ZSTD_rust_setBufferExpectations( + in_buffer_mode: c_int, + out_buffer_mode: c_int, + expected_in_buffer: *mut ZSTD_inBuffer, + expected_out_buffer_size: *mut usize, + output: *const ZSTD_outBuffer, + input: *const ZSTD_inBuffer, +) { + unsafe { + set_buffer_expectations( + in_buffer_mode, + out_buffer_mode, + expected_in_buffer, + expected_out_buffer_size, + output, + input, + ); + } +} + #[inline] fn select_sequence_copier(mode: c_int) -> c_int { debug_assert!( @@ -14597,6 +14644,75 @@ mod tests { ); } + #[test] + fn set_buffer_expectations_updates_only_enabled_stable_modes() { + let expected_src = b"expected".as_ptr().cast::(); + let input_src = b"input".as_ptr().cast::(); + let mut expected_input = ZSTD_inBuffer { + src: expected_src, + size: 3, + pos: 1, + }; + let input = ZSTD_inBuffer { + src: input_src, + size: 11, + pos: 7, + }; + let output = ZSTD_outBuffer { + dst: ptr::null_mut(), + size: 40, + pos: 9, + }; + let mut expected_output_size = 5; + + unsafe { + ZSTD_rust_setBufferExpectations( + ZSTD_BM_STABLE, + ZSTD_BM_STABLE, + &mut expected_input, + &mut expected_output_size, + &output, + &input, + ); + } + + assert_eq!(expected_input.src, input.src); + assert_eq!(expected_input.size, input.size); + assert_eq!(expected_input.pos, input.pos); + assert_eq!(expected_output_size, 31); + + let original_input_src = expected_input.src; + let original_input_size = expected_input.size; + let original_input_pos = expected_input.pos; + let original_output_size = expected_output_size; + let other_input = ZSTD_inBuffer { + src: expected_src, + size: 99, + pos: 22, + }; + let other_output = ZSTD_outBuffer { + dst: ptr::null_mut(), + size: 3, + pos: 8, + }; + + unsafe { + ZSTD_rust_setBufferExpectations( + ZSTD_BM_BUFFERED, + ZSTD_BM_BUFFERED, + &mut expected_input, + &mut expected_output_size, + &other_output, + &other_input, + ); + } + + assert_eq!(expected_input.src, original_input_src); + assert_eq!(expected_input.size, original_input_size); + assert_eq!(expected_input.pos, original_input_pos); + assert_eq!(expected_output_size, original_output_size); + } + #[test] fn sequence_api_plan_places_input_and_frame_checksums_around_blocks() { assert_eq!(