use std::io::{Read, Seek, Write};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use crate::util::{consume_zero_byte_length_stream, peek_u8, read_bytes, M3_MARKER, M4_MARKER};
pub fn decompress<I>(reader: &mut I, expected_size: Option<usize>) -> Result<Vec<u8>, crate::Error>
where
I: Read + Seek,
{
let mut result = Vec::<u8>::with_capacity(expected_size.unwrap_or_default());
let mut lbcur: u64;
let mut lblen: usize;
let mut state: usize = 0;
let mut n_state: usize;
if peek_u8(reader)? >= 22 {
let len: usize = (reader.read_u8()? - 17) as usize;
result.write_all(&read_bytes(reader, len)?)?;
state = 4;
} else if peek_u8(reader)? >= 18 {
n_state = (reader.read_u8()? - 17) as usize;
state = n_state;
result.write_all(&read_bytes(reader, n_state)?)?;
}
loop
{
let inst = reader.read_u8()?;
if (u32::from(inst) & 0xc0) != 0 {
lbcur = result.len() as u64
- u64::from(
(u32::from(reader.read_u8()?) << 3) + ((u32::from(inst) >> 2) & 0x7) + 1,
);
lblen = ((inst >> 5) as usize) + 1;
n_state = (inst & 0x3) as usize;
} else if (u32::from(inst) & M3_MARKER) != 0 {
lblen = ((inst & 0x1f) as usize).wrapping_add(2);
if lblen == 2 {
let offset = consume_zero_byte_length_stream(reader)?;
lblen += (offset * 255 + 31 + u64::from(reader.read_u8()?)) as usize;
}
n_state = reader.read_u16::<LittleEndian>()? as usize;
lbcur = result.len() as u64 - ((n_state >> 2).wrapping_add(1) as u64);
n_state &= 0x3;
} else if u32::from(inst) & M4_MARKER != 0 {
lblen = ((inst & 0x7) as usize).wrapping_add(2);
if lblen == 2 {
let offset = consume_zero_byte_length_stream(reader)?;
lblen += (offset * 255 + 7 + u64::from(reader.read_u8()?)) as usize;
}
n_state = reader.read_u16::<LittleEndian>()? as usize;
lbcur = (result.len() as u64).wrapping_sub(
((i32::from(inst & 0x8) << 11) as u64).wrapping_add((n_state >> 2_usize) as u64),
);
n_state &= 0x3;
if lbcur == result.len() as u64 {
break;
}
lbcur -= 16384;
} else if state == 0 {
let mut len: usize = (inst + 3) as usize;
if len == 3 {
let offset = consume_zero_byte_length_stream(reader)?;
len += (offset * 255 + 15 + u64::from(reader.read_u8()?)) as usize;
}
result.write_all(&read_bytes(reader, len)?)?;
state = 4;
continue;
} else if state != 4 {
n_state = (u32::from(inst) & 0x3) as usize;
lbcur = (result.len() as u64).wrapping_sub(u64::from(
(u32::from(inst) >> 2)
.wrapping_add((u32::from(reader.read_u8()?) << 2).wrapping_add(1)),
));
lblen = 2;
} else {
n_state = (inst & 0x3) as usize;
lbcur = (result.len() as u64)
- (((u32::from(inst) >> 2) + (u32::from(reader.read_u8()?) << 2) + 2049) as isize)
as u64;
lblen = 3;
}
for i in 0..lblen {
let val = result[lbcur as usize + i];
result.write_u8(val)?;
}
state = n_state;
result.write_all(&read_bytes(reader, n_state)?)?;
}
if lblen != 3 {
return Err(crate::Error::Unknown);
}
result.flush()?;
Ok(result)
}
pub fn decompress_all(data: &[u8], expected_size: Option<usize>) -> Result<Vec<u8>, crate::Error> {
let mut data_reader = std::io::Cursor::new(data);
decompress(&mut data_reader, expected_size)
}