use crate::compressor::CompressParams;
pub(crate) const MAX_ENCODER_WINDOW_BITS: usize = 30;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(crate) struct ResolvedWindow {
declared_bits: usize,
large: bool,
}
impl ResolvedWindow {
pub(crate) const fn new(params: &CompressParams) -> Self {
Self {
declared_bits: params.lgwin.bits() as usize,
large: params.lgwin.is_large(),
}
}
pub(crate) const fn at_least(self, bits: usize) -> Self {
if self.declared_bits >= bits {
self
} else {
Self {
declared_bits: bits,
large: self.large,
}
}
}
pub(crate) const fn is_large(self) -> bool {
self.large
}
pub(crate) const fn encoder_bits(self) -> usize {
if self.declared_bits > MAX_ENCODER_WINDOW_BITS {
MAX_ENCODER_WINDOW_BITS
} else {
self.declared_bits
}
}
pub(crate) const fn header(self) -> (u16, u32) {
let bits = self.declared_bits;
if self.large {
return (((bits as u16 & 0x3F) << 8) | 0x11, 14);
}
if bits == 16 {
(0, 1)
} else if bits == 17 {
(1, 7)
} else if bits > 17 {
((((bits - 17) << 1) | 0x01) as u16, 4)
} else {
((((bits - 8) << 4) | 0x01) as u16, 7)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::compressor::core::fast::constants::WINDOW_BITS_FAST;
use crate::compressor::{QualityLevel, WindowBits};
fn resolve(lgwin: WindowBits) -> ResolvedWindow {
ResolvedWindow::new(&CompressParams::new(QualityLevel::Q5, lgwin))
}
fn standard(bits: u8) -> ResolvedWindow {
resolve(WindowBits::standard(bits).expect("a legal ordinary window"))
}
fn large(bits: u8) -> ResolvedWindow {
resolve(WindowBits::large(bits).expect("a legal large window"))
}
#[test]
fn an_ordinary_window_matches_the_reference_encoding() {
assert_eq!(standard(16).header(), (0, 1));
assert_eq!(standard(17).header(), (1, 7));
assert_eq!(standard(18).header(), (3, 4));
assert_eq!(standard(22).header(), (11, 4));
assert_eq!(standard(24).header(), (15, 4));
assert_eq!(standard(10).header(), (0x21, 7));
}
#[test]
fn a_large_window_is_a_marker_and_six_bits() {
for bits in 10u8..=62 {
let (value, width) = large(bits).header();
assert_eq!(width, 14, "{bits} bits");
assert_eq!(value & 0xFF, 0x11, "{bits} bits");
assert_eq!(value >> 8, u16::from(bits) & 0x3F, "{bits} bits");
}
}
#[test]
fn a_large_window_is_chosen_only_when_it_is_requested() {
assert!(!standard(22).is_large());
assert!(large(22).is_large());
assert_ne!(standard(22).header(), large(22).header());
}
#[test]
fn retained_history_stops_at_thirty_bits() {
for bits in 10u8..=62 {
let window = large(bits);
let declared = usize::from(bits);
assert_eq!(window.encoder_bits(), declared.min(MAX_ENCODER_WINDOW_BITS));
assert!(window.encoder_bits() <= declared);
}
for bits in 10u8..=24 {
assert_eq!(standard(bits).encoder_bits(), usize::from(bits));
}
}
#[test]
fn raising_the_floor_keeps_the_syntax() {
assert_eq!(standard(10).at_least(WINDOW_BITS_FAST).header(), (3, 4));
assert_eq!(standard(22).at_least(WINDOW_BITS_FAST).header(), (11, 4));
let raised = large(12).at_least(WINDOW_BITS_FAST);
assert!(raised.is_large());
assert_eq!(raised.header(), ((18u16 << 8) | 0x11, 14));
assert_eq!(raised.encoder_bits(), 18);
}
}