flate2 Rust Library

repository·main·Indexed 22 days ago

https://github.com/rust-lang/flate2-rs

A streaming compression and decompression library for DEFLATE-based streams in Rust, supporting deflate, zlib, and gzip formats. It provides a choice between a pure-Rust backend (miniz_oxide) or high-performance C-based backends such as zlib-ng, zlib-rs, and zlib via Cargo features. The library includes encoders and decoders for various formats, including MultiGzDecoder for concatenated gzip files.

Tokens
6.5K
Snippets
17
Records
31
Agent score
77%

What's inside flate2

  1. Create a new release for flate2

    main

    To release a new version of the flate2 crate, you must update the version in Cargo.toml, publish to crates.io, and tag the commit in Git. Note that changes cannot be made directly to main due to branch protection; all changes must go through a Pull Request.

    # 1. Create a release branch
    git checkout -b release-<next-version>
    
    # 2. Update Cargo.toml with the new version
    # (Manual step: edit Cargo.toml to set version = "<next-version>")
    
    # 3. Create, approve, and merge a PR
    gh pr create
    
    # 4. Sync with main
    git checkout main
    git pull
    
    # 5. Publish to crates.io
    cargo publish
    
    # 6. Tag the release
    git tag <next-version>
    
    # 7. Push tags to GitHub
    git push --tags
    
    # 8. Generate GitHub release notes via the GitHub UI
  2. How GzEncoder and GzDecoder handle stream completion

    main

    Both GzEncoder and GzDecoder provide two ways to finalize the stream:

    1. try_finish(&mut self) -> io::Result<()>: Attempts to write out final chunks (like the gzip footer or CRC). This is useful if you want to keep ownership of the encoder/decoder. Note that after calling this, further calls to write may result in a panic.
    2. finish(self) -> io::Result<W>: Consumes the encoder/decoder and returns the underlying writer. This is the preferred way to re-acquire ownership of the inner stream.

    Note for Async users: If you are using asynchronous I/O, finish might not be suitable; use try_finish or a shutdown method instead.

  3. Handle multi-member Gzip files with MultiGzDecoder

    main

    Standard GzDecoder (in read, write, or bufread modules) only decodes the first member of a gzip file. If a file contains multiple gzip members, GzDecoder may return partial results.

    To decode all members of a gzip file into one continuous stream of bytes, use MultiGzDecoder. Note that MultiGzDecoder will return an error if any non-gzip data is encountered after the gzip members, matching the behavior of command-line tools like gunzip or zcat.

  4. How to choose a compression backend via feature flags

    main

    The flate2 crate supports multiple backends for compression and decompression. Since Cargo features are additive, if multiple backends are selected, they are activated in the following priority order:

    1. zlib-ng
    2. zlib-rs (typically the fastest, but uses unsafe Rust)
    3. miniz_oxide (the default or rust_backend feature; uses only safe Rust and does not require a C compiler)

    If you need bit-identical results to a specific C implementation, check the crate's README for available C backends.

  5. Configure compression backends

    main

    By default, flate2 uses miniz_oxide, a pure-Rust implementation. You can switch to other backends via Cargo features to optimize for performance or compatibility. When using non-default backends, it is recommended to set default-features = false to avoid pulling in the default miniz_oxide backend.

    Available Backends

    FeatureBackendNotes
    zlib-rszlib-rsHigh performance, uses some unsafe code. Often the fastest overall.
    zlib-ngzlib-ngC-based high-performance library. Can be faster than zlib-rs in specific cases.
    zlibzlibUses the standard zlib library. Useful if you already have zlib in your dependency graph.
    zlib-ng-compatzlib-ng (compat mode)Uses zlib-ng via libz-sys in compatibility mode.

    Warning on zlib-ng-compat: If any other crate in your dependency graph explicitly requests stock zlib or uses libz-sys without default-features = false, you will end up with stock zlib instead of zlib-ng.

    # Example: Using the high-performance zlib-rs backend
    [dependencies]
    flate2 = { version = "1.0.17", features = ["zlib-rs"], default-features = false }
    
    # Example: Using the zlib-ng C library
    [dependencies]
    flate2 = { version = "1.0.17", features = ["zlib-ng"], default-features = false }
    
    # Example: Using zlib for compatibility with existing C/Rust zlib dependencies
    [dependencies]
    flate2 = { version = "1.0.17", features = ["zlib"], default-features = false }
    
    # Example: Using zlib-ng in compatibility mode
    [dependencies]
    flate2 = { version = "1.0.17", features = ["zlib-ng-compat"], default-features = false }
  6. Organize compression/decompression by I/O type

    main

    The crate is organized into three main modules based on the type of I/O you are performing. Choose the module that matches your input/output type:

    • mod bufread: Use these if you can provide a std::io::BufRead type (e.g., a &[u8] slice or a std::io::BufReader).
    • mod read: Use these to wrap a std::io::Read type. Warning: These implementations may read past the end of the compressed data, making the underlying Read type unusable for subsequent reads. If you need to reuse the source, wrap it in a std::io::BufReader and use the bufread module instead.
    • mod write: Use these when working with std::io::Write types, which is particularly useful when dealing with async streams/iterators where a BufRead cannot be easily created.
  7. Decompress data using GzDecoder

    main

    To decompress data, use a decoder like GzDecoder. The decoder wraps a reader containing the compressed bytes. You can then use standard std::io methods like read_to_string to extract the original content.

    use std::io::prelude::*;
    use flate2::read::GzDecoder;
    
    fn main() {
        let mut d = GzDecoder::new("...".as_bytes());
        let mut s = String::new();
        d.read_to_string(&mut s).unwrap();
        println!("{}", s);
    }
  8. Compress data using ZlibEncoder

    main

    You can compress data by using an encoder like ZlibEncoder. The encoder wraps an underlying writer (such as a Vec<u8>) and applies compression based on a Compression level. Use .finish() to consume the encoder and retrieve the compressed data.

    use std::io::prelude::*;
    use flate2::Compression;
    use flate2::write::ZlibEncoder;
    
    fn main() {
        let mut e = ZlibEncoder::new(Vec::new(), Compression::default());
        e.write_all(b"foo").unwrap();
        e.write_all(b"bar").unwrap();
        let compressed_bytes = e.finish().unwrap();
    }
  9. Decompress a Gzip stream from an async iterator

    main

    When working with async streams, use the write module to decompress data by writing chunks from the stream into a GzDecoder.

    use futures::{Stream, StreamExt};
    use std::io::{Result, Write as _};
    
    async fn decompress_gzip_stream<S, I>(stream: S) -> Result<Vec<u8>>
    where
        S: Stream<Item = I>,
        I: AsRef<[u8]>
    {
        let mut stream = std::pin::pin!(stream);
        let mut w = Vec::<u8>::new();
        let mut decoder = flate2::write::GzDecoder::new(w);
        while let Some(input) = stream.next().await {
            decoder.write_all(input.as_ref())?;
        }
        decoder.finish()
    }
  10. Reset a GzDecoder with a new stream

    main

    You can reuse a GzDecoder instance by calling .reset(r), which replaces the current input stream with a new one r. This clears the internal state and discards any currently buffered data. This is useful for performance when processing multiple gzip streams sequentially.

    // Example of resetting a decoder
    let mut decoder = GzDecoder::new(compressed_data_1);
    // ... read data ...
    
    // Reset with a new stream
    decoder.reset(compressed_data_2);
    // ... read new data ...
  11. Manage DeflateEncoder state with reset()

    main

    If you need to reuse a DeflateEncoder with a different output stream, use the reset(w: W) method.

    This method will first finish encoding the current stream into the existing output stream. Once finished, it resets the internal state and replaces the current output stream with the provided stream w, returning the old stream. This is useful for avoiding re-allocations or managing multiple output targets.

    // Example concept for reset
    // e.reset(new_writer)? returns the old_writer