Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ jobs:
- run: cargo test --features zlib-ng --no-default-features
if: matrix.build != 'mingw'
- run: cargo test --features zlib-rs --no-default-features
if: matrix.build != 'mingw'
- run: cargo test --features cloudflare_zlib --no-default-features
if: matrix.build != 'mingw'
- run: |
Expand Down
21 changes: 13 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "flate2"
authors = ["Alex Crichton <alex@alexcrichton.com>", "Josh Triplett <josh@joshtriplett.org>"]
version = "1.1.7"
version = "1.1.8"
edition = "2018"
license = "MIT OR Apache-2.0"
readme = "README.md"
Expand Down Expand Up @@ -51,7 +51,7 @@ default = ["rust_backend"]
## Use the zlib-rs backend, a pure Rust rewrite of zlib.
## This is the fastest backend overall, providing excellent performance with some `unsafe` code.
## It does not require a C compiler but uses `unsafe` Rust for optimization.
zlib-rs = ["any_impl", "dep:zlib-rs"]
zlib-rs = ["any_zlib", "dep:zlib-rs"]

## Use the pure Rust `miniz_oxide` backend (default).
## This implementation uses only safe Rust code and doesn't require a C compiler.
Expand All @@ -63,11 +63,11 @@ rust_backend = ["miniz_oxide", "any_impl"]
## Use the system's installed zlib library.
## This is useful when you need compatibility with other C code that uses zlib,
## or when you want to use the system-provided zlib for consistency.
zlib = ["any_zlib", "libz-sys"]
zlib = ["any_zlib", "any_c_zlib", "libz-sys"]

## Use the system's installed zlib library with default features enabled.
## Similar to `zlib` but enables additional features from libz-sys.
zlib-default = ["any_zlib", "libz-sys/default"]
zlib-default = ["any_zlib", "any_c_zlib", "libz-sys/default"]

## Use zlib-ng in zlib-compat mode via libz-sys.
## This provides zlib-ng's performance improvements while maintaining compatibility.
Expand All @@ -78,18 +78,18 @@ zlib-default = ["any_zlib", "libz-sys/default"]
## See [the libz-sys README](https://github.com/rust-lang/libz-sys/blob/main/README.md) for details.
## To avoid that, use the `"zlib-ng"` feature instead.

zlib-ng-compat = ["zlib", "libz-sys/zlib-ng"]
zlib-ng-compat = ["any_zlib", "zlib", "libz-sys/zlib-ng"]

## Use the high-performance zlib-ng library directly.
## This typically provides better performance than stock zlib and works even when
## other dependencies use zlib. Requires a C compiler.
zlib-ng = ["any_zlib", "libz-ng-sys"]
zlib-ng = ["any_zlib", "any_c_zlib", "libz-ng-sys"]

## Use Cloudflare's optimized zlib implementation.
## This provides better performance than stock zlib on x86-64 (with SSE 4.2) and ARM64 (with NEON & CRC).
## * ⚠ Does not support 32-bit CPUs and is incompatible with mingw.
## * ⚠ May cause conflicts if other crates use different zlib versions.
cloudflare_zlib = ["any_zlib", "cloudflare-zlib-sys"]
cloudflare_zlib = ["any_zlib", "any_c_zlib", "cloudflare-zlib-sys"]

## Deprecated alias for `rust_backend`, provided for backwards compatibility.
## Use `rust_backend` instead.
Expand All @@ -100,10 +100,15 @@ miniz-sys = ["rust_backend"]
#! They are documented here to aid with maintenance.

## **Internal:** Marker feature indicating that any zlib-based C backend is enabled.
## This is automatically enabled by `zlib`, `zlib-ng`, `zlib-ng-compat`, or `cloudflare_zlib`.
## This is automatically enabled by `zlib-rs`, `zlib`, `zlib-ng`, `zlib-ng-compat`, and `cloudflare_zlib`.
## Do not enable this feature directly; instead, choose a specific backend feature.
any_zlib = ["any_impl"]

## **Internal:** Marker feature indicating that any C based fully zlib compatible backend is enabled.
## This is automatically enabled by `zlib`, `zlib-ng`, `zlib-ng-compat`, and `cloudflare_zlib`.
## Do not enable this feature directly; instead, choose a specific backend feature.
any_c_zlib = ["any_impl"]

## **Internal:** Marker feature indicating that any compression backend is enabled.
## This is automatically enabled by all backend features to ensure at least one implementation is available.
## Do not enable this feature directly; instead, choose a specific backend feature.
Expand Down
30 changes: 9 additions & 21 deletions src/ffi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,37 +60,25 @@ pub trait DeflateBackend: Backend {
}

// Default to Rust implementation unless explicitly opted in to a different backend.
#[cfg(feature = "any_zlib")]
#[cfg(feature = "any_c_zlib")]
mod c;
#[cfg(feature = "any_zlib")]
#[cfg(feature = "any_c_zlib")]
pub use self::c::*;

// Prefer zlib-rs when both Rust backends are enabled to avoid duplicate exports.
#[cfg(all(not(feature = "any_zlib"), feature = "zlib-rs"))]
// Prefer C backes when both Rust backends are enabled to avoid duplicate exports.
#[cfg(all(not(feature = "any_c_zlib"), feature = "zlib-rs"))]
mod zlib_rs;
#[cfg(all(not(feature = "any_zlib"), feature = "zlib-rs"))]
#[cfg(all(not(feature = "any_c_zlib"), feature = "zlib-rs"))]
pub use self::zlib_rs::*;

// Fallback to miniz_oxide when zlib-rs is not selected.
#[cfg(all(
not(feature = "any_zlib"),
not(feature = "zlib-rs"),
feature = "miniz_oxide"
))]
// Use to miniz_oxide when no fully compliant zlib is selected.
#[cfg(all(not(feature = "any_zlib"), feature = "miniz_oxide"))]
mod miniz_oxide;
#[cfg(all(
not(feature = "any_zlib"),
not(feature = "zlib-rs"),
feature = "miniz_oxide"
))]
#[cfg(all(not(feature = "any_zlib"), feature = "miniz_oxide"))]
pub use self::miniz_oxide::*;

// If no backend is enabled, fail fast with a clear error message.
#[cfg(all(
not(feature = "any_zlib"),
not(feature = "zlib-rs"),
not(feature = "miniz_oxide")
))]
#[cfg(not(feature = "any_impl"))]
compile_error!("No compression backend selected; enable one of `zlib`, `zlib-ng`, `zlib-rs`, or the default `rust_backend` feature.");

impl std::fmt::Debug for ErrorMessage {
Expand Down
7 changes: 3 additions & 4 deletions src/ffi/zlib_rs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,12 +242,11 @@ impl Deflate {
match self.inner.set_level(level.level() as i32) {
Ok(status) => match status {
Status::Ok => Ok(()),

Status::BufError => compress_failed(ErrorMessage(Some("insufficient space"))),

Status::StreamEnd => unreachable!(),
Status::StreamEnd => {
unreachable!("zlib-rs is known to never return the StreamEnd status")
}
},

Err(_) => self.compress_error(),
}
}
Expand Down
106 changes: 13 additions & 93 deletions src/mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ impl Compress {
/// # Panics
///
/// If `window_bits` does not fall into the range 9 ..= 15,
/// `new_with_window_bits` will panic.
/// this function will panic.
#[cfg(feature = "any_zlib")]
pub fn new_with_window_bits(
level: Compression,
Expand All @@ -239,7 +239,7 @@ impl Compress {
/// # Panics
///
/// If `window_bits` does not fall into the range 9 ..= 15,
/// `new_with_window_bits` will panic.
/// this function will panic.
#[cfg(feature = "any_zlib")]
pub fn new_gzip(level: Compression, window_bits: u8) -> Compress {
assert!(
Expand All @@ -266,7 +266,7 @@ impl Compress {
/// Specifies the compression dictionary to use.
///
/// Returns the Adler-32 checksum of the dictionary.
#[cfg(feature = "any_zlib")]
#[cfg(feature = "any_c_zlib")]
pub fn set_dictionary(&mut self, dictionary: &[u8]) -> Result<u32, CompressError> {
// SAFETY: The field `inner` must always be accessed as a raw pointer,
// since it points to a cyclic structure. No copies of `inner` can be
Expand All @@ -289,7 +289,7 @@ impl Compress {
/// Specifies the compression dictionary to use.
///
/// Returns the Adler-32 checksum of the dictionary.
#[cfg(all(not(feature = "any_zlib"), feature = "zlib-rs"))]
#[cfg(all(not(feature = "any_c_zlib"), feature = "zlib-rs"))]
pub fn set_dictionary(&mut self, dictionary: &[u8]) -> Result<u32, CompressError> {
self.inner.set_dictionary(dictionary)
}
Expand All @@ -311,14 +311,14 @@ impl Compress {
/// the compression of the available input data before changing the
/// compression level. Flushing the stream before calling this method
/// ensures that the function will succeed on the first call.
#[cfg(any(feature = "any_zlib", feature = "zlib-rs"))]
#[cfg(feature = "any_zlib")]
pub fn set_level(&mut self, level: Compression) -> Result<(), CompressError> {
#[cfg(all(not(feature = "any_zlib"), feature = "zlib-rs"))]
#[cfg(all(not(feature = "any_c_zlib"), feature = "zlib-rs"))]
{
self.inner.set_level(level)
}

#[cfg(feature = "any_zlib")]
#[cfg(feature = "any_c_zlib")]
{
use std::os::raw::c_int;
// SAFETY: The field `inner` must always be accessed as a raw pointer,
Expand Down Expand Up @@ -415,7 +415,7 @@ impl Decompress {
/// # Panics
///
/// If `window_bits` does not fall into the range 9 ..= 15,
/// `new_with_window_bits` will panic.
/// this function will panic.
#[cfg(feature = "any_zlib")]
pub fn new_with_window_bits(zlib_header: bool, window_bits: u8) -> Decompress {
assert!(
Expand All @@ -435,7 +435,7 @@ impl Decompress {
/// # Panics
///
/// If `window_bits` does not fall into the range 9 ..= 15,
/// `new_with_window_bits` will panic.
/// this function will panic.
#[cfg(feature = "any_zlib")]
pub fn new_gzip(window_bits: u8) -> Decompress {
assert!(
Expand Down Expand Up @@ -536,7 +536,7 @@ impl Decompress {
}

/// Specifies the decompression dictionary to use.
#[cfg(feature = "any_zlib")]
#[cfg(feature = "any_c_zlib")]
pub fn set_dictionary(&mut self, dictionary: &[u8]) -> Result<u32, DecompressError> {
// SAFETY: The field `inner` must always be accessed as a raw pointer,
// since it points to a cyclic structure. No copies of `inner` can be
Expand All @@ -558,7 +558,7 @@ impl Decompress {
}

/// Specifies the decompression dictionary to use.
#[cfg(all(not(feature = "any_zlib"), feature = "zlib-rs"))]
#[cfg(all(not(feature = "any_c_zlib"), feature = "zlib-rs"))]
pub fn set_dictionary(&mut self, dictionary: &[u8]) -> Result<u32, DecompressError> {
self.inner.set_dictionary(dictionary)
}
Expand Down Expand Up @@ -723,86 +723,6 @@ mod tests {
assert!(dst.starts_with(string));
}

#[cfg(feature = "any_zlib")]
#[test]
fn set_dictionary_with_zlib_header() {
let string = "hello, hello!".as_bytes();
let dictionary = "hello".as_bytes();

let mut encoded = Vec::with_capacity(1024);

let mut encoder = Compress::new(Compression::default(), true);

let dictionary_adler = encoder.set_dictionary(&dictionary).unwrap();

encoder
.compress_vec(string, &mut encoded, FlushCompress::Finish)
.unwrap();

assert_eq!(encoder.total_in(), string.len() as u64);
assert_eq!(encoder.total_out(), encoded.len() as u64);

let mut decoder = Decompress::new(true);
let mut decoded = [0; 1024];
let decompress_error = decoder
.decompress(&encoded, &mut decoded, FlushDecompress::Finish)
.expect_err("decompression should fail due to requiring a dictionary");

let required_adler = decompress_error.needs_dictionary()
.expect("the first call to decompress should indicate a dictionary is required along with the required Adler-32 checksum");

assert_eq!(required_adler, dictionary_adler,
"the Adler-32 checksum should match the value when the dictionary was set on the compressor");

let actual_adler = decoder.set_dictionary(&dictionary).unwrap();

assert_eq!(required_adler, actual_adler);

// Decompress the rest of the input to the remainder of the output buffer
let total_in = decoder.total_in();
let total_out = decoder.total_out();

let decompress_result = decoder.decompress(
&encoded[total_in as usize..],
&mut decoded[total_out as usize..],
FlushDecompress::Finish,
);
assert!(decompress_result.is_ok());

assert_eq!(&decoded[..decoder.total_out() as usize], string);
}

#[cfg(feature = "any_zlib")]
#[test]
fn set_dictionary_raw() {
let string = "hello, hello!".as_bytes();
let dictionary = "hello".as_bytes();

let mut encoded = Vec::with_capacity(1024);

let mut encoder = Compress::new(Compression::default(), false);

encoder.set_dictionary(&dictionary).unwrap();

encoder
.compress_vec(string, &mut encoded, FlushCompress::Finish)
.unwrap();

assert_eq!(encoder.total_in(), string.len() as u64);
assert_eq!(encoder.total_out(), encoded.len() as u64);

let mut decoder = Decompress::new(false);

decoder.set_dictionary(&dictionary).unwrap();

let mut decoded = [0; 1024];
let decompress_result = decoder.decompress(&encoded, &mut decoded, FlushDecompress::Finish);

assert!(decompress_result.is_ok());

assert_eq!(&decoded[..decoder.total_out() as usize], string);
}

#[cfg(feature = "any_zlib")]
#[test]
fn test_gzip_flate() {
Expand All @@ -829,15 +749,15 @@ mod tests {
assert_eq!(&decoded[..decoder.total_out() as usize], string);
}

#[cfg(any(feature = "any_zlib", feature = "zlib-rs"))]
#[cfg(feature = "any_zlib")]
#[test]
fn test_error_message() {
let mut decoder = Decompress::new(false);
let mut decoded = [0; 128];
let garbage = b"xbvxzi";

let err = decoder
.decompress(&*garbage, &mut decoded, FlushDecompress::Finish)
.decompress(garbage, &mut decoded, FlushDecompress::Finish)
.unwrap_err();

assert_eq!(err.message(), Some("invalid stored block lengths"));
Expand Down
Loading