mod huffman_tree;
mod read_byte;
use huffman_tree::HuffmanTree;
use read_byte::ReadByte;
use std::io;
use crate::Error;
const CIRC_SIZE: usize = 8192;
const BLOCK_SIZE: usize = 0x1fff0;
#[derive(Default)]
struct GetBitContext {
newbits: u32,
bitsavail: usize,
}
impl GetBitContext {
fn get<R: io::Read>(&mut self, reader: &mut R) -> std::io::Result<bool> {
let b = (self.newbits >> 31) & 1;
self.bitsavail -= 1;
if self.bitsavail < 16 {
self.newbits |= (reader.read_byte()? as u32) << 8;
self.newbits |= reader.read_byte()? as u32;
self.bitsavail += 16;
}
self.newbits <<= 1;
Ok(b != 0)
}
}
pub struct LzhReader<'a, R: io::Read + Sized> {
inner: &'a mut R,
uncompressed_size: isize,
hufftree: HuffmanTree<256>,
lz_ptr: usize,
lz_buff: [u8; CIRC_SIZE],
lz_lengths: HuffmanTree<64>,
lz_offsets: HuffmanTree<128>,
outstat: OutStatSeen,
context: GetBitContext,
output: Vec<u8>,
savechar: u8,
}
#[derive(Debug, Copy, Clone, Default)]
enum OutStatSeen {
#[default]
None,
Esc1,
Esc2,
}
impl<'a, R: io::Read + io::Seek> LzhReader<'a, R> {
pub fn try_new(reader: &'a mut R, uncompressed_size: usize) -> Result<Self, Error> {
Ok(Self {
inner: reader,
lz_ptr: 0,
context: GetBitContext::default(),
uncompressed_size: uncompressed_size as isize,
hufftree: HuffmanTree::new(),
lz_lengths: HuffmanTree::new(),
lz_offsets: HuffmanTree::new(),
lz_buff: [Default::default(); CIRC_SIZE],
outstat: Default::default(),
output: Vec::with_capacity(uncompressed_size),
savechar: 0,
})
}
fn decode_bytes(&mut self) -> io::Result<()> {
self.lz_buff[CIRC_SIZE - 3] = 0;
self.lz_buff[CIRC_SIZE - 2] = 0;
self.lz_buff[CIRC_SIZE - 1] = 0;
self.lz_ptr = 0;
while self.uncompressed_size != 0 {
self.hufftree.read_from(&mut self.inner)?;
self.lz_lengths.read_from(&mut self.inner)?;
self.lz_offsets.read_from(&mut self.inner)?;
let mut block_count = 0;
self.context.newbits = (self.inner.read_byte()? as u32) << 8;
self.context.newbits |= self.inner.read_byte()? as u32;
self.context.newbits <<= 16;
self.context.bitsavail = 16;
while block_count < BLOCK_SIZE && self.uncompressed_size != 0 {
if self.get_bit()? {
let ch = self.get_huff_byte_tree()?;
self.outch(ch);
block_count += 2;
} else {
let mut lz_length = self.get_huff_byte_length()? as i32;
let lz_offset = self.get_huff_byte_offset()? as i32;
let lz_offset = (lz_offset << 6) | (self.get6bits()? as i32);
let mut bptr = self.lz_ptr as isize - lz_offset as isize;
while lz_length > 0 {
lz_length -= 1;
let byte =
self.lz_buff[(bptr & (CIRC_SIZE.cast_signed() - 1)).cast_unsigned()];
bptr += 1;
self.outch(byte);
}
block_count += 3;
}
}
}
Ok(())
}
fn outch(&mut self, mut ch: u8) {
const ESC1: u8 = 0x81;
const ESC2: u8 = 0x82;
self.lz_buff[self.lz_ptr & (CIRC_SIZE - 1)] = ch;
self.lz_ptr += 1;
match self.outstat {
OutStatSeen::None => {
if ch == ESC1 && self.uncompressed_size != 1 {
self.outstat = OutStatSeen::Esc1;
} else {
self.savechar = ch;
self.output.push(ch);
self.uncompressed_size -= 1;
}
}
OutStatSeen::Esc1 => {
if ch == ESC2 {
self.outstat = OutStatSeen::Esc2;
} else {
self.savechar = ESC1;
self.output.push(ESC1);
self.uncompressed_size -= 1;
if self.uncompressed_size == 0 {
return;
}
if ch == ESC1 && self.uncompressed_size != 1 {
return;
}
self.outstat = OutStatSeen::None;
self.savechar = ch;
self.output.push(ch);
self.uncompressed_size -= 1;
}
}
OutStatSeen::Esc2 => {
self.outstat = OutStatSeen::None;
if ch != 0 {
while ch - 1 != 0 {
ch -= 1;
self.output.push(self.savechar);
self.uncompressed_size -= 1;
if self.uncompressed_size == 0 {
return;
}
}
} else {
self.output.push(ESC1);
self.uncompressed_size -= 1;
if self.uncompressed_size == 0 {
return;
}
self.savechar = ESC2;
self.output.push(self.savechar);
self.uncompressed_size -= 1;
}
}
}
}
fn get6bits(&mut self) -> io::Result<u8> {
let mut cn: u32;
let LzhReader { context, inner, .. } = self;
let b = (context.newbits >> 26) & 0x3f;
context.bitsavail -= 6;
context.newbits <<= 6;
if context.bitsavail < 16 {
cn = (inner.read_byte()? as u32) << 8;
cn |= inner.read_byte()? as u32;
context.newbits |= cn << (16 - context.bitsavail);
context.bitsavail += 16;
}
Ok(b as u8)
}
fn get_huff_byte_tree(&mut self) -> io::Result<u8> {
let LzhReader {
inner,
context,
hufftree,
..
} = self;
let mut np = 0;
while hufftree.entries[np].flag == 0 {
let s = context.get(inner)?;
np = if s {
hufftree.entries[np].one as usize
} else {
hufftree.entries[np].zero as usize
}
}
Ok(hufftree.entries[np].byte)
}
fn get_huff_byte_offset(&mut self) -> io::Result<u8> {
let LzhReader {
inner,
context,
lz_offsets,
..
} = self;
let mut np = 0;
while lz_offsets.entries[np].flag == 0 {
let s = context.get(inner)?;
np = if s {
lz_offsets.entries[np].one as usize
} else {
lz_offsets.entries[np].zero as usize
}
}
Ok(lz_offsets.entries[np].byte)
}
fn get_huff_byte_length(&mut self) -> io::Result<u8> {
let LzhReader {
inner,
context,
lz_lengths,
..
} = self;
let mut np = 0;
while lz_lengths.entries[np].flag == 0 {
let s = context.get(inner)?;
np = if s {
lz_lengths.entries[np].one as usize
} else {
lz_lengths.entries[np].zero as usize
}
}
Ok(lz_lengths.entries[np].byte)
}
fn get_bit(&mut self) -> io::Result<bool> {
let LzhReader { context, inner, .. } = self;
context.get(inner)
}
}
impl<R: io::Read + std::io::Seek> std::io::Read for LzhReader<'_, R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.decode_bytes()?;
buf.copy_from_slice(&self.output);
Ok(buf.len())
}
}