use pith_digest::{Error, Result};
pub struct Bits<'a> {
data: &'a [u8],
pub pos: usize,
acc: u64,
bitcnt: u32,
marker: Option<u8>,
stalled: bool,
}
impl<'a> Bits<'a> {
pub fn new(data: &'a [u8], pos: usize) -> Self {
Bits {
data,
pos,
acc: 0,
bitcnt: 0,
marker: None,
stalled: false,
}
}
pub fn consume_rst(&mut self, expected: u8) -> bool {
self.acc = 0;
self.bitcnt = 0;
while self.pos < self.data.len() && self.data[self.pos] != 0xff {
self.pos += 1;
}
let mut i = self.pos;
while i < self.data.len() && self.data[i] == 0xff {
i += 1;
}
if i >= self.data.len() {
return false;
}
let code = self.data[i];
if (0xd0..=0xd7).contains(&code) && code - 0xd0 == expected {
self.pos = i + 1;
self.marker = None;
self.stalled = false;
true
} else {
false
}
}
fn byte(&mut self) -> Option<u8> {
if self.stalled || self.marker.is_some() {
return None;
}
let b = *self.data.get(self.pos)?;
if b != 0xff {
self.pos += 1;
return Some(b);
}
let mut i = self.pos;
while i < self.data.len() && self.data[i] == 0xff {
i += 1;
}
if i >= self.data.len() {
self.marker = Some(0xff); self.pos = i;
return None;
}
let code = self.data[i];
if code == 0x00 {
self.pos = i + 1;
return Some(0xff);
}
self.marker = Some(code);
None
}
pub fn bits(&mut self, n: u32) -> Result<u32> {
debug_assert!((1..=16).contains(&n));
while self.bitcnt < n {
match self.byte() {
Some(b) => {
self.acc = (self.acc << 8) | u64::from(b);
self.bitcnt += 8;
}
None => {
self.stalled = true;
return Err(Error::Truncated {
what: "entropy-coded data",
needed: n as usize,
found: self.bitcnt as usize,
});
}
}
}
let shift = self.bitcnt - n;
let v = (self.acc >> shift) as u32 & ((1u32 << n) - 1);
self.bitcnt -= n;
self.acc &= if self.bitcnt == 0 {
0
} else {
(1u64 << self.bitcnt) - 1
};
Ok(v)
}
pub fn receive(&mut self, s: u32) -> Result<u32> {
if s == 0 {
return Ok(0);
}
self.bits(s)
}
pub fn extend(&mut self, s: u32) -> Result<i32> {
let v = self.receive(s)?;
Ok(extend(v, s))
}
}
#[inline]
pub fn extend(v: u32, s: u32) -> i32 {
if s == 0 {
return 0;
}
let vt = 1i32 << (s - 1);
let v = v as i32;
if v < vt { v - (vt << 1) + 1 } else { v }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn stuffing_and_marker_stop() {
let data = [0x12, 0xff, 0x00, 0x34, 0xff, 0xd9, 0xaa];
let mut b = Bits::new(&data, 0);
assert_eq!(b.bits(8).unwrap(), 0x12);
assert_eq!(b.bits(8).unwrap(), 0xff); assert_eq!(b.bits(8).unwrap(), 0x34);
assert!(b.bits(1).is_err()); }
#[test]
fn msb_first_bit_order() {
let data = [0b1011_0011];
let mut b = Bits::new(&data, 0);
assert_eq!(b.bits(1).unwrap(), 1);
assert_eq!(b.bits(3).unwrap(), 0b011);
assert_eq!(b.bits(4).unwrap(), 0b0011);
assert!(b.bits(1).is_err());
}
#[test]
fn restart_marker_consumption() {
let data = [0xab, 0xff, 0xff, 0xd1, 0x55];
let mut b = Bits::new(&data, 0);
assert_eq!(b.bits(8).unwrap(), 0xab);
assert!(!b.consume_rst(0)); assert!(b.consume_rst(1)); assert_eq!(b.bits(8).unwrap(), 0x55);
}
#[test]
fn extend_sign_magnitude() {
assert_eq!(extend(0b011, 3), -4); assert_eq!(extend(0b000, 3), -7); assert_eq!(extend(0b100, 3), 4);
assert_eq!(extend(0b111, 3), 7);
assert_eq!(extend(0, 1), -1);
assert_eq!(extend(1, 1), 1);
assert_eq!(extend(0, 0), 0);
}
#[test]
fn stalled_reader_short_circuits() {
let data = [0x00u8];
let mut b = Bits::new(&data, 0);
b.stalled = true;
assert!(b.bits(1).is_err());
assert_eq!(b.byte(), None);
}
}