diff --git a/lib/compress/zstd_compress_internal.h b/lib/compress/zstd_compress_internal.h index 443b5c6b1..d4610c6a7 100644 --- a/lib/compress/zstd_compress_internal.h +++ b/lib/compress/zstd_compress_internal.h @@ -668,19 +668,16 @@ MEM_STATIC size_t ZSTD_minGain(size_t srcSize, ZSTD_strategy strat) return (srcSize >> minlog) + 2; } +int ZSTD_rust_params_literalsCompressionIsDisabled(int literalCompressionMode, + int strategy, + U32 targetLength); + MEM_STATIC int ZSTD_literalsCompressionIsDisabled(const ZSTD_CCtx_params* cctxParams) { - switch (cctxParams->literalCompressionMode) { - case ZSTD_ps_enable: - return 0; - case ZSTD_ps_disable: - return 1; - default: - assert(0 /* impossible: pre-validated */); - ZSTD_FALLTHROUGH; - case ZSTD_ps_auto: - return (cctxParams->cParams.strategy == ZSTD_fast) && (cctxParams->cParams.targetLength > 0); - } + return ZSTD_rust_params_literalsCompressionIsDisabled( + (int)cctxParams->literalCompressionMode, + (int)cctxParams->cParams.strategy, + cctxParams->cParams.targetLength); } /*! ZSTD_safecopyLiterals() : diff --git a/rust/src/zstd_compress_params.rs b/rust/src/zstd_compress_params.rs index 3fab8a83f..0cf067a76 100644 --- a/rust/src/zstd_compress_params.rs +++ b/rust/src/zstd_compress_params.rs @@ -449,6 +449,37 @@ pub extern "C" fn ZSTD_rust_params_resolveExternalSequenceValidation(mode: c_int resolve_external_sequence_validation(mode) } +#[inline] +fn literals_compression_is_disabled( + literal_compression_mode: c_int, + strategy: c_int, + target_length: u32, +) -> c_int { + match literal_compression_mode { + ZSTD_RUST_PS_ENABLE => 0, + ZSTD_RUST_PS_DISABLE => 1, + ZSTD_RUST_PS_AUTO => c_int::from(strategy == ZSTD_FAST && target_length > 0), + _ => { + debug_assert!(false, "invalid ZSTD_ParamSwitch_e literal mode"); + c_int::from(strategy == ZSTD_FAST && target_length > 0) + } + } +} + +/// C ABI for `ZSTD_literalsCompressionIsDisabled()`. +/// +/// The C wrapper extracts these scalar fields so the private +/// `ZSTD_CCtx_params` layout does not cross the Rust boundary. Invalid modes +/// retain C's debug assertion and release-mode fallthrough to the auto policy. +#[no_mangle] +pub extern "C" fn ZSTD_rust_params_literalsCompressionIsDisabled( + literal_compression_mode: c_int, + strategy: c_int, + target_length: u32, +) -> c_int { + literals_compression_is_disabled(literal_compression_mode, strategy, target_length) +} + #[inline] fn cdict_indices_are_tagged(cparams: ZSTD_compressionParameters) -> bool { cparams.strategy == ZSTD_FAST || cparams.strategy == ZSTD_DFAST @@ -1835,6 +1866,45 @@ mod tests { } } + #[test] + fn literal_compression_policy_matches_mode_strategy_and_target_length() { + for strategy in [ZSTD_FAST, ZSTD_DFAST, ZSTD_BTULTRA2] { + for target_length in [0, 1] { + assert_eq!( + ZSTD_rust_params_literalsCompressionIsDisabled( + ZSTD_RUST_PS_ENABLE, + strategy, + target_length, + ), + 0 + ); + assert_eq!( + ZSTD_rust_params_literalsCompressionIsDisabled( + ZSTD_RUST_PS_DISABLE, + strategy, + target_length, + ), + 1 + ); + assert_eq!( + ZSTD_rust_params_literalsCompressionIsDisabled( + ZSTD_RUST_PS_AUTO, + strategy, + target_length, + ), + c_int::from(strategy == ZSTD_FAST && target_length > 0) + ); + } + } + } + + #[cfg(debug_assertions)] + #[test] + #[should_panic] + fn literal_compression_policy_rejects_invalid_mode_in_debug() { + literals_compression_is_disabled(99, ZSTD_FAST, 1); + } + #[test] fn level_tables_match_representative_clevels_entries() { let large = select_cparams(3, ZSTD_CONTENTSIZE_UNKNOWN, 0, ZSTD_RUST_CPM_UNKNOWN);