use alloc::vec::Vec;
use crate::{
ByteReader, DICT_SIZE_MAX, Read,
decoder::LzmaDecoder,
error_invalid_data, error_invalid_input, error_out_of_memory,
lz::LzDecoder,
range_dec::{RangeCoderState, RangeDecoder, SliceRangeReader},
stream::{Action, Status, StreamResult},
};
pub fn get_memory_usage_by_props(dict_size: u32, props_byte: u8) -> crate::Result<u32> {
if dict_size > DICT_SIZE_MAX {
return Err(error_invalid_input("dict size too large"));
}
if props_byte > (4 * 5 + 4) * 9 + 8 {
return Err(error_invalid_input("invalid props byte"));
}
let props = props_byte % (9 * 5);
let lp = props / 9;
let lc = props - lp * 9;
get_memory_usage(dict_size, lc as u32, lp as u32)
}
pub fn get_memory_usage(dict_size: u32, lc: u32, lp: u32) -> crate::Result<u32> {
if lc > 8 || lp > 4 {
return Err(error_invalid_input("invalid lc or lp"));
}
Ok(10 + get_dict_size(dict_size)? / 1024 + ((2 * 0x300) << (lc + lp)) / 1024)
}
fn get_dict_size(dict_size: u32) -> crate::Result<u32> {
if dict_size > DICT_SIZE_MAX {
return Err(error_invalid_input("dict size too large"));
}
let dict_size = dict_size.max(4096);
Ok((dict_size + 15) & !15)
}
pub struct LzmaReader<R> {
lz: LzDecoder,
rc: RangeDecoder<R>,
lzma: LzmaDecoder,
end_reached: bool,
relaxed_end_cond: bool,
remaining_size: u64,
}
impl<R> LzmaReader<R> {
pub fn into_inner(self) -> R {
self.rc.into_inner()
}
pub fn inner(&self) -> &R {
self.rc.inner()
}
pub fn inner_mut(&mut self) -> &mut R {
self.rc.inner_mut()
}
}
impl<R: Read> LzmaReader<R> {
fn construct1(
reader: R,
uncomp_size: u64,
mut props: u8,
dict_size: u32,
preset_dict: Option<&[u8]>,
) -> crate::Result<Self> {
if props > (4 * 5 + 4) * 9 + 8 {
return Err(error_invalid_input("invalid props byte"));
}
let pb = props / (9 * 5);
props -= pb * 9 * 5;
let lp = props / 9;
let lc = props - lp * 9;
if dict_size > DICT_SIZE_MAX {
return Err(error_invalid_input("dict size too large"));
}
Self::construct2(
reader,
uncomp_size,
lc as _,
lp as _,
pb as _,
dict_size,
preset_dict,
)
}
fn construct2(
reader: R,
uncomp_size: u64,
lc: u32,
lp: u32,
pb: u32,
dict_size: u32,
preset_dict: Option<&[u8]>,
) -> crate::Result<Self> {
if lc > 8 || lp > 4 || pb > 4 {
return Err(error_invalid_input("invalid lc or lp or pb"));
}
let mut dict_size = get_dict_size(dict_size)?;
let preset_size = preset_dict
.map(|dict| dict.len().min(dict_size as usize) as u64)
.unwrap_or(0);
let min_history_size = uncomp_size.saturating_add(preset_size);
if uncomp_size <= u64::MAX / 2 && dict_size as u64 > min_history_size {
dict_size = get_dict_size(min_history_size as u32)?;
}
let rc = RangeDecoder::new_stream(reader);
let rc = match rc {
Ok(r) => r,
Err(e) => {
return Err(e);
}
};
let lz = LzDecoder::new(get_dict_size(dict_size)? as _, preset_dict);
let lzma = LzmaDecoder::new(lc, lp, pb);
Ok(Self {
lz,
rc,
lzma,
end_reached: false,
relaxed_end_cond: true,
remaining_size: uncomp_size,
})
}
pub fn new_mem_limit(
mut reader: R,
mem_limit_kb: u32,
preset_dict: Option<&[u8]>,
) -> crate::Result<Self> {
let props = reader.read_u8()?;
let dict_size = reader.read_u32()?;
let uncomp_size = reader.read_u64()?;
let need_mem = get_memory_usage_by_props(dict_size, props)?;
if mem_limit_kb < need_mem {
return Err(error_out_of_memory(
"needed memory too big for mem_limit_kb",
));
}
Self::construct1(reader, uncomp_size, props, dict_size, preset_dict)
}
pub fn new_with_props(
reader: R,
uncomp_size: u64,
props: u8,
dict_size: u32,
preset_dict: Option<&[u8]>,
) -> crate::Result<Self> {
Self::construct1(reader, uncomp_size, props, dict_size, preset_dict)
}
pub fn new(
reader: R,
uncomp_size: u64,
lc: u32,
lp: u32,
pb: u32,
dict_size: u32,
preset_dict: Option<&[u8]>,
) -> crate::Result<Self> {
Self::construct2(reader, uncomp_size, lc, lp, pb, dict_size, preset_dict)
}
fn read_decode(&mut self, buf: &mut [u8]) -> crate::Result<usize> {
if buf.is_empty() {
return Ok(0);
}
if self.end_reached {
return Ok(0);
}
self.lz.ensure_capacity()?;
let mut size: u64 = 0;
let mut len = buf.len() as u64;
let mut off: u64 = 0;
while len > 0 {
let mut copy_size_max = len;
if self.remaining_size <= u64::MAX / 2 && self.remaining_size < len {
copy_size_max = self.remaining_size;
}
self.lz.set_limit(copy_size_max as usize);
match self.lzma.decode(&mut self.lz, &mut self.rc) {
Ok(_) => {}
Err(error) => {
if self.remaining_size != u64::MAX || !self.lzma.end_marker_detected() {
return Err(error);
}
self.end_reached = true;
self.rc.normalize();
}
}
let copied_size = self.lz.flush(buf, off as _)? as u64;
off = off.saturating_add(copied_size);
len = len.saturating_sub(copied_size);
size = size.saturating_add(copied_size);
if self.remaining_size <= u64::MAX / 2 {
self.remaining_size = self.remaining_size.saturating_sub(copied_size);
if self.remaining_size == 0 {
self.end_reached = true;
}
}
if self.end_reached {
if self.lz.has_pending()
|| (!self.relaxed_end_cond && !self.rc.is_stream_finished())
{
return Err(error_invalid_data("end reached but not decoder finished"));
}
return Ok(size as _);
}
}
Ok(size as _)
}
}
impl<R: Read> Read for LzmaReader<R> {
fn read(&mut self, buf: &mut [u8]) -> crate::Result<usize> {
self.read_decode(buf)
}
}
const IN_REQUIRED: usize = 20;
const CARRY_CAP: usize = 2 * IN_REQUIRED;
#[derive(Clone, Copy, PartialEq, Eq)]
enum LzmaState {
Header,
RcInit,
Decode,
DrainOutput,
Finished,
}
fn room_for(lz: Option<&LzDecoder>, remaining_size: u64) -> usize {
let Some(lz) = lz else {
return 0;
};
let mut room = lz.available_space();
if remaining_size <= u64::MAX / 2 {
room = room.min(remaining_size.min(usize::MAX as u64) as usize);
}
room
}
pub struct LzmaStream {
state: LzmaState,
lz: Option<LzDecoder>,
lzma: Option<LzmaDecoder>,
rc: RangeCoderState,
carry: [u8; CARRY_CAP],
carry_len: usize,
accum: Vec<u8>,
accum_needed: usize,
remaining_size: u64,
mem_limit_kb: u32,
preset_dict: Option<Vec<u8>>,
end_reached: bool,
failed: bool,
total_in: u64,
total_out: u64,
}
impl LzmaStream {
fn with_parts(
state: LzmaState,
lz: Option<LzDecoder>,
lzma: Option<LzmaDecoder>,
accum_needed: usize,
remaining_size: u64,
mem_limit_kb: u32,
preset_dict: Option<&[u8]>,
) -> Self {
Self {
state,
lz,
lzma,
rc: RangeCoderState::default(),
carry: [0; CARRY_CAP],
carry_len: 0,
accum: Vec::new(),
accum_needed,
remaining_size,
mem_limit_kb,
preset_dict: preset_dict.map(|dict| dict.to_vec()),
end_reached: false,
failed: false,
total_in: 0,
total_out: 0,
}
}
pub fn new_mem_limit(mem_limit_kb: u32, preset_dict: Option<&[u8]>) -> Self {
Self::with_parts(
LzmaState::Header,
None,
None,
13,
u64::MAX,
mem_limit_kb,
preset_dict,
)
}
pub fn new_with_props(
uncomp_size: u64,
mut props: u8,
dict_size: u32,
preset_dict: Option<&[u8]>,
) -> crate::Result<Self> {
if props > (4 * 5 + 4) * 9 + 8 {
return Err(error_invalid_input("invalid props byte"));
}
let pb = props / (9 * 5);
props -= pb * 9 * 5;
let lp = props / 9;
let lc = props - lp * 9;
if dict_size > DICT_SIZE_MAX {
return Err(error_invalid_input("dict size too large"));
}
Self::new(
uncomp_size,
lc as _,
lp as _,
pb as _,
dict_size,
preset_dict,
)
}
pub fn new(
uncomp_size: u64,
lc: u32,
lp: u32,
pb: u32,
dict_size: u32,
preset_dict: Option<&[u8]>,
) -> crate::Result<Self> {
let (lz, lzma) = build_decoders(uncomp_size, lc, lp, pb, dict_size, preset_dict)?;
Ok(Self::with_parts(
LzmaState::RcInit,
Some(lz),
Some(lzma),
5,
uncomp_size,
u32::MAX,
preset_dict,
))
}
pub fn total_in(&self) -> u64 {
self.total_in
}
pub fn total_out(&self) -> u64 {
self.total_out
}
pub fn is_finished(&self) -> bool {
self.state == LzmaState::Finished
}
pub fn has_output(&self) -> bool {
self.lz.as_ref().is_some_and(|lz| lz.has_output())
}
pub fn unused_input(&self) -> &[u8] {
if self.state == LzmaState::Finished {
&self.carry[..self.carry_len]
} else {
&[]
}
}
pub fn process(
&mut self,
input: &[u8],
output: &mut [u8],
action: Action,
) -> crate::Result<StreamResult> {
if self.failed {
return Err(error_invalid_data("LZMA stream already failed"));
}
let result = self.process_inner(input, output, action);
if result.is_err() {
self.failed = true;
}
result
}
fn process_inner(
&mut self,
input: &[u8],
output: &mut [u8],
action: Action,
) -> crate::Result<StreamResult> {
let mut in_pos = 0;
let mut out_pos = 0;
let mut stalled = false;
loop {
match self.state {
LzmaState::Finished => {
return Ok(StreamResult {
bytes_consumed: in_pos,
bytes_produced: out_pos,
status: Status::StreamEnd,
});
}
LzmaState::Header => {
if let Some(result) = self.accumulate(input, action, &mut in_pos, out_pos)? {
return Ok(result);
}
self.parse_header()?;
}
LzmaState::RcInit => {
if let Some(result) = self.accumulate(input, action, &mut in_pos, out_pos)? {
return Ok(result);
}
self.init_range_coder()?;
}
LzmaState::Decode => {
if stalled {
return Ok(StreamResult {
bytes_consumed: in_pos,
bytes_produced: out_pos,
status: Status::Ok,
});
}
if self.remaining_size == 0 {
self.end_reached = true;
}
let space = {
let lz = self.lz_mut()?;
lz.ensure_capacity()?;
lz.available_space()
};
if self.end_reached || space == 0 {
self.state = LzmaState::DrainOutput;
continue;
}
let (consumed, produced) = self.decode_pass(input, &mut in_pos, action)?;
if consumed == 0 && produced == 0 && !self.end_reached {
stalled = true;
}
self.state = LzmaState::DrainOutput;
}
LzmaState::DrainOutput => {
if out_pos >= output.len() {
return Ok(StreamResult {
bytes_consumed: in_pos,
bytes_produced: out_pos,
status: Status::Ok,
});
}
let (flushed, has_output) = {
let lz = self.lz_mut()?;
let flushed = lz.flush_partial(&mut output[out_pos..]);
(flushed, lz.has_output())
};
out_pos += flushed;
self.total_out += flushed as u64;
if has_output {
return Ok(StreamResult {
bytes_consumed: in_pos,
bytes_produced: out_pos,
status: Status::Ok,
});
}
self.state = if self.end_reached {
LzmaState::Finished
} else {
LzmaState::Decode
};
}
}
}
}
fn lz_mut(&mut self) -> crate::Result<&mut LzDecoder> {
self.lz
.as_mut()
.ok_or_else(|| error_invalid_data("LZMA decoder not initialized"))
}
fn accumulate(
&mut self,
input: &[u8],
action: Action,
in_pos: &mut usize,
out_pos: usize,
) -> crate::Result<Option<StreamResult>> {
while self.accum.len() < self.accum_needed {
if *in_pos >= input.len() {
if action == Action::Finish {
return Err(error_invalid_data("unexpected end of LZMA stream"));
}
return Ok(Some(StreamResult {
bytes_consumed: *in_pos,
bytes_produced: out_pos,
status: Status::Ok,
}));
}
let need = self.accum_needed - self.accum.len();
let to_copy = need.min(input.len() - *in_pos);
self.accum
.extend_from_slice(&input[*in_pos..*in_pos + to_copy]);
*in_pos += to_copy;
self.total_in += to_copy as u64;
}
Ok(None)
}
fn parse_header(&mut self) -> crate::Result<()> {
let props = self.accum[0];
let dict_size =
u32::from_le_bytes([self.accum[1], self.accum[2], self.accum[3], self.accum[4]]);
let uncomp_size = u64::from_le_bytes([
self.accum[5],
self.accum[6],
self.accum[7],
self.accum[8],
self.accum[9],
self.accum[10],
self.accum[11],
self.accum[12],
]);
let need_mem = get_memory_usage_by_props(dict_size, props)?;
if self.mem_limit_kb < need_mem {
return Err(error_out_of_memory(
"needed memory too big for mem_limit_kb",
));
}
let mut props = props;
let pb = props / (9 * 5);
props -= pb * 9 * 5;
let lp = props / 9;
let lc = props - lp * 9;
if dict_size > DICT_SIZE_MAX {
return Err(error_invalid_input("dict size too large"));
}
let (lz, lzma) = build_decoders(
uncomp_size,
lc as _,
lp as _,
pb as _,
dict_size,
self.preset_dict.as_deref(),
)?;
self.lz = Some(lz);
self.lzma = Some(lzma);
self.remaining_size = uncomp_size;
self.accum.clear();
self.accum_needed = 5;
self.state = LzmaState::RcInit;
Ok(())
}
fn init_range_coder(&mut self) -> crate::Result<()> {
if self.accum[0] != 0x00 {
return Err(error_invalid_input("range decoder first byte is not zero"));
}
self.rc = RangeCoderState {
range: 0xFFFF_FFFF,
code: u32::from_be_bytes([self.accum[1], self.accum[2], self.accum[3], self.accum[4]]),
};
self.accum.clear();
self.accum_needed = 0;
self.state = LzmaState::Decode;
Ok(())
}
fn decode_pass(
&mut self,
input: &[u8],
in_pos: &mut usize,
action: Action,
) -> crate::Result<(usize, usize)> {
let mut consumed = 0;
let mut produced = 0;
let finish_now = action == Action::Finish && *in_pos >= input.len();
let mut carry_blocked = false;
if self.carry_len > 0 && !finish_now {
let carry_len = self.carry_len;
let m = (input.len() - *in_pos).min(CARRY_CAP - carry_len);
let filled = carry_len + m;
let mut scratch = self.carry;
scratch[carry_len..filled].copy_from_slice(&input[*in_pos..*in_pos + m]);
let symbol_limit = filled.saturating_sub(IN_REQUIRED - 1);
let (pos, decoded) = self.run(&scratch[..filled], filled, symbol_limit)?;
produced += decoded;
if pos >= carry_len {
let extra = pos - carry_len;
*in_pos += extra;
self.total_in += extra as u64;
consumed += extra;
self.carry_len = 0;
} else {
let residue = filled - pos;
self.carry[..residue].copy_from_slice(&scratch[pos..filled]);
self.carry_len = residue;
*in_pos += m;
self.total_in += m as u64;
consumed += m;
carry_blocked = true;
}
}
if !carry_blocked && !self.end_reached {
let avail = input.len() - *in_pos;
if avail >= IN_REQUIRED {
let symbol_limit = avail - (IN_REQUIRED - 1);
let (pos, decoded) = self.run(&input[*in_pos..], avail, symbol_limit)?;
produced += decoded;
*in_pos += pos;
self.total_in += pos as u64;
consumed += pos;
}
}
if !carry_blocked && !self.end_reached {
let rest = input.len() - *in_pos;
if rest > 0 && rest < IN_REQUIRED {
debug_assert_eq!(self.carry_len, 0);
self.carry[..rest].copy_from_slice(&input[*in_pos..]);
self.carry_len = rest;
*in_pos = input.len();
self.total_in += rest as u64;
consumed += rest;
}
}
if action == Action::Finish && !self.end_reached && *in_pos >= input.len() {
produced += self.decode_finish_tail()?;
}
Ok((consumed, produced))
}
fn decode_finish_tail(&mut self) -> crate::Result<usize> {
if room_for(self.lz.as_ref(), self.remaining_size) == 0 {
return Ok(0);
}
let carry_len = self.carry_len;
let mut scratch = [0u8; CARRY_CAP + IN_REQUIRED + 1];
scratch[..carry_len].copy_from_slice(&self.carry[..carry_len]);
let padded_len = carry_len + IN_REQUIRED + 1;
let (pos, produced) = self.run(&scratch[..padded_len], carry_len, carry_len + 1)?;
if pos > carry_len {
return Err(error_invalid_data("truncated LZMA stream"));
}
self.carry.copy_within(pos..carry_len, 0);
self.carry_len = carry_len - pos;
Ok(produced)
}
fn run(
&mut self,
buf: &[u8],
real_len: usize,
symbol_limit: usize,
) -> crate::Result<(usize, usize)> {
let Self {
lz,
lzma,
rc: rc_state,
remaining_size,
end_reached,
..
} = self;
let room = room_for(lz.as_ref(), *remaining_size);
if room == 0 {
return Ok((0, 0));
}
let (lz, lzma) = match (lz.as_mut(), lzma.as_mut()) {
(Some(lz), Some(lzma)) => (lz, lzma),
_ => return Err(error_invalid_data("LZMA decoder not initialized")),
};
let pos_before = lz.get_pos();
lz.set_limit(room);
let mut rc = RangeDecoder::from_parts(
SliceRangeReader::new(buf, real_len, symbol_limit),
*rc_state,
);
let decode_result = lzma.decode(lz, &mut rc);
let produced = lz.get_pos() - pos_before;
let mut error = None;
if let Err(decode_error) = decode_result {
if *remaining_size != u64::MAX || !lzma.end_marker_detected() {
error = Some(decode_error);
} else {
if rc.can_normalize() {
rc.normalize();
}
if rc.is_stream_finished() {
*end_reached = true;
} else {
error = Some(error_invalid_data("LZMA stream not properly terminated"));
}
}
}
let pos = rc.inner().pos().min(buf.len());
*rc_state = rc.state();
if let Some(error) = error {
return Err(error);
}
if *remaining_size <= u64::MAX / 2 {
*remaining_size -= produced as u64;
if *remaining_size == 0 {
*end_reached = true;
}
}
if *end_reached && lz.has_pending() {
return Err(error_invalid_data("end reached but not decoder finished"));
}
Ok((pos, produced))
}
}
fn build_decoders(
uncomp_size: u64,
lc: u32,
lp: u32,
pb: u32,
dict_size: u32,
preset_dict: Option<&[u8]>,
) -> crate::Result<(LzDecoder, LzmaDecoder)> {
if lc > 8 || lp > 4 || pb > 4 {
return Err(error_invalid_input("invalid lc or lp or pb"));
}
let mut dict_size = get_dict_size(dict_size)?;
let preset_size = preset_dict
.map(|dict| dict.len().min(dict_size as usize) as u64)
.unwrap_or(0);
let min_history_size = uncomp_size.saturating_add(preset_size);
if uncomp_size <= u64::MAX / 2 && dict_size as u64 > min_history_size {
dict_size = get_dict_size(min_history_size as u32)?;
}
let lz = LzDecoder::new(get_dict_size(dict_size)? as _, preset_dict);
let lzma = LzmaDecoder::new(lc, lp, pb);
Ok((lz, lzma))
}