Bürli
Pure Rust Brotli codec. The decoder reads standard Brotli streams produced at all normal quality levels. The encoder currently supports qualities 0 through 5.
Why Bürli
Fast q0..q5 encoder. Covers the transfer-oriented part of Brotli's speed/ratio curve. Input that samples as incompressible is stored, or searched with skip acceleration instead of matched in full.
Fast on small inputs. Encodes and decodes small inputs, from 512 B, faster than rust-brotli, mbrotli, and Google's C library. See the small-input charts below.
Memory safety. No unsafe. Every Bürli crate is
#![forbid(unsafe_code)].
Small API. One-shot helpers for simple use, caller-buffer variants for tight
loops, reusable contexts for repeated work, and std::io streaming wrappers
when needed.
JavaScript and TypeScript. The Brotli codec is available as
@paddor/burli, compiled to WebAssembly.
Performance
API
// One-shot (allocating)
let compressed = burli::compress(input, 5)?;
let original = burli::decompress(&compressed)?;
// One-shot into caller buffer
let n = burli::compress_into(input, &mut output_buf, 5)?;
burli::decompress_into(&compressed, &mut output_vec)?;
// Reusable context
let mut compressor = burli::Compressor::new(5)?;
let compressed = compressor.compress(input)?;
let mut decompressor = burli::Decompressor::new();
let original = decompressor.decompress(&compressed)?;
// Validate without retaining decoded output
burli::validate(&compressed)?;Streaming
use std::io::{Read, Write};
let mut enc = burli::StreamEncoder::new(Vec::new(), 5)?;
enc.write_all(input)?;
let compressed = enc.finish()?;
let mut dec = burli::StreamDecoder::new(&compressed[..]);
let mut decoded = Vec::new();
dec.read_to_end(&mut decoded)?;Raw Dictionaries
Raw LZ77 prefix dictionaries are decode-only for now:
let dictionary = burli::decode::RawDictionary::new(bytes);
let original = burli::decompress_with_raw_dictionary(&compressed, &dictionary)?;Safety
SAFETY.md documents the no-unsafe rule and Brotli bug classes
that Bürli is designed to prevent.
Bounded decompression is first-class: use decompress_with_options,
Decompressor::with_options, StreamDecoder::with_options, or
decompress_into_slice for untrusted input. Plain decompress() has no
practical output cap.
Current safety checks include Kani coverage for low-level primitives and more
than 6 hours of libFuzzer coverage across decode, encode/decode round trips,
corruption cases, streaming, and C Brotli cross-checks.
Design
DESIGN.md covers the implemented encode/decode pipeline, bit I/O, Huffman tables, backward copies, dictionaries, streaming, and quality policy.
DEVELOPMENT.md is the contributor workflow reference for builds, tests, releases, fuzzing, benchmarks, and chart regeneration.
Levels
Bürli's encoder covers the fast end of Brotli. q0 favors throughput: it matches in one pass and codes low-compressibility blocks with literals only, or stores them. q1 through q5 spend progressively more work on matching and entropy coding for better ratios.
The current encoder stops at q5. Brotli q6 through q11 spend much more CPU for ratios that usually matter more for archival storage than transfer pipelines. All implemented qualities produce standard Brotli streams decoded by the same decoder.