noflate
A zero-dependency DEFLATE (RFC 1951), gzip (RFC 1952), and zlib (RFC 1950) encoder and decoder.
no_std(requires onlyalloc)- No
unsafecode (#![forbid(unsafe_code)]) - Sans-io: the library performs no I/O itself — callers feed bytes in and consume bytes out, making it usable with any I/O strategy
- WebSocket permessage-deflate (RFC 7692) support
Examples
One-shot DEFLATE
let input = b"Hello, DEFLATE!";
let compressed = compress?;
let decompressed = decompress?;
assert_eq!;
Streaming encoder
let mut encoder = new;
encoder.feed?;
encoder.feed?;
encoder.finish?;
let compressed = encoder.output.to_vec;
encoder.advance;
assert_eq!;
Streaming decoder
let compressed = compress?;
let mut decoder = new;
decoder.feed?;
let out = decoder.output.to_vec;
decoder.advance;
assert!;
assert_eq!;
gzip / zlib
let gz = compress?;
assert_eq!;
let zl = compress?;
assert_eq!;
std::io::{Read, Write} interop
noflate performs no I/O itself, but the sans-io API plugs into
std::io::Write and std::io::Read with a small adapter — Write::write
forwards to feed and drains output into the inner sink, and
Read::read pulls from output and tops up the decoder via feed from
the inner source. See examples/io_bridge.rs
for a runnable DeflateWriter / DeflateReader pair; the same pattern
works verbatim for gzip and zlib.
WebSocket permessage-deflate (RFC 7692)
Supported via Encoder::sync_flush
and Encoder::reset_history.
See their docs for the sender/receiver pattern.
Benchmarks
The repository ships three benchmark binaries:
BENCH_REPEATS=30
BENCH_REPEATS=30
BENCH_REPEATS=30
The numbers below are rough indicators only — throughput fluctuates substantially with hardware, runner load, workload size, and specific input. Depending on the environment, noflate can be faster or slower than flate2 on the same operation. Re-run the Benchmark workflow (Actions → Benchmark → Run workflow) or run the examples locally before making performance-sensitive decisions.
- Commit:
7ef5eec - Source: GitHub Actions, standard runners
ubuntu-latest: AMD EPYC 7763 (Milan, Zen 3) or 9V74 (Genoa, Zen 4); rarely Intel Xeon Platinum 8370C (Ice Lake) — 4 vCPU x86_64 Azure VMs; the runner pool mixes SKUsmacos-latest: Apple M1 Virtual (3 vCPU, arm64)
- Toolchain:
rustc 1.95.0,--release - Methodology:
BENCH_REPEATS=30per run (best-of reported); aggregated across 40 workflow runs by median within each CPU SKU (23× EPYC 7763, 15× EPYC 9V74, 40× M1; 2× Intel Xeon runs omitted — too few samples). Median rather than best-of across runs because runner load varies and best-of would cherry-pick lucky-fast instances. Encode throughput is of the raw input; decode throughput is of the decompressed output.
DEFLATE, 1 MiB English text (MB/s):
| platform | noflate enc | flate2 enc | noflate dec | flate2 dec |
|---|---|---|---|---|
| ubuntu — EPYC 7763 (Zen 3) | 427 | 363 | 6652 | 3557 |
| ubuntu — EPYC 9V74 (Zen 4) | 390 | 498 | 7080 | 3485 |
| macos — M1 (Virtual) | 600 | 994 | 7395 | 2977 |
Encode compression ratio (compressed / original — deterministic, identical across runners):
| input | noflate | flate2 |
|---|---|---|
| english 1 KiB | 0.1494 | 0.1504 |
| english 64 KiB | 0.0064 | 0.0065 |
| english 1 MiB | 0.0040 | 0.0040 |
| zeros 64 KiB | 0.0012 | 0.0012 |
| random 64 KiB | 1.0011 | 1.0002 |
Noflate's ratio is within ~0.1 % of flate2 across the board — slightly better on short text (more thorough length-limited Huffman), slightly worse on ultra-short stored payloads (e.g. 64 KiB of zeros: 79 bytes vs 78 bytes) and on incompressible input.
Checksums of 1 MiB (MB/s):
| platform | noflate CRC-32 | crc32fast | noflate Adler-32 | adler32 crate |
|---|---|---|---|---|
| ubuntu — EPYC 7763 (Zen 3) | 2256 | 12172 | 3037 | 3007 |
| ubuntu — EPYC 9V74 (Zen 4) | 2006 | 10800 | 2804 | 2705 |
| macos — M1 (Virtual) | 3045 | 7968 | 2763 | 2661 |
Notes on these numbers:
- The DEFLATE decoder is consistently faster than
flate2on text — about 1.9× on EPYC 7763, 2.0× on EPYC 9V74, and 2.5× on macOS for the 1 MiB case. Random data also favours noflate. See the raw workflow logs for the full matrix. - The DEFLATE encoder picture is CPU-dependent: on EPYC 7763 noflate is ~1.2× faster than
flate2on 1 MiB English text, but on the newer EPYC 9V74 (~0.8×) and on macOS M1 (~0.6×) it's slower —flate2's 1 MiB encode benefits more from newer ISAs than noflate does. Per-call setup cost dominates near 1 KiB inputs (3–5× slower thanflate2across all SKUs). - Adler-32 matches the
adler32crate on every runner (within ~5 %). - CRC-32 is ~5× slower than
crc32fast's PCLMULQDQ path on x86_64 and ~2.6× slower on macOS M1 — the price of staying portable, safe (#![forbid(unsafe_code)]), and free of CPU-specific intrinsics.