zip/
compression.rs

1//! Possible ZIP compression methods.
2
3use std::{fmt, io};
4
5#[allow(deprecated)]
6/// Identifies the storage format used to compress a file within a ZIP archive.
7///
8/// Each file's compression method is stored alongside it, allowing the
9/// contents to be read without context.
10///
11/// When creating ZIP files, you may choose the method to use with
12/// [`crate::write::FileOptions::compression_method`]
13#[derive(Copy, Clone, PartialEq, Eq, Debug)]
14#[cfg_attr(fuzzing, derive(arbitrary::Arbitrary))]
15#[non_exhaustive]
16pub enum CompressionMethod {
17    /// Store the file as is
18    Stored,
19    /// Compress the file using Deflate
20    #[cfg(feature = "_deflate-any")]
21    Deflated,
22    /// Compress the file using Deflate64.
23    /// Decoding deflate64 is supported but encoding deflate64 is not supported.
24    #[cfg(feature = "deflate64")]
25    Deflate64,
26    /// Compress the file using BZIP2
27    #[cfg(feature = "bzip2")]
28    Bzip2,
29    /// Encrypted using AES.
30    ///
31    /// The actual compression method has to be taken from the AES extra data field
32    /// or from `ZipFileData`.
33    #[cfg(feature = "aes-crypto")]
34    Aes,
35    /// Compress the file using ZStandard
36    #[cfg(feature = "zstd")]
37    Zstd,
38    /// Compress the file using LZMA
39    #[cfg(feature = "lzma")]
40    Lzma,
41    /// Compress the file using XZ
42    #[cfg(feature = "xz")]
43    Xz,
44    /// Unsupported compression method
45    #[cfg_attr(
46        not(fuzzing),
47        deprecated(since = "0.5.7", note = "use the constants instead")
48    )]
49    Unsupported(u16),
50}
51#[allow(deprecated, missing_docs)]
52/// All compression methods defined for the ZIP format
53impl CompressionMethod {
54    pub const STORE: Self = CompressionMethod::Stored;
55    pub const SHRINK: Self = CompressionMethod::Unsupported(1);
56    pub const REDUCE_1: Self = CompressionMethod::Unsupported(2);
57    pub const REDUCE_2: Self = CompressionMethod::Unsupported(3);
58    pub const REDUCE_3: Self = CompressionMethod::Unsupported(4);
59    pub const REDUCE_4: Self = CompressionMethod::Unsupported(5);
60    pub const IMPLODE: Self = CompressionMethod::Unsupported(6);
61    #[cfg(feature = "_deflate-any")]
62    pub const DEFLATE: Self = CompressionMethod::Deflated;
63    #[cfg(not(feature = "_deflate-any"))]
64    pub const DEFLATE: Self = CompressionMethod::Unsupported(8);
65    #[cfg(feature = "deflate64")]
66    pub const DEFLATE64: Self = CompressionMethod::Deflate64;
67    #[cfg(not(feature = "deflate64"))]
68    pub const DEFLATE64: Self = CompressionMethod::Unsupported(9);
69    pub const PKWARE_IMPLODE: Self = CompressionMethod::Unsupported(10);
70    #[cfg(feature = "bzip2")]
71    pub const BZIP2: Self = CompressionMethod::Bzip2;
72    #[cfg(not(feature = "bzip2"))]
73    pub const BZIP2: Self = CompressionMethod::Unsupported(12);
74    #[cfg(not(feature = "lzma"))]
75    pub const LZMA: Self = CompressionMethod::Unsupported(14);
76    #[cfg(feature = "lzma")]
77    pub const LZMA: Self = CompressionMethod::Lzma;
78    pub const IBM_ZOS_CMPSC: Self = CompressionMethod::Unsupported(16);
79    pub const IBM_TERSE: Self = CompressionMethod::Unsupported(18);
80    pub const ZSTD_DEPRECATED: Self = CompressionMethod::Unsupported(20);
81    #[cfg(feature = "zstd")]
82    pub const ZSTD: Self = CompressionMethod::Zstd;
83    #[cfg(not(feature = "zstd"))]
84    pub const ZSTD: Self = CompressionMethod::Unsupported(93);
85    pub const MP3: Self = CompressionMethod::Unsupported(94);
86    #[cfg(feature = "xz")]
87    pub const XZ: Self = CompressionMethod::Xz;
88    #[cfg(not(feature = "xz"))]
89    pub const XZ: Self = CompressionMethod::Unsupported(95);
90    pub const JPEG: Self = CompressionMethod::Unsupported(96);
91    pub const WAVPACK: Self = CompressionMethod::Unsupported(97);
92    pub const PPMD: Self = CompressionMethod::Unsupported(98);
93    #[cfg(feature = "aes-crypto")]
94    pub const AES: Self = CompressionMethod::Aes;
95    #[cfg(not(feature = "aes-crypto"))]
96    pub const AES: Self = CompressionMethod::Unsupported(99);
97}
98impl CompressionMethod {
99    pub(crate) const fn parse_from_u16(val: u16) -> Self {
100        match val {
101            0 => CompressionMethod::Stored,
102            #[cfg(feature = "_deflate-any")]
103            8 => CompressionMethod::Deflated,
104            #[cfg(feature = "deflate64")]
105            9 => CompressionMethod::Deflate64,
106            #[cfg(feature = "bzip2")]
107            12 => CompressionMethod::Bzip2,
108            #[cfg(feature = "lzma")]
109            14 => CompressionMethod::Lzma,
110            #[cfg(feature = "xz")]
111            95 => CompressionMethod::Xz,
112            #[cfg(feature = "zstd")]
113            93 => CompressionMethod::Zstd,
114            #[cfg(feature = "aes-crypto")]
115            99 => CompressionMethod::Aes,
116            #[allow(deprecated)]
117            v => CompressionMethod::Unsupported(v),
118        }
119    }
120
121    /// Converts a u16 to its corresponding CompressionMethod
122    #[deprecated(
123        since = "0.5.7",
124        note = "use a constant to construct a compression method"
125    )]
126    pub const fn from_u16(val: u16) -> CompressionMethod {
127        Self::parse_from_u16(val)
128    }
129
130    pub(crate) const fn serialize_to_u16(self) -> u16 {
131        match self {
132            CompressionMethod::Stored => 0,
133            #[cfg(feature = "_deflate-any")]
134            CompressionMethod::Deflated => 8,
135            #[cfg(feature = "deflate64")]
136            CompressionMethod::Deflate64 => 9,
137            #[cfg(feature = "bzip2")]
138            CompressionMethod::Bzip2 => 12,
139            #[cfg(feature = "aes-crypto")]
140            CompressionMethod::Aes => 99,
141            #[cfg(feature = "zstd")]
142            CompressionMethod::Zstd => 93,
143            #[cfg(feature = "lzma")]
144            CompressionMethod::Lzma => 14,
145            #[cfg(feature = "xz")]
146            CompressionMethod::Xz => 95,
147            #[allow(deprecated)]
148            CompressionMethod::Unsupported(v) => v,
149        }
150    }
151
152    /// Converts a CompressionMethod to a u16
153    #[deprecated(
154        since = "0.5.7",
155        note = "to match on other compression methods, use a constant"
156    )]
157    pub const fn to_u16(self) -> u16 {
158        self.serialize_to_u16()
159    }
160}
161
162impl Default for CompressionMethod {
163    fn default() -> Self {
164        #[cfg(feature = "_deflate-any")]
165        return CompressionMethod::Deflated;
166
167        #[cfg(not(feature = "_deflate-any"))]
168        return CompressionMethod::Stored;
169    }
170}
171
172impl fmt::Display for CompressionMethod {
173    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174        // Just duplicate what the Debug format looks like, i.e, the enum key:
175        write!(f, "{self:?}")
176    }
177}
178
179/// The compression methods which have been implemented.
180pub const SUPPORTED_COMPRESSION_METHODS: &[CompressionMethod] = &[
181    CompressionMethod::Stored,
182    #[cfg(feature = "_deflate-any")]
183    CompressionMethod::Deflated,
184    #[cfg(feature = "deflate64")]
185    CompressionMethod::Deflate64,
186    #[cfg(feature = "bzip2")]
187    CompressionMethod::Bzip2,
188    #[cfg(feature = "zstd")]
189    CompressionMethod::Zstd,
190    #[cfg(feature = "xz")]
191    CompressionMethod::Xz,
192];
193
194pub(crate) enum Decompressor<R: io::BufRead> {
195    Stored(R),
196    #[cfg(feature = "_deflate-any")]
197    Deflated(flate2::bufread::DeflateDecoder<R>),
198    #[cfg(feature = "deflate64")]
199    Deflate64(deflate64::Deflate64Decoder<R>),
200    #[cfg(feature = "bzip2")]
201    Bzip2(bzip2::bufread::BzDecoder<R>),
202    #[cfg(feature = "zstd")]
203    Zstd(zstd::Decoder<'static, R>),
204    #[cfg(feature = "lzma")]
205    Lzma(Box<crate::read::lzma::LzmaDecoder<R>>),
206    #[cfg(feature = "xz")]
207    Xz(xz2::bufread::XzDecoder<R>),
208}
209
210impl<R: io::BufRead> io::Read for Decompressor<R> {
211    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
212        match self {
213            Decompressor::Stored(r) => r.read(buf),
214            #[cfg(feature = "_deflate-any")]
215            Decompressor::Deflated(r) => r.read(buf),
216            #[cfg(feature = "deflate64")]
217            Decompressor::Deflate64(r) => r.read(buf),
218            #[cfg(feature = "bzip2")]
219            Decompressor::Bzip2(r) => r.read(buf),
220            #[cfg(feature = "zstd")]
221            Decompressor::Zstd(r) => r.read(buf),
222            #[cfg(feature = "lzma")]
223            Decompressor::Lzma(r) => r.read(buf),
224            #[cfg(feature = "xz")]
225            Decompressor::Xz(r) => r.read(buf),
226        }
227    }
228}
229
230impl<R: io::BufRead> Decompressor<R> {
231    pub fn new(reader: R, compression_method: CompressionMethod) -> crate::result::ZipResult<Self> {
232        Ok(match compression_method {
233            CompressionMethod::Stored => Decompressor::Stored(reader),
234            #[cfg(feature = "_deflate-any")]
235            CompressionMethod::Deflated => {
236                Decompressor::Deflated(flate2::bufread::DeflateDecoder::new(reader))
237            }
238            #[cfg(feature = "deflate64")]
239            CompressionMethod::Deflate64 => {
240                Decompressor::Deflate64(deflate64::Deflate64Decoder::with_buffer(reader))
241            }
242            #[cfg(feature = "bzip2")]
243            CompressionMethod::Bzip2 => Decompressor::Bzip2(bzip2::bufread::BzDecoder::new(reader)),
244            #[cfg(feature = "zstd")]
245            CompressionMethod::Zstd => Decompressor::Zstd(zstd::Decoder::with_buffer(reader)?),
246            #[cfg(feature = "lzma")]
247            CompressionMethod::Lzma => {
248                Decompressor::Lzma(Box::new(crate::read::lzma::LzmaDecoder::new(reader)))
249            }
250            #[cfg(feature = "xz")]
251            CompressionMethod::Xz => Decompressor::Xz(xz2::bufread::XzDecoder::new(reader)),
252            _ => {
253                return Err(crate::result::ZipError::UnsupportedArchive(
254                    "Compression method not supported",
255                ))
256            }
257        })
258    }
259
260    /// Consumes this decoder, returning the underlying reader.
261    pub fn into_inner(self) -> R {
262        match self {
263            Decompressor::Stored(r) => r,
264            #[cfg(feature = "_deflate-any")]
265            Decompressor::Deflated(r) => r.into_inner(),
266            #[cfg(feature = "deflate64")]
267            Decompressor::Deflate64(r) => r.into_inner(),
268            #[cfg(feature = "bzip2")]
269            Decompressor::Bzip2(r) => r.into_inner(),
270            #[cfg(feature = "zstd")]
271            Decompressor::Zstd(r) => r.finish(),
272            #[cfg(feature = "lzma")]
273            Decompressor::Lzma(r) => r.into_inner(),
274            #[cfg(feature = "xz")]
275            Decompressor::Xz(r) => r.into_inner(),
276        }
277    }
278}
279
280#[cfg(test)]
281mod test {
282    use super::{CompressionMethod, SUPPORTED_COMPRESSION_METHODS};
283
284    #[test]
285    fn from_eq_to() {
286        for v in 0..(u16::MAX as u32 + 1) {
287            let from = CompressionMethod::parse_from_u16(v as u16);
288            let to = from.serialize_to_u16() as u32;
289            assert_eq!(v, to);
290        }
291    }
292
293    #[test]
294    fn to_eq_from() {
295        fn check_match(method: CompressionMethod) {
296            let to = method.serialize_to_u16();
297            let from = CompressionMethod::parse_from_u16(to);
298            let back = from.serialize_to_u16();
299            assert_eq!(to, back);
300        }
301
302        for &method in SUPPORTED_COMPRESSION_METHODS {
303            check_match(method);
304        }
305    }
306
307    #[test]
308    fn to_display_fmt() {
309        fn check_match(method: CompressionMethod) {
310            let debug_str = format!("{method:?}");
311            let display_str = format!("{method}");
312            assert_eq!(debug_str, display_str);
313        }
314
315        for &method in SUPPORTED_COMPRESSION_METHODS {
316            check_match(method);
317        }
318    }
319}