use bitvec::{access::BitSafeU8, order::Lsb0, vec::BitVec};
use std::{
cmp::Ordering,
collections::{BinaryHeap, HashMap, VecDeque},
hash::Hash,
};
use crate::{error::DecodeError, WChar, WString};
#[derive(Debug)]
enum HuffmanTree<C> {
Node(Box<HuffmanTree<C>>, Box<HuffmanTree<C>>),
Leaf(C, u32),
}
impl<C> HuffmanTree<C> {
fn frequency(&self) -> u32 {
match *self {
HuffmanTree::Node(ref left, ref right) => left.frequency() + right.frequency(),
HuffmanTree::Leaf(_, freq) => freq,
}
}
}
impl<C> PartialEq for HuffmanTree<C> {
fn eq(&self, other: &Self) -> bool {
self.frequency().eq(&other.frequency())
}
}
impl<C> Eq for HuffmanTree<C> {}
impl<C> Ord for HuffmanTree<C> {
fn cmp(&self, other: &Self) -> Ordering {
self.frequency().cmp(&other.frequency()).reverse()
}
}
impl<C> PartialOrd for HuffmanTree<C> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
#[derive(Default)]
pub(crate) struct FrequencyMap<C: HuffmanChar>(HashMap<C, u32>);
impl<C: HuffmanChar> FrequencyMap<C> {
pub fn push_iter<I: IntoIterator<Item = C>>(&mut self, iter: I) {
iter.into_iter().for_each(|value| self.push(value))
}
#[inline]
pub fn push(&mut self, value: C) {
*self.0.entry(value).or_insert(0) += 1;
}
}
pub trait HuffmanString: 'static {
type Char: HuffmanChar;
fn new() -> Self;
fn append_char(&mut self, value: Self::Char);
fn len(&self) -> usize;
}
impl HuffmanString for String {
type Char = char;
#[inline]
fn new() -> Self {
String::new()
}
#[inline]
fn append_char(&mut self, value: Self::Char) {
self.push(value)
}
#[inline]
fn len(&self) -> usize {
self.len()
}
}
impl HuffmanString for WString {
type Char = WChar;
#[inline]
fn new() -> Self {
WString::new()
}
#[inline]
fn append_char(&mut self, value: Self::Char) {
self.push(value)
}
#[inline]
fn len(&self) -> usize {
self.len()
}
}
pub trait HuffmanChar: Hash + PartialEq + Eq + Copy + 'static {
const NULL: Self;
fn as_symbol(self) -> i32;
fn from_symbol(value: i32) -> Self;
}
impl HuffmanChar for char {
const NULL: Self = '\0';
#[inline]
fn as_symbol(self) -> i32 {
self as i32
}
#[inline]
fn from_symbol(value: i32) -> Self {
value as u8 as char
}
}
impl HuffmanChar for WChar {
const NULL: Self = 0;
#[inline]
fn as_symbol(self) -> i32 {
self as i32
}
#[inline]
fn from_symbol(value: i32) -> Self {
value as WChar
}
}
pub(crate) struct Huffman<C: HuffmanChar> {
mapping: HashMap<C, BitVec>,
pairs: Vec<(i32, i32)>,
}
impl<C: HuffmanChar> Huffman<C> {
pub fn new(freq: FrequencyMap<C>) -> Self {
let huffman_tree = Self::build_tree(freq);
let mapping = Self::generate_huffman_codes(&huffman_tree);
let pairs = Self::collect_pairs(&huffman_tree);
Self { mapping, pairs }
}
pub fn get_pairs(&self) -> &[(i32, i32)] {
&self.pairs
}
pub fn encode<I: IntoIterator<Item = C>>(&self, iter: I, output: &mut BitVec<BitSafeU8, Lsb0>) {
iter.into_iter()
.filter_map(|code| self.mapping.get(&code))
.for_each(|value| output.extend(value))
}
pub fn encode_null(&self, output: &mut BitVec<BitSafeU8, Lsb0>) {
let code = self
.mapping
.get(&C::NULL)
.expect("Missing null byte encoding");
output.extend(code);
}
pub fn decode<S: HuffmanString<Char = C>>(
compressed_data: &[u8],
pairs: &[(i32, i32)],
position: usize,
max_length: usize,
) -> Result<S, DecodeError> {
let mut sb = S::new();
let mut cur_node = pairs.len() - 1;
let end = compressed_data.len() * 8;
let mut pos = position;
while pos < end && sb.len() < max_length {
let sample = compressed_data[pos / 8] & (1 << (pos % 8));
let next = pairs[cur_node];
let next = if sample != 0 { next.1 } else { next.0 };
if next < 0 {
let ch = -1 - next;
if ch == 0 {
break;
}
sb.append_char(S::Char::from_symbol(ch));
cur_node = pairs.len() - 1;
} else {
cur_node = next as usize;
if cur_node > pairs.len() {
return Err(DecodeError::MalformedDecompressionNodes);
}
}
pos += 1;
}
Ok(sb)
}
fn build_tree(freq: FrequencyMap<C>) -> HuffmanTree<C> {
let mut heap = BinaryHeap::new();
for (char, freq) in freq.0 {
heap.push(HuffmanTree::Leaf(char, freq));
}
if heap.is_empty() {
return HuffmanTree::Leaf(C::NULL, 0);
}
while heap.len() > 1 {
let left = heap.pop().unwrap();
let right = heap.pop().unwrap();
heap.push(HuffmanTree::Node(Box::new(left), Box::new(right)));
}
heap.pop().unwrap()
}
fn generate_huffman_codes(node: &HuffmanTree<C>) -> HashMap<C, BitVec> {
let mut codes = HashMap::new();
let mut stack = VecDeque::new();
stack.push_back((node, BitVec::new()));
while let Some((current_node, prefix)) = stack.pop_back() {
match current_node {
HuffmanTree::Node(left, right) => {
let mut left_prefix = prefix.clone();
left_prefix.push(false);
stack.push_back((left, left_prefix));
let mut right_prefix = prefix;
right_prefix.push(true);
stack.push_back((right, right_prefix));
}
HuffmanTree::Leaf(char, _) => {
codes.insert(*char, prefix);
}
}
}
codes
}
fn collect_pairs(root: &HuffmanTree<C>) -> Vec<(i32, i32)> {
let mut pairs_unordered: Vec<(i32, i32)> = Vec::new();
let mut pair_refs: Vec<usize> = Vec::new();
let mut tree_ref: HashMap<*const HuffmanTree<C>, usize> = HashMap::new();
let mut queue: VecDeque<&HuffmanTree<C>> = VecDeque::new();
let push_pair = |pairs: &mut Vec<(i32, i32)>, pair: (i32, i32)| {
let pair_index = pairs.len();
pairs.push(pair);
pair_index
};
let root_pair = push_pair(&mut pairs_unordered, (0, 0));
tree_ref.insert(root, root_pair);
queue.push_back(root);
while let Some(node) = queue.pop_front() {
let node_index = *tree_ref
.get(&(node as *const _))
.expect("Missing mapping for current node");
let HuffmanTree::Node(left_node, right_node) = node else {
panic!("Invalid operation: leaf node in queue")
};
let left_value = &mut pairs_unordered[node_index].0;
if let HuffmanTree::Leaf(symbol, _) = left_node.as_ref() {
*left_value = -1 - (*symbol).as_symbol();
} else {
*left_value = pair_refs.len() as i32;
let pair_index = push_pair(&mut pairs_unordered, (0, 0));
tree_ref.insert(left_node.as_ref(), pair_index);
pair_refs.push(pair_index);
queue.push_back(left_node.as_ref());
}
let right_value = &mut pairs_unordered[node_index].1;
if let HuffmanTree::Leaf(symbol, _) = right_node.as_ref() {
*right_value = -1 - (*symbol).as_symbol();
} else {
*right_value = pair_refs.len() as i32;
let pair_index = push_pair(&mut pairs_unordered, (0, 0));
tree_ref.insert(right_node.as_ref(), pair_index);
pair_refs.push(pair_index);
queue.push_back(right_node.as_ref());
}
}
pair_refs.push(root_pair);
pair_refs
.into_iter()
.map(|index| pairs_unordered[index])
.collect()
}
}