use crate::error::Result;
const BITS: usize = 16;
const HSIZE: usize = 69001;
const INIT_BITS: usize = 9;
#[inline]
fn maxcode(n: usize) -> usize {
(1 << n) - 1
}
pub struct Lzw {
max_bits: u8,
block_mode: bool,
}
impl Lzw {
pub fn new(max_bits: u8, block_mode: bool) -> Self {
Lzw { max_bits, block_mode }
}
pub fn decomp(&mut self, input: &[u8]) -> Result<Vec<u8>> {
let max_bits = self.max_bits as usize;
let block_mode = self.block_mode;
let maxmaxcode: usize = 1 << max_bits;
let mut block_buf = [0u8; BITS];
let mut block_bit_offset: usize = 0;
let mut block_bit_size: usize = 0;
let mut input_pos: usize = 0;
let mut clear_flag = false;
let mut n_bits: usize = INIT_BITS;
let mut maxcode_val = maxcode(n_bits);
let mut free_ent: usize = if block_mode { 257 } else { 256 };
let mut tab_prefix: Vec<u16> = vec![0; HSIZE];
let mut tab_suffix: Vec<u8> = vec![0; HSIZE];
let mut output: Vec<u8> = Vec::new();
let mut stack: Vec<u8> = Vec::with_capacity(8192);
macro_rules! getcode {
() => {{
if clear_flag || block_bit_offset >= block_bit_size || free_ent > maxcode_val {
if free_ent > maxcode_val {
n_bits += 1;
if n_bits == max_bits {
maxcode_val = maxmaxcode;
} else {
maxcode_val = maxcode(n_bits);
}
}
if clear_flag {
n_bits = INIT_BITS;
maxcode_val = maxcode(n_bits);
clear_flag = false;
}
let avail = input.len().saturating_sub(input_pos);
let to_read = n_bits.min(avail);
if to_read == 0 {
None::<usize>
} else {
block_buf[..to_read].copy_from_slice(&input[input_pos..input_pos + to_read]);
input_pos += to_read;
block_bit_offset = 0;
let bit_count = (to_read << 3) as isize - (n_bits as isize - 1);
if bit_count <= 0 {
None::<usize>
} else {
block_bit_size = bit_count as usize;
let bp = block_bit_offset >> 3;
let r_off = block_bit_offset & 7;
let mut code = (block_buf[bp] as usize) >> r_off;
let mut bits_got = 8 - r_off;
if bits_got < n_bits {
code |= (block_buf[bp + 1] as usize) << bits_got;
bits_got += 8;
}
if bits_got < n_bits {
code |= (block_buf[bp + 2] as usize) << bits_got;
}
code &= (1usize << n_bits) - 1;
block_bit_offset += n_bits;
Some(code)
}
}
} else {
let bp = block_bit_offset >> 3;
let r_off = block_bit_offset & 7;
let mut code = (block_buf[bp] as usize) >> r_off;
let mut bits_got = 8 - r_off;
if bits_got < n_bits {
code |= (block_buf[bp + 1] as usize) << bits_got;
bits_got += 8;
}
if bits_got < n_bits {
code |= (block_buf[bp + 2] as usize) << bits_got;
}
code &= (1usize << n_bits) - 1;
block_bit_offset += n_bits;
Some(code)
}
}};
}
let first = match getcode!() {
Some(c) => c,
None => return Ok(output),
};
if first > 255 {
return Err(crate::error::ArchiveError::DecompressionFailed {
entry: String::new(),
reason: "First code > 255".to_string(),
});
}
let mut finchar = first as u8;
let mut oldcode = first;
output.push(finchar);
#[allow(clippy::while_let_loop)]
loop {
let incode = match getcode!() {
Some(c) => c,
None => break,
};
if incode == 256 && block_mode {
tab_prefix.fill(0);
free_ent = 257;
clear_flag = true;
let next = match getcode!() {
Some(c) => c,
None => break,
};
if next > 255 {
return Err(crate::error::ArchiveError::DecompressionFailed {
entry: String::new(),
reason: format!("Code {} after CLEAR is not a literal", next),
});
}
finchar = next as u8;
oldcode = next;
output.push(finchar);
continue;
}
let mut code = incode;
if code >= free_ent {
if code > free_ent {
return Err(crate::error::ArchiveError::DecompressionFailed {
entry: String::new(),
reason: format!("Invalid code {} > free_ent {}", code, free_ent),
});
}
stack.push(finchar);
code = oldcode;
}
while code >= 256 {
stack.push(tab_suffix[code]);
code = tab_prefix[code] as usize;
}
finchar = code as u8;
output.push(finchar);
while let Some(c) = stack.pop() {
output.push(c);
}
if free_ent < maxmaxcode {
tab_prefix[free_ent] = oldcode as u16;
tab_suffix[free_ent] = finchar;
free_ent += 1;
}
oldcode = incode;
}
Ok(output)
}
}