COOLJAPAN
← All posts

OxiArc 0.4.2 Released — Five New Crates, a Resumable DEFLATE Core, and flate2's Exit from HTTP and Images

OxiArc 0.4.2 adds oxiarc-http, oxiarc-png, oxiarc-jpeg, oxiarc-tiff, and oxiarc-image — closing the two routes (ureq's default gzip feature, and the image crate's png/tiff codecs) that pulled flate2 into 39 of 93 ~/work project lockfiles. A rewritten resumable DEFLATE/zlib/gzip core backs all of it. 5,505 tests passing, Pure Rust.

release oxiarc http png jpeg tiff pure-rust compression archiving

An ecosystem-wide dependency audit found flate2 in 39 of 93 ~/work project lockfiles — not because anyone chose it, but because ureq’s default feature and the image crate’s png/tiff codecs quietly pulled it in underneath a Pure Rust workspace.

Today we released OxiArc 0.4.2 — the P2/P3 program: HTTP Content-Encoding decoding and three new image container formats, all Pure Rust, closing both routes for good. Five new crates — oxiarc-http, oxiarc-png, oxiarc-jpeg, oxiarc-tiff, oxiarc-image — bring the workspace from 13 to 18 member crates. No archive or stream wire format changed, and no public read/decode-path API was removed.

No C, no Fortran, no zlib, no libjpeg, no libpng, no libtiff, no external shared libraries. Just clean, memory-safe archiving, compression, and now HTTP content-coding and image decoding, that compiles to a single static binary, targets WASM, and runs everywhere. OxiArc is the Pure Rust replacement for the zip, tar, gzip, zstd, and 7-zip tools — and for the Rust crates zip, flate2, zstd, bzip2, lz4, tar, snap, brotli, miniz_oxide, png, jpeg-decoder/zune-jpeg, tiff, and now image itself.

Why OxiArc 0.4.2 is a game changer

OxiArc 0.4.2 ends all of that:

Technical Deep Dive: one push-decoder shape, four formats

  1. The shared core. InflateStream::inflate(&mut self, input, output, flush) -> InflateProgress is the same “feed bytes, get bytes, ask again” shape as ZstdStream::decode and BrotliStream’s meta-block-resumable loop — one push API serving both the archive/compression side (P2) and the new HTTP/image side (P3).
  2. PNG’s IDAT chain and an HTTP chunked body are the same problem. oxiarc-png and oxiarc-http both consume InflateStream/WrappedInflate directly rather than each growing its own partial decoder — proven by a new tests/cross_crate_inflate.rs that splits a PNG IDAT chain, an HTTP gzip body, and a TIFF Deflate strip across arbitrary byte boundaries and checks all three decode to byte-identical output through the one shared core.
  3. JPEG inside TIFF, without concatenating buffers. oxiarc-jpeg’s TableSet/decode_abbreviated_into surface was built specifically so oxiarc-tiff’s JPEGTables (compression 7) support could reuse the real decoder rather than reassembling a synthetic JPEG stream.
  4. Negotiation is RFC text, not paraphrase. oxiarc-http::negotiate honours * and q=0 per RFC 9110 §8.4.1.2, and the spec’s own example tables are runnable tests, not restated as prose.

Getting Started

cargo add oxiarc-http oxiarc-png
use oxiarc_http::{AcceptEncoding, ContentCoding, EncodeOptions, encode_body, negotiate};

fn main() {
    // Client side: advertise every coding this build can decode.
    let accept = AcceptEncoding::all_supported();
    let header_value = accept.to_header_value(); // None => send no header at all

    // Server side: negotiate against what it received, and only what this
    // build can actually produce.
    let available: Vec<ContentCoding> = [
        ContentCoding::Zstd,
        ContentCoding::Brotli,
        ContentCoding::Gzip,
        ContentCoding::Deflate,
    ]
    .into_iter()
    .filter(ContentCoding::is_encodable)
    .collect();
    let chosen = negotiate(header_value.as_deref(), &available)
        .expect("identity is always acceptable here, so this never fails");

    let body = b"hello, world! hello, world! hello, world!";
    if let Some(coding) = chosen {
        let compressed = encode_body(&coding, body, EncodeOptions::default())
            .expect("`available` only ever contains codings this build can encode");
        assert!(compressed.len() < body.len());
    }
}
use std::fs::File;

fn main() -> Result<(), oxiarc_png::DecodingError> {
    let decoder = oxiarc_png::Decoder::new(File::open("image.png")?);
    let mut reader = decoder.read_info()?;
    while let Some(row) = reader.next_row()? {
        let _pixels: &[u8] = row.data();
    }
    Ok(())
}

What’s New in 0.4.2

Tips

This is the foundation

HTTP content-coding and image decoding are exactly the kind of “boring, everywhere” I/O that either quietly stays Pure Rust or quietly doesn’t. NumRS2, SciRS2, ToRSh, OxiGeo, OxiMedia, VoiRS, TrustFormers, and SkLearS all pin oxiarc-* crates for archive, compression, and now HTTP/image I/O.

Repository: https://github.com/cool-japan/oxiarc

Star the repo if you’d rather your dependency tree said “Pure Rust” than “Pure Rust, except for this one gzip feature flag.”

The era of shipping a C decoder because the convenient feature flag defaulted to it is over. Pure Rust archiving, compression, HTTP, and images — fast, safe, and sovereign — is here.

KitaSan at COOLJAPAN OÜ September 12, 2026

↑ Back to all posts