compression_codecs/xz/
decoder.rs

1use crate::{Decode, Xz2Decoder};
2use compression_core::util::PartialBuffer;
3use std::{
4    convert::TryInto,
5    io::{Error, ErrorKind, Result},
6};
7
8/// Xz decoding stream
9#[derive(Debug)]
10pub struct XzDecoder {
11    inner: Xz2Decoder,
12    skip_padding: Option<u8>,
13}
14
15impl Default for XzDecoder {
16    fn default() -> Self {
17        Self {
18            inner: Xz2Decoder::new(usize::MAX.try_into().unwrap()),
19            skip_padding: None,
20        }
21    }
22}
23
24impl XzDecoder {
25    pub fn new() -> Self {
26        Self::default()
27    }
28
29    pub fn with_memlimit(memlimit: u64) -> Self {
30        Self {
31            inner: Xz2Decoder::new(memlimit),
32            skip_padding: None,
33        }
34    }
35
36    #[cfg(feature = "xz-parallel")]
37    pub fn parallel(threads: std::num::NonZeroU32, memlimit: u64) -> Self {
38        Self {
39            inner: Xz2Decoder::parallel(threads, memlimit),
40            skip_padding: None,
41        }
42    }
43}
44
45impl Decode for XzDecoder {
46    fn reinit(&mut self) -> Result<()> {
47        self.skip_padding = Some(4);
48        self.inner.reinit()
49    }
50
51    fn decode(
52        &mut self,
53        input: &mut PartialBuffer<impl AsRef<[u8]>>,
54        output: &mut PartialBuffer<impl AsRef<[u8]> + AsMut<[u8]>>,
55    ) -> Result<bool> {
56        if let Some(ref mut count) = self.skip_padding {
57            while input.unwritten().first() == Some(&0) {
58                input.advance(1);
59                *count -= 1;
60                if *count == 0 {
61                    *count = 4;
62                }
63            }
64            if input.unwritten().is_empty() {
65                return Ok(true);
66            }
67            // If this is non-padding then it cannot start with null bytes, so it must be invalid
68            // padding
69            if *count != 4 {
70                return Err(Error::new(
71                    ErrorKind::InvalidData,
72                    "stream padding was not a multiple of 4 bytes",
73                ));
74            }
75            self.skip_padding = None;
76        }
77        self.inner.decode(input, output)
78    }
79
80    fn flush(
81        &mut self,
82        output: &mut PartialBuffer<impl AsRef<[u8]> + AsMut<[u8]>>,
83    ) -> Result<bool> {
84        if self.skip_padding.is_some() {
85            return Ok(true);
86        }
87        self.inner.flush(output)
88    }
89
90    fn finish(
91        &mut self,
92        output: &mut PartialBuffer<impl AsRef<[u8]> + AsMut<[u8]>>,
93    ) -> Result<bool> {
94        if self.skip_padding.is_some() {
95            return Ok(true);
96        }
97        self.inner.finish(output)
98    }
99}