diff --git a/Cargo.toml b/Cargo.toml index 2e848f2c..8104b2b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ sha1 = {version = "0.10.5", optional = true } time = { version = "0.3.22", optional = true, default-features = false, features = ["std"] } zstd = { version = "0.12.3", optional = true, default-features = false } zopfli = { version = "0.7.4", optional = true } +deflate64 = { version = "0.1.5", optional = true } [target.'cfg(any(all(target_arch = "arm", target_pointer_width = "32"), target_arch = "mips", target_arch = "powerpc"))'.dependencies] crossbeam-utils = "0.8.16" diff --git a/README.md b/README.md index 324baf51..d5a7325e 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ Supported compression formats: * stored (i.e. none) * deflate +* deflate64 (decompression only) * bzip2 * zstd @@ -52,6 +53,7 @@ The features available are: This is the fastest `deflate` implementation available. * `deflate-zopfli`: Enables deflating files with the `zopfli` library (used when compression quality is 10..=264). This is the most effective `deflate` implementation available. +* `deflate64`: Enables the deflate64 compression algorithm. Decompression is only supported. * `bzip2`: Enables the BZip2 compression algorithm. * `time`: Enables features using the [time](https://github.com/rust-lang-deprecated/time) crate. * `chrono`: Enables converting last-modified `zip_next::DateTime` to and from `chrono::NaiveDateTime`. diff --git a/src/compression.rs b/src/compression.rs index fddc54c2..5352c487 100644 --- a/src/compression.rs +++ b/src/compression.rs @@ -25,6 +25,10 @@ pub enum CompressionMethod { feature = "deflate-zopfli" ))] Deflated, + /// Compress the file using Deflate64. + /// Decoding deflate64 is supported but encoding deflate64 is not supported. + #[cfg(feature = "deflate64")] + Deflate64, /// Compress the file using BZIP2 #[cfg(feature = "bzip2")] Bzip2, @@ -70,6 +74,9 @@ impl CompressionMethod { feature = "deflate-zopfli" )))] pub const DEFLATE: Self = CompressionMethod::Unsupported(8); + #[cfg(feature = "deflate64")] + pub const DEFLATE64: Self = CompressionMethod::Deflate64; + #[cfg(not(feature = "deflate64"))] pub const DEFLATE64: Self = CompressionMethod::Unsupported(9); pub const PKWARE_IMPLODE: Self = CompressionMethod::Unsupported(10); #[cfg(feature = "bzip2")] @@ -112,6 +119,8 @@ impl CompressionMethod { feature = "deflate-zopfli" ))] 8 => CompressionMethod::Deflated, + #[cfg(feature = "deflate64")] + 9 => CompressionMethod::Deflate64, #[cfg(feature = "bzip2")] 12 => CompressionMethod::Bzip2, #[cfg(feature = "zstd")] @@ -140,6 +149,8 @@ impl CompressionMethod { feature = "deflate-zopfli" ))] CompressionMethod::Deflated => 8, + #[cfg(feature = "deflate64")] + CompressionMethod::Deflate64 => 9, #[cfg(feature = "bzip2")] CompressionMethod::Bzip2 => 12, #[cfg(feature = "aes-crypto")] @@ -219,6 +230,8 @@ pub const SUPPORTED_COMPRESSION_METHODS: &[CompressionMethod] = &[ feature = "deflate-zopfli" ))] CompressionMethod::Deflated, + #[cfg(feature = "deflate64")] + CompressionMethod::Deflate64, #[cfg(feature = "bzip2")] CompressionMethod::Bzip2, #[cfg(feature = "zstd")] diff --git a/src/read.rs b/src/read.rs index 2c7809ce..5f8484fd 100644 --- a/src/read.rs +++ b/src/read.rs @@ -24,6 +24,9 @@ use std::sync::Arc; ))] use flate2::read::DeflateDecoder; +#[cfg(feature = "deflate64")] +use deflate64::Deflate64Decoder; + #[cfg(feature = "bzip2")] use bzip2::read::BzDecoder; @@ -132,6 +135,8 @@ pub(crate) enum ZipFileReader<'a> { feature = "deflate-zlib-ng" ))] Deflated(Crc32Reader<DeflateDecoder<CryptoReader<'a>>>), + #[cfg(feature = "deflate64")] + Deflate64(Crc32Reader<Deflate64Decoder<io::BufReader<CryptoReader<'a>>>>), #[cfg(feature = "bzip2")] Bzip2(Crc32Reader<BzDecoder<CryptoReader<'a>>>), #[cfg(feature = "zstd")] @@ -151,6 +156,8 @@ impl<'a> Read for ZipFileReader<'a> { feature = "deflate-zlib-ng" ))] ZipFileReader::Deflated(r) => r.read(buf), + #[cfg(feature = "deflate64")] + ZipFileReader::Deflate64(r) => r.read(buf), #[cfg(feature = "bzip2")] ZipFileReader::Bzip2(r) => r.read(buf), #[cfg(feature = "zstd")] @@ -173,6 +180,8 @@ impl<'a> ZipFileReader<'a> { feature = "deflate-zlib-ng" ))] ZipFileReader::Deflated(r) => r.into_inner().into_inner().into_inner(), + #[cfg(feature = "deflate64")] + ZipFileReader::Deflate64(r) => r.into_inner().into_inner().into_inner().into_inner(), #[cfg(feature = "bzip2")] ZipFileReader::Bzip2(r) => r.into_inner().into_inner().into_inner(), #[cfg(feature = "zstd")] @@ -283,6 +292,11 @@ pub(crate) fn make_reader( let deflate_reader = DeflateDecoder::new(reader); ZipFileReader::Deflated(Crc32Reader::new(deflate_reader, crc32, ae2_encrypted)) } + #[cfg(feature = "deflate64")] + CompressionMethod::Deflate64 => { + let deflate64_reader = Deflate64Decoder::new(reader); + ZipFileReader::Deflate64(Crc32Reader::new(deflate64_reader, crc32, ae2_encrypted)) + } #[cfg(feature = "bzip2")] CompressionMethod::Bzip2 => { let bzip2_reader = BzDecoder::new(reader); diff --git a/src/write.rs b/src/write.rs index f3545cf6..7ae82c85 100644 --- a/src/write.rs +++ b/src/write.rs @@ -1273,6 +1273,12 @@ impl<W: Write + Seek> GenericZipWriter<W> { } unreachable!() } + #[cfg(feature = "deflate64")] + CompressionMethod::Deflate64 => { + Err(ZipError::UnsupportedArchive( + "Compressing Deflate64 is not supported", + )) + } #[cfg(feature = "bzip2")] CompressionMethod::Bzip2 => { let level = clamp_opt( diff --git a/tests/data/README.md b/tests/data/README.md new file mode 100644 index 00000000..c1cdf9c4 --- /dev/null +++ b/tests/data/README.md @@ -0,0 +1,6 @@ +A few assets in directory is copied from [dotnet-assets]. + +- [`deflate64.zip`](./deflate64.zip) is originally at https://github.com/dotnet/runtime-assets/blob/95277f38e68b66f1b48600d90d456c32c9ae0fa2/src/System.IO.Compression.TestData/ZipTestData/compat/deflate64.zip +- [`folder/binary.wmv`](./folder/binary.wmv) is originally at https://github.com/dotnet/runtime-assets/tree/95277f38e68b66f1b48600d90d456c32c9ae0fa2/src/System.IO.Compression.TestData/ZipTestData/refzipfolders/normal/binary.wmv + +[dotnet-assets]: https://github.com/dotnet/runtime-assets diff --git a/tests/data/deflate64.zip b/tests/data/deflate64.zip new file mode 100644 index 00000000..88167f88 Binary files /dev/null and b/tests/data/deflate64.zip differ diff --git a/tests/data/folder/binary.wmv b/tests/data/folder/binary.wmv new file mode 100644 index 00000000..aee3e9ce Binary files /dev/null and b/tests/data/folder/binary.wmv differ diff --git a/tests/deflate64.rs b/tests/deflate64.rs new file mode 100644 index 00000000..9eb9f4b6 --- /dev/null +++ b/tests/deflate64.rs @@ -0,0 +1,21 @@ +#![cfg(feature = "deflate64")] + +use std::io::{self, Read}; +use zip_next::ZipArchive; + +#[test] +fn decompress_deflate64() { + let mut v = Vec::new(); + v.extend_from_slice(include_bytes!("data/deflate64.zip")); + let mut archive = ZipArchive::new(io::Cursor::new(v)).expect("couldn't open test zip file"); + + let mut file = archive + .by_name("binary.wmv") + .expect("couldn't find file in archive"); + assert_eq!("binary.wmv", file.name()); + + let mut content = Vec::new(); + file.read_to_end(&mut content) + .expect("couldn't read encrypted and compressed file"); + assert_eq!(include_bytes!("data/folder/binary.wmv"), &content[..]); +} diff --git a/tests/end_to_end.rs b/tests/end_to_end.rs index 70e59f61..039bd6b0 100644 --- a/tests/end_to_end.rs +++ b/tests/end_to_end.rs @@ -12,6 +12,9 @@ use zip_next::{CompressionMethod, ZipWriter, SUPPORTED_COMPRESSION_METHODS}; #[test] fn end_to_end() { for &method in SUPPORTED_COMPRESSION_METHODS { + if method == CompressionMethod::DEFLATE64 { + continue; + } let file = &mut Cursor::new(Vec::new()); println!("Writing file with {method} compression"); @@ -28,6 +31,9 @@ fn end_to_end() { #[test] fn copy() { for &method in SUPPORTED_COMPRESSION_METHODS { + if method == CompressionMethod::DEFLATE64 { + continue; + } let src_file = &mut Cursor::new(Vec::new()); write_test_archive(src_file, method, false); @@ -67,6 +73,9 @@ fn copy() { #[test] fn append() { for &method in SUPPORTED_COMPRESSION_METHODS { + if method == CompressionMethod::DEFLATE64 { + continue; + } for shallow_copy in &[false, true] { println!("Writing file with {method} compression, shallow_copy {shallow_copy}"); let mut file = &mut Cursor::new(Vec::new());