feat: add Zstandard compression

- add dependency on zstd crate
- add zstd feature to Cargo.toml
- update README
- update example with Zstd
- add Zstd support to the library

Notes:
- This work is mainly based on this original PR: https://github.com/zip-rs/zip/pull/240

Tested:
- During the development of the original PR
This commit is contained in:
Alexander Zaitsev 2022-01-23 14:49:48 +03:00
parent 82cb917883
commit 0b82d905b3
6 changed files with 54 additions and 3 deletions

View file

@ -17,6 +17,7 @@ byteorder = "1.3"
bzip2 = { version = "0.4", optional = true }
crc32fast = "1.1.1"
thiserror = "1.0.7"
zstd = { version = "0.10", optional = true }
[dev-dependencies]
bencher = "0.1"
@ -28,7 +29,7 @@ deflate = ["flate2/rust_backend"]
deflate-miniz = ["flate2/default"]
deflate-zlib = ["flate2/zlib"]
unreserved = []
default = ["bzip2", "deflate", "time"]
default = ["bzip2", "deflate", "time", "zstd"]
[[bench]]
name = "read_entry"

View file

@ -17,6 +17,7 @@ Supported compression formats:
* stored (i.e. none)
* deflate
* bzip2
* zstd
Currently unsupported zip extensions:
@ -42,9 +43,10 @@ zip = { version = "0.5", default-features = false }
The features available are:
* `deflate`: Enables the deflate compression algorithm, which is the default for zipfiles
* `deflate`: Enables the deflate compression algorithm, which is the default for zip files.
* `bzip2`: Enables the BZip2 compression algorithm.
* `time`: Enables features using the [time](https://github.com/rust-lang-deprecated/time) crate.
* `zstd`: Enables the Zstandard compression algorithm.
All of these are enabled by default.

View file

@ -32,6 +32,11 @@ const METHOD_BZIP2: Option<zip::CompressionMethod> = Some(zip::CompressionMethod
#[cfg(not(feature = "bzip2"))]
const METHOD_BZIP2: Option<zip::CompressionMethod> = None;
#[cfg(feature = "zstd")]
const METHOD_ZSTD: Option<zip::CompressionMethod> = Some(zip::CompressionMethod::Zstd);
#[cfg(not(feature = "zstd"))]
const METHOD_ZSTD: Option<zip::CompressionMethod> = None;
fn real_main() -> i32 {
let args: Vec<_> = std::env::args().collect();
if args.len() < 3 {
@ -44,7 +49,7 @@ fn real_main() -> i32 {
let src_dir = &*args[1];
let dst_file = &*args[2];
for &method in [METHOD_STORED, METHOD_DEFLATED, METHOD_BZIP2].iter() {
for &method in [METHOD_STORED, METHOD_DEFLATED, METHOD_BZIP2, METHOD_ZSTD].iter() {
if method.is_none() {
continue;
}

View file

@ -24,6 +24,9 @@ pub enum CompressionMethod {
/// Compress the file using BZIP2
#[cfg(feature = "bzip2")]
Bzip2,
/// Compress the file using ZStandard
#[cfg(feature = "zstd")]
Zstd,
/// Unsupported compression method
#[deprecated(since = "0.5.7", note = "use the constants instead")]
Unsupported(u16),
@ -60,6 +63,9 @@ impl CompressionMethod {
pub const IBM_ZOS_CMPSC: Self = CompressionMethod::Unsupported(16);
pub const IBM_TERSE: Self = CompressionMethod::Unsupported(18);
pub const ZSTD_DEPRECATED: Self = CompressionMethod::Unsupported(20);
#[cfg(feature = "zstd")]
pub const ZSTD: Self = CompressionMethod::Zstd;
#[cfg(not(feature = "zstd"))]
pub const ZSTD: Self = CompressionMethod::Unsupported(93);
pub const MP3: Self = CompressionMethod::Unsupported(94);
pub const XZ: Self = CompressionMethod::Unsupported(95);
@ -85,6 +91,8 @@ impl CompressionMethod {
8 => CompressionMethod::Deflated,
#[cfg(feature = "bzip2")]
12 => CompressionMethod::Bzip2,
#[cfg(feature = "zstd")]
93 => CompressionMethod::Zstd,
v => CompressionMethod::Unsupported(v),
}
@ -107,6 +115,9 @@ impl CompressionMethod {
CompressionMethod::Deflated => 8,
#[cfg(feature = "bzip2")]
CompressionMethod::Bzip2 => 12,
#[cfg(feature = "zstd")]
CompressionMethod::Zstd => 93,
CompressionMethod::Unsupported(v) => v,
}
}
@ -145,6 +156,9 @@ mod test {
methods.push(CompressionMethod::Deflated);
#[cfg(feature = "bzip2")]
methods.push(CompressionMethod::Bzip2);
#[cfg(feature = "zstd")]
methods.push(CompressionMethod::Zstd);
methods
}

View file

@ -24,6 +24,9 @@ use flate2::read::DeflateDecoder;
#[cfg(feature = "bzip2")]
use bzip2::read::BzDecoder;
#[cfg(feature = "zstd")]
use zstd::stream::read::Decoder as ZstdDecoder;
mod ffi {
pub const S_IFDIR: u32 = 0o0040000;
pub const S_IFREG: u32 = 0o0100000;
@ -90,6 +93,8 @@ enum ZipFileReader<'a> {
Deflated(Crc32Reader<flate2::read::DeflateDecoder<CryptoReader<'a>>>),
#[cfg(feature = "bzip2")]
Bzip2(Crc32Reader<BzDecoder<CryptoReader<'a>>>),
#[cfg(feature = "zstd")]
Zstd(Crc32Reader<ZstdDecoder<'a, io::BufReader<CryptoReader<'a>>>>),
}
impl<'a> Read for ZipFileReader<'a> {
@ -106,6 +111,8 @@ impl<'a> Read for ZipFileReader<'a> {
ZipFileReader::Deflated(r) => r.read(buf),
#[cfg(feature = "bzip2")]
ZipFileReader::Bzip2(r) => r.read(buf),
#[cfg(feature = "zstd")]
ZipFileReader::Zstd(r) => r.read(buf),
}
}
}
@ -125,6 +132,8 @@ impl<'a> ZipFileReader<'a> {
ZipFileReader::Deflated(r) => r.into_inner().into_inner().into_inner(),
#[cfg(feature = "bzip2")]
ZipFileReader::Bzip2(r) => r.into_inner().into_inner().into_inner(),
#[cfg(feature = "zstd")]
ZipFileReader::Zstd(r) => r.into_inner().finish().into_inner().into_inner(),
}
}
}
@ -210,6 +219,11 @@ fn make_reader<'a>(
let bzip2_reader = BzDecoder::new(reader);
ZipFileReader::Bzip2(Crc32Reader::new(bzip2_reader, crc32))
}
#[cfg(feature = "zstd")]
CompressionMethod::Zstd => {
let zstd_reader = ZstdDecoder::new(reader).unwrap();
ZipFileReader::Zstd(Crc32Reader::new(zstd_reader, crc32))
}
_ => panic!("Compression method not supported"),
}
}

View file

@ -25,6 +25,9 @@ use bzip2::write::BzEncoder;
#[cfg(feature = "time")]
use time::OffsetDateTime;
#[cfg(feature = "zstd")]
use zstd::stream::write::Encoder as ZstdEncoder;
enum GenericZipWriter<W: Write + io::Seek> {
Closed,
Storer(W),
@ -36,6 +39,8 @@ enum GenericZipWriter<W: Write + io::Seek> {
Deflater(DeflateEncoder<W>),
#[cfg(feature = "bzip2")]
Bzip2(BzEncoder<W>),
#[cfg(feature = "zstd")]
Zstd(ZstdEncoder<'static, W>),
}
/// ZIP archive generator
@ -807,6 +812,8 @@ impl<W: Write + io::Seek> GenericZipWriter<W> {
GenericZipWriter::Deflater(w) => w.finish()?,
#[cfg(feature = "bzip2")]
GenericZipWriter::Bzip2(w) => w.finish()?,
#[cfg(feature = "zstd")]
GenericZipWriter::Zstd(w) => w.finish()?,
GenericZipWriter::Closed => {
return Err(io::Error::new(
io::ErrorKind::BrokenPipe,
@ -833,6 +840,10 @@ impl<W: Write + io::Seek> GenericZipWriter<W> {
CompressionMethod::Bzip2 => {
GenericZipWriter::Bzip2(BzEncoder::new(bare, bzip2::Compression::default()))
}
#[cfg(feature = "zstd")]
CompressionMethod::Zstd => {
GenericZipWriter::Zstd(ZstdEncoder::new(bare, 0).unwrap())
}
CompressionMethod::Unsupported(..) => {
return Err(ZipError::UnsupportedArchive("Unsupported compression"))
}
@ -853,6 +864,8 @@ impl<W: Write + io::Seek> GenericZipWriter<W> {
GenericZipWriter::Deflater(ref mut w) => Some(w as &mut dyn Write),
#[cfg(feature = "bzip2")]
GenericZipWriter::Bzip2(ref mut w) => Some(w as &mut dyn Write),
#[cfg(feature = "zstd")]
GenericZipWriter::Zstd(ref mut w) => Some(w as &mut dyn Write),
GenericZipWriter::Closed => None,
}
}
@ -882,6 +895,8 @@ impl<W: Write + io::Seek> GenericZipWriter<W> {
GenericZipWriter::Deflater(..) => Some(CompressionMethod::Deflated),
#[cfg(feature = "bzip2")]
GenericZipWriter::Bzip2(..) => Some(CompressionMethod::Bzip2),
#[cfg(feature = "zstd")]
GenericZipWriter::Zstd(..) => Some(CompressionMethod::Zstd),
GenericZipWriter::Closed => None,
}
}