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