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