extern crate alloc;
use alloc::boxed::Box;
use alloc::vec::Vec;
use crate::error::Error;
use crate::traits::{Algorithm, Decoder as DecoderTrait, Encoder as EncoderTrait, Progress};
mod lzma_payload;
const ENC_CHUNK_SIZE: usize = 65_536;
#[derive(Debug, Clone, Copy, Default)]
pub struct Lzma2;
impl Algorithm for Lzma2 {
const NAME: &'static str = "lzma2";
type Encoder = Encoder;
type Decoder = Decoder;
fn encoder() -> Encoder {
Encoder::new()
}
fn decoder() -> Decoder {
Decoder::new()
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
struct CompHeader {
unc_top5: u32,
needs_props: bool,
read: u8,
unc_low_hi: u8,
unc_low_lo: u8,
cmp_hi: u8,
cmp_lo: u8,
props: u8,
}
impl CompHeader {
fn unpack(&self) -> (u32, u32, u8) {
let unc_low = ((self.unc_low_hi as u32) << 8) | (self.unc_low_lo as u32);
let unc = (self.unc_top5 << 16) | unc_low;
let cmp = ((self.cmp_hi as u32) << 8) | (self.cmp_lo as u32);
(unc + 1, cmp + 1, self.props)
}
}
enum DecPhase {
Control,
UncompSize { ctrl: u8, idx: u8, hi: u8 },
UncompData { remaining: u32 },
CompHdr(CompHeader),
CompData {
cmp_remaining: u32,
unc_remaining: u32,
},
CompDrain { unc_remaining: u32 },
Done,
}
pub struct Decoder {
phase: DecPhase,
poisoned: bool,
inner: Option<Box<crate::lzma::Decoder>>,
}
impl Decoder {
pub const fn new() -> Self {
Self {
phase: DecPhase::Control,
poisoned: false,
inner: None,
}
}
fn poison(&mut self, e: Error) -> Error {
self.poisoned = true;
e
}
fn prime_inner(&mut self, props: u8, uncompressed: u32) -> Result<(), Error> {
if props >= 9 * 5 * 5 {
return Err(Error::BadHeader);
}
let dict_size: u32 = uncompressed.max(4096);
let unc_u64: u64 = uncompressed as u64;
let mut header = [0u8; 13];
header[0] = props;
header[1..5].copy_from_slice(&dict_size.to_le_bytes());
header[5..13].copy_from_slice(&unc_u64.to_le_bytes());
let inner = self
.inner
.get_or_insert_with(|| Box::new(crate::lzma::Decoder::new()));
inner.reset();
let mut nothing: [u8; 0] = [];
let p = inner.decode(&header, &mut nothing)?;
debug_assert_eq!(p.consumed, header.len());
debug_assert_eq!(p.written, 0);
Ok(())
}
}
impl Default for Decoder {
fn default() -> Self {
Self::new()
}
}
impl DecoderTrait for Decoder {
fn decode(&mut self, input: &[u8], output: &mut [u8]) -> Result<Progress, Error> {
if self.poisoned {
return Err(Error::Corrupt);
}
let mut consumed = 0usize;
let mut written = 0usize;
loop {
let initial_consumed = consumed;
let initial_written = written;
match self.phase {
DecPhase::Control => {
if consumed == input.len() {
return Ok(Progress {
consumed,
written,
done: false,
});
}
let ctrl = input[consumed];
consumed += 1;
match ctrl {
0x00 => {
self.phase = DecPhase::Done;
}
0x01 | 0x02 => {
self.phase = DecPhase::UncompSize {
ctrl,
idx: 0,
hi: 0,
};
}
0xE0..=0xFF => {
self.phase = DecPhase::CompHdr(CompHeader {
unc_top5: (ctrl as u32) & 0x1F,
needs_props: true,
read: 0,
unc_low_hi: 0,
unc_low_lo: 0,
cmp_hi: 0,
cmp_lo: 0,
props: 0,
});
}
0x80..=0xDF => {
return Err(self.poison(Error::Unsupported));
}
_ => {
return Err(self.poison(Error::Corrupt));
}
}
}
DecPhase::UncompSize { ctrl, idx, hi } => {
if consumed == input.len() {
return Ok(Progress {
consumed,
written,
done: false,
});
}
let b = input[consumed];
consumed += 1;
if idx == 0 {
self.phase = DecPhase::UncompSize {
ctrl,
idx: 1,
hi: b,
};
} else {
let size = ((hi as u32) << 8) | (b as u32);
let size = size + 1;
self.phase = DecPhase::UncompData { remaining: size };
}
}
DecPhase::UncompData { remaining } => {
if remaining == 0 {
self.phase = DecPhase::Control;
continue;
}
if consumed == input.len() || written == output.len() {
return Ok(Progress {
consumed,
written,
done: false,
});
}
let in_left = input.len() - consumed;
let out_left = output.len() - written;
let n = core::cmp::min(remaining as usize, core::cmp::min(in_left, out_left));
output[written..written + n].copy_from_slice(&input[consumed..consumed + n]);
consumed += n;
written += n;
let new_remaining = remaining - n as u32;
self.phase = if new_remaining == 0 {
DecPhase::Control
} else {
DecPhase::UncompData {
remaining: new_remaining,
}
};
}
DecPhase::CompHdr(mut hdr) => {
let needed = if hdr.needs_props { 5 } else { 4 };
while hdr.read < needed && consumed < input.len() {
let b = input[consumed];
consumed += 1;
match hdr.read {
0 => hdr.unc_low_hi = b,
1 => hdr.unc_low_lo = b,
2 => hdr.cmp_hi = b,
3 => hdr.cmp_lo = b,
4 => hdr.props = b,
_ => unreachable!(),
}
hdr.read += 1;
}
if hdr.read < needed {
self.phase = DecPhase::CompHdr(hdr);
return Ok(Progress {
consumed,
written,
done: false,
});
}
let (unc, cmp, props) = hdr.unpack();
self.prime_inner(props, unc).map_err(|e| self.poison(e))?;
self.phase = DecPhase::CompData {
cmp_remaining: cmp,
unc_remaining: unc,
};
}
DecPhase::CompData {
mut cmp_remaining,
mut unc_remaining,
} => {
if unc_remaining == 0 {
self.phase = DecPhase::Control;
continue;
}
if cmp_remaining == 0 {
self.phase = DecPhase::CompDrain { unc_remaining };
continue;
}
if consumed == input.len() {
self.phase = DecPhase::CompData {
cmp_remaining,
unc_remaining,
};
return Ok(Progress {
consumed,
written,
done: false,
});
}
if written == output.len() {
self.phase = DecPhase::CompData {
cmp_remaining,
unc_remaining,
};
return Ok(Progress {
consumed,
written,
done: false,
});
}
let inner = match self.inner.as_mut() {
Some(i) => i,
None => {
return Err(self.poison(Error::Corrupt));
}
};
let in_left = input.len() - consumed;
let feed = core::cmp::min(cmp_remaining as usize, in_left);
let out_room = core::cmp::min(unc_remaining as usize, output.len() - written);
let p = inner
.decode(
&input[consumed..consumed + feed],
&mut output[written..written + out_room],
)
.map_err(|e| self.poison(e))?;
consumed += p.consumed;
written += p.written;
cmp_remaining -= p.consumed as u32;
unc_remaining -= p.written as u32;
self.phase = DecPhase::CompData {
cmp_remaining,
unc_remaining,
};
}
DecPhase::CompDrain { mut unc_remaining } => {
if unc_remaining == 0 {
self.phase = DecPhase::Control;
continue;
}
if written == output.len() {
self.phase = DecPhase::CompDrain { unc_remaining };
return Ok(Progress {
consumed,
written,
done: false,
});
}
let inner = match self.inner.as_mut() {
Some(i) => i,
None => return Err(self.poison(Error::Corrupt)),
};
let out_room = core::cmp::min(unc_remaining as usize, output.len() - written);
let p = inner
.finish(&mut output[written..written + out_room])
.map_err(|e| self.poison(e))?;
written += p.written;
unc_remaining -= p.written as u32;
self.phase = DecPhase::CompDrain { unc_remaining };
if p.done && unc_remaining > 0 {
return Err(self.poison(Error::Corrupt));
}
if p.written == 0 && !p.done {
return Ok(Progress {
consumed,
written,
done: false,
});
}
}
DecPhase::Done => {
return Ok(Progress {
consumed,
written,
done: false,
});
}
}
if consumed == initial_consumed && written == initial_written {
return Ok(Progress {
consumed,
written,
done: false,
});
}
}
}
fn finish(&mut self, output: &mut [u8]) -> Result<Progress, Error> {
if self.poisoned {
return Err(Error::Corrupt);
}
let empty: [u8; 0] = [];
let p = self.decode(&empty, output)?;
match self.phase {
DecPhase::Done => Ok(Progress {
consumed: 0,
written: p.written,
done: true,
}),
DecPhase::Control => {
Err(self.poison(Error::UnexpectedEnd))
}
_ => Err(self.poison(Error::UnexpectedEnd)),
}
}
fn reset(&mut self) {
self.phase = DecPhase::Control;
self.poisoned = false;
if let Some(inner) = self.inner.as_mut() {
inner.reset();
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum EncPhase {
Accumulating,
Draining,
NeedEosMarker,
Done,
}
pub struct Encoder {
phase: EncPhase,
in_buf: Vec<u8>,
out_buf: Vec<u8>,
out_pos: usize,
}
impl Encoder {
pub const fn new() -> Self {
Self {
phase: EncPhase::Accumulating,
in_buf: Vec::new(),
out_buf: Vec::new(),
out_pos: 0,
}
}
fn write_uncomp_header(buf: &mut Vec<u8>, size: u32) {
debug_assert!((1..=65_536).contains(&size));
let v = size - 1; buf.push(0x01);
buf.push(((v >> 8) & 0xFF) as u8);
buf.push((v & 0xFF) as u8);
}
fn write_comp_header(buf: &mut Vec<u8>, uncompressed: u32, compressed: u32, props: u8) {
debug_assert!((1..=65_536).contains(&uncompressed));
debug_assert!((1..=65_536).contains(&compressed));
let unc_m1 = uncompressed - 1; let cmp_m1 = compressed - 1; let unc_top5 = ((unc_m1 >> 16) & 0x1F) as u8;
buf.push(0xE0 | unc_top5);
buf.push(((unc_m1 >> 8) & 0xFF) as u8);
buf.push((unc_m1 & 0xFF) as u8);
buf.push(((cmp_m1 >> 8) & 0xFF) as u8);
buf.push((cmp_m1 & 0xFF) as u8);
buf.push(props);
}
fn finalize_chunk(&mut self) {
debug_assert!(!self.in_buf.is_empty());
debug_assert!(self.in_buf.len() <= ENC_CHUNK_SIZE);
self.out_buf.clear();
self.out_pos = 0;
let unc_size = self.in_buf.len() as u32;
let payload = lzma_payload::encode_payload(&self.in_buf);
let comp_total = 6usize + payload.len();
let uncomp_total = 3usize + self.in_buf.len();
let use_compressed = payload.len() <= 65_536 && comp_total < uncomp_total;
if use_compressed {
Self::write_comp_header(
&mut self.out_buf,
unc_size,
payload.len() as u32,
lzma_payload::ENC_PROPS_BYTE,
);
self.out_buf.extend_from_slice(&payload);
} else {
Self::write_uncomp_header(&mut self.out_buf, unc_size);
self.out_buf.extend_from_slice(&self.in_buf);
}
self.in_buf.clear();
self.phase = EncPhase::Draining;
}
fn drain_to(&mut self, output: &mut [u8], written: &mut usize) {
let remaining = self.out_buf.len() - self.out_pos;
let out_left = output.len() - *written;
let n = remaining.min(out_left);
if n > 0 {
output[*written..*written + n]
.copy_from_slice(&self.out_buf[self.out_pos..self.out_pos + n]);
*written += n;
self.out_pos += n;
}
if self.out_pos == self.out_buf.len() {
self.out_buf.clear();
self.out_pos = 0;
if self.phase == EncPhase::Draining {
self.phase = EncPhase::Accumulating;
}
}
}
}
impl Default for Encoder {
fn default() -> Self {
Self::new()
}
}
impl EncoderTrait for Encoder {
fn encode(&mut self, input: &[u8], output: &mut [u8]) -> Result<Progress, Error> {
let mut consumed = 0usize;
let mut written = 0usize;
loop {
match self.phase {
EncPhase::Accumulating => {
if consumed == input.len() {
return Ok(Progress {
consumed,
written,
done: false,
});
}
let room = ENC_CHUNK_SIZE - self.in_buf.len();
let take = (input.len() - consumed).min(room);
self.in_buf
.extend_from_slice(&input[consumed..consumed + take]);
consumed += take;
if self.in_buf.len() == ENC_CHUNK_SIZE {
self.finalize_chunk();
}
}
EncPhase::Draining => {
if written == output.len() {
return Ok(Progress {
consumed,
written,
done: false,
});
}
self.drain_to(output, &mut written);
}
EncPhase::NeedEosMarker | EncPhase::Done => {
return Err(Error::Corrupt);
}
}
}
}
fn finish(&mut self, output: &mut [u8]) -> Result<Progress, Error> {
let mut written = 0usize;
loop {
match self.phase {
EncPhase::Accumulating => {
if !self.in_buf.is_empty() {
self.finalize_chunk();
} else {
self.out_buf.clear();
self.out_buf.push(0x00);
self.out_pos = 0;
self.phase = EncPhase::NeedEosMarker;
}
}
EncPhase::Draining => {
if written == output.len() {
return Ok(Progress {
consumed: 0,
written,
done: false,
});
}
self.drain_to(output, &mut written);
}
EncPhase::NeedEosMarker => {
if written == output.len() {
return Ok(Progress {
consumed: 0,
written,
done: false,
});
}
self.drain_to(output, &mut written);
if self.out_buf.is_empty() {
self.phase = EncPhase::Done;
}
}
EncPhase::Done => {
return Ok(Progress {
consumed: 0,
written,
done: true,
});
}
}
}
}
fn reset(&mut self) {
self.phase = EncPhase::Accumulating;
self.in_buf.clear();
self.out_buf.clear();
self.out_pos = 0;
}
}