use core::num::NonZeroU32;
#[cfg(not(feature = "std"))]
use alloc::boxed::Box;
use crate::error::{LhaResult, LhaError};
use crate::stub_io::Read;
use crate::bitstream::*;
use crate::statictree::*;
use crate::ringbuf::*;
use super::Decoder;
const NUM_COMMANDS: usize = 510;
const NUM_TEMP_CODELEN: usize = 20;
pub trait LhaDecoderConfig {
type RingBuffer: RingBuffer;
const HISTORY_BITS: u32;
const OFFSET_BITS: u32;
}
#[derive(Debug)]
pub struct LhaV2Decoder<C: LhaDecoderConfig, R> {
bit_reader: BitStream<R>,
command_tree: HuffTree,
offset_tree: HuffTree,
remaining_commands: u16,
copy_progress: Option<(u32, NonZeroU32)>,
ringbuf: Box<C::RingBuffer>,
}
macro_rules! impl_lhav2_decoder {
($cfg_name:ident, HISTORY_BITS=$history_bits:literal, OFFSET_BITS=$offset_bits:literal) => {
#[derive(Debug)]
pub struct $cfg_name;
impl LhaDecoderConfig for $cfg_name {
type RingBuffer = RingArrayBuf<{1 << $history_bits - 1}>;
const HISTORY_BITS: u32 = $history_bits;
const OFFSET_BITS: u32 = $offset_bits;
}
};
}
impl_lhav2_decoder!(Lh5DecoderCfg, HISTORY_BITS=14, OFFSET_BITS=4);
impl_lhav2_decoder!(Lh7DecoderCfg, HISTORY_BITS=17, OFFSET_BITS=5);
#[cfg(feature = "lhx")]
impl_lhav2_decoder!(LhxDecoderCfg, HISTORY_BITS=20, OFFSET_BITS=5);
pub type Lh5Decoder<R> = LhaV2Decoder<Lh5DecoderCfg, R>;
pub type Lh7Decoder<R> = LhaV2Decoder<Lh7DecoderCfg, R>;
#[cfg(feature = "lhx")]
pub type LhxDecoder<R> = LhaV2Decoder<LhxDecoderCfg, R>;
impl<C: LhaDecoderConfig, R: Read> LhaV2Decoder<C, R> {
pub fn new(rd: R) -> LhaV2Decoder<C, R> {
let bit_reader = BitStream::new(rd);
let ringbuf = Default::default();
let command_tree = HuffTree::with_capacity(NUM_COMMANDS * 2);
let offset_tree = HuffTree::with_capacity(NUM_TEMP_CODELEN * 2);
LhaV2Decoder {
bit_reader,
ringbuf,
command_tree,
offset_tree,
remaining_commands: 0,
copy_progress: None
}
}
fn read_code_length(&mut self) -> LhaResult<u8, R> {
let mut len: u8 = self.bit_reader.read_bits(3)?;
if len == 7 {
while self.bit_reader.read_bit()? {
len = len.checked_add(1).ok_or_else(||
LhaError::Decompress("code length overflow"))?;
}
}
Ok(len)
}
fn read_code_skip(&mut self, skip_range: u16) -> LhaResult<usize, R> {
let (bits, increment) = match skip_range {
0 => return Ok(1),
1 => (4, 3), _ => (9, 20), };
self.bit_reader.read_bits(bits).map(|skip: usize| skip + increment)
}
fn read_temp_tree(&mut self) -> LhaResult<(), R> {
let mut code_lengths = [0u8; NUM_TEMP_CODELEN];
let num_codes: usize = self.bit_reader.read_bits(5)?;
if num_codes == 0 {
let code = self.bit_reader.read_bits(5)?;
self.offset_tree.set_single(code);
return Ok(());
}
if num_codes > NUM_TEMP_CODELEN {
return Err(LhaError::Decompress("temporary codelen table has invalid size"))
}
for p in code_lengths[0..num_codes.min(3)].iter_mut() {
*p = self.read_code_length()?;
}
let skip: usize = self.bit_reader.read_bits(2)?;
if 3 + skip > num_codes {
return Err(LhaError::Decompress("temporary codelen table has invalid size"))}
for p in code_lengths[3 + skip..num_codes].iter_mut() {
*p = self.read_code_length()?;
}
self.offset_tree.build_tree(&code_lengths[0..num_codes])
.map_err(LhaError::Decompress)?;
Ok(())
}
fn read_command_tree(&mut self) -> LhaResult<(), R> {
let mut code_lengths = [0u8; NUM_COMMANDS];
let num_codes: usize = self.bit_reader.read_bits(9)?;
if num_codes == 0 {
let code = self.bit_reader.read_bits(9)?;
self.command_tree.set_single(code);
return Ok(());
}
if num_codes > NUM_COMMANDS {
return Err(LhaError::Decompress("commands codelen table has invalid size"))
}
let mut index = 0;
'outer: while index < num_codes {
for (n, p) in code_lengths[index..num_codes].iter_mut().enumerate() {
match self.offset_tree.read_entry(&mut self.bit_reader)? {
skip_range @ 0..=2 => {
let skip_count = self.read_code_skip(skip_range)?;
index += n + skip_count;
continue 'outer;
}
code => {
*p = (code - 2) as u8;
}
}
}
break;
}
self.command_tree.build_tree(&code_lengths[0..num_codes])
.map_err(LhaError::Decompress)?;
Ok(())
}
fn read_offset_tree(&mut self) -> LhaResult<(), R> {
debug_assert!(NUM_TEMP_CODELEN >= C::HISTORY_BITS as usize);
let mut code_lengths = [0u8; NUM_TEMP_CODELEN];
let num_codes: usize = self.bit_reader.read_bits(C::OFFSET_BITS)?;
if num_codes == 0 {
let code = self.bit_reader.read_bits(C::OFFSET_BITS)?;
self.offset_tree.set_single(code);
return Ok(());
}
if num_codes > C::HISTORY_BITS as usize {
return Err(LhaError::Decompress("offset codelen table has invalid size"))
}
for p in code_lengths[0..num_codes].iter_mut() {
*p = self.read_code_length()?;
}
self.offset_tree.build_tree(&code_lengths[0..num_codes])
.map_err(LhaError::Decompress)?;
Ok(())
}
fn begin_new_block(&mut self) -> LhaResult<(), R> {
self.remaining_commands = self.bit_reader.read_bits(16)?;
self.read_temp_tree()?;
self.read_command_tree()?;
self.read_offset_tree()
}
#[inline]
fn read_command(&mut self) -> LhaResult<u16, R> {
self.command_tree.read_entry(&mut self.bit_reader)
}
#[inline]
fn read_offset(&mut self) -> LhaResult<u32, R> {
match self.offset_tree.read_entry(&mut self.bit_reader)?.into() {
res @ 0..=1 => Ok(res),
bits => {
let res: u32 = self.bit_reader.read_bits(bits - 1)?;
Ok(res | (1 << (bits - 1)))
}
}
}
fn copy_from_history<'a, I: Iterator<Item=&'a mut u8> + ExactSizeIterator>(
&mut self,
target: I,
offset: usize,
count: usize
) -> LhaResult<(), R>
{
let history_iter = self.ringbuf.iter_from_offset(offset);
let count_after = count - target.len().min(count);
for (t, s) in target.zip(history_iter).take(count) {
*t = s;
}
self.copy_progress = NonZeroU32::new(count_after as u32)
.map(|count| (offset as u32, count));
Ok(())
}
}
impl<C: LhaDecoderConfig, R: Read> Decoder<R> for LhaV2Decoder<C, R>
where R::Error: core::fmt::Debug
{
type Error = R::Error;
fn into_inner(self) -> R {
self.bit_reader.into_inner()
}
fn fill_buffer(&mut self, buf: &mut[u8]) -> LhaResult<(), R> {
let buflen = buf.len();
let mut target = buf.iter_mut();
if let Some((offset, count)) = self.copy_progress {
self.copy_from_history(&mut target,
offset as usize,
count.get() as usize)?;
}
while let Some(dst) = target.next() {
while self.remaining_commands == 0 {
self.begin_new_block()?;
}
self.remaining_commands -= 1;
match self.read_command()? {
code @ 0..=0xff => {
let value = code as u8;
*dst = value;
self.ringbuf.push(value);
}
count => {
let offset = self.read_offset()?;
let index = buflen - target.len() - 1;
target = buf[index..].iter_mut();
self.copy_from_history(&mut target,
offset as usize,
(count - 0x100 + 3).into())?;
}
}
}
Ok(())
}
}
#[cfg(feature = "std")]
#[cfg(test)]
mod tests {
use super::*;
use super::super::DecoderAny;
use std::fs;
use std::io;
#[test]
fn lhav2_works() {
println!("DecoderAny<Empty> {}", core::mem::size_of::<DecoderAny<io::Empty>>());
println!("DecoderAny<fs::File> {}", core::mem::size_of::<DecoderAny<fs::File>>());
println!("Lh7Decoder<Empty> {}", core::mem::size_of::<Lh7Decoder<io::Empty>>());
println!("Lh7Decoder<File> {}", core::mem::size_of::<Lh7Decoder<fs::File>>());
println!("BitStream<File> {}", core::mem::size_of::<BitStream<fs::File>>());
println!("HuffTree {}", core::mem::size_of::<HuffTree>());
println!("Option<(u32, NonZeroU32)> {}", core::mem::size_of::<Option<(u32, NonZeroU32)>>());
println!("Box<C::RingBuffer> {}", core::mem::size_of::<Box<<Lh7DecoderCfg as LhaDecoderConfig>::RingBuffer>>());
}
}