use alloc::vec::Vec;
use crate::{
ByteReader, DICT_SIZE_MAX, Read,
decoder::LzmaDecoder,
error_eof, error_invalid_data, error_invalid_input, error_out_of_memory, error_unsupported,
filter::{FilterConfig, StreamFilter},
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;
pub(crate) const RC_INIT_SIZE: usize = 5;
const DRAIN_SIZE_MAX: usize = 4096;
#[derive(Clone, Copy, PartialEq, Eq)]
enum LzmaState {
Header,
RcInit,
Decode,
DrainOutput,
Finished,
}
fn room_for(lz: &LzDecoder, remaining_size: u64) -> usize {
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(crate) struct Limits {
pub(crate) remaining_size: u64,
pub(crate) compressed_left: Option<u64>,
pub(crate) allow_end_marker: bool,
pub(crate) end_reached: bool,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) enum InputEnd {
More,
Caller,
Length,
}
pub(crate) struct LzmaCore {
rc: RangeCoderState,
carry: [u8; CARRY_CAP],
carry_len: usize,
}
impl LzmaCore {
pub(crate) fn new() -> Self {
Self {
rc: RangeCoderState::default(),
carry: [0; CARRY_CAP],
carry_len: 0,
}
}
pub(crate) fn reset(&mut self) {
self.rc = RangeCoderState::default();
self.carry_len = 0;
}
pub(crate) fn init_rc(&mut self, bytes: &[u8; 5]) -> crate::Result<()> {
if bytes[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([bytes[1], bytes[2], bytes[3], bytes[4]]),
};
Ok(())
}
pub(crate) fn rc_finished(&self) -> bool {
self.rc.code == 0
}
pub(crate) fn unused_input(&self) -> &[u8] {
&self.carry[..self.carry_len]
}
pub(crate) fn feed(
&mut self,
lz: &mut LzDecoder,
lzma: &mut LzmaDecoder,
input: &[u8],
limits: &mut Limits,
input_end: InputEnd,
) -> crate::Result<(usize, usize)> {
let input_ends_here = input_end != InputEnd::More;
let input = match limits.compressed_left {
Some(left) => &input[..input.len().min(left.min(usize::MAX as u64) as usize)],
None => input,
};
let mut in_pos = 0;
let mut produced = 0;
let finish_now = input_ends_here && input.is_empty();
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(lz, lzma, limits, &scratch[..filled], filled, symbol_limit)?;
produced += decoded;
if pos >= carry_len {
in_pos += pos - carry_len;
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;
carry_blocked = true;
}
}
if !carry_blocked && !limits.end_reached {
let avail = input.len() - in_pos;
if avail >= IN_REQUIRED {
let symbol_limit = avail - (IN_REQUIRED - 1);
let (pos, decoded) =
self.run(lz, lzma, limits, &input[in_pos..], avail, symbol_limit)?;
produced += decoded;
in_pos += pos;
}
}
if !carry_blocked && !limits.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();
}
}
if let Some(left) = limits.compressed_left.as_mut() {
*left -= in_pos as u64;
}
if input_ends_here && !limits.end_reached && in_pos >= input.len() {
produced += self.decode_finish_tail(lz, lzma, limits, input_end)?;
}
Ok((in_pos, produced))
}
fn decode_finish_tail(
&mut self,
lz: &mut LzDecoder,
lzma: &mut LzmaDecoder,
limits: &mut Limits,
input_end: InputEnd,
) -> crate::Result<usize> {
if room_for(lz, limits.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(
lz,
lzma,
limits,
&scratch[..padded_len],
carry_len,
carry_len + 1,
)?;
if pos > carry_len {
return Err(match input_end {
InputEnd::Length => error_invalid_data("LZMA symbol runs past the compressed size"),
_ => error_eof("truncated LZMA stream"),
});
}
self.carry.copy_within(pos..carry_len, 0);
self.carry_len = carry_len - pos;
Ok(produced)
}
fn run(
&mut self,
lz: &mut LzDecoder,
lzma: &mut LzmaDecoder,
limits: &mut Limits,
buf: &[u8],
real_len: usize,
symbol_limit: usize,
) -> crate::Result<(usize, usize)> {
let room = room_for(lz, limits.remaining_size);
if room == 0 {
return Ok((0, 0));
}
let pos_before = lz.get_pos();
lz.set_limit(room);
let mut rc =
RangeDecoder::from_parts(SliceRangeReader::new(buf, real_len, symbol_limit), self.rc);
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 !limits.allow_end_marker || !lzma.end_marker_detected() {
error = Some(decode_error);
} else {
if rc.can_normalize() {
rc.normalize();
}
if rc.is_stream_finished() {
limits.end_reached = true;
} else {
error = Some(error_invalid_data("LZMA stream not properly terminated"));
}
}
}
let pos = rc.inner().pos().min(buf.len());
self.rc = rc.state();
if let Some(error) = error {
return Err(error);
}
if limits.remaining_size <= u64::MAX / 2 {
limits.remaining_size -= produced as u64;
if limits.remaining_size == 0 {
limits.end_reached = true;
}
}
if limits.end_reached && lz.has_pending() {
return Err(error_invalid_data("end reached but not decoder finished"));
}
Ok((pos, produced))
}
}
pub struct LzmaStream {
state: LzmaState,
lz: Option<LzDecoder>,
lzma: Option<LzmaDecoder>,
core: LzmaCore,
limits: Limits,
accum: Vec<u8>,
accum_needed: usize,
filter: Option<StreamFilter>,
filter_buf: Vec<u8>,
filter_pos: usize,
mem_limit_kb: u32,
preset_dict: Option<Vec<u8>>,
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,
core: LzmaCore::new(),
limits: Limits {
remaining_size,
compressed_left: None,
allow_end_marker: remaining_size == u64::MAX,
end_reached: false,
},
accum: Vec::new(),
accum_needed,
filter: None,
filter_buf: Vec::new(),
filter_pos: 0,
mem_limit_kb,
preset_dict: preset_dict.map(|dict| dict.to_vec()),
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 set_filters(&mut self, filters: &[FilterConfig]) -> crate::Result<()> {
if self.total_in != 0 {
return Err(error_invalid_input("filters set after decoding started"));
}
if filters.len() > 1 {
return Err(error_unsupported("only one filter is supported"));
}
let Some(config) = filters.first() else {
self.filter = None;
return Ok(());
};
self.filter = Some(StreamFilter::new(config)?);
Ok(())
}
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()) || self.filter_pos < self.settled_end()
}
pub fn unused_input(&self) -> &[u8] {
if self.state == LzmaState::Finished {
self.core.unused_input()
} 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 {
if self.filter_pos < self.settled_end() && out_pos < output.len() {
self.emit_filtered(output, &mut out_pos);
continue;
}
match self.state {
LzmaState::Finished => {
self.finish_filter();
if self.filter_pos < self.settled_end() {
if out_pos >= output.len() {
return Ok(StreamResult {
bytes_consumed: in_pos,
bytes_produced: out_pos,
status: Status::Ok,
});
}
continue;
}
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.limits.remaining_size == 0 {
self.limits.end_reached = true;
}
let space = {
let lz = self.lz_mut()?;
lz.ensure_capacity()?;
lz.available_space()
};
if self.limits.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.limits.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,
});
}
if self.filter.is_some() {
self.drain_and_filter()?;
} else if !self.flush_output(output, &mut out_pos)? {
return Ok(StreamResult {
bytes_consumed: in_pos,
bytes_produced: out_pos,
status: Status::Ok,
});
}
}
}
}
}
fn flush_output(&mut self, output: &mut [u8], out_pos: &mut usize) -> crate::Result<bool> {
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(false);
}
self.finish_drain();
Ok(true)
}
fn settled_end(&self) -> usize {
self.filter_buf.len() - self.filter_held_back()
}
fn filter_held_back(&self) -> usize {
self.filter.as_ref().map_or(0, |filter| filter.held_back())
}
fn finish_filter(&mut self) {
if let Some(filter) = self.filter.as_mut() {
filter.finish();
}
}
fn drain_and_filter(&mut self) -> crate::Result<()> {
let filter_start = self.settled_end();
let has_output = {
let Self {
lz,
filter,
filter_buf,
..
} = self;
let lz = lz
.as_mut()
.ok_or_else(|| error_invalid_data("LZMA decoder not initialized"))?;
let drained = Self::flush_to_buf(lz, filter_buf, DRAIN_SIZE_MAX);
if drained > 0 {
let unfiltered = &mut filter_buf[filter_start..];
if let Some(filter) = filter.as_mut() {
filter.decode(unfiltered);
}
}
lz.has_output()
};
if !has_output {
self.finish_drain();
}
Ok(())
}
fn emit_filtered(&mut self, output: &mut [u8], out_pos: &mut usize) {
let settled_end = self.settled_end();
let n = (settled_end - self.filter_pos).min(output.len() - *out_pos);
output[*out_pos..*out_pos + n]
.copy_from_slice(&self.filter_buf[self.filter_pos..self.filter_pos + n]);
*out_pos += n;
self.total_out += n as u64;
self.filter_pos += n;
if self.filter_pos == settled_end {
self.compact_filter_buf();
}
}
fn compact_filter_buf(&mut self) {
let held_back = self.filter_held_back();
let tail_start = self.filter_buf.len() - held_back;
if tail_start > 0 {
self.filter_buf.copy_within(tail_start.., 0);
self.filter_buf.truncate(held_back);
}
self.filter_pos = 0;
}
fn flush_to_buf(lz: &mut LzDecoder, buf: &mut Vec<u8>, limit: usize) -> usize {
let mut tmp = [0u8; DRAIN_SIZE_MAX];
let cap = limit.min(tmp.len());
let n = lz.flush_partial(&mut tmp[..cap]);
if n > 0 {
buf.extend_from_slice(&tmp[..n]);
}
n
}
fn finish_drain(&mut self) {
self.state = if self.limits.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_eof("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.limits.remaining_size = uncomp_size;
self.limits.allow_end_marker = uncomp_size == u64::MAX;
self.accum.clear();
self.accum_needed = 5;
self.state = LzmaState::RcInit;
Ok(())
}
fn init_range_coder(&mut self) -> crate::Result<()> {
let bytes: [u8; 5] = self.accum[..]
.try_into()
.map_err(|_| error_invalid_input("range coder init needs five bytes"))?;
self.core.init_rc(&bytes)?;
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 Self {
lz,
lzma,
core,
limits,
..
} = self;
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 input_end = if action == Action::Finish {
InputEnd::Caller
} else {
InputEnd::More
};
let (consumed, produced) = core.feed(lz, lzma, &input[*in_pos..], limits, input_end)?;
*in_pos += consumed;
self.total_in += consumed as u64;
Ok((consumed, 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))
}
#[cfg(test)]
mod tests {
use super::*;
const RAW: [u8; 24] = [
0, 36, 25, 73, 152, 111, 22, 2, 140, 232, 230, 91, 177, 71, 198, 206, 183, 99, 255, 255,
60, 172, 0, 0,
];
fn parts() -> (LzDecoder, LzmaDecoder, LzmaCore) {
let (mut lz, lzma) = build_decoders(u64::MAX, 3, 0, 2, 0x0080_0000, None).unwrap();
lz.ensure_capacity().unwrap();
let mut core = LzmaCore::new();
core.init_rc(&[RAW[0], RAW[1], RAW[2], RAW[3], RAW[4]])
.unwrap();
(lz, lzma, core)
}
fn limits(compressed_left: Option<u64>) -> Limits {
Limits {
remaining_size: u64::MAX,
compressed_left,
allow_end_marker: true,
end_reached: false,
}
}
#[test]
fn compressed_budget_stops_the_core() {
let (mut lz, mut lzma, mut core) = parts();
let mut limits = limits(Some(4));
let (consumed, produced) = core
.feed(&mut lz, &mut lzma, &RAW[5..], &mut limits, InputEnd::More)
.unwrap();
assert_eq!(consumed, 4);
assert_eq!(produced, 0);
assert_eq!(limits.compressed_left, Some(0));
let (consumed, produced) = core
.feed(&mut lz, &mut lzma, &RAW[9..], &mut limits, InputEnd::More)
.unwrap();
assert_eq!(consumed, 0);
assert_eq!(produced, 0);
}
#[test]
fn no_budget_decodes_the_whole_payload() {
let (mut lz, mut lzma, mut core) = parts();
let mut limits = limits(None);
let (consumed, produced) = core
.feed(&mut lz, &mut lzma, &RAW[5..], &mut limits, InputEnd::Caller)
.unwrap();
assert_eq!(consumed, RAW.len() - 5);
assert_eq!(produced, 13);
assert!(limits.end_reached);
let mut out = [0u8; 13];
assert_eq!(lz.flush_partial(&mut out), 13);
assert_eq!(&out, b"Hello, world!");
}
}