pub(crate) const MAX_NPOSTFIX: u32 = 3;
pub(crate) const MAX_NDIRECT: u32 = 120;
pub(crate) const NUM_DISTANCE_SHORT_CODES: u32 = 16;
pub(crate) const MAX_DISTANCE_BITS: u32 = 24;
pub(crate) const LARGE_MAX_DISTANCE_BITS: u32 = 62;
pub(crate) const MAX_ALLOWED_DISTANCE: u32 = 0x7FFF_FFFC;
pub(crate) const NUM_HISTOGRAM_DISTANCE_SYMBOLS: usize = 544;
pub(crate) const MAX_SIMPLE_DISTANCE_ALPHABET_SIZE: usize = 140;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(crate) struct DistanceParams {
pub(crate) postfix_bits: u32,
pub(crate) num_direct: u32,
pub(crate) alphabet_size_max: u32,
pub(crate) alphabet_size_limit: u32,
pub(crate) max_distance: u32,
}
impl DistanceParams {
pub(crate) const fn new(postfix_bits: u32, num_direct: u32) -> Self {
let alphabet_size_max =
NUM_DISTANCE_SHORT_CODES + num_direct + (MAX_DISTANCE_BITS << (postfix_bits + 1));
let max_distance = num_direct + (1u32 << (MAX_DISTANCE_BITS + postfix_bits + 2))
- (1u32 << (postfix_bits + 2));
Self {
postfix_bits,
num_direct,
alphabet_size_max,
alphabet_size_limit: alphabet_size_max,
max_distance,
}
}
pub(crate) const fn new_large(postfix_bits: u32, num_direct: u32) -> Self {
let alphabet_size_max =
NUM_DISTANCE_SHORT_CODES + num_direct + (LARGE_MAX_DISTANCE_BITS << (postfix_bits + 1));
let limit = distance_code_limit(MAX_ALLOWED_DISTANCE, postfix_bits, num_direct);
Self {
postfix_bits,
num_direct,
alphabet_size_max,
alphabet_size_limit: limit.max_alphabet_size,
max_distance: limit.max_distance,
}
}
pub(crate) const fn for_window(large_window: bool, postfix_bits: u32, num_direct: u32) -> Self {
if large_window {
Self::new_large(postfix_bits, num_direct)
} else {
Self::new(postfix_bits, num_direct)
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(crate) struct DistanceCodeLimit {
pub(crate) max_alphabet_size: u32,
pub(crate) max_distance: u32,
}
const fn distance_code_limit(
max_distance: u32,
postfix_bits: u32,
num_direct: u32,
) -> DistanceCodeLimit {
if max_distance <= num_direct {
return DistanceCodeLimit {
max_alphabet_size: max_distance + NUM_DISTANCE_SHORT_CODES,
max_distance,
};
}
let forbidden = max_distance + 1;
let postfix = (1u32 << postfix_bits) - 1;
let offset = ((forbidden - num_direct - 1) >> postfix_bits) + 4;
let mut distance_bits = 0u32;
let mut rest = offset / 2;
while rest != 0 {
distance_bits += 1;
rest >>= 1;
}
distance_bits -= 1;
let half = (offset >> distance_bits) & 1;
let group = ((distance_bits - 1) << 1) | half;
if group == 0 {
return DistanceCodeLimit {
max_alphabet_size: num_direct + NUM_DISTANCE_SHORT_CODES,
max_distance: num_direct,
};
}
let group = group - 1;
let distance_bits = (group >> 1) + 1;
let extra = (1u32 << distance_bits) - 1;
let start = (1u32 << (distance_bits + 1)) - 4 + ((group & 1) << distance_bits);
DistanceCodeLimit {
max_alphabet_size: ((group << postfix_bits) | postfix)
+ num_direct
+ NUM_DISTANCE_SHORT_CODES
+ 1,
max_distance: ((start + extra) << postfix_bits) + postfix + num_direct + 1,
}
}
impl Default for DistanceParams {
fn default() -> Self {
Self::new(0, 0)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn legal_pairs() -> Vec<(u32, u32)> {
let mut pairs = Vec::new();
for postfix_bits in 0..=MAX_NPOSTFIX {
for direct_codes in 0..=MAX_NDIRECT {
let groups = (direct_codes >> postfix_bits) & 0x0F;
if (groups << postfix_bits) == direct_codes {
pairs.push((postfix_bits, direct_codes));
}
}
}
pairs
}
#[test]
fn the_ordinary_alphabet_is_unchanged_by_the_extension() {
let default = DistanceParams::new(0, 0);
assert_eq!(default.alphabet_size_max, 64);
assert_eq!(default.alphabet_size_limit, 64);
assert_eq!(default.max_distance, (1 << 26) - 4);
assert_eq!(DistanceParams::for_window(false, 0, 0), default);
}
#[test]
fn the_large_alphabet_is_sized_for_sixty_two_distance_bits() {
let large = DistanceParams::new_large(0, 0);
assert_eq!(large.alphabet_size_max, 16 + (62 << 1));
assert_eq!(large.alphabet_size_limit, 74);
assert_eq!(large.max_distance, MAX_ALLOWED_DISTANCE);
assert_eq!(DistanceParams::for_window(true, 0, 0), large);
}
#[test]
fn the_large_alphabet_never_outgrows_a_distance_histogram() {
for (postfix_bits, direct_codes) in legal_pairs() {
let large = DistanceParams::new_large(postfix_bits, direct_codes);
assert!(
large.alphabet_size_limit as usize <= NUM_HISTOGRAM_DISTANCE_SYMBOLS,
"npostfix {postfix_bits}, ndirect {direct_codes}"
);
assert!(
large.alphabet_size_limit <= large.alphabet_size_max,
"npostfix {postfix_bits}, ndirect {direct_codes}"
);
}
assert_eq!(
DistanceParams::new_large(MAX_NPOSTFIX, 120).alphabet_size_limit as usize,
NUM_HISTOGRAM_DISTANCE_SYMBOLS
);
}
#[test]
fn a_large_alphabet_reaches_further_than_the_ordinary_one() {
for (postfix_bits, direct_codes) in legal_pairs() {
let ordinary = DistanceParams::new(postfix_bits, direct_codes);
let large = DistanceParams::new_large(postfix_bits, direct_codes);
assert!(
large.max_distance >= ordinary.max_distance,
"npostfix {postfix_bits}, ndirect {direct_codes}"
);
assert!(
large.alphabet_size_max > ordinary.alphabet_size_max,
"npostfix {postfix_bits}, ndirect {direct_codes}"
);
assert!(
large.max_distance <= MAX_ALLOWED_DISTANCE,
"npostfix {postfix_bits}, ndirect {direct_codes}"
);
}
}
#[test]
fn a_limit_below_the_direct_codes_degenerates_cleanly() {
assert_eq!(
distance_code_limit(8, 0, 16),
DistanceCodeLimit {
max_alphabet_size: 8 + NUM_DISTANCE_SHORT_CODES,
max_distance: 8,
}
);
assert_eq!(
distance_code_limit(9, 0, 8),
DistanceCodeLimit {
max_alphabet_size: 8 + NUM_DISTANCE_SHORT_CODES,
max_distance: 8,
}
);
}
#[test]
fn the_default_alphabet_is_the_one_without_postfix_or_direct_codes() {
assert_eq!(DistanceParams::default(), DistanceParams::new(0, 0));
}
}