Skip to main content

zip/
compression.rs

1//! Possible ZIP compression methods.
2
3use crate::format::compression::Compression;
4use core::fmt;
5use std::{fmt::Debug, io};
6
7#[allow(deprecated)]
8/// Identifies the storage format used to compress a file within a ZIP archive.
9///
10/// Each file's compression method is stored alongside it, allowing the
11/// contents to be read without context.
12///
13/// When creating ZIP files, you may choose the method to use with
14/// [`crate::write::FileOptions::compression_method`]
15#[derive(Copy, Clone, PartialEq, Eq, Debug)]
16#[cfg_attr(feature = "_arbitrary", derive(arbitrary::Arbitrary))]
17#[non_exhaustive]
18pub enum CompressionMethod {
19    /// Store the file as is
20    Stored,
21    /// Compress the file using Deflate
22    #[cfg(feature = "_deflate-any")]
23    Deflated,
24    /// Compress the file using Deflate64.
25    /// Decoding deflate64 is supported but encoding deflate64 is not supported.
26    #[cfg(feature = "deflate64")]
27    Deflate64,
28    /// Compress the file using BZIP2
29    #[cfg(feature = "_bzip2_any")]
30    Bzip2,
31    /// Encrypted using AES.
32    ///
33    /// The actual compression method has to be taken from the AES extra data field
34    /// or from `ZipFileData`.
35    #[cfg(feature = "aes-crypto")]
36    Aes,
37    /// Compress the file using `ZStandard`
38    #[cfg(feature = "zstd")]
39    Zstd,
40    /// Compress the file using LZMA
41    #[cfg(feature = "lzma")]
42    Lzma,
43    #[cfg(feature = "legacy-zip")]
44    /// Method 1 Shrink
45    Shrink,
46    #[cfg(feature = "legacy-zip")]
47    /// Reduce (Method 2-5)
48    Reduce(u8),
49    #[cfg(feature = "legacy-zip")]
50    /// Method 6 Implode/explode
51    Implode,
52    /// Compress the file using XZ
53    #[cfg(feature = "xz")]
54    Xz,
55    /// Compress the file using `PPMd`
56    #[cfg(feature = "ppmd")]
57    Ppmd,
58    /// Unsupported compression method
59    #[cfg_attr(
60        not(any(fuzzing, feature = "_arbitrary")),
61        deprecated(since = "0.5.7", note = "use the constants instead")
62    )]
63    Unsupported(u16),
64}
65#[allow(deprecated, missing_docs)]
66/// All compression methods defined for the ZIP format
67impl CompressionMethod {
68    pub const STORE: Self = CompressionMethod::Stored;
69    #[cfg(feature = "legacy-zip")]
70    pub const SHRINK: Self = CompressionMethod::Shrink;
71    #[cfg(not(feature = "legacy-zip"))]
72    /// Legacy compression method (enable feature `legacy-zip` to get support)
73    pub const SHRINK: Self = CompressionMethod::Unsupported(1);
74    #[cfg(feature = "legacy-zip")]
75    /// Legacy compression method
76    pub const REDUCE_1: Self = CompressionMethod::Reduce(1);
77    #[cfg(not(feature = "legacy-zip"))]
78    /// Legacy compression method (enable feature `legacy-zip` to get support)
79    pub const REDUCE_1: Self = CompressionMethod::Unsupported(2);
80    #[cfg(feature = "legacy-zip")]
81    /// Legacy compression method
82    pub const REDUCE_2: Self = CompressionMethod::Reduce(2);
83    #[cfg(not(feature = "legacy-zip"))]
84    /// Legacy compression method (enable feature `legacy-zip` to get support)
85    pub const REDUCE_2: Self = CompressionMethod::Unsupported(3);
86    #[cfg(feature = "legacy-zip")]
87    /// Legacy compression method
88    pub const REDUCE_3: Self = CompressionMethod::Reduce(3);
89    #[cfg(not(feature = "legacy-zip"))]
90    /// Legacy compression method (enable feature `legacy-zip` to get support)
91    pub const REDUCE_3: Self = CompressionMethod::Unsupported(4);
92    #[cfg(feature = "legacy-zip")]
93    /// Legacy compression method
94    pub const REDUCE_4: Self = CompressionMethod::Reduce(4);
95    #[cfg(not(feature = "legacy-zip"))]
96    /// Legacy compression method (enable feature `legacy-zip` to get support)
97    pub const REDUCE_4: Self = CompressionMethod::Unsupported(5);
98    #[cfg(feature = "legacy-zip")]
99    /// Legacy compression method
100    pub const IMPLODE: Self = CompressionMethod::Implode;
101    #[cfg(not(feature = "legacy-zip"))]
102    /// Legacy compression method (enable feature `legacy-zip` to get support)
103    pub const IMPLODE: Self = CompressionMethod::Unsupported(6);
104    #[cfg(feature = "_deflate-any")]
105    pub const DEFLATE: Self = CompressionMethod::Deflated;
106    #[cfg(not(feature = "_deflate-any"))]
107    pub const DEFLATE: Self = CompressionMethod::Unsupported(8);
108    #[cfg(feature = "deflate64")]
109    pub const DEFLATE64: Self = CompressionMethod::Deflate64;
110    #[cfg(not(feature = "deflate64"))]
111    pub const DEFLATE64: Self = CompressionMethod::Unsupported(9);
112    pub const PKWARE_IMPLODE: Self = CompressionMethod::Unsupported(10);
113    #[cfg(feature = "_bzip2_any")]
114    pub const BZIP2: Self = CompressionMethod::Bzip2;
115    #[cfg(not(feature = "_bzip2_any"))]
116    pub const BZIP2: Self = CompressionMethod::Unsupported(12);
117    #[cfg(not(feature = "lzma"))]
118    pub const LZMA: Self = CompressionMethod::Unsupported(14);
119    #[cfg(feature = "lzma")]
120    pub const LZMA: Self = CompressionMethod::Lzma;
121    pub const IBM_ZOS_CMPSC: Self = CompressionMethod::Unsupported(16);
122    pub const IBM_TERSE: Self = CompressionMethod::Unsupported(18);
123    pub const ZSTD_DEPRECATED: Self = CompressionMethod::Unsupported(20);
124    #[cfg(feature = "zstd")]
125    pub const ZSTD: Self = CompressionMethod::Zstd;
126    #[cfg(not(feature = "zstd"))]
127    pub const ZSTD: Self = CompressionMethod::Unsupported(93);
128    pub const MP3: Self = CompressionMethod::Unsupported(94);
129    #[cfg(feature = "xz")]
130    pub const XZ: Self = CompressionMethod::Xz;
131    #[cfg(not(feature = "xz"))]
132    pub const XZ: Self = CompressionMethod::Unsupported(95);
133    pub const JPEG: Self = CompressionMethod::Unsupported(96);
134    pub const WAVPACK: Self = CompressionMethod::Unsupported(97);
135    #[cfg(feature = "ppmd")]
136    pub const PPMD: Self = CompressionMethod::Ppmd;
137    #[cfg(not(feature = "ppmd"))]
138    pub const PPMD: Self = CompressionMethod::Unsupported(98);
139    #[cfg(feature = "aes-crypto")]
140    pub const AES: Self = CompressionMethod::Aes;
141
142    // E.4 The compression method field (section 4.4.5) is set to 99
143    // to indicate a file has been encrypted using this method.
144    #[cfg(not(feature = "aes-crypto"))]
145    pub const AES: Self = CompressionMethod::Unsupported(99);
146
147    #[cfg(feature = "_deflate-any")]
148    pub const DEFAULT: Self = CompressionMethod::Deflated;
149
150    #[cfg(all(not(feature = "_deflate-any"), feature = "_bzip2_any"))]
151    pub const DEFAULT: Self = CompressionMethod::Bzip2;
152
153    #[cfg(all(not(feature = "_deflate-any"), not(feature = "_bzip2_any")))]
154    pub const DEFAULT: Self = CompressionMethod::Stored;
155}
156impl CompressionMethod {
157    /// Convert the compression method (as `u16`) to a name
158    #[must_use]
159    pub const fn name_from_u16(val: u16) -> &'static str {
160        match val {
161            0 => "Stored",
162            1 => "Shrink",
163            2 => "Reduce(1)",
164            3 => "Reduce(2)",
165            4 => "Reduce(3)",
166            5 => "Reduce(4)",
167            6 => "Implode",
168            8 => "Deflated",
169            9 => "Deflate64",
170            12 => "Bzip2",
171            14 => "Lzma",
172            95 => "Xz",
173            93 => "Zstd",
174            98 => "Ppmd",
175            99 => "Aes",
176            _ => "Unknown",
177        }
178    }
179
180    pub(crate) const fn parse_from_u16(val: u16) -> Self {
181        match val {
182            0 => CompressionMethod::Stored,
183            #[cfg(feature = "legacy-zip")]
184            1 => CompressionMethod::Shrink,
185            #[cfg(feature = "legacy-zip")]
186            2 => CompressionMethod::Reduce(1),
187            #[cfg(feature = "legacy-zip")]
188            3 => CompressionMethod::Reduce(2),
189            #[cfg(feature = "legacy-zip")]
190            4 => CompressionMethod::Reduce(3),
191            #[cfg(feature = "legacy-zip")]
192            5 => CompressionMethod::Reduce(4),
193            #[cfg(feature = "legacy-zip")]
194            6 => CompressionMethod::Implode,
195            #[cfg(feature = "_deflate-any")]
196            8 => CompressionMethod::Deflated,
197            #[cfg(feature = "deflate64")]
198            9 => CompressionMethod::Deflate64,
199            #[cfg(feature = "_bzip2_any")]
200            12 => CompressionMethod::Bzip2,
201            #[cfg(feature = "lzma")]
202            14 => CompressionMethod::Lzma,
203            #[cfg(feature = "xz")]
204            95 => CompressionMethod::Xz,
205            #[cfg(feature = "zstd")]
206            93 => CompressionMethod::Zstd,
207            #[cfg(feature = "ppmd")]
208            98 => CompressionMethod::Ppmd,
209            #[cfg(feature = "aes-crypto")]
210            99 => CompressionMethod::Aes,
211            #[allow(deprecated)]
212            v => CompressionMethod::Unsupported(v),
213        }
214    }
215
216    /// Converts a u16 to its corresponding `CompressionMethod`
217    #[deprecated(
218        since = "0.5.7",
219        note = "use a constant to construct a compression method"
220    )]
221    #[must_use]
222    pub const fn from_u16(val: u16) -> CompressionMethod {
223        Self::parse_from_u16(val)
224    }
225
226    pub(crate) const fn serialize_to_u16(self) -> u16 {
227        match self {
228            CompressionMethod::Stored => 0,
229            #[cfg(feature = "legacy-zip")]
230            CompressionMethod::Shrink => 1,
231            #[cfg(feature = "legacy-zip")]
232            CompressionMethod::Reduce(n) => 1 + n as u16,
233            #[cfg(feature = "legacy-zip")]
234            CompressionMethod::Implode => 6,
235
236            #[cfg(feature = "_deflate-any")]
237            CompressionMethod::Deflated => 8,
238            #[cfg(feature = "deflate64")]
239            CompressionMethod::Deflate64 => 9,
240            #[cfg(feature = "_bzip2_any")]
241            CompressionMethod::Bzip2 => 12,
242            #[cfg(feature = "zstd")]
243            CompressionMethod::Zstd => 93,
244            #[cfg(feature = "lzma")]
245            CompressionMethod::Lzma => 14,
246            #[cfg(feature = "xz")]
247            CompressionMethod::Xz => 95,
248            #[cfg(feature = "ppmd")]
249            CompressionMethod::Ppmd => 98,
250            // E.4 The compression method field (section 4.4.5) is set to 99
251            // to indicate a file has been encrypted using this method.
252            #[cfg(feature = "aes-crypto")]
253            CompressionMethod::Aes => 99,
254            #[allow(deprecated)]
255            CompressionMethod::Unsupported(v) => v,
256        }
257    }
258
259    /// Converts a `CompressionMethod` to a u16
260    #[deprecated(
261        since = "0.5.7",
262        note = "to match on other compression methods, use a constant"
263    )]
264    #[must_use]
265    pub const fn to_u16(self) -> u16 {
266        self.serialize_to_u16()
267    }
268
269    /// Transform to `Compression`
270    #[must_use]
271    pub const fn into_compression(self) -> Compression {
272        match self {
273            CompressionMethod::Stored => Compression::Stored,
274            #[cfg(feature = "legacy-zip")]
275            CompressionMethod::Shrink => Compression::Shrink,
276            #[cfg(feature = "legacy-zip")]
277            CompressionMethod::Reduce(n) => match n {
278                1 => Compression::ReduceFactor1,
279                2 => Compression::ReduceFactor2,
280                3 => Compression::ReduceFactor3,
281                4 => Compression::ReduceFactor4,
282                _ => Compression::Unknown(n as u16),
283            },
284            #[cfg(feature = "legacy-zip")]
285            CompressionMethod::Implode => Compression::Implode,
286            #[cfg(feature = "_deflate-any")]
287            CompressionMethod::Deflated => Compression::Deflated,
288            #[cfg(feature = "deflate64")]
289            CompressionMethod::Deflate64 => Compression::Deflate64,
290            #[cfg(feature = "_bzip2_any")]
291            CompressionMethod::Bzip2 => Compression::Bzip2,
292            #[cfg(feature = "zstd")]
293            CompressionMethod::Zstd => Compression::Zstd,
294            #[cfg(feature = "lzma")]
295            CompressionMethod::Lzma => Compression::Lzma,
296            #[cfg(feature = "xz")]
297            CompressionMethod::Xz => Compression::Xz,
298            #[cfg(feature = "ppmd")]
299            CompressionMethod::Ppmd => Compression::Ppmd,
300            // E.4 The compression method field (section 4.4.5) is set to 99
301            // to indicate a file has been encrypted using this method.
302            #[cfg(feature = "aes-crypto")]
303            CompressionMethod::Aes => Compression::Aes,
304            #[allow(deprecated)]
305            CompressionMethod::Unsupported(v) => Compression::Unknown(v),
306        }
307    }
308}
309
310impl From<CompressionMethod> for Compression {
311    fn from(value: CompressionMethod) -> Self {
312        value.into_compression()
313    }
314}
315
316impl Default for CompressionMethod {
317    #[cfg(feature = "_deflate-any")]
318    fn default() -> Self {
319        CompressionMethod::Deflated
320    }
321
322    #[cfg(not(feature = "_deflate-any"))]
323    fn default() -> Self {
324        CompressionMethod::Stored
325    }
326}
327
328impl fmt::Display for CompressionMethod {
329    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
330        // Just duplicate what the Debug format looks like, i.e, the enum key:
331        let method_u16 = self.serialize_to_u16();
332        let name = Self::name_from_u16(method_u16);
333        write!(f, "{name}")
334    }
335}
336
337/// The compression methods which have been implemented.
338pub const SUPPORTED_COMPRESSION_METHODS: &[CompressionMethod] = &[
339    CompressionMethod::Stored,
340    #[cfg(feature = "_deflate-any")]
341    CompressionMethod::Deflated,
342    #[cfg(feature = "deflate64")]
343    CompressionMethod::Deflate64,
344    #[cfg(feature = "_bzip2_any")]
345    CompressionMethod::Bzip2,
346    #[cfg(feature = "zstd")]
347    CompressionMethod::Zstd,
348    #[cfg(feature = "xz")]
349    CompressionMethod::Xz,
350    #[cfg(feature = "ppmd")]
351    CompressionMethod::Ppmd,
352];
353
354#[cfg(feature = "zstd")]
355pub(crate) enum Zstd<R: io::BufRead> {
356    FailedToInitialize,
357    Initialized(zstd::Decoder<'static, R>),
358    Uninitialized(R),
359}
360
361pub(crate) enum Decompressor<R: io::BufRead> {
362    Stored(R),
363    #[cfg(feature = "deflate-flate2")]
364    Deflated(flate2::bufread::DeflateDecoder<R>),
365    #[cfg(feature = "deflate64")]
366    Deflate64(deflate64::Deflate64Decoder<R>),
367    #[cfg(feature = "_bzip2_any")]
368    Bzip2(bzip2::bufread::BzDecoder<R>),
369    #[cfg(feature = "zstd")]
370    Zstd(Zstd<R>),
371    #[cfg(feature = "lzma")]
372    Lzma(Lzma<R>),
373    #[cfg(feature = "legacy-zip")]
374    Shrink(crate::legacy::shrink::ShrinkDecoder<R>),
375    #[cfg(feature = "legacy-zip")]
376    Reduce(crate::legacy::reduce::ReduceDecoder<R>),
377    #[cfg(feature = "legacy-zip")]
378    Implode(crate::legacy::implode::ImplodeDecoder<R>),
379    #[cfg(feature = "xz")]
380    Xz(Box<lzma_rust2::XzReader<R>>),
381    #[cfg(feature = "ppmd")]
382    Ppmd(Ppmd<R>),
383}
384
385impl<R: io::BufRead> Debug for Decompressor<R> {
386    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
387        match self {
388            Self::Stored(_) => write!(f, "StoredDecompressor"),
389            #[cfg(feature = "deflate-flate2")]
390            Self::Deflated(_) => write!(f, "DeflatedDecompressor"),
391            #[cfg(feature = "deflate64")]
392            Self::Deflate64(_) => write!(f, "Deflate64Decompressor"),
393            #[cfg(feature = "_bzip2_any")]
394            Self::Bzip2(_) => write!(f, "Bzip2Decompressor"),
395            #[cfg(feature = "zstd")]
396            Self::Zstd(_) => write!(f, "ZstdDecompressor"),
397            #[cfg(feature = "lzma")]
398            Self::Lzma(_) => write!(f, "LzmaDecompressor"),
399            #[cfg(feature = "legacy-zip")]
400            Self::Shrink(_) => write!(f, "ShrinkDecompressor"),
401            #[cfg(feature = "legacy-zip")]
402            Self::Reduce(_) => write!(f, "ReduceDecompressor"),
403            #[cfg(feature = "legacy-zip")]
404            Self::Implode(_) => write!(f, "ImplodeDecompressor"),
405            #[cfg(feature = "xz")]
406            Self::Xz(_) => write!(f, "XzDecompressor"),
407            #[cfg(feature = "ppmd")]
408            Self::Ppmd(_) => write!(f, "PpmdDecompressor"),
409        }
410    }
411}
412
413#[cfg(feature = "lzma")]
414pub(crate) enum Lzma<R: io::BufRead> {
415    Uninitialized {
416        reader: Option<R>,
417        uncompressed_size: u64,
418    },
419    Initialized(Box<lzma_rust2::LzmaReader<R>>),
420}
421
422#[cfg(feature = "ppmd")]
423pub(crate) enum Ppmd<R: io::BufRead> {
424    Uninitialized(Option<R>),
425    Initialized(Box<ppmd_rust::Ppmd8Decoder<R>>),
426}
427
428impl<R: io::BufRead> io::Read for Decompressor<R> {
429    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
430        if buf.is_empty() {
431            return Ok(0);
432        }
433        match self {
434            Decompressor::Stored(r) => r.read(buf),
435            #[cfg(feature = "deflate-flate2")]
436            Decompressor::Deflated(r) => r.read(buf),
437            #[cfg(feature = "deflate64")]
438            Decompressor::Deflate64(r) => r.read(buf),
439            #[cfg(feature = "_bzip2_any")]
440            Decompressor::Bzip2(r) => r.read(buf),
441            #[cfg(feature = "zstd")]
442            Decompressor::Zstd(r) => {
443                let mut decoder = match core::mem::replace(r, Zstd::FailedToInitialize) {
444                    Zstd::Initialized(decoder) => decoder,
445                    Zstd::Uninitialized(inner) => zstd::Decoder::with_buffer(inner)?,
446                    Zstd::FailedToInitialize => {
447                        return Err(io::Error::other("Failed to start Zstd decoder"));
448                    }
449                };
450                let result = decoder.read(buf);
451                *r = Zstd::Initialized(decoder);
452                result
453            }
454            #[cfg(feature = "lzma")]
455            Decompressor::Lzma(r) => match r {
456                Lzma::Uninitialized {
457                    reader,
458                    uncompressed_size,
459                } => {
460                    let mut reader = reader.take().ok_or_else(|| {
461                        io::Error::other("Reader was not set while reading LZMA data")
462                    })?;
463
464                    // 5.8.8.1 LZMA Version Information & 5.8.8.2 LZMA Properties Size
465                    let mut header = [0; 4];
466                    reader.read_exact(&mut header)?;
467                    let _version_information =
468                        u16::from_le_bytes(header[0..2].try_into().map_err(|e| {
469                            std::io::Error::other(format!("Cannot transform header to u16: {e}"))
470                        })?);
471                    let properties_size =
472                        u16::from_le_bytes(header[2..4].try_into().map_err(|e| {
473                            std::io::Error::other(format!("Cannot transform header to u16: {e}"))
474                        })?);
475                    if properties_size != 5 {
476                        return Err(io::Error::new(
477                            io::ErrorKind::InvalidInput,
478                            format!("unexpected LZMA properties size of {properties_size}"),
479                        ));
480                    }
481
482                    let mut props_data = [0; 5];
483                    reader.read_exact(&mut props_data)?;
484                    let props = props_data[0];
485                    let dict_size =
486                        u32::from_le_bytes(props_data[1..5].try_into().map_err(|e| {
487                            std::io::Error::other(format!("Cannot transform header to u32: {e}"))
488                        })?);
489
490                    // We don't need to handle the end-of-stream marker here, since the LZMA reader
491                    // stops at the end-of-stream marker OR when it has decoded uncompressed_size bytes, whichever comes first.
492                    let mut decompressor = lzma_rust2::LzmaReader::new_with_props(
493                        reader,
494                        *uncompressed_size,
495                        props,
496                        dict_size,
497                        None,
498                    )?;
499
500                    let read = decompressor.read(buf)?;
501
502                    *r = Lzma::Initialized(Box::new(decompressor));
503
504                    Ok(read)
505                }
506                Lzma::Initialized(decompressor) => decompressor.read(buf),
507            },
508            #[cfg(feature = "xz")]
509            Decompressor::Xz(r) => r.read(buf),
510            #[cfg(feature = "ppmd")]
511            Decompressor::Ppmd(r) => match r {
512                Ppmd::Uninitialized(reader) => {
513                    let mut reader = reader.take().ok_or_else(|| {
514                        io::Error::other("Reader was not set while reading PPMd data")
515                    })?;
516
517                    let mut buffer = [0; 2];
518                    reader.read_exact(&mut buffer)?;
519                    let parameters = u16::from_le_bytes(buffer);
520
521                    let order = u32::from((parameters & 0x0F) + 1);
522                    let memory_size = 1024 * 1024 * u32::from(((parameters >> 4) & 0xFF) + 1);
523                    let restoration_method = (parameters >> 12) & 0x0F;
524
525                    let mut decompressor = ppmd_rust::Ppmd8Decoder::new(
526                        reader,
527                        order,
528                        memory_size,
529                        restoration_method.into(),
530                    )
531                    .map_err(|error| match error {
532                        ppmd_rust::Error::RangeDecoderInitialization => io::Error::new(
533                            io::ErrorKind::InvalidData,
534                            "PPMd range coder initialization failed",
535                        ),
536                        ppmd_rust::Error::InvalidParameter => {
537                            io::Error::new(io::ErrorKind::InvalidInput, "Invalid PPMd parameter")
538                        }
539                        ppmd_rust::Error::IoError(io_error) => io_error,
540                        ppmd_rust::Error::MemoryAllocation => {
541                            io::Error::new(io::ErrorKind::OutOfMemory, "Memory allocation failed")
542                        }
543                    })?;
544
545                    let read = decompressor.read(buf)?;
546
547                    *r = Ppmd::Initialized(Box::new(decompressor));
548
549                    Ok(read)
550                }
551                Ppmd::Initialized(decompressor) => decompressor.read(buf),
552            },
553            #[cfg(feature = "legacy-zip")]
554            Decompressor::Shrink(r) => r.read(buf),
555            #[cfg(feature = "legacy-zip")]
556            Decompressor::Reduce(r) => r.read(buf),
557            #[cfg(feature = "legacy-zip")]
558            Decompressor::Implode(r) => r.read(buf),
559        }
560    }
561}
562
563impl<R: io::BufRead> Decompressor<R> {
564    pub fn new(
565        reader: R,
566        compression_method: CompressionMethod,
567        #[cfg_attr(not(any(feature = "lzma", feature = "legacy-zip")), allow(unused))]
568        uncompressed_size: u64,
569        #[cfg_attr(not(feature = "legacy-zip"), allow(unused))] flags: u16,
570    ) -> crate::result::ZipResult<Self> {
571        Ok(match compression_method {
572            CompressionMethod::Stored => Decompressor::Stored(reader),
573            #[cfg(feature = "deflate-flate2")]
574            CompressionMethod::Deflated => {
575                Decompressor::Deflated(flate2::bufread::DeflateDecoder::new(reader))
576            }
577            #[cfg(feature = "deflate64")]
578            CompressionMethod::Deflate64 => {
579                Decompressor::Deflate64(deflate64::Deflate64Decoder::with_buffer(reader))
580            }
581            #[cfg(feature = "_bzip2_any")]
582            CompressionMethod::Bzip2 => Decompressor::Bzip2(bzip2::bufread::BzDecoder::new(reader)),
583            #[cfg(feature = "zstd")]
584            CompressionMethod::Zstd => Decompressor::Zstd(Zstd::Uninitialized(reader)),
585            #[cfg(feature = "lzma")]
586            CompressionMethod::Lzma => Decompressor::Lzma(Lzma::Uninitialized {
587                reader: Some(reader),
588                uncompressed_size,
589            }),
590            #[cfg(feature = "xz")]
591            CompressionMethod::Xz => {
592                Decompressor::Xz(Box::new(lzma_rust2::XzReader::new(reader, false)))
593            }
594            #[cfg(feature = "ppmd")]
595            CompressionMethod::Ppmd => Decompressor::Ppmd(Ppmd::Uninitialized(Some(reader))),
596            #[cfg(feature = "legacy-zip")]
597            CompressionMethod::Shrink => Decompressor::Shrink(
598                crate::legacy::shrink::ShrinkDecoder::new(reader, uncompressed_size),
599            ),
600            #[cfg(feature = "legacy-zip")]
601            CompressionMethod::Reduce(n) => Decompressor::Reduce(
602                crate::legacy::reduce::ReduceDecoder::new(reader, uncompressed_size, n),
603            ),
604            #[cfg(feature = "legacy-zip")]
605            CompressionMethod::Implode => Decompressor::Implode(
606                crate::legacy::implode::ImplodeDecoder::new(reader, uncompressed_size, flags),
607            ),
608            method => {
609                let method = method.serialize_to_u16();
610                return Err(crate::result::ZipError::CompressionMethodNotSupported(
611                    method,
612                ));
613            }
614        })
615    }
616
617    /// Consumes this decoder, returning the underlying reader.
618    #[allow(clippy::infallible_destructuring_match)]
619    pub fn into_inner(self) -> io::Result<R> {
620        let inner = match self {
621            Decompressor::Stored(r) => r,
622            #[cfg(feature = "deflate-flate2")]
623            Decompressor::Deflated(r) => r.into_inner(),
624            #[cfg(feature = "deflate64")]
625            Decompressor::Deflate64(r) => r.into_inner(),
626            #[cfg(feature = "_bzip2_any")]
627            Decompressor::Bzip2(r) => r.into_inner(),
628            #[cfg(feature = "zstd")]
629            Decompressor::Zstd(Zstd::Initialized(r)) => r.finish(),
630            #[cfg(feature = "zstd")]
631            Decompressor::Zstd(Zstd::Uninitialized(r)) => r,
632            #[cfg(feature = "zstd")]
633            Decompressor::Zstd(Zstd::FailedToInitialize) => {
634                return Err(io::Error::other("Failed to start Zstd decoder"));
635            }
636            #[cfg(feature = "lzma")]
637            Decompressor::Lzma(r) => match r {
638                Lzma::Uninitialized { mut reader, .. } => reader
639                    .take()
640                    .ok_or_else(|| io::Error::other("Reader was not set"))?,
641                Lzma::Initialized(decoder) => decoder.into_inner(),
642            },
643            #[cfg(feature = "legacy-zip")]
644            Decompressor::Shrink(r) => r.into_inner(),
645            #[cfg(feature = "legacy-zip")]
646            Decompressor::Reduce(r) => r.into_inner(),
647            #[cfg(feature = "legacy-zip")]
648            Decompressor::Implode(r) => r.into_inner(),
649            #[cfg(feature = "xz")]
650            Decompressor::Xz(r) => r.into_inner(),
651            #[cfg(feature = "ppmd")]
652            Decompressor::Ppmd(r) => match r {
653                Ppmd::Uninitialized(mut reader) => reader
654                    .take()
655                    .ok_or_else(|| io::Error::other("Reader was not set"))?,
656                Ppmd::Initialized(decoder) => decoder.into_inner(),
657            },
658        };
659        Ok(inner)
660    }
661}
662
663#[cfg(test)]
664mod tests {
665    use super::{CompressionMethod, SUPPORTED_COMPRESSION_METHODS};
666
667    #[test]
668    fn from_eq_to() {
669        for v in 0..(u32::from(u16::MAX) + 1) {
670            let from = CompressionMethod::parse_from_u16(v as u16);
671            let to = u32::from(from.serialize_to_u16());
672            assert_eq!(v, to);
673        }
674    }
675
676    #[test]
677    fn to_eq_from() {
678        fn check_match(method: CompressionMethod) {
679            let to = method.serialize_to_u16();
680            let from = CompressionMethod::parse_from_u16(to);
681            let back = from.serialize_to_u16();
682            assert_eq!(to, back);
683        }
684
685        for &method in SUPPORTED_COMPRESSION_METHODS {
686            check_match(method);
687        }
688    }
689
690    #[test]
691    fn to_display_fmt() {
692        fn check_match(method: CompressionMethod) {
693            let debug_str = format!("{method:?}");
694            let display_str = format!("{method}");
695            assert_eq!(debug_str, display_str);
696        }
697
698        for &method in SUPPORTED_COMPRESSION_METHODS {
699            check_match(method);
700        }
701    }
702
703    #[cfg(feature = "legacy-zip")]
704    #[test]
705    fn check_reduce() {
706        assert_eq!(CompressionMethod::Reduce(1), CompressionMethod::REDUCE_1);
707        assert_eq!(CompressionMethod::Reduce(1).serialize_to_u16(), 2);
708        assert_eq!(CompressionMethod::REDUCE_1.serialize_to_u16(), 2);
709    }
710}