use super::tree::HuffmanTree;
use crate::error::{Result, ZiporaError};
#[derive(Debug, Clone, Copy, Default)]
#[repr(C)]
pub struct HuffmanEncSymbol {
pub bits: u16,
pub bit_count: u16,
}
impl HuffmanEncSymbol {
#[inline(always)]
pub const fn new(bits: u16, bit_count: u16) -> Self {
Self { bits, bit_count }
}
}
#[derive(Debug)]
pub(crate) struct BitStreamWriter {
pub(crate) buffer: Vec<u8>,
pub(crate) current: u64,
pub(crate) bit_count: usize,
}
impl BitStreamWriter {
pub(crate) fn new() -> Self {
Self {
buffer: Vec::new(),
current: 0,
bit_count: 0,
}
}
#[inline]
pub(crate) fn write(&mut self, bits: u64, count: usize) {
debug_assert!(count <= 64);
self.current |= bits << self.bit_count;
self.bit_count += count;
while self.bit_count >= 8 {
self.buffer.push(self.current as u8);
self.current >>= 8;
self.bit_count -= 8;
}
}
pub(crate) fn finish(mut self) -> Vec<u8> {
if self.bit_count > 0 {
self.buffer.push(self.current as u8);
}
self.buffer
}
#[inline]
#[allow(dead_code)]
pub(crate) fn len_bits(&self) -> usize {
self.buffer.len() * 8 + self.bit_count
}
}
#[derive(Debug)]
pub struct HuffmanEncoder {
tree: HuffmanTree,
}
impl HuffmanEncoder {
pub fn new(data: &[u8]) -> Result<Self> {
let tree = HuffmanTree::from_data(data)?;
Ok(Self { tree })
}
pub fn from_frequencies(frequencies: &[u32; 256]) -> Result<Self> {
let tree = HuffmanTree::from_frequencies(frequencies)?;
Ok(Self { tree })
}
pub fn encode(&self, data: &[u8]) -> Result<Vec<u8>> {
if data.is_empty() {
return Ok(Vec::new());
}
let mut bits = Vec::new();
for &symbol in data {
if let Some(code) = self.tree.get_code(symbol) {
bits.extend_from_slice(code);
} else {
return Err(ZiporaError::invalid_data(format!(
"Symbol {} not in Huffman tree",
symbol
)));
}
}
let mut result = Vec::new();
let mut current_byte = 0u8;
let mut bit_count = 0;
for bit in bits {
if bit {
current_byte |= 1 << bit_count;
}
bit_count += 1;
if bit_count == 8 {
result.push(current_byte);
current_byte = 0;
bit_count = 0;
}
}
if bit_count > 0 {
result.push(current_byte);
}
Ok(result)
}
pub fn tree(&self) -> &HuffmanTree {
&self.tree
}
pub fn estimate_compression_ratio(&self, data: &[u8]) -> f64 {
if data.is_empty() {
return 0.0;
}
let mut total_bits = 0;
for &symbol in data {
if let Some(code) = self.tree.get_code(symbol) {
total_bits += code.len();
}
}
let compressed_bytes = total_bits.div_ceil(8);
compressed_bytes as f64 / data.len() as f64
}
}