compression_codecs/xz2/
encoder.rs1use compression_core::{util::PartialBuffer, Level};
2use liblzma::stream::{Action, Check, Status, Stream};
3use std::{
4 convert::{TryFrom, TryInto},
5 fmt, io,
6};
7
8use crate::{
9 lzma::params::{LzmaEncoderParams, LzmaOptions},
10 Encode, Xz2FileFormat,
11};
12
13pub struct Xz2Encoder {
15 stream: Stream,
16 params: LzmaEncoderParams,
17}
18
19impl fmt::Debug for Xz2Encoder {
20 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21 f.debug_struct("Xz2Encoder").finish_non_exhaustive()
22 }
23}
24
25impl TryFrom<LzmaEncoderParams> for Xz2Encoder {
26 type Error = liblzma::stream::Error;
27
28 fn try_from(params: LzmaEncoderParams) -> Result<Self, Self::Error> {
29 let stream = Stream::try_from(¶ms)?;
30 Ok(Self {
31 stream,
32 params: params.clone(),
33 })
34 }
35}
36
37fn xz2_level(level: Level) -> u32 {
38 match level {
39 Level::Fastest => 0,
40 Level::Best => 9,
41 Level::Precise(quality) => quality.try_into().unwrap_or(0).clamp(0, 9),
42 _ => 5,
43 }
44}
45
46impl Xz2Encoder {
47 pub fn new(format: Xz2FileFormat, level: Level) -> Self {
48 let preset = xz2_level(level);
49 let params = match format {
50 Xz2FileFormat::Xz => LzmaEncoderParams::Easy {
51 preset,
52 check: Check::Crc64,
53 },
54 Xz2FileFormat::Lzma => {
55 let options = LzmaOptions::default().preset(preset);
56 LzmaEncoderParams::Lzma { options }
57 }
58 };
59
60 Self::try_from(params).unwrap()
61 }
62
63 #[cfg(feature = "xz-parallel")]
64 pub fn xz_parallel(level: Level, threads: std::num::NonZeroU32) -> Self {
65 use crate::lzma::params::MtStreamBuilder;
66
67 let preset = xz2_level(level);
68 let mut builder = MtStreamBuilder::default();
69 builder
70 .threads(threads)
71 .timeout_ms(300)
72 .preset(preset)
73 .check(Check::Crc64);
74 let params = LzmaEncoderParams::MultiThread { builder };
75 Self::try_from(params).unwrap()
76 }
77}
78
79impl Encode for Xz2Encoder {
80 fn encode(
81 &mut self,
82 input: &mut PartialBuffer<impl AsRef<[u8]>>,
83 output: &mut PartialBuffer<impl AsRef<[u8]> + AsMut<[u8]>>,
84 ) -> io::Result<()> {
85 let previous_in = self.stream.total_in() as usize;
86 let previous_out = self.stream.total_out() as usize;
87
88 let status = self
89 .stream
90 .process(input.unwritten(), output.unwritten_mut(), Action::Run)?;
91
92 input.advance(self.stream.total_in() as usize - previous_in);
93 output.advance(self.stream.total_out() as usize - previous_out);
94
95 match status {
96 Status::Ok | Status::StreamEnd => Ok(()),
97 Status::GetCheck => Err(io::Error::other("Unexpected lzma integrity check")),
98 Status::MemNeeded => Err(io::Error::other("out of memory")),
99 }
100 }
101
102 fn flush(
103 &mut self,
104 output: &mut PartialBuffer<impl AsRef<[u8]> + AsMut<[u8]>>,
105 ) -> io::Result<bool> {
106 let previous_out = self.stream.total_out() as usize;
107
108 let action = match &self.params {
109 #[cfg(feature = "xz-parallel")]
111 LzmaEncoderParams::MultiThread { builder: _ } => Action::FullFlush,
112 _ => Action::SyncFlush,
113 };
114
115 let status = self.stream.process(&[], output.unwritten_mut(), action)?;
116
117 output.advance(self.stream.total_out() as usize - previous_out);
118
119 match status {
120 Status::Ok => Ok(false),
121 Status::StreamEnd => Ok(true),
122 Status::GetCheck => Err(io::Error::other("Unexpected lzma integrity check")),
123 Status::MemNeeded => Err(io::Error::other("out of memory")),
124 }
125 }
126
127 fn finish(
128 &mut self,
129 output: &mut PartialBuffer<impl AsRef<[u8]> + AsMut<[u8]>>,
130 ) -> io::Result<bool> {
131 let previous_out = self.stream.total_out() as usize;
132
133 let status = self
134 .stream
135 .process(&[], output.unwritten_mut(), Action::Finish)?;
136
137 output.advance(self.stream.total_out() as usize - previous_out);
138
139 match status {
140 Status::Ok => Ok(false),
141 Status::StreamEnd => Ok(true),
142 Status::GetCheck => Err(io::Error::other("Unexpected lzma integrity check")),
143 Status::MemNeeded => Err(io::Error::other("out of memory")),
144 }
145 }
146}