Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
19 changes: 12 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "flate2"
authors = ["Alex Crichton <[email protected]>", "Josh Triplett <[email protected]>"]
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_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_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 @@ -83,13 +83,13 @@ zlib-ng-compat = ["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_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_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_zlib"]

## **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"))]
// Only bring in `zlib-rs` if there is no C-based backend.
#[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 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