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
77impl<R: io::BufRead + fmt::Debug> fmt::Debug for Decompressor<R> {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        f.debug_struct("Decompressor")
80            .field("method", &self.compression_method())
81            .field("reader", &self.get_ref())
82            .finish()
83    }
84}
85
86enum DecompressorImpl<R> {
87    Store(R),
88    #[cfg(feature = "deflate")]
89    Deflate(flate2::bufread::DeflateDecoder<R>),
90    #[cfg(feature = "zstd")]
91    Zstd(zstd::Decoder<'static, R>),
92}
93
94impl<R: io::BufRead> Decompressor<R> {
95    /// Creates a new `Decompressor`.
96    ///
97    /// Compression methods unsupported by this crate will cause an error.
98    pub fn new(reader: R, method: CompressionMethod) -> io::Result<Self> {
99        let inner = match method {
100            CompressionMethod::STORE => DecompressorImpl::Store(reader),
101            #[cfg(feature = "deflate")]
102            CompressionMethod::DEFLATE => {
103                DecompressorImpl::Deflate(flate2::bufread::DeflateDecoder::new(reader))
104            }
105            #[cfg(feature = "zstd")]
106            CompressionMethod::ZSTD => DecompressorImpl::Zstd(zstd::Decoder::with_buffer(reader)?),
107            _ => return Err(unsupported_method(method)),
108        };
109
110        Ok(Self(inner))
111    }
112
113    /// Returns the compression method used by this decompressor
114    pub fn compression_method(&self) -> CompressionMethod {
115        match self.0 {
116            DecompressorImpl::Store(_) => CompressionMethod::STORE,
117            #[cfg(feature = "deflate")]
118            DecompressorImpl::Deflate(_) => CompressionMethod::DEFLATE,
119            #[cfg(feature = "zstd")]
120            DecompressorImpl::Zstd(_) => CompressionMethod::ZSTD,
121        }
122    }
123
124    /// Gets a shared reference to the underlying reader.
125    pub fn get_ref(&self) -> &R {
126        match &self.0 {
127            DecompressorImpl::Store(r) => r,
128            #[cfg(feature = "deflate")]
129            DecompressorImpl::Deflate(r) => r.get_ref(),
130            #[cfg(feature = "zstd")]
131            DecompressorImpl::Zstd(r) => r.get_ref(),
132        }
133    }
134
135    /// Gets a mutable reference to the underlying reader.
136    ///
137    /// It is inadvisable to directly read from the underlying reader.
138    pub fn get_mut(&mut self) -> &mut R {
139        match &mut self.0 {
140            DecompressorImpl::Store(r) => r,
141            #[cfg(feature = "deflate")]
142            DecompressorImpl::Deflate(r) => r.get_mut(),
143            #[cfg(feature = "zstd")]
144            DecompressorImpl::Zstd(r) => r.get_mut(),
145        }
146    }
147}
148
149impl<R: io::BufRead> io::Read for Decompressor<R> {
150    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
151        match &mut self.0 {
152            DecompressorImpl::Store(r) => r.read(buf),
153            #[cfg(feature = "deflate")]
154            DecompressorImpl::Deflate(r) => r.read(buf),
155            #[cfg(feature = "zstd")]
156            DecompressorImpl::Zstd(r) => r.read(buf),
157        }
158    }
159
160    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
161        match &mut self.0 {
162            DecompressorImpl::Store(r) => r.read_to_end(buf),
163            #[cfg(feature = "deflate")]
164            DecompressorImpl::Deflate(r) => r.read_to_end(buf),
165            #[cfg(feature = "zstd")]
166            DecompressorImpl::Zstd(r) => r.read_to_end(buf),
167        }
168    }
169
170    fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
171        match &mut self.0 {
172            DecompressorImpl::Store(r) => r.read_to_string(buf),
173            #[cfg(feature = "deflate")]
174            DecompressorImpl::Deflate(r) => r.read_to_string(buf),
175            #[cfg(feature = "zstd")]
176            DecompressorImpl::Zstd(r) => r.read_to_string(buf),
177        }
178    }
179}
180
181/// An adapter to compress a stream.
182pub struct Compressor<W: io::Write>(CompressorImpl<W>);
183
184impl<W: io::Write + fmt::Debug> fmt::Debug for Compressor<W> {
185    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186        f.debug_struct("Compressor")
187            .field("method", &self.compression_method())
188            .field("writer", &self.get_ref())
189            .finish()
190    }
191}
192
193enum CompressorImpl<W: io::Write> {
194    Store(W),
195    #[cfg(feature = "deflate")]
196    Deflate(flate2::write::DeflateEncoder<W>),
197    #[cfg(feature = "zstd")]
198    Zstd(zstd::Encoder<'static, W>),
199}
200
201impl<W: io::Write> Compressor<W> {
202    /// Creates a new `Compressor`.
203    ///
204    /// The `level` parameter signification depends on the method used. `None`
205    /// always means "default level" and is always supported.
206    ///
207    /// Compression methods unsupported by this crate will cause an error.
208    pub fn new(writer: W, method: CompressionMethod, level: Option<i32>) -> io::Result<Self> {
209        // Avoid the "unused" warning if not using the default features
210        let _ = level;
211
212        let inner = match method {
213            CompressionMethod::STORE => CompressorImpl::Store(writer),
214            #[cfg(feature = "deflate")]
215            CompressionMethod::DEFLATE => {
216                let level = match level {
217                    Some(level) => flate2::Compression::new(level as _),
218                    None => flate2::Compression::default(),
219                };
220
221                CompressorImpl::Deflate(flate2::write::DeflateEncoder::new(writer, level))
222            }
223            #[cfg(feature = "zstd")]
224            CompressionMethod::ZSTD => {
225                let level = level.unwrap_or(0);
226                CompressorImpl::Zstd(zstd::Encoder::new(writer, level)?)
227            }
228            _ => return Err(unsupported_method(method)),
229        };
230
231        Ok(Self(inner))
232    }
233
234    /// Returns the compression method used by this compressor
235    pub fn compression_method(&self) -> CompressionMethod {
236        match self.0 {
237            CompressorImpl::Store(_) => CompressionMethod::STORE,
238            #[cfg(feature = "deflate")]
239            CompressorImpl::Deflate(_) => CompressionMethod::DEFLATE,
240            #[cfg(feature = "zstd")]
241            CompressorImpl::Zstd(_) => CompressionMethod::ZSTD,
242        }
243    }
244
245    /// Gets a shared reference to the underlying writer.
246    pub fn get_ref(&self) -> &W {
247        match &self.0 {
248            CompressorImpl::Store(w) => w,
249            #[cfg(feature = "deflate")]
250            CompressorImpl::Deflate(w) => w.get_ref(),
251            #[cfg(feature = "zstd")]
252            CompressorImpl::Zstd(w) => w.get_ref(),
253        }
254    }
255
256    /// Gets a mutable reference to the underlying writer.
257    ///
258    /// It is inadvisable to directly write to the underlying writer.
259    pub fn get_mut(&mut self) -> &mut W {
260        match &mut self.0 {
261            CompressorImpl::Store(w) => w,
262            #[cfg(feature = "deflate")]
263            CompressorImpl::Deflate(w) => w.get_mut(),
264            #[cfg(feature = "zstd")]
265            CompressorImpl::Zstd(w) => w.get_mut(),
266        }
267    }
268
269    /// Attempts to finish the stream.
270    ///
271    /// You must finish the stream when you're done writing.
272    pub fn do_finish(&mut self) -> io::Result<()> {
273        match &mut self.0 {
274            CompressorImpl::Store(_) => Ok(()),
275            #[cfg(feature = "deflate")]
276            CompressorImpl::Deflate(w) => w.try_finish(),
277            #[cfg(feature = "zstd")]
278            CompressorImpl::Zstd(w) => w.do_finish(),
279        }
280    }
281
282    /// Finishes the stream and get the writer back.
283    ///
284    /// You must finish the stream when you're done writing.
285    pub fn finish(self) -> io::Result<W> {
286        match self.0 {
287            CompressorImpl::Store(w) => Ok(w),
288            #[cfg(feature = "deflate")]
289            CompressorImpl::Deflate(w) => w.finish(),
290            #[cfg(feature = "zstd")]
291            CompressorImpl::Zstd(w) => w.finish(),
292        }
293    }
294}
295
296impl<W: io::Write> io::Write for Compressor<W> {
297    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
298        match &mut self.0 {
299            CompressorImpl::Store(w) => w.write(buf),
300            #[cfg(feature = "deflate")]
301            CompressorImpl::Deflate(w) => w.write(buf),
302            #[cfg(feature = "zstd")]
303            CompressorImpl::Zstd(w) => w.write(buf),
304        }
305    }
306
307    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
308        match &mut self.0 {
309            CompressorImpl::Store(w) => w.write_all(buf),
310            #[cfg(feature = "deflate")]
311            CompressorImpl::Deflate(w) => w.write_all(buf),
312            #[cfg(feature = "zstd")]
313            CompressorImpl::Zstd(w) => w.write_all(buf),
314        }
315    }
316
317    fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
318        match &mut self.0 {
319            CompressorImpl::Store(w) => w.write_vectored(bufs),
320            #[cfg(feature = "deflate")]
321            CompressorImpl::Deflate(w) => w.write_vectored(bufs),
322            #[cfg(feature = "zstd")]
323            CompressorImpl::Zstd(w) => w.write_vectored(bufs),
324        }
325    }
326
327    fn flush(&mut self) -> io::Result<()> {
328        match &mut self.0 {
329            CompressorImpl::Store(w) => w.flush(),
330            #[cfg(feature = "deflate")]
331            CompressorImpl::Deflate(w) => w.flush(),
332            #[cfg(feature = "zstd")]
333            CompressorImpl::Zstd(w) => w.flush(),
334        }
335    }
336}