use crate::lossy::constants::Prob;
use crate::lossy::work::work;
pub(crate) struct BoolDecoder<'a> {
input: &'a [u8],
pos: usize,
range: u32,
value: u32,
bit_count: u32,
exhausted: bool,
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct BoolState {
pos: usize,
range: u32,
value: u32,
bit_count: u32,
exhausted: bool,
}
impl<'a> BoolDecoder<'a> {
pub(crate) fn new(input: &'a [u8]) -> Self {
let b0 = input.first().copied().unwrap_or(0);
let b1 = input.get(1).copied().unwrap_or(0);
Self {
input,
pos: 2,
range: 255,
value: (u32::from(b0) << 8) | u32::from(b1),
bit_count: 0,
exhausted: false,
}
}
pub(crate) const fn resume(input: &'a [u8], st: BoolState) -> Self {
Self {
input,
pos: st.pos,
range: st.range,
value: st.value,
bit_count: st.bit_count,
exhausted: st.exhausted,
}
}
pub(crate) const fn state(&self) -> BoolState {
BoolState {
pos: self.pos,
range: self.range,
value: self.value,
bit_count: self.bit_count,
exhausted: self.exhausted,
}
}
pub(crate) const fn is_exhausted(&self) -> bool {
self.exhausted
}
pub(crate) fn read_bool(&mut self, prob: Prob) -> bool {
work!(BoolRead);
let split = 1 + (((self.range - 1) * u32::from(prob)) >> 8);
let big_split = split << 8;
let bit = self.value >= big_split;
if bit {
self.range -= split;
self.value -= big_split;
} else {
self.range = split;
}
if self.range < 128 {
let n = self.range.leading_zeros() - 24;
work!(BoolRenorm, u64::from(n));
self.range <<= n;
self.value <<= n;
let new_bit_count = self.bit_count + n;
if new_bit_count >= 8 {
let shift = new_bit_count - 8;
match self.input.get(self.pos) {
Some(&b) => self.value |= u32::from(b) << shift,
None => self.exhausted = true,
}
self.pos += 1;
self.bit_count = shift;
} else {
self.bit_count = new_bit_count;
}
}
bit
}
pub(crate) fn read_flag(&mut self) -> bool {
self.read_bool(128)
}
pub(crate) fn read_literal(&mut self, n: u32) -> u32 {
let mut v = 0;
for _ in 0..n {
v = (v << 1) | u32::from(self.read_flag());
}
v
}
pub(crate) fn read_signed(&mut self, n: u32) -> i32 {
let magnitude = i32::try_from(self.read_literal(n)).unwrap_or(i32::MAX);
self.apply_sign(magnitude)
}
pub(crate) fn apply_sign(&mut self, magnitude: i32) -> i32 {
if self.read_flag() {
-magnitude
} else {
magnitude
}
}
}
#[cfg(test)]
mod tests {
use super::BoolDecoder;
#[test]
fn first_flag_is_the_top_value_bit() {
assert!(BoolDecoder::new(&[0x80, 0x00]).read_flag());
assert!(!BoolDecoder::new(&[0x7f, 0xff]).read_flag());
}
#[test]
fn renormalization_reads_high_probability_ones() {
let mut d = BoolDecoder::new(&[0xff, 0x00, 0x00, 0x00]);
assert_eq!(
(d.read_flag(), d.read_flag(), d.read_flag()),
(true, true, true)
);
}
#[test]
fn nontrivial_probability_split_is_hand_computed() {
let mut d = BoolDecoder::new(&[0xc0, 0x00, 0x00, 0x00]);
assert!(d.read_bool(150));
assert!(!d.read_bool(150));
}
#[test]
fn short_partition_reads_are_zero_padded() {
let mut d = BoolDecoder::new(&[]);
assert_eq!(d.read_literal(16), 0);
assert!(!d.read_flag());
}
#[test]
fn exhausted_latches_only_after_reading_past_the_end() {
let mut within = BoolDecoder::new(&[0x80, 0x00, 0x00, 0x00]);
within.read_flag();
assert!(
!within.is_exhausted(),
"a read within bounds must not latch"
);
let mut short = BoolDecoder::new(&[0xff]);
for _ in 0..8 {
short.read_flag();
}
assert!(
short.is_exhausted(),
"reading past the end must latch exhausted"
);
}
}