#![allow(clippy::collapsible_match, clippy::collapsible_if)]
use alloc::boxed::Box;
use alloc::vec::Vec;
use crate::error::Error;
use crate::traits::{Algorithm, RawDecoder, RawEncoder, RawProgress};
mod lzma2_decoder;
mod lzma2_encoder;
use lzma2_decoder::{Lzma2Props, LzmaCore, lzma2_dict_size};
use lzma2_encoder::{EncoderParams, LZMA2_PROPS_BYTE, encode_lzma_chunk};
const STREAM_MAGIC: [u8; 6] = [0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00];
const FOOTER_MAGIC: [u8; 2] = [0x59, 0x5A];
const STREAM_FLAGS_CHECK_CRC32: u8 = 0x01;
const STREAM_FLAGS: [u8; 2] = [0x00, STREAM_FLAGS_CHECK_CRC32];
const FILTER_ID_LZMA2: u8 = 0x21;
const LZMA2_DICT_FLAG: u8 = 0x14;
const LZMA2_DICT_SIZE: u32 = 4 * 1024 * 1024;
const LZMA2_CHUNK_MAX: usize = 65_536;
#[derive(Clone, Copy)]
struct Crc32 {
state: u32,
}
impl Crc32 {
const fn new() -> Self {
Self { state: 0xFFFF_FFFF }
}
fn update(&mut self, data: &[u8]) {
let mut s = self.state;
for &b in data {
let idx = ((s ^ b as u32) & 0xFF) as usize;
s = (s >> 8) ^ CRC32_TABLE[idx];
}
self.state = s;
}
const fn finalize(self) -> u32 {
self.state ^ 0xFFFF_FFFF
}
}
const CRC32_TABLE: [u32; 256] = {
let mut table = [0u32; 256];
let mut i = 0u32;
while i < 256 {
let mut c = i;
let mut k = 0;
while k < 8 {
c = if c & 1 != 0 {
0xEDB8_8320 ^ (c >> 1)
} else {
c >> 1
};
k += 1;
}
table[i as usize] = c;
i += 1;
}
table
};
fn crc32(data: &[u8]) -> u32 {
let mut c = Crc32::new();
c.update(data);
c.finalize()
}
fn varint_encode(mut value: u64, out: &mut Vec<u8>) {
while value >= 0x80 {
out.push(((value & 0x7F) as u8) | 0x80);
value >>= 7;
}
out.push(value as u8);
}
fn varint_decode(buf: &[u8], pos: &mut usize) -> Result<Option<u64>, Error> {
let start = *pos;
let mut value: u64 = 0;
let mut shift: u32 = 0;
let mut i = start;
loop {
if i >= buf.len() {
return Ok(None);
}
let b = buf[i];
i += 1;
if shift >= 63 && (b as u64) >> (63 - shift.min(63)) != 0 {
return Err(Error::Corrupt);
}
value |= ((b & 0x7F) as u64) << shift;
if b & 0x80 == 0 {
if b == 0 && i - start > 1 {
return Err(Error::Corrupt);
}
*pos = i;
return Ok(Some(value));
}
shift += 7;
if shift > 63 {
return Err(Error::Corrupt);
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct EncoderConfig {
pub level: u8,
}
impl Default for EncoderConfig {
fn default() -> Self {
Self { level: 6 }
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Xz;
impl Algorithm for Xz {
const NAME: &'static str = "xz";
type Encoder = Encoder;
type Decoder = Decoder;
type EncoderConfig = EncoderConfig;
type DecoderConfig = ();
fn encoder_with(c: Self::EncoderConfig) -> Encoder {
Encoder::with_config(c)
}
fn decoder_with(_: ()) -> Decoder {
Decoder::new()
}
}
fn build_stream_header() -> [u8; 12] {
let crc = crc32(&STREAM_FLAGS).to_le_bytes();
[
STREAM_MAGIC[0],
STREAM_MAGIC[1],
STREAM_MAGIC[2],
STREAM_MAGIC[3],
STREAM_MAGIC[4],
STREAM_MAGIC[5],
STREAM_FLAGS[0],
STREAM_FLAGS[1],
crc[0],
crc[1],
crc[2],
crc[3],
]
}
fn build_block_header() -> Vec<u8> {
let body_then_pad = {
let total = 12usize;
let mut h = Vec::with_capacity(total);
h.push(0x02); h.push(0x00); h.push(FILTER_ID_LZMA2);
h.push(0x01); h.push(LZMA2_DICT_FLAG);
while h.len() < total - 4 {
h.push(0x00);
}
h
};
let mut out = body_then_pad;
let crc = crc32(&out).to_le_bytes();
out.extend_from_slice(&crc);
debug_assert_eq!(out.len() % 4, 0);
out
}
fn build_stream_footer(index_size: u32) -> [u8; 12] {
debug_assert!(index_size >= 4 && index_size.is_multiple_of(4));
let stored_back = (index_size / 4) - 1;
let back_le = stored_back.to_le_bytes();
let mut body = [0u8; 6];
body[..4].copy_from_slice(&back_le);
body[4] = STREAM_FLAGS[0];
body[5] = STREAM_FLAGS[1];
let crc = crc32(&body).to_le_bytes();
[
crc[0],
crc[1],
crc[2],
crc[3],
body[0],
body[1],
body[2],
body[3],
body[4],
body[5],
FOOTER_MAGIC[0],
FOOTER_MAGIC[1],
]
}
fn build_index(records: &[(u64, u64)]) -> Vec<u8> {
let mut body = Vec::new();
body.push(0x00); varint_encode(records.len() as u64, &mut body);
for &(unpadded, uncompressed) in records {
varint_encode(unpadded, &mut body);
varint_encode(uncompressed, &mut body);
}
while body.len() % 4 != 0 {
body.push(0x00);
}
let crc = crc32(&body).to_le_bytes();
body.extend_from_slice(&crc);
debug_assert_eq!(body.len() % 4, 0);
body
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum EncPhase {
StreamHeader,
BlockHeader,
Body,
DrainPending,
BlockTrailer,
Tail,
Done,
}
pub struct Encoder {
phase: EncPhase,
pending: Vec<u8>,
pending_idx: usize,
in_buf: Vec<u8>,
check: Crc32,
uncompressed_total: u64,
compressed_payload_bytes: u64,
block_header_len: u64,
enc_params: EncoderParams,
}
impl Encoder {
pub fn new() -> Self {
Self::with_config(EncoderConfig::default())
}
pub fn with_config(config: EncoderConfig) -> Self {
let header = build_stream_header();
let mut pending = Vec::with_capacity(12);
pending.extend_from_slice(&header);
Self {
phase: EncPhase::StreamHeader,
pending,
pending_idx: 0,
in_buf: Vec::new(),
check: Crc32::new(),
uncompressed_total: 0,
compressed_payload_bytes: 0,
block_header_len: build_block_header().len() as u64,
enc_params: EncoderParams::from_level(config.level),
}
}
fn drain_pending(&mut self, output: &mut [u8], written: &mut usize) -> bool {
while self.pending_idx < self.pending.len() && *written < output.len() {
output[*written] = self.pending[self.pending_idx];
*written += 1;
self.pending_idx += 1;
}
if self.pending_idx >= self.pending.len() {
self.pending.clear();
self.pending_idx = 0;
true
} else {
false
}
}
fn stage_chunk(&mut self, data: &[u8]) {
debug_assert!(!data.is_empty() && data.len() <= LZMA2_CHUNK_MAX);
let compressed = encode_lzma_chunk(data, LZMA2_DICT_SIZE, self.enc_params);
let use_compressed =
!compressed.is_empty() && compressed.len() <= 65_536 && compressed.len() < data.len();
if use_compressed {
self.stage_compressed_chunk(data, &compressed);
} else {
self.stage_uncompressed_chunk(data);
}
}
fn stage_compressed_chunk(&mut self, data: &[u8], compressed: &[u8]) {
debug_assert!(!data.is_empty() && data.len() <= LZMA2_CHUNK_MAX);
debug_assert!(!compressed.is_empty() && compressed.len() <= 65_536);
let uncomp_size_minus_1 = (data.len() - 1) as u32; let uncomp_top = ((uncomp_size_minus_1 >> 16) & 0x1F) as u8; let control: u8 = 0xE0 | uncomp_top;
let uncomp_mid = ((uncomp_size_minus_1 >> 8) & 0xFF) as u8;
let uncomp_lo = (uncomp_size_minus_1 & 0xFF) as u8;
let comp_size_minus_1 = (compressed.len() - 1) as u16;
let comp_hi = (comp_size_minus_1 >> 8) as u8;
let comp_lo = (comp_size_minus_1 & 0xFF) as u8;
let header_len = 6usize;
self.pending.reserve(header_len + compressed.len());
self.pending.push(control);
self.pending.push(uncomp_mid);
self.pending.push(uncomp_lo);
self.pending.push(comp_hi);
self.pending.push(comp_lo);
self.pending.push(LZMA2_PROPS_BYTE);
self.pending.extend_from_slice(compressed);
self.pending_idx = 0;
self.compressed_payload_bytes += (header_len + compressed.len()) as u64;
}
fn stage_uncompressed_chunk(&mut self, data: &[u8]) {
debug_assert!(!data.is_empty() && data.len() <= LZMA2_CHUNK_MAX);
let control: u8 = 0x01; let size_minus_1 = (data.len() - 1) as u16;
self.pending.reserve(3 + data.len());
self.pending.push(control);
self.pending.push((size_minus_1 >> 8) as u8);
self.pending.push((size_minus_1 & 0xFF) as u8);
self.pending.extend_from_slice(data);
self.pending_idx = 0;
self.compressed_payload_bytes += 3 + data.len() as u64;
}
fn stage_block_trailer(&mut self) {
self.pending.push(0x00);
self.compressed_payload_bytes += 1;
let unpadded_no_pad = self.block_header_len + self.compressed_payload_bytes + 4;
let pad = (4 - (unpadded_no_pad % 4) as usize) % 4;
for _ in 0..pad {
self.pending.push(0x00);
}
let check = self.check.finalize().to_le_bytes();
self.pending.extend_from_slice(&check);
self.pending_idx = 0;
}
}
impl Default for Encoder {
fn default() -> Self {
Self::new()
}
}
impl RawEncoder for Encoder {
fn raw_encode(&mut self, input: &[u8], output: &mut [u8]) -> Result<RawProgress, Error> {
let mut consumed = 0usize;
let mut written = 0usize;
loop {
let init_c = consumed;
let init_w = written;
let init_phase = self.phase;
match self.phase {
EncPhase::StreamHeader => {
if self.drain_pending(output, &mut written) {
let bh = build_block_header();
self.pending.extend_from_slice(&bh);
self.pending_idx = 0;
self.phase = EncPhase::BlockHeader;
} else {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
}
EncPhase::BlockHeader => {
if self.drain_pending(output, &mut written) {
self.phase = EncPhase::Body;
} else {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
}
EncPhase::Body => {
while consumed < input.len() && self.in_buf.len() < LZMA2_CHUNK_MAX {
let take =
(LZMA2_CHUNK_MAX - self.in_buf.len()).min(input.len() - consumed);
self.in_buf
.extend_from_slice(&input[consumed..consumed + take]);
consumed += take;
}
if self.in_buf.len() == LZMA2_CHUNK_MAX {
let data = core::mem::take(&mut self.in_buf);
self.check.update(&data);
self.uncompressed_total += data.len() as u64;
self.stage_chunk(&data);
self.phase = EncPhase::DrainPending;
} else {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
}
EncPhase::DrainPending => {
if self.drain_pending(output, &mut written) {
self.phase = EncPhase::Body;
} else {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
}
_ => {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
}
if consumed == init_c && written == init_w && self.phase == init_phase {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
}
}
fn raw_finish(&mut self, output: &mut [u8]) -> Result<RawProgress, Error> {
let mut written = 0usize;
loop {
let init_w = written;
let init_phase = self.phase;
match self.phase {
EncPhase::StreamHeader => {
if self.drain_pending(output, &mut written) {
let bh = build_block_header();
self.pending.extend_from_slice(&bh);
self.pending_idx = 0;
self.phase = EncPhase::BlockHeader;
} else {
return Ok(RawProgress {
consumed: 0,
written,
done: false,
});
}
}
EncPhase::BlockHeader => {
if self.drain_pending(output, &mut written) {
self.phase = EncPhase::Body;
} else {
return Ok(RawProgress {
consumed: 0,
written,
done: false,
});
}
}
EncPhase::Body => {
if !self.in_buf.is_empty() {
let data = core::mem::take(&mut self.in_buf);
self.check.update(&data);
self.uncompressed_total += data.len() as u64;
self.stage_chunk(&data);
self.phase = EncPhase::DrainPending;
} else {
self.stage_block_trailer();
self.phase = EncPhase::BlockTrailer;
}
}
EncPhase::DrainPending => {
if self.drain_pending(output, &mut written) {
self.phase = EncPhase::Body;
} else {
return Ok(RawProgress {
consumed: 0,
written,
done: false,
});
}
}
EncPhase::BlockTrailer => {
if self.drain_pending(output, &mut written) {
let unpadded_size =
self.block_header_len + self.compressed_payload_bytes + 4;
let idx = build_index(&[(unpadded_size, self.uncompressed_total)]);
let footer = build_stream_footer(idx.len() as u32);
self.pending.extend_from_slice(&idx);
self.pending.extend_from_slice(&footer);
self.pending_idx = 0;
self.phase = EncPhase::Tail;
} else {
return Ok(RawProgress {
consumed: 0,
written,
done: false,
});
}
}
EncPhase::Tail => {
if self.drain_pending(output, &mut written) {
self.phase = EncPhase::Done;
return Ok(RawProgress {
consumed: 0,
written,
done: true,
});
}
return Ok(RawProgress {
consumed: 0,
written,
done: false,
});
}
EncPhase::Done => {
return Ok(RawProgress {
consumed: 0,
written,
done: true,
});
}
}
if written == init_w && self.phase == init_phase {
if matches!(self.phase, EncPhase::Done) {
return Ok(RawProgress {
consumed: 0,
written,
done: true,
});
}
return Ok(RawProgress {
consumed: 0,
written,
done: false,
});
}
}
}
fn raw_reset(&mut self) {
let params = self.enc_params;
let header = build_stream_header();
let mut pending = Vec::with_capacity(12);
pending.extend_from_slice(&header);
*self = Self {
phase: EncPhase::StreamHeader,
pending,
pending_idx: 0,
in_buf: Vec::new(),
check: Crc32::new(),
uncompressed_total: 0,
compressed_payload_bytes: 0,
block_header_len: build_block_header().len() as u64,
enc_params: params,
};
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum DecPhase {
StreamHeader,
BlockOrIndex,
BlockHeader,
Lzma2Control,
Lzma2Data,
Lzma2CompHeader,
Lzma2CompBuffer,
Lzma2CompDrain,
BlockPadding,
BlockCheck,
Index,
StreamFooter,
Done,
}
pub struct Decoder {
phase: DecPhase,
scratch: Vec<u8>,
scratch_want: usize,
check_id: u8,
block_header_size: usize,
chunk_remaining: usize,
block_header_size_seen: u64,
block_compressed_seen: u64,
block_uncompressed_seen: u64,
check: Crc32,
blocks: Vec<(u64, u64)>,
index_buf: Vec<u8>,
index_records_remaining: u64,
index_records_total: u64,
index_pos: usize,
lzma2_dict_size: u32,
lzma_core: Option<Box<LzmaCore>>,
expecting_block_first_chunk: bool,
comp_ctrl: u8,
comp_uncomp_size: u32,
comp_size: u32,
comp_buf: Vec<u8>,
comp_decoded: Vec<u8>,
comp_decoded_pos: usize,
last_props: u8,
poisoned: bool,
}
impl Decoder {
pub fn new() -> Self {
Self {
phase: DecPhase::StreamHeader,
scratch: Vec::new(),
scratch_want: 12,
check_id: 0,
block_header_size: 0,
chunk_remaining: 0,
block_header_size_seen: 0,
block_compressed_seen: 0,
block_uncompressed_seen: 0,
check: Crc32::new(),
blocks: Vec::new(),
index_buf: Vec::new(),
index_records_remaining: 0,
index_records_total: 0,
index_pos: 0,
lzma2_dict_size: 0,
lzma_core: None,
expecting_block_first_chunk: false,
comp_ctrl: 0,
comp_uncomp_size: 0,
comp_size: 0,
comp_buf: Vec::new(),
comp_decoded: Vec::new(),
comp_decoded_pos: 0,
last_props: 0,
poisoned: false,
}
}
fn poison(&mut self, e: Error) -> Error {
self.poisoned = true;
e
}
fn fill_scratch(&mut self, input: &[u8], consumed: &mut usize) -> bool {
let need = self.scratch_want.saturating_sub(self.scratch.len());
let take = need.min(input.len() - *consumed);
if take > 0 {
self.scratch
.extend_from_slice(&input[*consumed..*consumed + take]);
*consumed += take;
}
self.scratch.len() >= self.scratch_want
}
fn parse_stream_header(&mut self) -> Result<(), Error> {
if self.scratch[..6] != STREAM_MAGIC {
return Err(self.poison(Error::BadHeader));
}
if self.scratch[6] != 0 {
return Err(self.poison(Error::Unsupported));
}
let check_id = self.scratch[7];
if check_id & 0xF0 != 0 {
return Err(self.poison(Error::Unsupported));
}
match check_id & 0x0F {
0x00 | 0x01 | 0x04 | 0x0A => {}
_ => return Err(self.poison(Error::Unsupported)),
}
self.check_id = check_id & 0x0F;
let stored_crc = u32::from_le_bytes([
self.scratch[8],
self.scratch[9],
self.scratch[10],
self.scratch[11],
]);
if stored_crc != crc32(&self.scratch[6..8]) {
return Err(self.poison(Error::ChecksumMismatch));
}
Ok(())
}
fn check_size(&self) -> usize {
match self.check_id {
0x00 => 0,
0x01 => 4,
0x04 => 8,
0x0A => 32,
_ => 0, }
}
fn parse_block_header(&mut self) -> Result<(), Error> {
let total = self.scratch.len();
debug_assert_eq!(total, self.block_header_size);
let stored = u32::from_le_bytes([
self.scratch[total - 4],
self.scratch[total - 3],
self.scratch[total - 2],
self.scratch[total - 1],
]);
if stored != crc32(&self.scratch[..total - 4]) {
return Err(self.poison(Error::ChecksumMismatch));
}
let flags = self.scratch[1];
let num_filters = ((flags & 0x03) + 1) as usize;
if flags & 0x3C != 0 {
return Err(self.poison(Error::Unsupported));
}
let has_compressed_size = flags & 0x40 != 0;
let has_uncompressed_size = flags & 0x80 != 0;
let mut cur = 2usize;
let body_end = total - 4;
if has_compressed_size {
varint_decode(&self.scratch[..body_end], &mut cur)?
.ok_or_else(|| self.poison(Error::Corrupt))?;
}
if has_uncompressed_size {
varint_decode(&self.scratch[..body_end], &mut cur)?
.ok_or_else(|| self.poison(Error::Corrupt))?;
}
if num_filters != 1 {
return Err(self.poison(Error::Unsupported));
}
let filter_id = varint_decode(&self.scratch[..body_end], &mut cur)?
.ok_or_else(|| self.poison(Error::Corrupt))?;
if filter_id != FILTER_ID_LZMA2 as u64 {
return Err(self.poison(Error::Unsupported));
}
let props_size = varint_decode(&self.scratch[..body_end], &mut cur)?
.ok_or_else(|| self.poison(Error::Corrupt))?;
if props_size != 1 {
return Err(self.poison(Error::Unsupported));
}
if cur >= body_end {
return Err(self.poison(Error::Corrupt));
}
let dict_flag = self.scratch[cur];
cur += 1;
if dict_flag & 0xC0 != 0 {
return Err(self.poison(Error::Unsupported));
}
match lzma2_dict_size(dict_flag) {
Ok(v) => self.lzma2_dict_size = v,
Err(e) => return Err(self.poison(e)),
}
while cur < body_end {
if self.scratch[cur] != 0 {
return Err(self.poison(Error::Corrupt));
}
cur += 1;
}
Ok(())
}
}
impl Default for Decoder {
fn default() -> Self {
Self::new()
}
}
impl RawDecoder for Decoder {
fn raw_decode(&mut self, input: &[u8], output: &mut [u8]) -> Result<RawProgress, Error> {
if self.poisoned {
return Err(Error::Corrupt);
}
let mut consumed = 0usize;
let mut written = 0usize;
loop {
let init_c = consumed;
let init_w = written;
let init_phase = self.phase;
match self.phase {
DecPhase::StreamHeader => {
let filled = self.fill_scratch(input, &mut consumed);
if self.scratch.len() >= 6 && self.scratch[..6] != STREAM_MAGIC {
return Err(self.poison(Error::BadHeader));
}
if filled {
self.parse_stream_header()?;
self.scratch.clear();
self.scratch_want = 1; self.phase = DecPhase::BlockOrIndex;
} else {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
}
DecPhase::BlockOrIndex => {
if self.fill_scratch(input, &mut consumed) {
let first = self.scratch[0];
if first == 0x00 {
self.index_buf.clear();
self.index_buf.push(0x00);
self.scratch.clear();
self.scratch_want = 1;
self.index_records_total = 0;
self.index_records_remaining = u64::MAX; self.index_pos = 1; self.phase = DecPhase::Index;
} else {
self.block_header_size = ((first as usize) + 1) * 4;
self.scratch_want = self.block_header_size;
self.block_header_size_seen = self.block_header_size as u64;
self.block_compressed_seen = 0;
self.block_uncompressed_seen = 0;
self.check = Crc32::new();
self.lzma_core = None;
self.expecting_block_first_chunk = true;
self.phase = DecPhase::BlockHeader;
}
} else {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
}
DecPhase::BlockHeader => {
if self.fill_scratch(input, &mut consumed) {
self.parse_block_header()?;
self.scratch.clear();
self.scratch_want = 1;
self.phase = DecPhase::Lzma2Control;
} else {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
}
DecPhase::Lzma2Control => {
if self.scratch.is_empty() {
self.scratch_want = 1;
if !self.fill_scratch(input, &mut consumed) {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
}
let control = self.scratch[0];
if control == 0x00 {
self.block_compressed_seen += 1;
self.scratch.clear();
let unpadded_no_pad = self.block_header_size_seen
+ self.block_compressed_seen
+ self.check_size() as u64;
let pad = (4 - (unpadded_no_pad % 4) as usize) % 4;
self.scratch_want = pad;
self.lzma_core = None;
self.comp_buf = Vec::new();
self.comp_decoded = Vec::new();
self.phase = DecPhase::BlockPadding;
} else if control == 0x01 || control == 0x02 {
if self.expecting_block_first_chunk && control != 0x01 {
return Err(self.poison(Error::Corrupt));
}
if control == 0x01 {
self.lzma_core = None;
}
self.expecting_block_first_chunk = false;
self.scratch_want = 3;
if !self.fill_scratch(input, &mut consumed) {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
self.block_compressed_seen += 3;
let len =
(((self.scratch[1] as usize) << 8) | self.scratch[2] as usize) + 1;
let _ = control; self.chunk_remaining = len;
self.scratch.clear();
self.scratch_want = 0;
self.phase = DecPhase::Lzma2Data;
} else if control >= 0x80 {
if self.expecting_block_first_chunk && control < 0xE0 {
return Err(self.poison(Error::Corrupt));
}
self.comp_ctrl = control;
let needs_props = (control & 0x40) != 0; self.scratch_want = if needs_props { 6 } else { 5 };
self.phase = DecPhase::Lzma2CompHeader;
} else {
return Err(self.poison(Error::Corrupt));
}
}
DecPhase::Lzma2CompHeader => {
if !self.fill_scratch(input, &mut consumed) {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
let control = self.scratch[0];
let needs_props = (control & 0x40) != 0;
let header_len = self.scratch.len();
let uncomp_top = (control & 0x1F) as u32;
let uncomp_mid = self.scratch[1] as u32;
let uncomp_lo = self.scratch[2] as u32;
let uncomp_size = ((uncomp_top << 16) | (uncomp_mid << 8) | uncomp_lo) + 1;
let comp_hi = self.scratch[3] as u32;
let comp_lo = self.scratch[4] as u32;
let comp_size = ((comp_hi << 8) | comp_lo) + 1;
self.comp_uncomp_size = uncomp_size;
self.comp_size = comp_size;
self.block_compressed_seen += header_len as u64;
let reset_bits = (control >> 5) & 0x03;
if reset_bits == 0b11 {
let props_byte = self.scratch[5];
let props = match Lzma2Props::parse(props_byte) {
Ok(p) => p,
Err(e) => return Err(self.poison(e)),
};
let dict_size = (self.lzma2_dict_size as usize).max(4096);
let dict_size = dict_size.min(128 * 1024 * 1024);
match self.lzma_core.as_mut() {
Some(core) if core.dict_capacity() == dict_size.max(1) => {
core.reset_full(props);
}
_ => {
self.lzma_core = Some(Box::new(LzmaCore::new(props, dict_size)));
}
}
self.last_props = props_byte;
} else if reset_bits == 0b10 {
let props_byte = self.scratch[5];
let props = match Lzma2Props::parse(props_byte) {
Ok(p) => p,
Err(e) => return Err(self.poison(e)),
};
if self.lzma_core.is_none() {
return Err(self.poison(Error::Corrupt));
}
let core_ref = self.lzma_core.as_mut().unwrap();
core_ref.replace_props(props);
core_ref.reset_state();
self.last_props = props_byte;
} else if reset_bits == 0b01 {
let _ = needs_props;
if self.lzma_core.is_none() {
return Err(self.poison(Error::Corrupt));
}
self.lzma_core.as_mut().unwrap().reset_state();
} else {
if self.lzma_core.is_none() {
return Err(self.poison(Error::Corrupt));
}
}
self.expecting_block_first_chunk = false;
self.scratch.clear();
self.comp_buf.clear();
self.scratch_want = 0;
self.phase = DecPhase::Lzma2CompBuffer;
}
DecPhase::Lzma2CompBuffer => {
let need = self.comp_size as usize - self.comp_buf.len();
let take = need.min(input.len() - consumed);
if take > 0 {
self.comp_buf
.extend_from_slice(&input[consumed..consumed + take]);
consumed += take;
}
if self.comp_buf.len() < self.comp_size as usize {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
self.block_compressed_seen += self.comp_size as u64;
self.comp_decoded.clear();
self.comp_decoded
.resize(self.comp_uncomp_size as usize, 0u8);
if self.lzma_core.is_none() {
return Err(self.poison(Error::Corrupt));
}
let init_res = self.lzma_core.as_mut().unwrap().init_range(&self.comp_buf);
if let Err(e) = init_res {
return Err(self.poison(e));
}
let dec_res = self
.lzma_core
.as_mut()
.unwrap()
.decode_chunk(&self.comp_buf, &mut self.comp_decoded);
if let Err(e) = dec_res {
return Err(self.poison(e));
}
self.comp_decoded_pos = 0;
self.phase = DecPhase::Lzma2CompDrain;
}
DecPhase::Lzma2CompDrain => {
let total = self.comp_decoded.len();
while self.comp_decoded_pos < total && written < output.len() {
let take = (total - self.comp_decoded_pos).min(output.len() - written);
let src =
&self.comp_decoded[self.comp_decoded_pos..self.comp_decoded_pos + take];
output[written..written + take].copy_from_slice(src);
self.check.update(src);
self.block_uncompressed_seen += take as u64;
self.comp_decoded_pos += take;
written += take;
}
if self.comp_decoded_pos >= total {
self.scratch.clear();
self.scratch_want = 1;
self.phase = DecPhase::Lzma2Control;
} else {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
}
DecPhase::Lzma2Data => {
while self.chunk_remaining > 0
&& consumed < input.len()
&& written < output.len()
{
let take = self
.chunk_remaining
.min(input.len() - consumed)
.min(output.len() - written);
let src = &input[consumed..consumed + take];
output[written..written + take].copy_from_slice(src);
self.check.update(src);
self.block_compressed_seen += take as u64;
self.block_uncompressed_seen += take as u64;
self.chunk_remaining -= take;
consumed += take;
written += take;
}
if self.chunk_remaining == 0 {
self.scratch.clear();
self.scratch_want = 1;
self.phase = DecPhase::Lzma2Control;
} else {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
}
DecPhase::BlockPadding => {
if self.scratch_want == 0 {
self.scratch.clear();
self.scratch_want = self.check_size();
self.phase = DecPhase::BlockCheck;
} else if self.fill_scratch(input, &mut consumed) {
if self.scratch.iter().any(|&b| b != 0) {
return Err(self.poison(Error::Corrupt));
}
self.scratch.clear();
self.scratch_want = self.check_size();
self.phase = DecPhase::BlockCheck;
} else {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
}
DecPhase::BlockCheck => {
if self.scratch_want == 0 {
let unpadded_size = self.block_header_size_seen
+ self.block_compressed_seen
+ self.check_size() as u64;
self.blocks
.push((unpadded_size, self.block_uncompressed_seen));
self.scratch.clear();
self.scratch_want = 1;
self.phase = DecPhase::BlockOrIndex;
} else if self.fill_scratch(input, &mut consumed) {
if self.check_id == 0x01 {
let got = u32::from_le_bytes([
self.scratch[0],
self.scratch[1],
self.scratch[2],
self.scratch[3],
]);
if got != self.check.finalize() {
return Err(self.poison(Error::ChecksumMismatch));
}
}
let unpadded_size = self.block_header_size_seen
+ self.block_compressed_seen
+ self.check_size() as u64;
self.blocks
.push((unpadded_size, self.block_uncompressed_seen));
self.scratch.clear();
self.scratch_want = 1;
self.phase = DecPhase::BlockOrIndex;
} else {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
}
DecPhase::Index => {
if consumed < input.len() {
self.index_buf.extend_from_slice(&input[consumed..]);
consumed = input.len();
}
loop {
if self.index_records_remaining == u64::MAX {
let mut p = self.index_pos;
match varint_decode(&self.index_buf, &mut p)? {
Some(n) => {
self.index_records_total = n;
self.index_records_remaining = n.saturating_mul(2);
self.index_pos = p;
self.blocks.reserve(n as usize);
}
None => break, }
} else if self.index_records_remaining > 0 {
let mut p = self.index_pos;
match varint_decode(&self.index_buf, &mut p)? {
Some(_v) => {
self.index_pos = p;
self.index_records_remaining -= 1;
}
None => break,
}
} else {
let body_len_so_far = self.index_pos;
let pad = (4 - (body_len_so_far % 4)) % 4;
let need_total = body_len_so_far + pad + 4;
if self.index_buf.len() < need_total {
break;
}
for &b in &self.index_buf[body_len_so_far..body_len_so_far + pad] {
if b != 0 {
return Err(self.poison(Error::Corrupt));
}
}
let crc_off = body_len_so_far + pad;
let stored = u32::from_le_bytes([
self.index_buf[crc_off],
self.index_buf[crc_off + 1],
self.index_buf[crc_off + 2],
self.index_buf[crc_off + 3],
]);
if stored != crc32(&self.index_buf[..crc_off]) {
return Err(self.poison(Error::ChecksumMismatch));
}
if self.blocks.len() as u64 != self.index_records_total {
return Err(self.poison(Error::Corrupt));
}
let blocks_snapshot: Vec<(u64, u64)> = self.blocks.clone();
let mut p = 1usize;
let _n = match varint_decode(&self.index_buf, &mut p)? {
Some(n) => n,
None => return Err(self.poison(Error::Corrupt)),
};
for &(blk_unpadded, blk_uncompressed) in &blocks_snapshot {
let unpadded = match varint_decode(&self.index_buf, &mut p)? {
Some(v) => v,
None => return Err(self.poison(Error::Corrupt)),
};
let uncompressed = match varint_decode(&self.index_buf, &mut p)? {
Some(v) => v,
None => return Err(self.poison(Error::Corrupt)),
};
if unpadded != blk_unpadded || uncompressed != blk_uncompressed {
return Err(self.poison(Error::TrailerMismatch));
}
}
let index_size = need_total as u32;
self.block_header_size = index_size as usize;
self.scratch.clear();
self.scratch_want = 12;
if self.index_buf.len() > need_total {
self.scratch
.extend_from_slice(&self.index_buf[need_total..]);
}
self.phase = DecPhase::StreamFooter;
break;
}
}
}
DecPhase::StreamFooter => {
if self.fill_scratch(input, &mut consumed) {
let s = &self.scratch[..];
let crc_stored = u32::from_le_bytes([s[0], s[1], s[2], s[3]]);
if crc_stored != crc32(&s[4..10]) {
return Err(self.poison(Error::ChecksumMismatch));
}
let back = u32::from_le_bytes([s[4], s[5], s[6], s[7]]);
let want_back = (self.block_header_size as u32 / 4) - 1;
if back != want_back {
return Err(self.poison(Error::TrailerMismatch));
}
if s[8] != STREAM_FLAGS[0] || (s[9] & 0x0F) != self.check_id {
return Err(self.poison(Error::Corrupt));
}
if s[10] != FOOTER_MAGIC[0] || s[11] != FOOTER_MAGIC[1] {
return Err(self.poison(Error::BadHeader));
}
self.phase = DecPhase::Done;
} else {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
}
DecPhase::Done => {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
}
if consumed == init_c && written == init_w && self.phase == init_phase {
return Ok(RawProgress {
consumed,
written,
done: false,
});
}
}
}
fn raw_finish(&mut self, output: &mut [u8]) -> Result<RawProgress, Error> {
if self.poisoned {
return Err(Error::Corrupt);
}
let empty: [u8; 0] = [];
let p = self.raw_decode(&empty, output)?;
if matches!(self.phase, DecPhase::Done) {
Ok(RawProgress {
consumed: 0,
written: p.written,
done: true,
})
} else {
Err(self.poison(Error::UnexpectedEnd))
}
}
fn raw_reset(&mut self) {
*self = Self::new();
}
}