compression_codecs/xz2/
encoder.rs

1use compression_core::{
2    util::{PartialBuffer, WriteBuffer},
3    Level,
4};
5use liblzma::stream::{Action, Check, Stream};
6use std::{
7    convert::{TryFrom, TryInto},
8    fmt, io,
9};
10
11use crate::{
12    lzma::params::{LzmaEncoderParams, LzmaOptions},
13    xz2::process_stream,
14    EncodeV2, Xz2FileFormat,
15};
16
17/// Xz2 encoding stream
18pub struct Xz2Encoder {
19    stream: Stream,
20    params: LzmaEncoderParams,
21}
22
23impl fmt::Debug for Xz2Encoder {
24    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25        f.debug_struct("Xz2Encoder").finish_non_exhaustive()
26    }
27}
28
29impl TryFrom<LzmaEncoderParams> for Xz2Encoder {
30    type Error = liblzma::stream::Error;
31
32    fn try_from(params: LzmaEncoderParams) -> Result<Self, Self::Error> {
33        let stream = Stream::try_from(&params)?;
34        Ok(Self {
35            stream,
36            params: params.clone(),
37        })
38    }
39}
40
41fn xz2_level(level: Level) -> u32 {
42    match level {
43        Level::Fastest => 0,
44        Level::Best => 9,
45        Level::Precise(quality) => quality.try_into().unwrap_or(0).clamp(0, 9),
46        _ => 5,
47    }
48}
49
50impl Xz2Encoder {
51    pub fn new(format: Xz2FileFormat, level: Level) -> Self {
52        let preset = xz2_level(level);
53        let params = match format {
54            Xz2FileFormat::Xz => LzmaEncoderParams::Easy {
55                preset,
56                check: Check::Crc64,
57            },
58            Xz2FileFormat::Lzma => {
59                let options = LzmaOptions::default().preset(preset);
60                LzmaEncoderParams::Lzma { options }
61            }
62        };
63
64        Self::try_from(params).unwrap()
65    }
66
67    #[cfg(feature = "xz-parallel")]
68    pub fn xz_parallel(level: Level, threads: std::num::NonZeroU32) -> Self {
69        use crate::lzma::params::MtStreamBuilder;
70
71        let preset = xz2_level(level);
72        let mut builder = MtStreamBuilder::default();
73        builder
74            .threads(threads)
75            .timeout_ms(300)
76            .preset(preset)
77            .check(Check::Crc64);
78        let params = LzmaEncoderParams::MultiThread { builder };
79        Self::try_from(params).unwrap()
80    }
81}
82
83impl EncodeV2 for Xz2Encoder {
84    fn encode(
85        &mut self,
86        input: &mut PartialBuffer<&[u8]>,
87        output: &mut WriteBuffer<'_>,
88    ) -> io::Result<()> {
89        process_stream(&mut self.stream, input, output, Action::Run).map(|_| ())
90    }
91
92    fn flush(&mut self, output: &mut WriteBuffer<'_>) -> io::Result<bool> {
93        let action = match &self.params {
94            // Multi-threaded streams don't support SyncFlush, use FullFlush instead
95            #[cfg(feature = "xz-parallel")]
96            LzmaEncoderParams::MultiThread { builder: _ } => Action::FullFlush,
97            _ => Action::SyncFlush,
98        };
99
100        process_stream(
101            &mut self.stream,
102            &mut PartialBuffer::new(&[]),
103            output,
104            action,
105        )
106    }
107
108    fn finish(&mut self, output: &mut WriteBuffer<'_>) -> io::Result<bool> {
109        process_stream(
110            &mut self.stream,
111            &mut PartialBuffer::new(&[]),
112            output,
113            Action::Finish,
114        )
115    }
116}