diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index 420137f2e..3b716b4d0 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -79,6 +79,11 @@ int ZSTD_rust_targetCBlockSizeAction(int bss, int isFirstBlock, int maybeRLE, int isRLE, size_t cSize, size_t srcSize, int strategy); +size_t ZSTD_rust_nextInputSizeHint(int inBufferMode, + size_t blockSizeMax, + size_t stableInNotConsumed, + size_t inBuffTarget, + size_t inBuffPos); size_t ZSTD_rust_CStreamInSize(void); size_t ZSTD_rust_CStreamOutSize(void); @@ -4524,14 +4529,20 @@ size_t ZSTD_initCStream(ZSTD_CStream* zcs, int compressionLevel) static size_t ZSTD_nextInputSizeHint(const ZSTD_CCtx* cctx) { - if (cctx->appliedParams.inBufferMode == ZSTD_bm_stable) { - return cctx->blockSizeMax - cctx->stableIn_notConsumed; - } - assert(cctx->appliedParams.inBufferMode == ZSTD_bm_buffered); - { size_t hintInSize = cctx->inBuffTarget - cctx->inBuffPos; - if (hintInSize==0) hintInSize = cctx->blockSizeMax; - return hintInSize; + int const inBufferMode = (int)cctx->appliedParams.inBufferMode; + if (inBufferMode == ZSTD_bm_stable) { + return ZSTD_rust_nextInputSizeHint(inBufferMode, + cctx->blockSizeMax, + cctx->stableIn_notConsumed, + cctx->inBuffTarget, + cctx->inBuffPos); } + assert(inBufferMode == ZSTD_bm_buffered); + return ZSTD_rust_nextInputSizeHint(inBufferMode, + cctx->blockSizeMax, + cctx->stableIn_notConsumed, + cctx->inBuffTarget, + cctx->inBuffPos); } /** ZSTD_compressStream_generic(): diff --git a/rust/src/zstd_compress.rs b/rust/src/zstd_compress.rs index a7b080eca..7912f918d 100644 --- a/rust/src/zstd_compress.rs +++ b/rust/src/zstd_compress.rs @@ -72,6 +72,9 @@ unsafe extern "C" { const ZSTD_FAST: c_int = 1; const ZSTD_DFAST: c_int = 2; +#[cfg(test)] +const ZSTD_BM_BUFFERED: c_int = 0; +const ZSTD_BM_STABLE: c_int = 1; const ZSTD_BLOCKSIZE_MAX: usize = 1 << 17; const ZSTD_CONTENTSIZE_UNKNOWN: u64 = u64::MAX; const ZSTD_TARGET_CBLOCK_BSS_COMPRESS: c_int = 0; @@ -216,6 +219,44 @@ pub unsafe extern "C" fn ZSTD_rust_updateFrameProgression( ) as c_int } +#[inline] +fn next_input_size_hint( + in_buffer_mode: c_int, + block_size_max: usize, + stable_in_not_consumed: usize, + in_buff_target: usize, + in_buff_pos: usize, +) -> usize { + if in_buffer_mode == ZSTD_BM_STABLE { + return block_size_max.wrapping_sub(stable_in_not_consumed); + } + + let hint_in_size = in_buff_target.wrapping_sub(in_buff_pos); + if hint_in_size == 0 { + block_size_max + } else { + hint_in_size + } +} + +/// Return the next input size required by the C streaming state machine. +#[no_mangle] +pub extern "C" fn ZSTD_rust_nextInputSizeHint( + in_buffer_mode: c_int, + block_size_max: usize, + stable_in_not_consumed: usize, + in_buff_target: usize, + in_buff_pos: usize, +) -> usize { + next_input_size_hint( + in_buffer_mode, + block_size_max, + stable_in_not_consumed, + in_buff_target, + in_buff_pos, + ) +} + #[inline] fn bitmix(mut val: u64, len: u64) -> u64 { val ^= val.rotate_right(49) ^ val.rotate_right(24); @@ -999,6 +1040,30 @@ mod tests { assert_eq!(produced, 220); } + #[test] + fn next_input_size_hint_uses_remaining_stable_block_capacity() { + let hint = ZSTD_rust_nextInputSizeHint(ZSTD_BM_STABLE, 256, 37, 99, 12); + + assert_eq!(next_input_size_hint(ZSTD_BM_STABLE, 256, 37, 99, 12), 219); + assert_eq!(hint, 219); + } + + #[test] + fn next_input_size_hint_replaces_empty_buffered_hint_with_block_size() { + assert_eq!( + ZSTD_rust_nextInputSizeHint(ZSTD_BM_BUFFERED, 256, 37, 128, 128), + 256 + ); + } + + #[test] + fn next_input_size_hint_returns_nonzero_buffered_hint() { + assert_eq!( + ZSTD_rust_nextInputSizeHint(ZSTD_BM_BUFFERED, 256, 37, 128, 32), + 96 + ); + } + #[test] fn public_one_shot_abi_is_c_compatible() { let entry: unsafe extern "C" fn(*mut c_void, usize, *const c_void, usize, c_int) -> usize =