eazip/
compression.rs

1use std::{fmt, io};
2
3/// A compression method used in an archive.
4///
5/// The field is the value as defined by the ZIP standard.
6#[derive(Clone, Copy, PartialEq, Eq)]
7pub struct CompressionMethod(pub u16);
8
9impl fmt::Debug for CompressionMethod {
10    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
11        let name = match *self {
12            Self::STORE => "store",
13            Self::DEFLATE => "deflate",
14            Self::DEFLATE64 => "deflate64",
15            Self::BZIP2 => "bzip2",
16            Self::LZMA => "lzma",
17            Self::ZSTD_DEPRECATED => "zstd (deprecated)",
18            Self::ZSTD => "zstd",
19            Self::XZ => "xz",
20            Self::AES => "aes",
21            Self(n) => return write!(f, "unknown ({n})"),
22        };
23
24        f.write_str(name)
25    }
26}
27
28impl Default for CompressionMethod {
29    fn default() -> Self {
30        if cfg!(feature = "zstd") {
31            Self::ZSTD
32        } else if cfg!(feature = "deflate") {
33            Self::DEFLATE
34        } else {
35            Self::STORE
36        }
37    }
38}
39
40impl CompressionMethod {
41    /// The compressions methods natively supported by this crate.
42    ///
43    /// Exact value varies depending the enabled features.
44    pub const SUPPORTED: &[CompressionMethod] = &[
45        Self::STORE,
46        #[cfg(feature = "deflate")]
47        Self::DEFLATE,
48        #[cfg(feature = "zstd")]
49        Self::ZSTD,
50    ];
51}
52
53impl CompressionMethod {
54    /// No compression
55    pub const STORE: Self = Self(0);
56    pub const DEFLATE: Self = Self(8);
57    pub const DEFLATE64: Self = Self(9);
58    pub const BZIP2: Self = Self(12);
59    pub const LZMA: Self = Self(14);
60    pub const ZSTD_DEPRECATED: Self = Self(20);
61    pub const ZSTD: Self = Self(93);
62    pub const XZ: Self = Self(95);
63    pub const AES: Self = Self(99);
64}
65
66#[cold]
67fn unsupported_method(method: CompressionMethod) -> io::Error {
68    io::Error::new(
69        io::ErrorKind::Unsupported,
70        format!("Unsupported compression method: {method:?}"),
71    )
72}
73
74/// An adapter to decompress a stream.
75pub struct Decompressor<R>(DecompressorImpl<R>);
76
77enum DecompressorImpl<R> {
78    Store(R),
79    #[cfg(feature = "deflate")]
80    Deflate(flate2::bufread::DeflateDecoder<R>),
81    #[cfg(feature = "zstd")]
82    Zstd(zstd::Decoder<'static, R>),
83}
84
85impl<R: io::BufRead> Decompressor<R> {
86    /// Creates a new `Decompressor`.
87    ///
88    /// Compression methods unsupported by this crate will cause an error.
89    pub fn new(reader: R, method: CompressionMethod) -> io::Result<Self> {
90        let inner = match method {
91            CompressionMethod::STORE => DecompressorImpl::Store(reader),
92            #[cfg(feature = "deflate")]
93            CompressionMethod::DEFLATE => {
94                DecompressorImpl::Deflate(flate2::bufread::DeflateDecoder::new(reader))
95            }
96            #[cfg(feature = "zstd")]
97            CompressionMethod::ZSTD => DecompressorImpl::Zstd(zstd::Decoder::with_buffer(reader)?),
98            _ => return Err(unsupported_method(method)),
99        };
100
101        Ok(Self(inner))
102    }
103
104    /// Returns the compression method used by this decompressor
105    pub fn compression_method(&self) -> CompressionMethod {
106        match self.0 {
107            DecompressorImpl::Store(_) => CompressionMethod::STORE,
108            #[cfg(feature = "deflate")]
109            DecompressorImpl::Deflate(_) => CompressionMethod::DEFLATE,
110            #[cfg(feature = "zstd")]
111            DecompressorImpl::Zstd(_) => CompressionMethod::ZSTD,
112        }
113    }
114
115    /// Gets a mutable reference to the underlying reader.
116    ///
117    /// It is inadvisable to directly read from the underlying reader.
118    pub fn get_mut(&mut self) -> &mut R {
119        match &mut self.0 {
120            DecompressorImpl::Store(r) => r,
121            #[cfg(feature = "deflate")]
122            DecompressorImpl::Deflate(r) => r.get_mut(),
123            #[cfg(feature = "zstd")]
124            DecompressorImpl::Zstd(r) => r.get_mut(),
125        }
126    }
127}
128
129impl<R: io::BufRead> io::Read for Decompressor<R> {
130    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
131        match &mut self.0 {
132            DecompressorImpl::Store(r) => r.read(buf),
133            #[cfg(feature = "deflate")]
134            DecompressorImpl::Deflate(r) => r.read(buf),
135            #[cfg(feature = "zstd")]
136            DecompressorImpl::Zstd(r) => r.read(buf),
137        }
138    }
139
140    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
141        match &mut self.0 {
142            DecompressorImpl::Store(r) => r.read_to_end(buf),
143            #[cfg(feature = "deflate")]
144            DecompressorImpl::Deflate(r) => r.read_to_end(buf),
145            #[cfg(feature = "zstd")]
146            DecompressorImpl::Zstd(r) => r.read_to_end(buf),
147        }
148    }
149
150    fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
151        match &mut self.0 {
152            DecompressorImpl::Store(r) => r.read_to_string(buf),
153            #[cfg(feature = "deflate")]
154            DecompressorImpl::Deflate(r) => r.read_to_string(buf),
155            #[cfg(feature = "zstd")]
156            DecompressorImpl::Zstd(r) => r.read_to_string(buf),
157        }
158    }
159}
160
161/// An adapter to compress a stream.
162pub struct Compressor<W: io::Write>(CompressorImpl<W>);
163
164enum CompressorImpl<W: io::Write> {
165    Store(W),
166    #[cfg(feature = "deflate")]
167    Deflate(flate2::write::DeflateEncoder<W>),
168    #[cfg(feature = "zstd")]
169    Zstd(zstd::Encoder<'static, W>),
170}
171
172impl<W: io::Write> Compressor<W> {
173    /// Creates a new `Compressor`.
174    ///
175    /// The `level` parameter signification depends on the method used. `None`
176    /// always means "default level" and is always supported.
177    ///
178    /// Compression methods unsupported by this crate will cause an error.
179    pub fn new(writer: W, method: CompressionMethod, level: Option<i32>) -> io::Result<Self> {
180        // Avoid the "unused" warning if not using the default features
181        let _ = level;
182
183        let inner = match method {
184            CompressionMethod::STORE => CompressorImpl::Store(writer),
185            #[cfg(feature = "deflate")]
186            CompressionMethod::DEFLATE => {
187                let level = match level {
188                    Some(level) => flate2::Compression::new(level as _),
189                    None => flate2::Compression::default(),
190                };
191
192                CompressorImpl::Deflate(flate2::write::DeflateEncoder::new(writer, level))
193            }
194            #[cfg(feature = "zstd")]
195            CompressionMethod::ZSTD => {
196                let level = level.unwrap_or(0);
197                CompressorImpl::Zstd(zstd::Encoder::new(writer, level)?)
198            }
199            _ => return Err(unsupported_method(method)),
200        };
201
202        Ok(Self(inner))
203    }
204
205    /// Returns the compression method used by this compressor
206    pub fn compression_method(&self) -> CompressionMethod {
207        match self.0 {
208            CompressorImpl::Store(_) => CompressionMethod::STORE,
209            #[cfg(feature = "deflate")]
210            CompressorImpl::Deflate(_) => CompressionMethod::DEFLATE,
211            #[cfg(feature = "zstd")]
212            CompressorImpl::Zstd(_) => CompressionMethod::ZSTD,
213        }
214    }
215
216    /// Gets a mutable reference to the underlying writer.
217    ///
218    /// It is inadvisable to directly write to the underlying writer.
219    pub fn get_mut(&mut self) -> &mut W {
220        match &mut self.0 {
221            CompressorImpl::Store(w) => w,
222            #[cfg(feature = "deflate")]
223            CompressorImpl::Deflate(w) => w.get_mut(),
224            #[cfg(feature = "zstd")]
225            CompressorImpl::Zstd(w) => w.get_mut(),
226        }
227    }
228
229    /// Attempts to finish the stream.
230    ///
231    /// You must finish the stream when you're done writing.
232    pub fn do_finish(&mut self) -> io::Result<()> {
233        match &mut self.0 {
234            CompressorImpl::Store(_) => Ok(()),
235            #[cfg(feature = "deflate")]
236            CompressorImpl::Deflate(w) => w.try_finish(),
237            #[cfg(feature = "zstd")]
238            CompressorImpl::Zstd(w) => w.do_finish(),
239        }
240    }
241
242    /// Finishes the stream and get the writer back.
243    ///
244    /// You must finish the stream when you're done writing.
245    pub fn finish(self) -> io::Result<W> {
246        match self.0 {
247            CompressorImpl::Store(w) => Ok(w),
248            #[cfg(feature = "deflate")]
249            CompressorImpl::Deflate(w) => w.finish(),
250            #[cfg(feature = "zstd")]
251            CompressorImpl::Zstd(w) => w.finish(),
252        }
253    }
254}
255
256impl<W: io::Write> io::Write for Compressor<W> {
257    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
258        match &mut self.0 {
259            CompressorImpl::Store(w) => w.write(buf),
260            #[cfg(feature = "deflate")]
261            CompressorImpl::Deflate(w) => w.write(buf),
262            #[cfg(feature = "zstd")]
263            CompressorImpl::Zstd(w) => w.write(buf),
264        }
265    }
266
267    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
268        match &mut self.0 {
269            CompressorImpl::Store(w) => w.write_all(buf),
270            #[cfg(feature = "deflate")]
271            CompressorImpl::Deflate(w) => w.write_all(buf),
272            #[cfg(feature = "zstd")]
273            CompressorImpl::Zstd(w) => w.write_all(buf),
274        }
275    }
276
277    fn flush(&mut self) -> io::Result<()> {
278        match &mut self.0 {
279            CompressorImpl::Store(w) => w.flush(),
280            #[cfg(feature = "deflate")]
281            CompressorImpl::Deflate(w) => w.flush(),
282            #[cfg(feature = "zstd")]
283            CompressorImpl::Zstd(w) => w.flush(),
284        }
285    }
286}