diff --git a/lib/compress/zstd_compress.c b/lib/compress/zstd_compress.c index ce45c5de4..37c25fe8c 100644 --- a/lib/compress/zstd_compress.c +++ b/lib/compress/zstd_compress.c @@ -122,6 +122,8 @@ int ZSTD_rust_params_resolveEnableLdm( int ZSTD_rust_params_resolveExternalRepcodeSearch(int mode, int cLevel); int ZSTD_rust_params_cdictIndicesAreTagged(ZSTD_compressionParameters cParams); int ZSTD_rust_params_dedicatedDictSearchIsSupported(ZSTD_compressionParameters cParams); +U32 ZSTD_rust_params_dedicatedDictSearch_getHashLog(U32 hashLog); +U32 ZSTD_rust_params_dedicatedDictSearch_revertHashLog(U32 hashLog); /* CCtx parameter state is mirrored by rust/src/zstd_compress_params_api.rs. * Rust owns parameter bounds and clamping; C exposes only the @@ -5663,7 +5665,7 @@ static ZSTD_compressionParameters ZSTD_dedicatedDictSearch_getCParams(int const case ZSTD_greedy: case ZSTD_lazy: case ZSTD_lazy2: - cParams.hashLog += ZSTD_LAZY_DDSS_BUCKET_LOG; + cParams.hashLog = ZSTD_rust_params_dedicatedDictSearch_getHashLog(cParams.hashLog); break; case ZSTD_btlazy2: case ZSTD_btopt: @@ -5688,10 +5690,7 @@ static void ZSTD_dedicatedDictSearch_revertCParams( case ZSTD_greedy: case ZSTD_lazy: case ZSTD_lazy2: - cParams->hashLog -= ZSTD_LAZY_DDSS_BUCKET_LOG; - if (cParams->hashLog < ZSTD_HASHLOG_MIN) { - cParams->hashLog = ZSTD_HASHLOG_MIN; - } + cParams->hashLog = ZSTD_rust_params_dedicatedDictSearch_revertHashLog(cParams->hashLog); break; case ZSTD_btlazy2: case ZSTD_btopt: diff --git a/rust/src/zstd_compress_params.rs b/rust/src/zstd_compress_params.rs index 7daf98d0a..53c54cd42 100644 --- a/rust/src/zstd_compress_params.rs +++ b/rust/src/zstd_compress_params.rs @@ -32,6 +32,7 @@ const ZSTD_WINDOWLOG_MAX: c_int = 30; const ZSTD_WINDOWLOG_MAX: c_int = 31; const ZSTD_HASHLOG_MIN: c_int = 6; const ZSTD_HASHLOG_MAX: c_int = 30; +const ZSTD_LAZY_DDSS_BUCKET_LOG: u32 = 2; const ZSTD_CHAINLOG_MIN: c_int = ZSTD_HASHLOG_MIN; #[cfg(target_pointer_width = "32")] const ZSTD_CHAINLOG_MAX: c_int = 29; @@ -439,6 +440,18 @@ fn dedicated_dict_search_is_supported(cparams: ZSTD_compressionParameters) -> bo && cparams.chainLog <= 24 } +#[inline] +fn dedicated_dict_search_get_hash_log(hash_log: u32) -> u32 { + hash_log.wrapping_add(ZSTD_LAZY_DDSS_BUCKET_LOG) +} + +#[inline] +fn dedicated_dict_search_revert_hash_log(hash_log: u32) -> u32 { + hash_log + .wrapping_sub(ZSTD_LAZY_DDSS_BUCKET_LOG) + .max(ZSTD_HASHLOG_MIN as u32) +} + /// C ABI for `ZSTD_CDictIndicesAreTagged()`. #[no_mangle] pub extern "C" fn ZSTD_rust_params_cdictIndicesAreTagged( @@ -455,6 +468,18 @@ pub extern "C" fn ZSTD_rust_params_dedicatedDictSearchIsSupported( c_int::from(dedicated_dict_search_is_supported(cparams)) } +/// C ABI for the hash-log adjustment used by `ZSTD_dedicatedDictSearch_getCParams()`. +#[no_mangle] +pub extern "C" fn ZSTD_rust_params_dedicatedDictSearch_getHashLog(hash_log: u32) -> u32 { + dedicated_dict_search_get_hash_log(hash_log) +} + +/// C ABI for the hash-log adjustment used by `ZSTD_dedicatedDictSearch_revertCParams()`. +#[no_mangle] +pub extern "C" fn ZSTD_rust_params_dedicatedDictSearch_revertHashLog(hash_log: u32) -> u32 { + dedicated_dict_search_revert_hash_log(hash_log) +} + #[inline] fn dict_and_window_log(window_log: u32, src_size: u64, dict_size: u64) -> u32 { /* 1ULL << ZSTD_WINDOWLOG_MAX, which is smaller for 32-bit builds. */ @@ -1258,6 +1283,61 @@ mod tests { assert_eq!(supported(ZSTD_LAZY2, 25, 26), 0); } + #[test] + fn dedicated_dict_search_hash_log_adjustments_match_strategy_cases() { + let strategies = [ + ZSTD_FAST, + ZSTD_DFAST, + ZSTD_GREEDY, + ZSTD_LAZY, + ZSTD_LAZY2, + ZSTD_BTLAZY2, + ZSTD_BTOPT, + ZSTD_BTULTRA, + ZSTD_BTULTRA2, + ]; + + for strategy in strategies { + let should_adjust = matches!(strategy, ZSTD_GREEDY | ZSTD_LAZY | ZSTD_LAZY2); + let original = 12; + let adjusted = if should_adjust { + ZSTD_rust_params_dedicatedDictSearch_getHashLog(original) + } else { + original + }; + assert_eq!(adjusted, if should_adjust { 14 } else { original }); + + let reverted = if should_adjust { + ZSTD_rust_params_dedicatedDictSearch_revertHashLog(adjusted) + } else { + adjusted + }; + assert_eq!(reverted, original); + } + + assert_eq!(ZSTD_rust_params_dedicatedDictSearch_getHashLog(u32::MAX), 1); + assert_eq!( + ZSTD_rust_params_dedicatedDictSearch_revertHashLog(ZSTD_HASHLOG_MIN as u32), + ZSTD_HASHLOG_MIN as u32 + ); + assert_eq!( + ZSTD_rust_params_dedicatedDictSearch_revertHashLog(ZSTD_HASHLOG_MIN as u32 + 1), + ZSTD_HASHLOG_MIN as u32 + ); + assert_eq!( + ZSTD_rust_params_dedicatedDictSearch_revertHashLog(ZSTD_HASHLOG_MIN as u32 + 2), + ZSTD_HASHLOG_MIN as u32 + ); + assert_eq!( + ZSTD_rust_params_dedicatedDictSearch_revertHashLog(0), + u32::MAX - 1 + ); + assert_eq!( + ZSTD_rust_params_dedicatedDictSearch_revertHashLog(1), + u32::MAX + ); + } + #[test] fn external_sequence_validation_preserves_its_int_mode() { for mode in [c_int::MIN, -1, 0, 1, c_int::MAX] {