Project

burli

0.0
The project is in a healthy, maintained state
Memory-safe Brotli compression in Rust, with the API of the brotli gem
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Runtime

~> 0.9
 Project Readme

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

Brotli pipeline benchmark

x86_64 details

per-file encode/decode matrix per-file pipeline encode speed vs compression ratio Silesia encode speed vs compression ratio small input encode throughput small input decode throughput

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.