use super::gear_hash::{LDM_BUCKET_SIZE_LOG, LDM_HASH_RLOG, LDM_MIN_MATCH_LENGTH};
pub(crate) const LDM_HASHLOG_MIN: u32 = 6;
pub(crate) const LDM_HASHLOG_MAX: u32 = 30;
pub(crate) const LDM_BUCKETSIZELOG_MAX: u32 = 8;
pub(crate) const STRATEGY_BTULTRA: u32 = 8;
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub(crate) struct LdmParams {
pub(crate) window_log: u32,
pub(crate) hash_log: u32,
pub(crate) hash_rate_log: u32,
pub(crate) min_match_length: u32,
pub(crate) bucket_size_log: u32,
}
impl LdmParams {
pub(crate) fn adjust_for(window_log: u32, strategy: u32) -> Self {
assert!(
(1..=9).contains(&strategy),
"strategy must be a donor 1..=9 ordinal (donor asserts at \
zstd_ldm.c:149 + zstd_ldm.c:167)"
);
let mut params = Self {
window_log,
..Self::default()
};
if params.hash_rate_log == 0 {
if params.hash_log > 0 && params.window_log > params.hash_log {
params.hash_rate_log = params.window_log - params.hash_log;
} else {
params.hash_rate_log = LDM_HASH_RLOG - (strategy / 3);
}
}
if params.hash_log == 0 {
params.hash_log = if params.window_log <= params.hash_rate_log {
LDM_HASHLOG_MIN
} else {
bounded(
LDM_HASHLOG_MIN,
params.window_log - params.hash_rate_log,
LDM_HASHLOG_MAX,
)
};
}
if params.min_match_length == 0 {
let mut mml = LDM_MIN_MATCH_LENGTH as u32;
if strategy >= STRATEGY_BTULTRA {
mml /= 2;
}
params.min_match_length = mml;
}
if params.bucket_size_log == 0 {
params.bucket_size_log = bounded(LDM_BUCKET_SIZE_LOG, strategy, LDM_BUCKETSIZELOG_MAX);
}
params
}
pub(crate) const fn hash_table_entries(&self) -> usize {
1usize << self.hash_log
}
pub(crate) const fn bucket_slots(&self) -> usize {
1usize << self.bucket_size_log
}
}
#[inline]
const fn bounded(lo: u32, val: u32, hi: u32) -> u32 {
if val < lo {
lo
} else if val > hi {
hi
} else {
val
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[should_panic(expected = "strategy must be a donor 1..=9 ordinal")]
fn adjust_for_panics_on_out_of_range_strategy() {
let _ = LdmParams::adjust_for(27, 24);
}
#[test]
fn adjust_strategy_to_hash_rate_log_matches_donor_table() {
assert_eq!(LdmParams::adjust_for(27, 1).hash_rate_log, 7);
assert_eq!(LdmParams::adjust_for(27, 3).hash_rate_log, 6);
assert_eq!(LdmParams::adjust_for(27, 6).hash_rate_log, 5);
assert_eq!(LdmParams::adjust_for(27, 9).hash_rate_log, 4);
}
#[test]
fn adjust_hash_log_clamps_within_donor_bounds() {
assert_eq!(LdmParams::adjust_for(27, 1).hash_log, 20);
assert_eq!(LdmParams::adjust_for(10, 1).hash_log, LDM_HASHLOG_MIN);
assert_eq!(LdmParams::adjust_for(7, 1).hash_log, LDM_HASHLOG_MIN);
}
#[test]
fn adjust_min_match_halved_for_btultra_and_above() {
assert_eq!(
LdmParams::adjust_for(27, 7).min_match_length,
LDM_MIN_MATCH_LENGTH as u32
);
assert_eq!(
LdmParams::adjust_for(27, 8).min_match_length,
(LDM_MIN_MATCH_LENGTH / 2) as u32
);
assert_eq!(
LdmParams::adjust_for(27, 9).min_match_length,
(LDM_MIN_MATCH_LENGTH / 2) as u32
);
}
#[test]
fn adjust_bucket_size_log_clamps_strategy_to_donor_bounds() {
assert_eq!(LdmParams::adjust_for(27, 1).bucket_size_log, 4);
assert_eq!(LdmParams::adjust_for(27, 4).bucket_size_log, 4);
assert_eq!(LdmParams::adjust_for(27, 7).bucket_size_log, 7);
assert_eq!(LdmParams::adjust_for(27, 9).bucket_size_log, 8);
}
#[test]
fn derived_helpers_match_log_definitions() {
let p = LdmParams::adjust_for(27, 5);
assert_eq!(p.hash_table_entries(), 1usize << p.hash_log);
assert_eq!(p.bucket_slots(), 1usize << p.bucket_size_log);
}
}