pub(crate) mod tables;
use crate::error::BzzError;
use tables::{LPS_NEXT, MPS_NEXT, PROB, THRESHOLD};
static LEADING_ONES: [u8; 256] = {
let mut tbl = [0u8; 256];
let mut i = 0u16;
while i < 256 {
let mut val = i as u8;
let mut count = 0u8;
while val & 0x80 != 0 {
count += 1;
val <<= 1;
}
tbl[i as usize] = count;
i += 1;
}
tbl
};
#[inline(always)]
fn count_leading_ones(x: u16) -> u32 {
if x >= 0xff00 {
LEADING_ONES[(x & 0xff) as usize] as u32 + 8
} else {
LEADING_ONES[(x >> 8) as usize] as u32
}
}
pub(crate) struct ZpDecoder<'a> {
a: u16,
c: u16,
fence: u16,
bit_buf: u32,
bit_count: i32,
data: &'a [u8],
pos: usize,
}
impl<'a> ZpDecoder<'a> {
pub(crate) fn new(data: &'a [u8]) -> Result<Self, BzzError> {
if data.len() < 2 {
return Err(BzzError::TooShort);
}
let mut dec = ZpDecoder {
a: 0,
c: 0,
fence: 0,
bit_buf: 0,
bit_count: 0,
data,
pos: 0,
};
let high = dec.read_byte() as u16;
let low = dec.read_byte() as u16;
dec.c = (high << 8) | low;
dec.refill_buffer();
dec.fence = dec.c.min(0x7fff);
Ok(dec)
}
#[inline(always)]
fn read_byte(&mut self) -> u8 {
if self.pos < self.data.len() {
let b = self.data[self.pos];
self.pos += 1;
b
} else {
0xff
}
}
#[inline(always)]
fn refill_buffer(&mut self) {
while self.bit_count <= 24 {
let byte = self.read_byte();
self.bit_buf = (self.bit_buf << 8) | (byte as u32);
self.bit_count += 8;
}
}
#[inline(always)]
pub(crate) fn decode_bit(&mut self, ctx: &mut u8) -> bool {
let state = *ctx as usize;
let mps_bit = state & 1; let z = self.a as u32 + PROB[state] as u32;
if z <= self.fence as u32 {
self.a = z as u16;
return mps_bit != 0;
}
let boundary = 0x6000u32 + ((self.a as u32 + z) >> 2);
let z_clamped = z.min(boundary);
if z_clamped > self.c as u32 {
let lps_bit = 1 - mps_bit;
let complement = 0x10000u32 - z_clamped;
self.a = self.a.wrapping_add(complement as u16);
self.c = self.c.wrapping_add(complement as u16);
*ctx = LPS_NEXT[state];
self.renormalize();
lps_bit != 0
} else {
if self.a >= THRESHOLD[state] {
*ctx = MPS_NEXT[state];
}
self.bit_count -= 1;
self.a = (z_clamped << 1) as u16;
self.c = ((self.c as u32) << 1 | ((self.bit_buf >> self.bit_count as u32) & 1)) as u16;
if self.bit_count < 16 {
self.refill_buffer();
}
self.fence = self.c.min(0x7fff);
mps_bit != 0
}
}
pub(crate) fn is_exhausted(&self) -> bool {
self.pos >= self.data.len()
}
#[inline(always)]
pub(crate) fn decode_passthrough(&mut self) -> bool {
let z = 0x8000u16.wrapping_add(self.a >> 1);
self.passthrough_with_threshold(z)
}
#[inline(always)]
pub(crate) fn decode_passthrough_iw44(&mut self) -> bool {
let z = (0x8000u32 + (3u32 * self.a as u32) / 8) as u16;
self.passthrough_with_threshold(z)
}
#[inline(always)]
fn passthrough_with_threshold(&mut self, z: u16) -> bool {
if z > self.c {
let complement = 0x10000u32 - z as u32;
self.a = self.a.wrapping_add(complement as u16);
self.c = self.c.wrapping_add(complement as u16);
self.renormalize();
true
} else {
self.bit_count -= 1;
self.a = z.wrapping_mul(2);
self.c = ((self.c as u32) << 1 | ((self.bit_buf >> self.bit_count as u32) & 1)) as u16;
if self.bit_count < 16 {
self.refill_buffer();
}
self.fence = self.c.min(0x7fff);
false
}
}
#[inline(always)]
fn renormalize(&mut self) {
let shift = count_leading_ones(self.a);
self.bit_count -= shift as i32;
self.a = ((self.a as u32) << shift) as u16;
let mask = (1u32 << shift) - 1;
self.c =
((self.c as u32) << shift | ((self.bit_buf >> self.bit_count as u32) & mask)) as u16;
if self.bit_count < 16 {
self.refill_buffer();
}
self.fence = self.c.min(0x7fff);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::BzzError;
#[test]
fn zp_decoder_rejects_empty_input() {
assert!(matches!(ZpDecoder::new(&[]), Err(BzzError::TooShort)));
}
#[test]
fn zp_decoder_rejects_one_byte_input() {
assert!(matches!(ZpDecoder::new(&[0x00]), Err(BzzError::TooShort)));
}
#[test]
fn zp_decoder_accepts_two_byte_input() {
assert!(ZpDecoder::new(&[0x00, 0x00]).is_ok());
assert!(ZpDecoder::new(&[0xff, 0xff]).is_ok());
}
#[test]
fn leading_ones_table_spot_checks() {
assert_eq!(LEADING_ONES[0x00], 0); assert_eq!(LEADING_ONES[0x80], 1); assert_eq!(LEADING_ONES[0xC0], 2); assert_eq!(LEADING_ONES[0xFE], 7); assert_eq!(LEADING_ONES[0xFF], 8); }
#[test]
fn count_leading_ones_spot_checks() {
assert_eq!(count_leading_ones(0x0000), 0);
assert_eq!(count_leading_ones(0x8000), 1);
assert_eq!(count_leading_ones(0xC000), 2);
assert_eq!(count_leading_ones(0xFF00), 8);
assert_eq!(count_leading_ones(0xFF80), 9);
assert_eq!(count_leading_ones(0xFFFF), 16);
}
#[test]
fn zp_tables_spot_check() {
assert_eq!(PROB[0], 0x8000);
assert_eq!(PROB[250], 0x481a);
assert_eq!(MPS_NEXT[0], 84);
assert_eq!(LPS_NEXT[0], 145);
assert_eq!(THRESHOLD[83], 0);
assert_eq!(THRESHOLD[250], 0);
}
}