1use core::fmt;
4use std::io;
5
6#[allow(deprecated)]
7#[derive(Copy, Clone, PartialEq, Eq, Debug)]
15#[cfg_attr(feature = "_arbitrary", derive(arbitrary::Arbitrary))]
16#[non_exhaustive]
17pub enum CompressionMethod {
18 Stored,
20 #[cfg(feature = "_deflate-any")]
22 Deflated,
23 #[cfg(feature = "deflate64")]
26 Deflate64,
27 #[cfg(feature = "bzip2")]
29 Bzip2,
30 #[cfg(feature = "aes-crypto")]
35 Aes,
36 #[cfg(feature = "zstd")]
38 Zstd,
39 #[cfg(feature = "lzma")]
41 Lzma,
42 #[cfg(feature = "legacy-zip")]
43 Shrink,
45 #[cfg(feature = "legacy-zip")]
46 Reduce(u8),
48 #[cfg(feature = "legacy-zip")]
49 Implode,
51 #[cfg(feature = "xz")]
53 Xz,
54 #[cfg(feature = "ppmd")]
56 Ppmd,
57 #[cfg_attr(
59 not(any(fuzzing, feature = "_arbitrary")),
60 deprecated(since = "0.5.7", note = "use the constants instead")
61 )]
62 Unsupported(u16),
63}
64#[allow(deprecated, missing_docs)]
65impl CompressionMethod {
67 pub const STORE: Self = CompressionMethod::Stored;
68 #[cfg(feature = "legacy-zip")]
69 pub const SHRINK: Self = CompressionMethod::Shrink;
70 #[cfg(not(feature = "legacy-zip"))]
71 pub const SHRINK: Self = CompressionMethod::Unsupported(1);
73 #[cfg(feature = "legacy-zip")]
74 pub const REDUCE_1: Self = CompressionMethod::Reduce(1);
76 #[cfg(not(feature = "legacy-zip"))]
77 pub const REDUCE_1: Self = CompressionMethod::Unsupported(2);
79 #[cfg(feature = "legacy-zip")]
80 pub const REDUCE_2: Self = CompressionMethod::Reduce(2);
82 #[cfg(not(feature = "legacy-zip"))]
83 pub const REDUCE_2: Self = CompressionMethod::Unsupported(3);
85 #[cfg(feature = "legacy-zip")]
86 pub const REDUCE_3: Self = CompressionMethod::Reduce(3);
88 #[cfg(not(feature = "legacy-zip"))]
89 pub const REDUCE_3: Self = CompressionMethod::Unsupported(4);
91 #[cfg(feature = "legacy-zip")]
92 pub const REDUCE_4: Self = CompressionMethod::Reduce(4);
94 #[cfg(not(feature = "legacy-zip"))]
95 pub const REDUCE_4: Self = CompressionMethod::Unsupported(5);
97 #[cfg(feature = "legacy-zip")]
98 pub const IMPLODE: Self = CompressionMethod::Implode;
100 #[cfg(not(feature = "legacy-zip"))]
101 pub const IMPLODE: Self = CompressionMethod::Unsupported(6);
103 #[cfg(feature = "_deflate-any")]
104 pub const DEFLATE: Self = CompressionMethod::Deflated;
105 #[cfg(not(feature = "_deflate-any"))]
106 pub const DEFLATE: Self = CompressionMethod::Unsupported(8);
107 #[cfg(feature = "deflate64")]
108 pub const DEFLATE64: Self = CompressionMethod::Deflate64;
109 #[cfg(not(feature = "deflate64"))]
110 pub const DEFLATE64: Self = CompressionMethod::Unsupported(9);
111 pub const PKWARE_IMPLODE: Self = CompressionMethod::Unsupported(10);
112 #[cfg(feature = "bzip2")]
113 pub const BZIP2: Self = CompressionMethod::Bzip2;
114 #[cfg(not(feature = "bzip2"))]
115 pub const BZIP2: Self = CompressionMethod::Unsupported(12);
116 #[cfg(not(feature = "lzma"))]
117 pub const LZMA: Self = CompressionMethod::Unsupported(14);
118 #[cfg(feature = "lzma")]
119 pub const LZMA: Self = CompressionMethod::Lzma;
120 pub const IBM_ZOS_CMPSC: Self = CompressionMethod::Unsupported(16);
121 pub const IBM_TERSE: Self = CompressionMethod::Unsupported(18);
122 pub const ZSTD_DEPRECATED: Self = CompressionMethod::Unsupported(20);
123 #[cfg(feature = "zstd")]
124 pub const ZSTD: Self = CompressionMethod::Zstd;
125 #[cfg(not(feature = "zstd"))]
126 pub const ZSTD: Self = CompressionMethod::Unsupported(93);
127 pub const MP3: Self = CompressionMethod::Unsupported(94);
128 #[cfg(feature = "xz")]
129 pub const XZ: Self = CompressionMethod::Xz;
130 #[cfg(not(feature = "xz"))]
131 pub const XZ: Self = CompressionMethod::Unsupported(95);
132 pub const JPEG: Self = CompressionMethod::Unsupported(96);
133 pub const WAVPACK: Self = CompressionMethod::Unsupported(97);
134 #[cfg(feature = "ppmd")]
135 pub const PPMD: Self = CompressionMethod::Ppmd;
136 #[cfg(not(feature = "ppmd"))]
137 pub const PPMD: Self = CompressionMethod::Unsupported(98);
138 #[cfg(feature = "aes-crypto")]
139 pub const AES: Self = CompressionMethod::Aes;
140 #[cfg(not(feature = "aes-crypto"))]
141 pub const AES: Self = CompressionMethod::Unsupported(99);
142
143 #[cfg(feature = "_deflate-any")]
144 pub const DEFAULT: Self = CompressionMethod::Deflated;
145
146 #[cfg(all(not(feature = "_deflate-any"), feature = "bzip2"))]
147 pub const DEFAULT: Self = CompressionMethod::Bzip2;
148
149 #[cfg(all(not(feature = "_deflate-any"), not(feature = "bzip2")))]
150 pub const DEFAULT: Self = CompressionMethod::Stored;
151}
152impl CompressionMethod {
153 pub(crate) const fn parse_from_u16(val: u16) -> Self {
154 match val {
155 0 => CompressionMethod::Stored,
156 #[cfg(feature = "legacy-zip")]
157 1 => CompressionMethod::Shrink,
158 #[cfg(feature = "legacy-zip")]
159 2 => CompressionMethod::Reduce(1),
160 #[cfg(feature = "legacy-zip")]
161 3 => CompressionMethod::Reduce(2),
162 #[cfg(feature = "legacy-zip")]
163 4 => CompressionMethod::Reduce(3),
164 #[cfg(feature = "legacy-zip")]
165 5 => CompressionMethod::Reduce(4),
166 #[cfg(feature = "legacy-zip")]
167 6 => CompressionMethod::Implode,
168 #[cfg(feature = "_deflate-any")]
169 8 => CompressionMethod::Deflated,
170 #[cfg(feature = "deflate64")]
171 9 => CompressionMethod::Deflate64,
172 #[cfg(feature = "bzip2")]
173 12 => CompressionMethod::Bzip2,
174 #[cfg(feature = "lzma")]
175 14 => CompressionMethod::Lzma,
176 #[cfg(feature = "xz")]
177 95 => CompressionMethod::Xz,
178 #[cfg(feature = "zstd")]
179 93 => CompressionMethod::Zstd,
180 #[cfg(feature = "ppmd")]
181 98 => CompressionMethod::Ppmd,
182 #[cfg(feature = "aes-crypto")]
183 99 => CompressionMethod::Aes,
184 #[allow(deprecated)]
185 v => CompressionMethod::Unsupported(v),
186 }
187 }
188
189 #[deprecated(
191 since = "0.5.7",
192 note = "use a constant to construct a compression method"
193 )]
194 pub const fn from_u16(val: u16) -> CompressionMethod {
195 Self::parse_from_u16(val)
196 }
197
198 pub(crate) const fn serialize_to_u16(self) -> u16 {
199 match self {
200 CompressionMethod::Stored => 0,
201 #[cfg(feature = "legacy-zip")]
202 CompressionMethod::Shrink => 1,
203 #[cfg(feature = "legacy-zip")]
204 CompressionMethod::Reduce(n) => 1 + n as u16,
205 #[cfg(feature = "legacy-zip")]
206 CompressionMethod::Implode => 6,
207
208 #[cfg(feature = "_deflate-any")]
209 CompressionMethod::Deflated => 8,
210 #[cfg(feature = "deflate64")]
211 CompressionMethod::Deflate64 => 9,
212 #[cfg(feature = "bzip2")]
213 CompressionMethod::Bzip2 => 12,
214 #[cfg(feature = "aes-crypto")]
215 CompressionMethod::Aes => 99,
216 #[cfg(feature = "zstd")]
217 CompressionMethod::Zstd => 93,
218 #[cfg(feature = "lzma")]
219 CompressionMethod::Lzma => 14,
220 #[cfg(feature = "xz")]
221 CompressionMethod::Xz => 95,
222 #[cfg(feature = "ppmd")]
223 CompressionMethod::Ppmd => 98,
224 #[allow(deprecated)]
225 CompressionMethod::Unsupported(v) => v,
226 }
227 }
228
229 #[deprecated(
231 since = "0.5.7",
232 note = "to match on other compression methods, use a constant"
233 )]
234 pub const fn to_u16(self) -> u16 {
235 self.serialize_to_u16()
236 }
237}
238
239impl Default for CompressionMethod {
240 fn default() -> Self {
241 Self::DEFAULT
242 }
243}
244
245impl fmt::Display for CompressionMethod {
246 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
247 write!(f, "{self:?}")
249 }
250}
251
252pub const SUPPORTED_COMPRESSION_METHODS: &[CompressionMethod] = &[
254 CompressionMethod::Stored,
255 #[cfg(feature = "_deflate-any")]
256 CompressionMethod::Deflated,
257 #[cfg(feature = "deflate64")]
258 CompressionMethod::Deflate64,
259 #[cfg(feature = "bzip2")]
260 CompressionMethod::Bzip2,
261 #[cfg(feature = "zstd")]
262 CompressionMethod::Zstd,
263 #[cfg(feature = "xz")]
264 CompressionMethod::Xz,
265 #[cfg(feature = "ppmd")]
266 CompressionMethod::Ppmd,
267];
268
269pub(crate) enum Decompressor<R: io::BufRead> {
270 Stored(R),
271 #[cfg(feature = "deflate-flate2")]
272 Deflated(flate2::bufread::DeflateDecoder<R>),
273 #[cfg(feature = "deflate64")]
274 Deflate64(deflate64::Deflate64Decoder<R>),
275 #[cfg(feature = "bzip2")]
276 Bzip2(bzip2::bufread::BzDecoder<R>),
277 #[cfg(feature = "zstd")]
278 Zstd(zstd::Decoder<'static, R>),
279 #[cfg(feature = "lzma")]
280 Lzma(Lzma<R>),
281 #[cfg(feature = "legacy-zip")]
282 Shrink(crate::legacy::shrink::ShrinkDecoder<R>),
283 #[cfg(feature = "legacy-zip")]
284 Reduce(crate::legacy::reduce::ReduceDecoder<R>),
285 #[cfg(feature = "legacy-zip")]
286 Implode(crate::legacy::implode::ImplodeDecoder<R>),
287 #[cfg(feature = "xz")]
288 Xz(Box<lzma_rust2::XzReader<R>>),
289 #[cfg(feature = "ppmd")]
290 Ppmd(Ppmd<R>),
291}
292
293#[cfg(feature = "lzma")]
294pub(crate) enum Lzma<R: io::BufRead> {
295 Uninitialized {
296 reader: Option<R>,
297 uncompressed_size: u64,
298 },
299 Initialized(Box<lzma_rust2::LzmaReader<R>>),
300}
301
302#[cfg(feature = "ppmd")]
303pub(crate) enum Ppmd<R: io::BufRead> {
304 Uninitialized(Option<R>),
305 Initialized(Box<ppmd_rust::Ppmd8Decoder<R>>),
306}
307
308impl<R: io::BufRead> io::Read for Decompressor<R> {
309 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
310 match self {
311 Decompressor::Stored(r) => r.read(buf),
312 #[cfg(feature = "deflate-flate2")]
313 Decompressor::Deflated(r) => r.read(buf),
314 #[cfg(feature = "deflate64")]
315 Decompressor::Deflate64(r) => r.read(buf),
316 #[cfg(feature = "bzip2")]
317 Decompressor::Bzip2(r) => r.read(buf),
318 #[cfg(feature = "zstd")]
319 Decompressor::Zstd(r) => r.read(buf),
320 #[cfg(feature = "lzma")]
321 Decompressor::Lzma(r) => match r {
322 Lzma::Uninitialized {
323 reader,
324 uncompressed_size,
325 } => {
326 let mut reader = reader.take().ok_or_else(|| {
327 io::Error::other("Reader was not set while reading LZMA data")
328 })?;
329
330 let mut header = [0; 4];
332 reader.read_exact(&mut header)?;
333 let _version_information = u16::from_le_bytes(header[0..2].try_into().unwrap());
334 let properties_size = u16::from_le_bytes(header[2..4].try_into().unwrap());
335 if properties_size != 5 {
336 return Err(io::Error::new(
337 io::ErrorKind::InvalidInput,
338 format!("unexpected LZMA properties size of {properties_size}"),
339 ));
340 }
341
342 let mut props_data = [0; 5];
343 reader.read_exact(&mut props_data)?;
344 let props = props_data[0];
345 let dict_size = u32::from_le_bytes(props_data[1..5].try_into().unwrap());
346
347 let mut decompressor = lzma_rust2::LzmaReader::new_with_props(
350 reader,
351 *uncompressed_size,
352 props,
353 dict_size,
354 None,
355 )?;
356
357 let read = decompressor.read(buf)?;
358
359 *r = Lzma::Initialized(Box::new(decompressor));
360
361 Ok(read)
362 }
363 Lzma::Initialized(decompressor) => decompressor.read(buf),
364 },
365 #[cfg(feature = "xz")]
366 Decompressor::Xz(r) => r.read(buf),
367 #[cfg(feature = "ppmd")]
368 Decompressor::Ppmd(r) => match r {
369 Ppmd::Uninitialized(reader) => {
370 let mut reader = reader.take().ok_or_else(|| {
371 io::Error::other("Reader was not set while reading PPMd data")
372 })?;
373
374 let mut buffer = [0; 2];
375 reader.read_exact(&mut buffer)?;
376 let parameters = u16::from_le_bytes(buffer);
377
378 let order = ((parameters & 0x0F) + 1) as u32;
379 let memory_size = 1024 * 1024 * (((parameters >> 4) & 0xFF) + 1) as u32;
380 let restoration_method = (parameters >> 12) & 0x0F;
381
382 let mut decompressor = ppmd_rust::Ppmd8Decoder::new(
383 reader,
384 order,
385 memory_size,
386 restoration_method.into(),
387 )
388 .map_err(|error| match error {
389 ppmd_rust::Error::RangeDecoderInitialization => io::Error::new(
390 io::ErrorKind::InvalidData,
391 "PPMd range coder initialization failed",
392 ),
393 ppmd_rust::Error::InvalidParameter => {
394 io::Error::new(io::ErrorKind::InvalidInput, "Invalid PPMd parameter")
395 }
396 ppmd_rust::Error::IoError(io_error) => io_error,
397 ppmd_rust::Error::MemoryAllocation => {
398 io::Error::new(io::ErrorKind::OutOfMemory, "Memory allocation failed")
399 }
400 })?;
401
402 let read = decompressor.read(buf)?;
403
404 *r = Ppmd::Initialized(Box::new(decompressor));
405
406 Ok(read)
407 }
408 Ppmd::Initialized(decompressor) => decompressor.read(buf),
409 },
410 #[cfg(feature = "legacy-zip")]
411 Decompressor::Shrink(r) => r.read(buf),
412 #[cfg(feature = "legacy-zip")]
413 Decompressor::Reduce(r) => r.read(buf),
414 #[cfg(feature = "legacy-zip")]
415 Decompressor::Implode(r) => r.read(buf),
416 }
417 }
418}
419
420impl<R: io::BufRead> Decompressor<R> {
421 pub fn new(
422 reader: R,
423 compression_method: CompressionMethod,
424 #[cfg(any(feature = "lzma", feature = "legacy-zip"))] uncompressed_size: u64,
425 #[cfg(not(any(feature = "lzma", feature = "legacy-zip")))] _uncompressed_size: u64,
426 #[cfg(feature = "legacy-zip")] flags: u16,
427 #[cfg(not(feature = "legacy-zip"))] _flags: u16,
428 ) -> crate::result::ZipResult<Self> {
429 Ok(match compression_method {
430 CompressionMethod::Stored => Decompressor::Stored(reader),
431 #[cfg(feature = "deflate-flate2")]
432 CompressionMethod::Deflated => {
433 Decompressor::Deflated(flate2::bufread::DeflateDecoder::new(reader))
434 }
435 #[cfg(feature = "deflate64")]
436 CompressionMethod::Deflate64 => {
437 Decompressor::Deflate64(deflate64::Deflate64Decoder::with_buffer(reader))
438 }
439 #[cfg(feature = "bzip2")]
440 CompressionMethod::Bzip2 => Decompressor::Bzip2(bzip2::bufread::BzDecoder::new(reader)),
441 #[cfg(feature = "zstd")]
442 CompressionMethod::Zstd => Decompressor::Zstd(zstd::Decoder::with_buffer(reader)?),
443 #[cfg(feature = "lzma")]
444 CompressionMethod::Lzma => Decompressor::Lzma(Lzma::Uninitialized {
445 reader: Some(reader),
446 uncompressed_size,
447 }),
448 #[cfg(feature = "xz")]
449 CompressionMethod::Xz => {
450 Decompressor::Xz(Box::new(lzma_rust2::XzReader::new(reader, false)))
451 }
452 #[cfg(feature = "ppmd")]
453 CompressionMethod::Ppmd => Decompressor::Ppmd(Ppmd::Uninitialized(Some(reader))),
454 #[cfg(feature = "legacy-zip")]
455 CompressionMethod::Shrink => Decompressor::Shrink(
456 crate::legacy::shrink::ShrinkDecoder::new(reader, uncompressed_size),
457 ),
458 #[cfg(feature = "legacy-zip")]
459 CompressionMethod::Reduce(n) => Decompressor::Reduce(
460 crate::legacy::reduce::ReduceDecoder::new(reader, uncompressed_size, n),
461 ),
462 #[cfg(feature = "legacy-zip")]
463 CompressionMethod::Implode => Decompressor::Implode(
464 crate::legacy::implode::ImplodeDecoder::new(reader, uncompressed_size, flags),
465 ),
466 _ => {
467 return Err(crate::result::ZipError::UnsupportedArchive(
468 "Compression method not supported",
469 ))
470 }
471 })
472 }
473
474 #[allow(clippy::infallible_destructuring_match)]
476 pub fn into_inner(self) -> io::Result<R> {
477 let inner = match self {
478 Decompressor::Stored(r) => r,
479 #[cfg(feature = "deflate-flate2")]
480 Decompressor::Deflated(r) => r.into_inner(),
481 #[cfg(feature = "deflate64")]
482 Decompressor::Deflate64(r) => r.into_inner(),
483 #[cfg(feature = "bzip2")]
484 Decompressor::Bzip2(r) => r.into_inner(),
485 #[cfg(feature = "zstd")]
486 Decompressor::Zstd(r) => r.finish(),
487 #[cfg(feature = "lzma")]
488 Decompressor::Lzma(r) => match r {
489 Lzma::Uninitialized { mut reader, .. } => reader
490 .take()
491 .ok_or_else(|| io::Error::other("Reader was not set"))?,
492 Lzma::Initialized(decoder) => decoder.into_inner(),
493 },
494 #[cfg(feature = "legacy-zip")]
495 Decompressor::Shrink(r) => r.into_inner(),
496 #[cfg(feature = "legacy-zip")]
497 Decompressor::Reduce(r) => r.into_inner(),
498 #[cfg(feature = "legacy-zip")]
499 Decompressor::Implode(r) => r.into_inner(),
500 #[cfg(feature = "xz")]
501 Decompressor::Xz(r) => r.into_inner(),
502 #[cfg(feature = "ppmd")]
503 Decompressor::Ppmd(r) => match r {
504 Ppmd::Uninitialized(mut reader) => reader
505 .take()
506 .ok_or_else(|| io::Error::other("Reader was not set"))?,
507 Ppmd::Initialized(decoder) => decoder.into_inner(),
508 },
509 };
510 Ok(inner)
511 }
512}
513
514#[cfg(test)]
515mod test {
516 use super::{CompressionMethod, SUPPORTED_COMPRESSION_METHODS};
517
518 #[test]
519 fn from_eq_to() {
520 for v in 0..(u16::MAX as u32 + 1) {
521 let from = CompressionMethod::parse_from_u16(v as u16);
522 let to = from.serialize_to_u16() as u32;
523 assert_eq!(v, to);
524 }
525 }
526
527 #[test]
528 fn to_eq_from() {
529 fn check_match(method: CompressionMethod) {
530 let to = method.serialize_to_u16();
531 let from = CompressionMethod::parse_from_u16(to);
532 let back = from.serialize_to_u16();
533 assert_eq!(to, back);
534 }
535
536 for &method in SUPPORTED_COMPRESSION_METHODS {
537 check_match(method);
538 }
539 }
540
541 #[test]
542 fn to_display_fmt() {
543 fn check_match(method: CompressionMethod) {
544 let debug_str = format!("{method:?}");
545 let display_str = format!("{method}");
546 assert_eq!(debug_str, display_str);
547 }
548
549 for &method in SUPPORTED_COMPRESSION_METHODS {
550 check_match(method);
551 }
552 }
553}