diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 20570dc..3a37d66 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -11,7 +11,7 @@ jobs: - stable - beta - nightly - - 1.40.0 # MSRV + - 1.50.0 # MSRV fail-fast: false runs-on: ${{ matrix.os }} env: diff --git a/README.md b/README.md index 2c26908..74cac45 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![Documentation](https://docs.rs/lzma-rs/badge.svg)](https://docs.rs/lzma-rs) [![Safety Dance](https://img.shields.io/badge/unsafe-forbidden-success.svg)](https://github.com/rust-secure-code/safety-dance/) ![Build Status](https://github.com/gendx/lzma-rs/workflows/Build%20and%20run%20tests/badge.svg) -[![Minimum rust 1.40](https://img.shields.io/badge/rust-1.40%2B-orange.svg)](https://github.com/rust-lang/rust/blob/master/RELEASES.md#version-1400-2019-12-19) +[![Minimum rust 1.50](https://img.shields.io/badge/rust-1.50%2B-orange.svg)](https://github.com/rust-lang/rust/blob/master/RELEASES.md#version-1500-2021-02-11) This project is a decoder for LZMA and its variants written in pure Rust, with focus on clarity. It already supports LZMA, LZMA2 and a subset of the `.xz` file format. diff --git a/src/decode/lzbuffer.rs b/src/decode/lzbuffer.rs index 9409424..e5aacb7 100644 --- a/src/decode/lzbuffer.rs +++ b/src/decode/lzbuffer.rs @@ -39,11 +39,7 @@ impl LzAccumBuffer where W: io::Write, { - pub fn from_stream(stream: W) -> Self { - Self::from_stream_with_memlimit(stream, std::usize::MAX) - } - - pub fn from_stream_with_memlimit(stream: W, memlimit: usize) -> Self { + pub fn from_stream(stream: W, memlimit: usize) -> Self { Self { stream, buf: Vec::new(), @@ -175,7 +171,7 @@ impl LzCircularBuffer where W: io::Write, { - pub fn from_stream_with_memlimit(stream: W, dict_size: usize, memlimit: usize) -> Self { + pub fn from_stream(stream: W, dict_size: usize, memlimit: usize) -> Self { lzma_info!("Dict size in LZ buffer: {}", dict_size); Self { stream, diff --git a/src/decode/lzma.rs b/src/decode/lzma.rs index 313bdbc..686111e 100644 --- a/src/decode/lzma.rs +++ b/src/decode/lzma.rs @@ -1,9 +1,8 @@ -use crate::decode::lzbuffer; +use crate::decode::lzbuffer::LzBuffer; use crate::decode::rangecoder; use crate::error; use byteorder::{LittleEndian, ReadBytesExt}; use std::io; -use std::marker::PhantomData; use crate::decompress::Options; use crate::decompress::UnpackedSize; @@ -38,14 +37,39 @@ enum ProcessingStatus { Finished, } +#[derive(Copy, Clone)] +/// LZMA 'lclppb' decompression properties. +pub struct LzmaProperties { + /// The number of literal context bits. + /// + /// The most `lc` significant bits of the previous byte are part of the literal context. + /// `lc` must not be greater than 8. + pub lc: u32, // 0..=8 + /// The number of literal position bits. + /// + /// `lp` must not be greater than 4. + pub lp: u32, // 0..=4 + /// The number of position bits. + /// + /// The context for literal/match is plaintext offset modulo `2^pb`. + /// `pb` must not be greater than 4. + pub pb: u32, // 0..=4 +} + +impl LzmaProperties { + /// Assert the validity of the LZMA properties. + pub(crate) fn validate(&self) { + assert!(self.lc <= 8); + assert!(self.lp <= 4); + assert!(self.pb <= 4); + } +} + +/// LZMA decompression parameters. pub struct LzmaParams { - // most lc significant bits of previous byte are part of the literal context - lc: u32, // 0..8 - lp: u32, // 0..4 - // context for literal/match is plaintext offset modulo 2^pb - pb: u32, // 0..4 - dict_size: u32, - unpacked_size: Option, + pub properties: LzmaProperties, + pub dict_size: u32, + pub unpacked_size: Option, } impl LzmaParams { @@ -108,9 +132,7 @@ impl LzmaParams { lzma_info!("Unpacked size: {:?}", unpacked_size); let params = LzmaParams { - lc, - lp, - pb, + properties: LzmaProperties { lc, lp, pb }, dict_size, unpacked_size, }; @@ -119,21 +141,11 @@ impl LzmaParams { } } -pub struct DecoderState -where - W: io::Write, - LZB: lzbuffer::LzBuffer, -{ - _phantom: PhantomData, +pub(crate) struct DecoderState { // Buffer input data here if we need more for decompression. Up to // MAX_REQUIRED_INPUT bytes can be consumed during one iteration. partial_input_buf: std::io::Cursor<[u8; MAX_REQUIRED_INPUT]>, - pub output: LZB, - // most lc significant bits of previous byte are part of the literal context - pub lc: u32, // 0..8 - pub lp: u32, // 0..4 - // context for literal/match is plaintext offset modulo 2^pb - pub pb: u32, // 0..4 + pub(crate) lzma_props: LzmaProperties, unpacked_size: Option, literal_probs: Vec>, pos_slot_decoder: Vec, @@ -151,136 +163,75 @@ where rep_len_decoder: rangecoder::LenDecoder, } -// Initialize decoder with accumulating buffer -pub fn new_accum( - output: lzbuffer::LzAccumBuffer, - lc: u32, - lp: u32, - pb: u32, - unpacked_size: Option, -) -> DecoderState> -where - W: io::Write, -{ - DecoderState { - _phantom: PhantomData, - partial_input_buf: std::io::Cursor::new([0; MAX_REQUIRED_INPUT]), - output, - lc, - lp, - pb, - unpacked_size, - literal_probs: vec![vec![0x400; 0x300]; 1 << (lc + lp)], - pos_slot_decoder: vec![rangecoder::BitTree::new(6); 4], - align_decoder: rangecoder::BitTree::new(4), - pos_decoders: [0x400; 115], - is_match: [0x400; 192], - is_rep: [0x400; 12], - is_rep_g0: [0x400; 12], - is_rep_g1: [0x400; 12], - is_rep_g2: [0x400; 12], - is_rep_0long: [0x400; 192], - state: 0, - rep: [0; 4], - len_decoder: rangecoder::LenDecoder::new(), - rep_len_decoder: rangecoder::LenDecoder::new(), +impl DecoderState { + pub fn new(lzma_props: LzmaProperties, unpacked_size: Option) -> Self { + lzma_props.validate(); + DecoderState { + partial_input_buf: std::io::Cursor::new([0; MAX_REQUIRED_INPUT]), + lzma_props, + unpacked_size, + literal_probs: vec![vec![0x400; 0x300]; 1 << (lzma_props.lc + lzma_props.lp)], + pos_slot_decoder: vec![rangecoder::BitTree::new(6); 4], + align_decoder: rangecoder::BitTree::new(4), + pos_decoders: [0x400; 115], + is_match: [0x400; 192], + is_rep: [0x400; 12], + is_rep_g0: [0x400; 12], + is_rep_g1: [0x400; 12], + is_rep_g2: [0x400; 12], + is_rep_0long: [0x400; 192], + state: 0, + rep: [0; 4], + len_decoder: rangecoder::LenDecoder::new(), + rep_len_decoder: rangecoder::LenDecoder::new(), + } } -} -// Initialize decoder with circular buffer -pub fn new_circular( - output: W, - params: LzmaParams, -) -> error::Result>> -where - W: io::Write, -{ - new_circular_with_memlimit(output, params, std::usize::MAX) -} - -// Initialize decoder with circular buffer -pub fn new_circular_with_memlimit( - output: W, - params: LzmaParams, - memlimit: usize, -) -> error::Result>> -where - W: io::Write, -{ - // Decoder - let decoder = DecoderState { - _phantom: PhantomData, - output: lzbuffer::LzCircularBuffer::from_stream_with_memlimit( - output, - params.dict_size as usize, - memlimit, - ), - partial_input_buf: std::io::Cursor::new([0; MAX_REQUIRED_INPUT]), - lc: params.lc, - lp: params.lp, - pb: params.pb, - unpacked_size: params.unpacked_size, - literal_probs: vec![vec![0x400; 0x300]; 1 << (params.lc + params.lp)], - pos_slot_decoder: vec![rangecoder::BitTree::new(6); 4], - align_decoder: rangecoder::BitTree::new(4), - pos_decoders: [0x400; 115], - is_match: [0x400; 192], - is_rep: [0x400; 12], - is_rep_g0: [0x400; 12], - is_rep_g1: [0x400; 12], - is_rep_g2: [0x400; 12], - is_rep_0long: [0x400; 192], - state: 0, - rep: [0; 4], - len_decoder: rangecoder::LenDecoder::new(), - rep_len_decoder: rangecoder::LenDecoder::new(), - }; - - Ok(decoder) -} + pub fn reset_state(&mut self, new_props: LzmaProperties) { + new_props.validate(); + if self.lzma_props.lc + self.lzma_props.lp == new_props.lc + new_props.lp { + // We can reset here by filling the existing buffer with 0x400. + self.literal_probs.iter_mut().for_each(|v| v.fill(0x400)) + } else { + // We need to reallocate because of the new size of `lc+lp`. + self.literal_probs = vec![vec![0x400; 0x300]; 1 << (new_props.lc + new_props.lp)]; + } -impl DecoderState -where - W: io::Write, - LZB: lzbuffer::LzBuffer, -{ - pub fn reset_state(&mut self, lc: u32, lp: u32, pb: u32) { - self.lc = lc; - self.lp = lp; - self.pb = pb; - self.literal_probs = vec![vec![0x400; 0x300]; 1 << (lc + lp)]; - self.pos_slot_decoder = vec![rangecoder::BitTree::new(6); 4]; - self.align_decoder = rangecoder::BitTree::new(4); - self.pos_decoders = [0x400; 115]; - self.is_match = [0x400; 192]; - self.is_rep = [0x400; 12]; - self.is_rep_g0 = [0x400; 12]; - self.is_rep_g1 = [0x400; 12]; - self.is_rep_g2 = [0x400; 12]; - self.is_rep_0long = [0x400; 192]; + self.lzma_props = new_props; + self.pos_slot_decoder.iter_mut().for_each(|t| t.reset()); + self.align_decoder.reset(); + self.pos_decoders.fill(0x400); + self.is_match.fill(0x400); + self.is_rep.fill(0x400); + self.is_rep_g0.fill(0x400); + self.is_rep_g1.fill(0x400); + self.is_rep_g2.fill(0x400); + self.is_rep_0long.fill(0x400); self.state = 0; - self.rep = [0; 4]; - self.len_decoder = rangecoder::LenDecoder::new(); - self.rep_len_decoder = rangecoder::LenDecoder::new(); + self.rep.fill(0); + self.len_decoder.reset(); + self.rep_len_decoder.reset(); } pub fn set_unpacked_size(&mut self, unpacked_size: Option) { self.unpacked_size = unpacked_size; } - pub fn process<'a, R: io::BufRead>( + pub fn process<'a, W: io::Write, LZB: LzBuffer, R: io::BufRead>( &mut self, + output: &mut LZB, rangecoder: &mut rangecoder::RangeDecoder<'a, R>, ) -> error::Result<()> { - self.process_mode(rangecoder, ProcessingMode::Finish) + self.process_mode(output, rangecoder, ProcessingMode::Finish) } #[cfg(feature = "stream")] - pub fn process_stream<'a, R: io::BufRead>( + pub fn process_stream<'a, W: io::Write, LZB: LzBuffer, R: io::BufRead>( &mut self, + output: &mut LZB, rangecoder: &mut rangecoder::RangeDecoder<'a, R>, ) -> error::Result<()> { - self.process_mode(rangecoder, ProcessingMode::Partial) + self.process_mode(output, rangecoder, ProcessingMode::Partial) } /// Process the next iteration of the loop. @@ -289,12 +240,13 @@ where /// /// Returns `ProcessingStatus` to determine whether one should continue /// processing the loop. - fn process_next_inner<'a, R: io::BufRead>( + fn process_next_inner<'a, W: io::Write, LZB: LzBuffer, R: io::BufRead>( &mut self, + output: &mut LZB, rangecoder: &mut rangecoder::RangeDecoder<'a, R>, update: bool, ) -> error::Result { - let pos_state = self.output.len() & ((1 << self.pb) - 1); + let pos_state = output.len() & ((1 << self.lzma_props.pb) - 1); // Literal if !rangecoder.decode_bit( @@ -302,11 +254,11 @@ where &mut self.is_match[(self.state << 4) + pos_state], update, )? { - let byte: u8 = self.decode_literal(rangecoder, update)?; + let byte: u8 = self.decode_literal(output, rangecoder, update)?; if update { lzma_debug!("Literal: {}", byte); - self.output.append_literal(byte)?; + output.append_literal(byte)?; self.state = if self.state < 4 { 0 @@ -334,7 +286,7 @@ where if update { self.state = if self.state < 7 { 9 } else { 11 }; let dist = self.rep[0] + 1; - self.output.append_lz(1, dist)?; + output.append_lz(1, dist)?; } return Ok(ProcessingStatus::Continue); } @@ -399,17 +351,18 @@ where len += 2; let dist = self.rep[0] + 1; - self.output.append_lz(len, dist)?; + output.append_lz(len, dist)?; } Ok(ProcessingStatus::Continue) } - fn process_next<'a, R: io::BufRead>( + fn process_next<'a, W: io::Write, LZB: LzBuffer, R: io::BufRead>( &mut self, + output: &mut LZB, rangecoder: &mut rangecoder::RangeDecoder<'a, R>, ) -> error::Result { - self.process_next_inner(rangecoder, true) + self.process_next_inner(output, rangecoder, true) } /// Try to process the next iteration of the loop. @@ -417,10 +370,16 @@ where /// This will check to see if there is enough data to consume and advance the /// decompressor. Needed in streaming mode to avoid corrupting the state while /// processing incomplete chunks of data. - fn try_process_next(&mut self, buf: &[u8], range: u32, code: u32) -> error::Result<()> { + fn try_process_next>( + &mut self, + output: &mut LZB, + buf: &[u8], + range: u32, + code: u32, + ) -> error::Result<()> { let mut temp = std::io::Cursor::new(buf); let mut rangecoder = rangecoder::RangeDecoder::from_parts(&mut temp, range, code); - let _ = self.process_next_inner(&mut rangecoder, false)?; + let _ = self.process_next_inner(output, &mut rangecoder, false)?; Ok(()) } @@ -438,14 +397,15 @@ where Ok(()) } - fn process_mode<'a, R: io::BufRead>( + fn process_mode<'a, W: io::Write, LZB: LzBuffer, R: io::BufRead>( &mut self, - mut rangecoder: &mut rangecoder::RangeDecoder<'a, R>, + output: &mut LZB, + rangecoder: &mut rangecoder::RangeDecoder<'a, R>, mode: ProcessingMode, ) -> error::Result<()> { loop { if let Some(unpacked_size) = self.unpacked_size { - if self.output.len() as u64 >= unpacked_size { + if output.len() as u64 >= unpacked_size { break; } } else if match mode { @@ -468,6 +428,7 @@ where && (self.partial_input_buf.position() as usize) < MAX_REQUIRED_INPUT && self .try_process_next( + output, &tmp[..self.partial_input_buf.position() as usize], rangecoder.range, rangecoder.code, @@ -485,7 +446,7 @@ where rangecoder.range, rangecoder.code, ); - let res = self.process_next(&mut tmp_rangecoder)?; + let res = self.process_next(output, &mut tmp_rangecoder)?; // Update the actual rangecoder rangecoder.set(tmp_rangecoder.range, tmp_rangecoder.code); @@ -505,24 +466,24 @@ where if mode == ProcessingMode::Partial && buf.len() < MAX_REQUIRED_INPUT && self - .try_process_next(buf, rangecoder.range, rangecoder.code) + .try_process_next(output, buf, rangecoder.range, rangecoder.code) .is_err() { return self.read_partial_input_buf(rangecoder); } - if self.process_next(&mut rangecoder)? == ProcessingStatus::Finished { + if self.process_next(output, rangecoder)? == ProcessingStatus::Finished { break; }; } } if let Some(len) = self.unpacked_size { - if mode == ProcessingMode::Finish && len != self.output.len() as u64 { + if mode == ProcessingMode::Finish && len != output.len() as u64 { return Err(error::Error::LzmaError(format!( "Expected unpacked size of {} but decompressed to {}", len, - self.output.len() + output.len() ))); } } @@ -530,21 +491,22 @@ where Ok(()) } - fn decode_literal<'a, R: io::BufRead>( + fn decode_literal<'a, W: io::Write, LZB: LzBuffer, R: io::BufRead>( &mut self, + output: &mut LZB, rangecoder: &mut rangecoder::RangeDecoder<'a, R>, update: bool, ) -> error::Result { let def_prev_byte = 0u8; - let prev_byte = self.output.last_or(def_prev_byte) as usize; + let prev_byte = output.last_or(def_prev_byte) as usize; let mut result: usize = 1; - let lit_state = - ((self.output.len() & ((1 << self.lp) - 1)) << self.lc) + (prev_byte >> (8 - self.lc)); + let lit_state = ((output.len() & ((1 << self.lzma_props.lp) - 1)) << self.lzma_props.lc) + + (prev_byte >> (8 - self.lzma_props.lc)); let probs = &mut self.literal_probs[lit_state]; if self.state >= 7 { - let mut match_byte = self.output.last_n(self.rep[0] + 1)? as usize; + let mut match_byte = output.last_n(self.rep[0] + 1)? as usize; while result < 0x100 { let match_bit = (match_byte >> 7) & 1; diff --git a/src/decode/lzma2.rs b/src/decode/lzma2.rs index f359511..ee5e2f4 100644 --- a/src/decode/lzma2.rs +++ b/src/decode/lzma2.rs @@ -1,6 +1,7 @@ use crate::decode::lzbuffer; use crate::decode::lzbuffer::LzBuffer; use crate::decode::lzma; +use crate::decode::lzma::LzmaProperties; use crate::decode::rangecoder; use crate::error; use byteorder::{BigEndian, ReadBytesExt}; @@ -12,8 +13,15 @@ where R: io::BufRead, W: io::Write, { - let accum = lzbuffer::LzAccumBuffer::from_stream(output); - let mut decoder = lzma::new_accum(accum, 0, 0, 0, None); + let mut accum = lzbuffer::LzAccumBuffer::from_stream(output, usize::MAX); + let mut decoder = lzma::DecoderState::new( + LzmaProperties { + lc: 0, + lp: 0, + pb: 0, + }, + None, + ); loop { let status = input @@ -27,21 +35,22 @@ where break; } else if status == 1 { // uncompressed reset dict - parse_uncompressed(&mut decoder, input, true)?; + parse_uncompressed(&mut accum, input, true)?; } else if status == 2 { // uncompressed no reset - parse_uncompressed(&mut decoder, input, false)?; + parse_uncompressed(&mut accum, input, false)?; } else { - parse_lzma(&mut decoder, input, status)?; + parse_lzma(&mut accum, &mut decoder, input, status)?; } } - decoder.output.finish()?; + accum.finish()?; Ok(()) } fn parse_lzma( - decoder: &mut lzma::DecoderState>, + accum: &mut lzbuffer::LzAccumBuffer, + decoder: &mut lzma::DecoderState, input: &mut R, status: u8, ) -> error::Result<()> @@ -103,20 +112,16 @@ where ); if reset_dict { - decoder.output.reset()?; + accum.reset()?; } if reset_state { - let lc: u32; - let lp: u32; - let mut pb: u32; - - if reset_props { + let new_props = if reset_props { let props = input.read_u8().map_err(|e| { error::Error::LzmaError(format!("LZMA2 expected new properties: {}", e)) })?; - pb = props as u32; + let mut pb = props as u32; if pb >= 225 { return Err(error::Error::LzmaError(format!( "LZMA2 invalid properties: {} must be < 225", @@ -124,9 +129,9 @@ where ))); } - lc = pb % 9; + let lc = pb % 9; pb /= 9; - lp = pb % 5; + let lp = pb % 5; pb /= 5; if lc + lp > 4 { @@ -137,25 +142,24 @@ where } lzma_info!("Properties {{ lc: {}, lp: {}, pb: {} }}", lc, lp, pb); + LzmaProperties { lc, lp, pb } } else { - lc = decoder.lc; - lp = decoder.lp; - pb = decoder.pb; - } + decoder.lzma_props + }; - decoder.reset_state(lc, lp, pb); + decoder.reset_state(new_props); } - decoder.set_unpacked_size(Some(unpacked_size + decoder.output.len() as u64)); + decoder.set_unpacked_size(Some(unpacked_size + accum.len() as u64)); let mut taken = input.take(packed_size); let mut rangecoder = rangecoder::RangeDecoder::new(&mut taken) .map_err(|e| error::Error::LzmaError(format!("LZMA input too short: {}", e)))?; - decoder.process(&mut rangecoder) + decoder.process(accum, &mut rangecoder) } fn parse_uncompressed( - decoder: &mut lzma::DecoderState>, + accum: &mut lzbuffer::LzAccumBuffer, input: &mut R, reset_dict: bool, ) -> error::Result<()> @@ -175,7 +179,7 @@ where ); if reset_dict { - decoder.output.reset()?; + accum.reset()?; } let mut buf = vec![0; unpacked_size]; @@ -185,7 +189,7 @@ where unpacked_size, e )) })?; - decoder.output.append_bytes(buf.as_slice()); + accum.append_bytes(buf.as_slice()); Ok(()) } diff --git a/src/decode/rangecoder.rs b/src/decode/rangecoder.rs index a1ffacc..207be81 100644 --- a/src/decode/rangecoder.rs +++ b/src/decode/rangecoder.rs @@ -180,6 +180,10 @@ impl BitTree { ) -> io::Result { rangecoder.parse_reverse_bit_tree(self.num_bits, self.probs.as_mut_slice(), 0, update) } + + pub fn reset(&mut self) { + self.probs.fill(0x400); + } } pub struct LenDecoder { @@ -215,4 +219,12 @@ impl LenDecoder { Ok(self.high_coder.parse(rangecoder, update)? as usize + 16) } } + + pub fn reset(&mut self) { + self.choice = 0x400; + self.choice2 = 0x400; + self.low_coder.iter_mut().for_each(|t| t.reset()); + self.mid_coder.iter_mut().for_each(|t| t.reset()); + self.high_coder.reset(); + } } diff --git a/src/decode/stream.rs b/src/decode/stream.rs index 1fe7b67..43fca4f 100644 --- a/src/decode/stream.rs +++ b/src/decode/stream.rs @@ -1,5 +1,5 @@ use crate::decode::lzbuffer::{LzBuffer, LzCircularBuffer}; -use crate::decode::lzma::{new_circular, new_circular_with_memlimit, DecoderState, LzmaParams}; +use crate::decode::lzma::{DecoderState, LzmaParams}; use crate::decode::rangecoder::RangeDecoder; use crate::decompress::Options; use crate::error::Error; @@ -41,9 +41,10 @@ struct RunState where W: Write, { - decoder: DecoderState>, + decoder: DecoderState, range: u32, code: u32, + output: LzCircularBuffer, } impl Debug for RunState @@ -98,7 +99,7 @@ where pub fn get_output(&self) -> Option<&W> { self.state.as_ref().map(|state| match state { State::Header(output) => &output, - State::Data(state) => state.decoder.output.get_output(), + State::Data(state) => state.output.get_output(), }) } @@ -106,7 +107,7 @@ where pub fn get_output_mut(&mut self) -> Option<&mut W> { self.state.as_mut().map(|state| match state { State::Header(output) => output, - State::Data(state) => state.decoder.output.get_output_mut(), + State::Data(state) => state.output.get_output_mut(), }) } @@ -130,9 +131,11 @@ where Cursor::new(&self.tmp.get_ref()[0..self.tmp.position() as usize]); let mut range_decoder = RangeDecoder::from_parts(&mut stream, state.range, state.code); - state.decoder.process(&mut range_decoder)?; + state + .decoder + .process(&mut state.output, &mut range_decoder)?; } - let output = state.decoder.output.finish()?; + let output = state.output.finish()?; Ok(output) } } @@ -155,24 +158,25 @@ where ) -> crate::error::Result> { match LzmaParams::read_header(&mut input, options) { Ok(params) => { - let decoder = if let Some(memlimit) = options.memlimit { - new_circular_with_memlimit(output, params, memlimit) - } else { - new_circular(output, params) - }?; - + let decoder = DecoderState::new(params.properties, params.unpacked_size); + let output = LzCircularBuffer::from_stream( + output, + params.dict_size as usize, + options.memlimit.unwrap_or(usize::MAX), + ); // The RangeDecoder is only kept temporarily as we are processing // chunks of data. if let Ok(rangecoder) = RangeDecoder::new(&mut input) { Ok(State::Data(RunState { decoder, + output, range: rangecoder.range, code: rangecoder.code, })) } else { // Failed to create a RangeDecoder because we need more data, // try again later. - Ok(State::Header(decoder.output.into_output())) + Ok(State::Header(output.into_output())) } } // Failed to read_header() because we need more data, try again later. @@ -191,11 +195,12 @@ where // Try to process all bytes of data. state .decoder - .process_stream(&mut rangecoder) + .process_stream(&mut state.output, &mut rangecoder) .map_err(|e| -> io::Error { e.into() })?; Ok(RunState { decoder: state.decoder, + output: state.output, range: rangecoder.range, code: rangecoder.code, }) @@ -328,7 +333,7 @@ where if let Some(ref mut state) = self.state { match state { State::Header(_) => Ok(()), - State::Data(state) => state.decoder.output.get_output_mut().flush(), + State::Data(state) => state.output.get_output_mut().flush(), } } else { Ok(()) diff --git a/src/lib.rs b/src/lib.rs index 763e8d8..bd5a05f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,10 +9,12 @@ mod macros; mod decode; mod encode; + pub mod error; + mod xz; -use crate::decode::lzbuffer::LzBuffer; +use crate::decode::lzbuffer::{LzBuffer, LzCircularBuffer}; use std::io; /// Compression helpers. @@ -42,16 +44,17 @@ pub fn lzma_decompress_with_options( options: &decompress::Options, ) -> error::Result<()> { let params = decode::lzma::LzmaParams::read_header(input, options)?; - let mut decoder = if let Some(memlimit) = options.memlimit { - decode::lzma::new_circular_with_memlimit(output, params, memlimit)? - } else { - decode::lzma::new_circular(output, params)? - }; + let mut decoder = decode::lzma::DecoderState::new(params.properties, params.unpacked_size); + let mut output = LzCircularBuffer::from_stream( + output, + params.dict_size as usize, + options.memlimit.unwrap_or(usize::MAX), + ); let mut rangecoder = decode::rangecoder::RangeDecoder::new(input) .map_err(|e| error::Error::LzmaError(format!("LZMA stream too short: {}", e)))?; - decoder.process(&mut rangecoder)?; - decoder.output.finish()?; + decoder.process(&mut output, &mut rangecoder)?; + output.finish()?; Ok(()) }