use crate::error::HlsResult;
use super::table::DecodeNode;
pub(crate) struct DecodeReader {
id: usize,
buf: usize,
buf_size: usize,
tail: usize,
tail_size: usize,
}
impl DecodeReader {
pub fn new() -> Self {
Self {
id: 0,
buf: 0,
buf_size: 0,
tail: 0,
tail_size: 0,
}
}
pub fn decode(&mut self, byte: u8, dst: &mut Vec<u8>) -> HlsResult<()> {
self.buf <<= 8; self.buf_size += 8;
self.buf |= byte as usize; loop {
match self.buf_size {
..5 => break,
_ => self.decode_next(dst)?
}
}
Ok(())
}
pub fn finalize(&mut self, dst: &mut Vec<u8>) -> HlsResult<()> {
let shift_len = (self.buf_size as f64 / 5.0).ceil() as usize * 5 - self.buf_size;
self.buf <<= shift_len; self.buf_size += shift_len;
if self.buf_size >= 5 { if let Some(node) = Self::find_target(self.id, self.buf) && shift_len <= node.leftover as usize { self.decode_next(dst)?;
}
}
self.buf >>= shift_len; self.buf_size -= shift_len;
self.tail <<= self.buf_size; self.tail_size += self.buf_size;
self.tail |= self.buf;
self.buf = 0;
self.buf_size = 0;
if ![0, 1, 3, 7, 15, 31, 63, 127].contains(&self.tail) { return Err("Check huffman tail fail".into());
}
self.tail = 0; self.tail_size = 0;
Ok(())
}
fn decode_next(&mut self, dst: &mut Vec<u8>) -> HlsResult<()> {
let key = self.buf >> (self.buf_size - 5);
let node = Self::find_target(self.id, key).ok_or(format!("get huffman node error,key: {}", self.id))?;
self.buf -= key >> node.leftover << (self.buf_size - 5 + node.leftover as usize); self.buf_size -= 5 - node.leftover as usize;
self.tail <<= 5 - node.leftover as usize; self.tail |= key >> node.leftover;
self.tail_size += 5 - node.leftover as usize;
if let Some(ascii) = node.ascii {
self.id = 0;
self.tail = 0;
self.tail_size = 0;
if ascii < 256 { dst.push(ascii as u8);
Ok(())
} else {
Err("Invalid huffman table ascii".into())
}
} else if let Some(next_id) = node.next { self.id = next_id as usize;
Ok(())
} else {
Err("decode huffman error".into())
}
}
fn find_target(id: usize, key: usize) -> Option<&'static DecodeNode> {
let transitions = super::table::DECODE_TABLE.get(id)?;
transitions.get(key)
}
}