Brotli
Brotli is a modern compression algorithm developed by Google, optimized for web content. It achieves the best compression ratios among compressionz codecs.
At a Glance
Section titled “At a Glance”| Property | Value |
|---|---|
| Developer | |
| First Release | 2015 |
| Implementation | Vendored libbrotli |
| License | MIT |
Performance
Section titled “Performance”| Level | Compress | Decompress | Ratio |
|---|---|---|---|
| fast | 1.3 GB/s | 1.9 GB/s | 99.9% |
| default | 1.3 GB/s | 1.8 GB/s | 99.9% |
| best | 86 MB/s | 1.5 GB/s | 99.9%+ |
Features
Section titled “Features”- ✅ Excellent compression ratios
- ✅ Streaming support
- ✅ Supported by modern browsers
- ✅ Multiple compression levels
- ❌ No magic bytes (can’t auto-detect)
- ❌ No dictionary support (in this API)
- ❌ No built-in checksum
Basic Usage
Section titled “Basic Usage”const cz = @import("compressionz");
// Compressconst compressed = try cz.compress(.brotli, data, allocator);defer allocator.free(compressed);
// Decompressconst decompressed = try cz.decompress(.brotli, compressed, allocator);defer allocator.free(decompressed);Compression Levels
Section titled “Compression Levels”Brotli’s levels have a significant impact on both speed and ratio:
// Fast - suitable for dynamic contentconst fast = try cz.compressWithOptions(.brotli, data, allocator, .{ .level = .fast,});
// Default - balancedconst default = try cz.compressWithOptions(.brotli, data, allocator, .{ .level = .default,});
// Best - maximum compression for static assetsconst best = try cz.compressWithOptions(.brotli, data, allocator, .{ .level = .best,});Level Comparison
Section titled “Level Comparison”| Level | Compress | Decompress | Ratio | Use Case |
|---|---|---|---|---|
fast | 1.3 GB/s | 1.9 GB/s | 99.9% | Dynamic content |
default | 1.3 GB/s | 1.8 GB/s | 99.9% | General use |
best | 86 MB/s | 1.5 GB/s | 99.9%+ | Static assets |
Key insight: best level is ~15× slower than fast, but produces the smallest files. Use best only for one-time compression (static files).
Streaming
Section titled “Streaming”Brotli supports streaming for large files:
Streaming Compression
Section titled “Streaming Compression”const cz = @import("compressionz");const std = @import("std");
pub fn compressToBrotli(allocator: std.mem.Allocator, input_path: []const u8, output_path: []const u8) !void { const input = try std.fs.cwd().openFile(input_path, .{}); defer input.close();
const output = try std.fs.cwd().createFile(output_path, .{}); defer output.close();
var comp = try cz.compressor(.brotli, allocator, output.writer(), .{ .level = .best, // Worth the time for static files }); defer comp.deinit();
var buf: [65536]u8 = undefined; while (true) { const n = try input.read(&buf); if (n == 0) break; try comp.writer().writeAll(buf[0..n]); }
try comp.finish();}Streaming Decompression
Section titled “Streaming Decompression”pub fn decompressBrotli(allocator: std.mem.Allocator, path: []const u8) ![]u8 { const file = try std.fs.cwd().openFile(path, .{}); defer file.close();
var decomp = try cz.decompressor(.brotli, allocator, file.reader()); defer decomp.deinit();
return decomp.reader().readAllAlloc(allocator, 1024 * 1024 * 1024);}Direct API Access
Section titled “Direct API Access”const cz = @import("compressionz");
// Compress with levelconst compressed = try cz.brotli.compress(data, allocator, .best);defer allocator.free(compressed);
// Decompressconst decompressed = try cz.brotli.decompress(compressed, allocator);defer allocator.free(decompressed);
// With size limitconst limited = try cz.brotli.decompressWithLimit(compressed, allocator, max_size);Why Brotli for Web?
Section titled “Why Brotli for Web?”Brotli was specifically designed for web content:
1. Built-in Static Dictionary
Section titled “1. Built-in Static Dictionary”Brotli includes a 120 KB static dictionary with common web content:
- HTML tags (
<div>,<span>,<script>) - CSS properties (
background-color,font-family) - JavaScript keywords (
function,return,undefined) - Common words and phrases
This dramatically improves compression for typical web content.
2. Browser Support
Section titled “2. Browser Support”| Browser | Support |
|---|---|
| Chrome | ✅ Since v49 |
| Firefox | ✅ Since v44 |
| Safari | ✅ Since v11 |
| Edge | ✅ Since v15 |
| IE | ❌ |
~95% of browsers support Brotli.
3. HTTP Integration
Section titled “3. HTTP Integration”Accept-Encoding: gzip, deflate, brContent-Encoding: brWeb Asset Compression
Section titled “Web Asset Compression”Ideal workflow for static web assets:
const std = @import("std");const cz = @import("compressionz");
pub fn precompressAssets(allocator: std.mem.Allocator, assets_dir: []const u8) !void { var dir = try std.fs.cwd().openDir(assets_dir, .{ .iterate = true }); defer dir.close();
var iter = dir.iterate(); while (try iter.next()) |entry| { if (entry.kind != .file) continue;
// Skip already compressed if (std.mem.endsWith(u8, entry.name, ".br")) continue; if (std.mem.endsWith(u8, entry.name, ".gz")) continue;
const ext = std.fs.path.extension(entry.name); const should_compress = std.mem.eql(u8, ext, ".html") or std.mem.eql(u8, ext, ".css") or std.mem.eql(u8, ext, ".js") or std.mem.eql(u8, ext, ".json") or std.mem.eql(u8, ext, ".svg");
if (should_compress) { const input_path = try std.fs.path.join(allocator, &.{ assets_dir, entry.name }); defer allocator.free(input_path);
const data = try std.fs.cwd().readFileAlloc(allocator, input_path, 10 * 1024 * 1024); defer allocator.free(data);
// Compress with best level (one-time cost) const compressed = try cz.compressWithOptions(.brotli, data, allocator, .{ .level = .best, }); defer allocator.free(compressed);
const output_path = try std.fmt.allocPrint(allocator, "{s}.br", .{input_path}); defer allocator.free(output_path);
try std.fs.cwd().writeFile(output_path, compressed);
std.debug.print("{s}: {d} -> {d} bytes ({d:.1}%)\n", .{ entry.name, data.len, compressed.len, @as(f64, @floatFromInt(compressed.len)) / @as(f64, @floatFromInt(data.len)) * 100, }); } }}Format Details
Section titled “Format Details”No Magic Bytes
Section titled “No Magic Bytes”Unlike most formats, Brotli has no magic number:
// Cannot auto-detect Brotliconst codec = cz.Codec.detect(data); // Returns null for Brotli
// Must know it's Brotli beforehandconst decompressed = try cz.decompress(.brotli, data, allocator);Workaround: Use file extensions (.br) or content metadata to identify Brotli data.
Stream Format
Section titled “Stream Format”Brotli uses a meta-block structure:
┌─────────────────────────────────────────────────────┐│ Meta-block header ││ - ISLAST flag ││ - MLEN (meta-block length) ││ - Compressed/uncompressed flag │├─────────────────────────────────────────────────────┤│ Compressed data (if compressed) ││ - Literal insert-and-copy commands ││ - Distance codes ││ - Context modeling │├─────────────────────────────────────────────────────┤│ ... more meta-blocks ... │├─────────────────────────────────────────────────────┤│ Final meta-block (ISLAST=1) │└─────────────────────────────────────────────────────┘Algorithm Details
Section titled “Algorithm Details”Brotli combines multiple techniques:
- LZ77 matching — Find repeated sequences
- 2nd-order context modeling — Predict based on previous bytes
- Static dictionary — 120 KB of common web strings
- Asymmetric numeral systems (ANS) — Efficient entropy coding
Why Brotli Compresses Better
Section titled “Why Brotli Compresses Better”- Larger window size (up to 16 MB vs Gzip’s 32 KB)
- Better context modeling
- Optimized for text/web content
- Built-in dictionary for common patterns
Brotli vs Gzip
Section titled “Brotli vs Gzip”| Metric | Brotli (best) | Gzip (best) |
|---|---|---|
| Compress | 86 MB/s | 691 MB/s |
| Decompress | 1.5 GB/s | 2.9 GB/s |
| Ratio | 99.9%+ | 99.6% |
| Browser | ~95% | 100% |
| Web-optimized | Yes | No |
Typical Size Savings (Web Assets)
Section titled “Typical Size Savings (Web Assets)”| Content Type | Gzip | Brotli | Savings |
|---|---|---|---|
| HTML | 20% | 17% | 15% smaller |
| CSS | 15% | 12% | 20% smaller |
| JavaScript | 22% | 18% | 18% smaller |
| JSON | 10% | 8% | 20% smaller |
When to Use Brotli
Section titled “When to Use Brotli”Best for:
- Static web assets (CSS, JS, HTML)
- CDN content
- Pre-compressed files
- When bandwidth > CPU cost
Not ideal for:
- Dynamic content (use Gzip)
- Real-time compression (use LZ4/Snappy)
- General purpose (use Zstd)
- Legacy browser support (use Gzip)
Memory Considerations
Section titled “Memory Considerations”Brotli uses more memory than simpler algorithms:
| Operation | Memory |
|---|---|
| Compression | ~1 MB |
| Decompression | ~256 KB |
Consider this for memory-constrained environments.