diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e3fc98a4..3cf9af6d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -68,7 +68,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - target: [wasm32-unknown-unknown, wasm32-wasi] + target: [wasm32-unknown-unknown, wasm32-wasip1] steps: - uses: actions/checkout@v4 - name: Install Rust @@ -82,7 +82,7 @@ jobs: matrix: os: [windows-2022, macos-latest, ubuntu-latest] env: - version: 1.63.0 + version: 1.67.0 steps: - uses: actions/checkout@v4 - name: Install Rust (rustup) @@ -105,7 +105,7 @@ jobs: runs-on: ubuntu-latest env: # If this is changed to pass tests, then set `rust-version` in `Cargo.toml` to the same version. - version: 1.56.1 + version: 1.67.0 steps: - uses: actions/checkout@v4 - name: Install Rust (rustup) diff --git a/Cargo.toml b/Cargo.toml index 6d648210..ed0e7235 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,11 @@ [package] name = "flate2" authors = ["Alex Crichton ", "Josh Triplett "] -version = "1.0.35" +version = "1.1.0" edition = "2018" license = "MIT OR Apache-2.0" readme = "README.md" -rust-version = "1.56.1" +rust-version = "1.67.0" keywords = ["gzip", "deflate", "zlib", "zlib-ng", "encoding"] categories = ["compression", "api-bindings"] repository = "https://github.com/rust-lang/flate2-rs" @@ -22,16 +22,16 @@ exclude = [".*"] libz-sys = { version = "1.1.20", optional = true, default-features = false } libz-ng-sys = { version = "1.1.16", optional = true } # this matches the default features, but we don't want to depend on the default features staying the same -libz-rs-sys = { version = "0.4.0", optional = true, default-features = false, features = ["std", "rust-allocator"] } -cloudflare-zlib-sys = { version = "0.3.0", optional = true } -miniz_oxide = { version = "0.8.0", optional = true, default-features = false, features = ["with-alloc"] } +libz-rs-sys = { version = "0.4.2", optional = true, default-features = false, features = ["std", "rust-allocator"] } +cloudflare-zlib-sys = { version = "0.3.5", optional = true } +miniz_oxide = { version = "0.8.4", optional = true, default-features = false, features = ["with-alloc"] } crc32fast = "1.2.0" [target.'cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))'.dependencies] -miniz_oxide = { version = "0.8.0", default-features = false, features = ["with-alloc"] } +miniz_oxide = { version = "0.8.4", default-features = false, features = ["with-alloc"] } [dev-dependencies] -rand = "0.8" +rand = "0.9" quickcheck = { version = "1.0", default-features = false } [features] diff --git a/LICENSE-MIT b/LICENSE-MIT index 39e0ed66..0bb8a971 100644 --- a/LICENSE-MIT +++ b/LICENSE-MIT @@ -1,4 +1,4 @@ -Copyright (c) 2014 Alex Crichton +Copyright (c) 2014-2025 Alex Crichton Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated diff --git a/src/crc.rs b/src/crc.rs index 16f56019..991cc957 100644 --- a/src/crc.rs +++ b/src/crc.rs @@ -8,7 +8,7 @@ use crc32fast::Hasher; /// The CRC calculated by a [`CrcReader`]. /// /// [`CrcReader`]: struct.CrcReader.html -#[derive(Debug)] +#[derive(Debug, Default)] pub struct Crc { amt: u32, hasher: Hasher, @@ -23,19 +23,10 @@ pub struct CrcReader { crc: Crc, } -impl Default for Crc { - fn default() -> Self { - Self::new() - } -} - impl Crc { /// Create a new CRC. - pub fn new() -> Crc { - Crc { - amt: 0, - hasher: Hasher::new(), - } + pub fn new() -> Self { + Self::default() } /// Returns the current crc32 checksum. diff --git a/src/deflate/mod.rs b/src/deflate/mod.rs index 7f3bf70f..b5c5b6ca 100644 --- a/src/deflate/mod.rs +++ b/src/deflate/mod.rs @@ -6,7 +6,7 @@ pub mod write; mod tests { use std::io::prelude::*; - use rand::{thread_rng, Rng}; + use rand::{rng, Rng}; use super::{read, write}; use crate::Compression; @@ -17,7 +17,7 @@ mod tests { let mut w = write::DeflateEncoder::new(Vec::new(), Compression::default()); let v = crate::random_bytes().take(1024).collect::>(); for _ in 0..200 { - let to_write = &v[..thread_rng().gen_range(0..v.len())]; + let to_write = &v[..rng().random_range(0..v.len())]; real.extend(to_write.iter().copied()); w.write_all(to_write).unwrap(); } @@ -46,7 +46,7 @@ mod tests { let mut w = write::DeflateEncoder::new(Vec::new(), Compression::default()); let v = crate::random_bytes().take(1024).collect::>(); for _ in 0..200 { - let to_write = &v[..thread_rng().gen_range(0..v.len())]; + let to_write = &v[..rng().random_range(0..v.len())]; real.extend(to_write.iter().copied()); w.write_all(to_write).unwrap(); } diff --git a/src/ffi/c.rs b/src/ffi/c.rs index 226fe0e3..3ffebbc4 100644 --- a/src/ffi/c.rs +++ b/src/ffi/c.rs @@ -8,7 +8,7 @@ use std::ptr; use super::*; use crate::mem; -#[derive(Default)] +#[derive(Clone, Default)] pub struct ErrorMessage(Option<&'static str>); impl ErrorMessage { @@ -50,27 +50,38 @@ impl Default for StreamWrapper { reserved: 0, opaque: ptr::null_mut(), state: ptr::null_mut(), - #[cfg(all( - feature = "any_zlib", - not(any(feature = "cloudflare-zlib-sys", feature = "libz-rs-sys")) + + #[cfg(any( + // zlib-ng + feature = "zlib-ng", + // libz-sys + all(not(feature = "cloudflare_zlib"), not(feature = "zlib-ng"), not(feature = "zlib-rs")) ))] zalloc: allocator::zalloc, - #[cfg(all( - feature = "any_zlib", - not(any(feature = "cloudflare-zlib-sys", feature = "libz-rs-sys")) + #[cfg(any( + // zlib-ng + feature = "zlib-ng", + // libz-sys + all(not(feature = "cloudflare_zlib"), not(feature = "zlib-ng"), not(feature = "zlib-rs")) ))] zfree: allocator::zfree, - #[cfg(all(feature = "any_zlib", feature = "cloudflare-zlib-sys"))] + #[cfg( + // cloudflare-zlib + all(feature = "cloudflare_zlib", not(feature = "zlib-rs"), not(feature = "zlib-ng")), + )] zalloc: Some(allocator::zalloc), - #[cfg(all(feature = "any_zlib", feature = "cloudflare-zlib-sys"))] + #[cfg( + // cloudflare-zlib + all(feature = "cloudflare_zlib", not(feature = "zlib-rs"), not(feature = "zlib-ng")), + )] zfree: Some(allocator::zfree), // for zlib-rs, it is most efficient to have it provide the allocator. // The libz-rs-sys dependency is configured to use the rust system allocator - #[cfg(all(feature = "any_zlib", feature = "libz-rs-sys"))] + #[cfg(all(feature = "zlib-rs", not(feature = "zlib-ng")))] zalloc: None, - #[cfg(all(feature = "any_zlib", feature = "libz-rs-sys"))] + #[cfg(all(feature = "zlib-rs", not(feature = "zlib-ng")))] zfree: None, })), } @@ -87,7 +98,14 @@ impl Drop for StreamWrapper { } } -#[cfg(all(feature = "any_zlib", not(feature = "libz-rs-sys")))] +#[cfg(any( + // zlib-ng + feature = "zlib-ng", + // cloudflare-zlib + all(feature = "cloudflare_zlib", not(feature = "zlib-rs"), not(feature = "zlib-ng")), + // libz-sys + all(not(feature = "cloudflare_zlib"), not(feature = "zlib-ng"), not(feature = "zlib-rs")), +))] mod allocator { use super::*; @@ -405,17 +423,19 @@ mod c_backend { #[cfg(feature = "zlib-ng")] use libz_ng_sys as libz; - #[cfg(all(not(feature = "zlib-ng"), feature = "zlib-rs"))] + #[cfg(all(feature = "zlib-rs", not(feature = "zlib-ng")))] use libz_rs_sys as libz; - #[cfg(all(not(feature = "zlib-ng"), feature = "cloudflare_zlib"))] + #[cfg( + // cloudflare-zlib + all(feature = "cloudflare_zlib", not(feature = "zlib-rs"), not(feature = "zlib-ng")), + )] use cloudflare_zlib_sys as libz; - #[cfg(all( - not(feature = "cloudflare_zlib"), - not(feature = "zlib-ng"), - not(feature = "zlib-rs") - ))] + #[cfg( + // libz-sys + all(not(feature = "cloudflare_zlib"), not(feature = "zlib-ng"), not(feature = "zlib-rs")), + )] use libz_sys as libz; pub use libz::deflate as mz_deflate; @@ -447,7 +467,7 @@ mod c_backend { #[cfg(feature = "zlib-ng")] const ZLIB_VERSION: &'static str = "2.1.0.devel\0"; #[cfg(all(not(feature = "zlib-ng"), feature = "zlib-rs"))] - const ZLIB_VERSION: &'static str = "1.3.0-zlib-rs-0.4.0\0"; + const ZLIB_VERSION: &'static str = "1.3.0-zlib-rs-0.4.2\0"; #[cfg(not(any(feature = "zlib-ng", feature = "zlib-rs")))] const ZLIB_VERSION: &'static str = "1.2.8\0"; diff --git a/src/ffi/rust.rs b/src/ffi/rust.rs index bed6629a..5b3c1937 100644 --- a/src/ffi/rust.rs +++ b/src/ffi/rust.rs @@ -17,7 +17,7 @@ use super::*; use crate::mem; // miniz_oxide doesn't provide any error messages (yet?) -#[derive(Default)] +#[derive(Clone, Default)] pub struct ErrorMessage; impl ErrorMessage { @@ -50,6 +50,16 @@ impl fmt::Debug for Inflate { } } +impl From for MZFlush { + fn from(value: FlushDecompress) -> Self { + match value { + FlushDecompress::None => Self::None, + FlushDecompress::Sync => Self::Sync, + FlushDecompress::Finish => Self::Finish, + } + } +} + impl InflateBackend for Inflate { fn make(zlib_header: bool, _window_bits: u8) -> Self { let format = format_from_bool(zlib_header); @@ -67,9 +77,8 @@ impl InflateBackend for Inflate { output: &mut [u8], flush: FlushDecompress, ) -> Result { - let flush = MZFlush::new(flush as i32).unwrap(); - - let res = inflate::stream::inflate(&mut self.inner, input, output, flush); + let mz_flush = flush.into(); + let res = inflate::stream::inflate(&mut self.inner, input, output, mz_flush); self.total_in += res.bytes_consumed as u64; self.total_out += res.bytes_written as u64; @@ -123,6 +132,17 @@ impl fmt::Debug for Deflate { } } +impl From for MZFlush { + fn from(value: FlushCompress) -> Self { + match value { + FlushCompress::None => Self::None, + FlushCompress::Partial | FlushCompress::Sync => Self::Sync, + FlushCompress::Full => Self::Full, + FlushCompress::Finish => Self::Finish, + } + } +} + impl DeflateBackend for Deflate { fn make(level: Compression, zlib_header: bool, _window_bits: u8) -> Self { // Check in case the integer value changes at some point. @@ -145,8 +165,8 @@ impl DeflateBackend for Deflate { output: &mut [u8], flush: FlushCompress, ) -> Result { - let flush = MZFlush::new(flush as i32).unwrap(); - let res = deflate::stream::deflate(&mut self.inner, input, output, flush); + let mz_flush = flush.into(); + let res = deflate::stream::deflate(&mut self.inner, input, output, mz_flush); self.total_in += res.bytes_consumed as u64; self.total_out += res.bytes_written as u64; diff --git a/src/gz/bufread.rs b/src/gz/bufread.rs index e01e5284..e19c717a 100644 --- a/src/gz/bufread.rs +++ b/src/gz/bufread.rs @@ -10,9 +10,7 @@ use crate::Compression; fn copy(into: &mut [u8], from: &[u8], pos: &mut usize) -> usize { let min = cmp::min(into.len(), from.len() - *pos); - for (slot, val) in into.iter_mut().zip(from[*pos..*pos + min].iter()) { - *slot = *val; - } + into[..min].copy_from_slice(&from[*pos..*pos + min]); *pos += min; min } @@ -82,17 +80,18 @@ impl GzEncoder { return Ok(0); } let crc = self.inner.get_ref().crc(); - let ref arr = [ - (crc.sum() >> 0) as u8, - (crc.sum() >> 8) as u8, - (crc.sum() >> 16) as u8, - (crc.sum() >> 24) as u8, + let calced_crc_bytes = crc.sum().to_le_bytes(); + let arr = [ + calced_crc_bytes[0], + calced_crc_bytes[1], + calced_crc_bytes[2], + calced_crc_bytes[3], (crc.amount() >> 0) as u8, (crc.amount() >> 8) as u8, (crc.amount() >> 16) as u8, (crc.amount() >> 24) as u8, ]; - Ok(copy(into, arr, &mut self.pos)) + Ok(copy(into, &arr, &mut self.pos)) } } diff --git a/src/gz/mod.rs b/src/gz/mod.rs index 31a69611..3a24a754 100644 --- a/src/gz/mod.rs +++ b/src/gz/mod.rs @@ -87,7 +87,7 @@ impl GzHeader { } } -#[derive(Debug)] +#[derive(Debug, Default)] pub enum GzHeaderState { Start(u8, [u8; 10]), Xlen(Option>, u8, [u8; 2]), @@ -95,15 +95,10 @@ pub enum GzHeaderState { Filename(Option>), Comment(Option>), Crc(Option>, u8, [u8; 2]), + #[default] Complete, } -impl Default for GzHeaderState { - fn default() -> Self { - Self::Complete - } -} - #[derive(Debug, Default)] pub struct GzHeaderParser { state: GzHeaderState, @@ -120,7 +115,7 @@ impl GzHeaderParser { } } - fn parse<'a, R: Read>(&mut self, r: &'a mut R) -> Result<()> { + fn parse(&mut self, r: &mut R) -> Result<()> { loop { match &mut self.state { GzHeaderState::Start(count, buffer) => { @@ -163,7 +158,7 @@ impl GzHeaderParser { if let Some(crc) = crc { crc.update(buffer); } - let xlen = parse_le_u16(&buffer); + let xlen = parse_le_u16(buffer); self.header.extra = Some(vec![0; xlen as usize]); self.state = GzHeaderState::Extra(crc.take(), 0); } else { @@ -209,7 +204,7 @@ impl GzHeaderParser { while (*count as usize) < buffer.len() { *count += read_into(r, &mut buffer[*count as usize..])? as u8; } - let stored_crc = parse_le_u16(&buffer); + let stored_crc = parse_le_u16(buffer); let calced_crc = crc.sum() as u16; if stored_crc != calced_crc { return Err(corrupt()); @@ -253,13 +248,11 @@ fn read_into(r: &mut R, buffer: &mut [u8]) -> Result { } // Read `r` up to the first nul byte, pushing non-nul bytes to `buffer`. -fn read_to_nul(r: &mut R, buffer: &mut Vec) -> Result<()> { +fn read_to_nul(r: &mut R, buffer: &mut Vec) -> Result<()> { let mut bytes = r.bytes(); loop { match bytes.next().transpose()? { - Some(byte) if byte == 0 => { - return Ok(()); - } + Some(0) => return Ok(()), Some(_) if buffer.len() == MAX_HEADER_BUF => { return Err(Error::new( ErrorKind::InvalidInput, @@ -277,7 +270,7 @@ fn read_to_nul(r: &mut R, buffer: &mut Vec) -> Result<()> { } fn parse_le_u16(buffer: &[u8; 2]) -> u16 { - (buffer[0] as u16) | ((buffer[1] as u16) << 8) + u16::from_le_bytes(*buffer) } fn bad_header() -> Error { @@ -317,7 +310,7 @@ fn corrupt() -> Error { /// # Ok(()) /// # } /// ``` -#[derive(Debug)] +#[derive(Debug, Default)] pub struct GzBuilder { extra: Option>, filename: Option, @@ -326,22 +319,10 @@ pub struct GzBuilder { mtime: u32, } -impl Default for GzBuilder { - fn default() -> Self { - Self::new() - } -} - impl GzBuilder { /// Create a new blank builder with no header by default. pub fn new() -> GzBuilder { - GzBuilder { - extra: None, - filename: None, - comment: None, - operating_system: None, - mtime: 0, - } + Self::default() } /// Configure the `mtime` field in the gzip header. @@ -464,7 +445,7 @@ mod tests { use super::{read, write, GzBuilder, GzHeaderParser}; use crate::{Compression, GzHeader}; - use rand::{thread_rng, Rng}; + use rand::{rng, Rng}; #[test] fn roundtrip() { @@ -493,7 +474,7 @@ mod tests { let mut w = write::GzEncoder::new(Vec::new(), Compression::default()); let v = crate::random_bytes().take(1024).collect::>(); for _ in 0..200 { - let to_write = &v[..thread_rng().gen_range(0..v.len())]; + let to_write = &v[..rng().random_range(0..v.len())]; real.extend(to_write.iter().copied()); w.write_all(to_write).unwrap(); } diff --git a/src/lib.rs b/src/lib.rs index c827413f..16838c83 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -242,5 +242,5 @@ fn random_bytes() -> impl Iterator { use rand::Rng; use std::iter; - iter::repeat(()).map(|_| rand::thread_rng().gen()) + iter::repeat(()).map(|_| rand::rng().random()) } diff --git a/src/mem.rs b/src/mem.rs index 86fa8d3b..60f8f89d 100644 --- a/src/mem.rs +++ b/src/mem.rs @@ -49,6 +49,16 @@ pub enum FlushCompress { /// accumulate before producing output in order to maximize compression. None = ffi::MZ_NO_FLUSH as isize, + /// All pending output is flushed to the output buffer, but the output is + /// not aligned to a byte boundary. + /// + /// All input data so far will be available to the decompressor (as with + /// `Flush::Sync`). This completes the current deflate block and follows it + /// with an empty fixed codes block that is 10 bytes long, and it assures + /// that enough bytes are output in order for the decompressor to finish the + /// block before the empty fixed code block. + Partial = ffi::MZ_PARTIAL_FLUSH as isize, + /// All pending output is flushed to the output buffer and the output is /// aligned on a byte boundary so that the decompressor can get all input /// data available so far. @@ -58,16 +68,6 @@ pub enum FlushCompress { /// deflate block and follow it with an empty stored block. Sync = ffi::MZ_SYNC_FLUSH as isize, - /// All pending output is flushed to the output buffer, but the output is - /// not aligned to a byte boundary. - /// - /// All of the input data so far will be available to the decompressor (as - /// with `Flush::Sync`. This completes the current deflate block and follows - /// it with an empty fixed codes block that is 10 bites long, and it assures - /// that enough bytes are output in order for the decompressor to finish the - /// block before the empty fixed code block. - Partial = ffi::MZ_PARTIAL_FLUSH as isize, - /// All output is flushed as with `Flush::Sync` and the compression state is /// reset so decompression can restart from this point if previous /// compressed data has been damaged or if random access is desired. @@ -109,7 +109,7 @@ pub enum FlushDecompress { } /// The inner state for an error when decompressing -#[derive(Debug)] +#[derive(Clone, Debug)] pub(crate) enum DecompressErrorInner { General { msg: ErrorMessage }, NeedsDictionary(u32), @@ -117,7 +117,7 @@ pub(crate) enum DecompressErrorInner { /// Error returned when a decompression object finds that the input stream of /// bytes was not a valid input stream of bytes. -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct DecompressError(pub(crate) DecompressErrorInner); impl DecompressError { @@ -147,7 +147,7 @@ pub(crate) fn decompress_need_dict(adler: u32) -> Result /// Error returned when a compression object is used incorrectly or otherwise /// generates an error. -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct CompressError { pub(crate) msg: ErrorMessage, } diff --git a/src/zio.rs b/src/zio.rs index ea25922a..3e6b9eb3 100644 --- a/src/zio.rs +++ b/src/zio.rs @@ -2,7 +2,9 @@ use std::io; use std::io::prelude::*; use std::mem; -use crate::{Compress, Decompress, DecompressError, FlushCompress, FlushDecompress, Status}; +use crate::{ + Compress, CompressError, Decompress, DecompressError, FlushCompress, FlushDecompress, Status, +}; #[derive(Debug)] pub struct Writer { @@ -12,6 +14,7 @@ pub struct Writer { } pub trait Ops { + type Error: Into; type Flush: Flush; fn total_in(&self) -> u64; fn total_out(&self) -> u64; @@ -20,16 +23,17 @@ pub trait Ops { input: &[u8], output: &mut [u8], flush: Self::Flush, - ) -> Result; + ) -> Result; fn run_vec( &mut self, input: &[u8], output: &mut Vec, flush: Self::Flush, - ) -> Result; + ) -> Result; } impl Ops for Compress { + type Error = CompressError; type Flush = FlushCompress; fn total_in(&self) -> u64 { self.total_in() @@ -42,20 +46,21 @@ impl Ops for Compress { input: &[u8], output: &mut [u8], flush: FlushCompress, - ) -> Result { - Ok(self.compress(input, output, flush).unwrap()) + ) -> Result { + self.compress(input, output, flush) } fn run_vec( &mut self, input: &[u8], output: &mut Vec, flush: FlushCompress, - ) -> Result { - Ok(self.compress_vec(input, output, flush).unwrap()) + ) -> Result { + self.compress_vec(input, output, flush) } } impl Ops for Decompress { + type Error = DecompressError; type Flush = FlushDecompress; fn total_in(&self) -> u64 { self.total_in() @@ -170,7 +175,9 @@ impl Writer { self.dump()?; let before = self.data.total_out(); - self.data.run_vec(&[], &mut self.buf, D::Flush::finish())?; + self.data + .run_vec(&[], &mut self.buf, Flush::finish()) + .map_err(Into::into)?; if before == self.data.total_out() { return Ok(()); } @@ -254,8 +261,8 @@ impl Write for Writer { fn flush(&mut self) -> io::Result<()> { self.data - .run_vec(&[], &mut self.buf, D::Flush::sync()) - .unwrap(); + .run_vec(&[], &mut self.buf, Flush::sync()) + .map_err(Into::into)?; // Unfortunately miniz doesn't actually tell us when we're done with // pulling out all the data from the internal stream. To remedy this we @@ -266,8 +273,8 @@ impl Write for Writer { self.dump()?; let before = self.data.total_out(); self.data - .run_vec(&[], &mut self.buf, D::Flush::none()) - .unwrap(); + .run_vec(&[], &mut self.buf, Flush::none()) + .map_err(Into::into)?; if before == self.data.total_out() { break; } diff --git a/src/zlib/mod.rs b/src/zlib/mod.rs index 1a293ba0..5a99b0eb 100644 --- a/src/zlib/mod.rs +++ b/src/zlib/mod.rs @@ -7,7 +7,7 @@ mod tests { use std::io; use std::io::prelude::*; - use rand::{thread_rng, Rng}; + use rand::{rng, Rng}; use crate::zlib::{read, write}; use crate::Compression; @@ -18,7 +18,7 @@ mod tests { let mut w = write::ZlibEncoder::new(Vec::new(), Compression::default()); let v = crate::random_bytes().take(1024).collect::>(); for _ in 0..200 { - let to_write = &v[..thread_rng().gen_range(0..v.len())]; + let to_write = &v[..rng().random_range(0..v.len())]; real.extend(to_write.iter().copied()); w.write_all(to_write).unwrap(); } @@ -47,7 +47,7 @@ mod tests { let mut w = write::ZlibEncoder::new(Vec::new(), Compression::default()); let v = crate::random_bytes().take(1024).collect::>(); for _ in 0..200 { - let to_write = &v[..thread_rng().gen_range(0..v.len())]; + let to_write = &v[..rng().random_range(0..v.len())]; real.extend(to_write.iter().copied()); w.write_all(to_write).unwrap(); } diff --git a/src/zlib/write.rs b/src/zlib/write.rs index 1d629b3c..77c1c899 100644 --- a/src/zlib/write.rs +++ b/src/zlib/write.rs @@ -180,7 +180,7 @@ impl Read for ZlibEncoder { /// This structure implements a [`Write`] and will emit a stream of decompressed /// data when fed a stream of compressed data. /// -/// After decoding a single member of the ZLIB data this writer will return the number of bytes up to +/// After decoding a single member of the ZLIB data this writer will return the number of bytes up /// to the end of the ZLIB member and subsequent writes will return Ok(0) allowing the caller to /// handle any data following the ZLIB member. ///