sit-algos 0.3.0

Implementation of decompression algorithms used by StuffIt Expander and related applications
Documentation
use std::io::{self, Read, Seek};

use bitstream_io::{BigEndian, BitRead, BitReader};

use super::huffman_decoder::HuffmanDecoder;

#[derive(thiserror::Error, Debug)]
pub enum Error {
    #[error("Invalid huffman tree")]
    InvalidTree,

    #[error(transparent)]
    Io(#[from] io::Error),
}

pub struct HuffmanReader<R: Read + Seek> {
    inner: bitstream_io::BitReader<R, BigEndian>,
    uncompressed_size: u64,
    offset: u64,
    tree: HuffmanDecoder,
}

impl<R: Read + Seek> HuffmanReader<R> {
    pub fn try_from(inner: R, uncompressed_size: u64) -> Result<Self, Error> {
        let mut inner = bitstream_io::BitReader::new(inner);
        let tree = Self::read_hufftree(&mut inner)?;

        Ok(Self {
            inner,
            uncompressed_size,
            offset: 0,
            tree,
        })
    }

    fn read_hufftree(
        reader: &mut bitstream_io::BitReader<R, BigEndian>,
    ) -> Result<HuffmanDecoder, Error> {
        let mut tree = HuffmanDecoder::new();
        Self::read_hufftree_node(reader, &mut tree, 0)?;
        tree.make_table(false);
        Ok(tree)
    }

    #[inline]
    fn read_hufftree_node(
        reader: &mut BitReader<R, BigEndian>,
        tree: &mut HuffmanDecoder,
        node: i32,
    ) -> Result<(), Error> {
        match reader.read_bit()? {
            true => {
                let value: u8 = reader.read_var(8)?;
                tree.set_leaf_value(node, value as i32);
            }
            false => {
                let left = tree.new_node();
                Self::read_hufftree_node(reader, tree, left)?;

                let right = tree.new_node();
                Self::read_hufftree_node(reader, tree, right)?;

                tree.set_branch(node, false, left);
                tree.set_branch(node, true, right);
            }
        }

        Ok(())
    }

    pub fn into_inner(self) -> R {
        self.inner.into_reader()
    }
}

impl<R: io::Read + io::Seek> io::Read for HuffmanReader<R> {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        for (idx, byte) in buf.iter_mut().enumerate() {
            if self.stream_position()? >= self.stream_len()? {
                return Ok(idx);
            }

            match self.tree.next_symbol(&mut self.inner) {
                Ok(value) => {
                    *byte = value as u8;
                    self.offset += 1;
                }
                Err(e) => return Err(e),
            }
        }

        Ok(buf.len())
    }
}

impl<R: io::Read + io::Seek> io::Seek for HuffmanReader<R> {
    fn seek(&mut self, _: io::SeekFrom) -> io::Result<u64> {
        todo!()
    }

    #[inline]
    fn stream_len(&mut self) -> io::Result<u64> {
        Ok(self.uncompressed_size)
    }

    #[inline]
    fn stream_position(&mut self) -> io::Result<u64> {
        Ok(self.offset)
    }
}