compression_codecs/xz2/
decoder.rs

1use crate::{lzma::params::LzmaDecoderParams, DecodeV2, DecodedSize};
2use compression_core::util::{PartialBuffer, WriteBuffer};
3use liblzma::stream::{Action, Status, Stream};
4use std::{
5    convert::TryFrom,
6    fmt,
7    io::{self, Cursor},
8};
9
10/// Xz2 decoding stream
11pub struct Xz2Decoder {
12    stream: Stream,
13    params: LzmaDecoderParams,
14}
15
16impl fmt::Debug for Xz2Decoder {
17    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18        f.debug_struct("Xz2Decoder").finish_non_exhaustive()
19    }
20}
21
22impl TryFrom<LzmaDecoderParams> for Xz2Decoder {
23    type Error = liblzma::stream::Error;
24
25    fn try_from(params: LzmaDecoderParams) -> Result<Self, Self::Error> {
26        let stream = Stream::try_from(&params)?;
27        Ok(Self { stream, params })
28    }
29}
30
31impl Xz2Decoder {
32    pub fn new(mem_limit: u64) -> Self {
33        let params = LzmaDecoderParams::Auto {
34            mem_limit,
35            flags: 0,
36        };
37        Self::try_from(params).unwrap()
38    }
39
40    #[cfg(feature = "xz-parallel")]
41    pub fn parallel(threads: std::num::NonZeroU32, mem_limit: u64) -> Self {
42        use crate::lzma::params::MtStreamBuilder;
43
44        let mut builder = MtStreamBuilder::default();
45
46        builder
47            .threads(threads)
48            .timeout_ms(300)
49            .mem_limit_stop(mem_limit);
50
51        let params = LzmaDecoderParams::MultiThread { builder };
52
53        Self::try_from(params).unwrap()
54    }
55
56    /// Return `Ok(true)` on stream ends.
57    fn process(
58        &mut self,
59        input: &[u8],
60        output: &mut WriteBuffer<'_>,
61        action: Action,
62    ) -> io::Result<bool> {
63        let previous_out = self.stream.total_out() as usize;
64
65        let status = self
66            .stream
67            .process(input, output.initialize_unwritten(), action)?;
68
69        output.advance(self.stream.total_out() as usize - previous_out);
70
71        match status {
72            Status::Ok => Ok(false),
73            Status::StreamEnd => Ok(true),
74            Status::GetCheck => Err(io::Error::other("Unexpected lzma integrity check")),
75            Status::MemNeeded => Err(io::ErrorKind::OutOfMemory.into()),
76        }
77    }
78}
79
80impl DecodeV2 for Xz2Decoder {
81    fn reinit(&mut self) -> io::Result<()> {
82        *self = Self::try_from(self.params.clone())?;
83        Ok(())
84    }
85
86    fn decode(
87        &mut self,
88        input: &mut PartialBuffer<&[u8]>,
89        output: &mut WriteBuffer<'_>,
90    ) -> io::Result<bool> {
91        let previous_in = self.stream.total_in() as usize;
92
93        let res = self.process(input.unwritten(), output, Action::Run);
94        input.advance(self.stream.total_in() as usize - previous_in);
95
96        res
97    }
98
99    fn flush(&mut self, _output: &mut WriteBuffer<'_>) -> io::Result<bool> {
100        // While decoding flush is a noop
101        Ok(true)
102    }
103
104    fn finish(&mut self, output: &mut WriteBuffer<'_>) -> io::Result<bool> {
105        self.process(&[], output, Action::Finish)
106    }
107}
108
109impl DecodedSize for Xz2Decoder {
110    fn decoded_size(input: &[u8]) -> io::Result<u64> {
111        let cursor = Cursor::new(input);
112        liblzma::uncompressed_size(cursor)
113    }
114}
115
116#[cfg(test)]
117mod tests {
118    use std::convert::TryFrom;
119
120    use crate::{
121        lzma::params::{LzmaDecoderParams, LzmaFilter, LzmaFilters, LzmaOptions},
122        Xz2Decoder,
123    };
124
125    #[test]
126    fn test_lzma_decoder_from_params() {
127        let filters = LzmaFilters::default().add_filter(LzmaFilter::Lzma2(LzmaOptions::default()));
128        let params = LzmaDecoderParams::Raw { filters };
129        Xz2Decoder::try_from(params).unwrap();
130    }
131}