brotli_fast

pub package Null Safety Dart CI codecov GitHub Tag Last Commits Code size License

A fast, pure Dart Brotli encoder and decoder with no native dependencies and no package dependencies. It runs everywhere Dart runs (VM, AOT, Flutter, and the web through dart2js and dart2wasm) and produces the same bytes on every platform.

import 'package:brotli_fast/brotli_fast.dart';

final packed = brotliEncode(bytes);              // quality 5
final smallest = brotliEncode(bytes, quality: 11);
final restored = brotliDecode(packed);

Features

  • All of Brotli: qualities 0–11 and window sizes 10–24 bits on the encoder, and the full RFC 7932 format on the decoder, including the static dictionary and every transform.
  • Fast: a dedicated fast tier for qualities 0–5 and a tuned high-ratio encoder for 6–11; a decoder with a fused hot loop and a 64-bit bit reader on the VM. See Performance.
  • One-shot, dart:convert and streams: brotliEncode/brotliDecode, the brotli codec, and stream transformers that emit output as input arrives.
  • Dictionaries: raw (compound) dictionaries for both directions, which help small, similar payloads, and serialized shared dictionaries on decode.
  • Large-window format: windows up to 1 GiB (non-standard, opt-in on decode).
  • Safe decoding: malformed or truncated input throws a typed BrotliException, and a size limit guards against decompression bombs.
  • Checked against the reference: CI decodes our streams with Google's brotli CLI, and theirs with ours, at every quality.

Usage

One-shot

import 'package:brotli_fast/brotli_fast.dart';

final packed = brotliEncode(data, quality: 9);
final restored = brotliDecode(packed);

quality trades speed for size: 0–5 favor speed (5, the default, is a good balance), 6–11 favor ratio and get progressively slower. windowBits (default 22, a 4 MiB window) can be lowered to save memory on the decoding side.

Strings

import 'dart:convert';

final text = utf8.fuse(brotli);
final packed = text.encode('héllo, wörld');
final restored = text.decode(packed);

Streams

brotli.encoder and brotli.decoder are stream transformers:

import 'dart:io';

await File('data.json')
    .openRead()
    .transform(brotli.encoder)
    .pipe(File('data.json.br').openWrite());

await File('data.json.br')
    .openRead()
    .transform(brotli.decoder)
    .pipe(File('data.json').openWrite());

Use BrotliCodec(quality: 9) or BrotliEncoder(quality: 9) for other settings. The streaming encoder emits a compressed block for every 128 KiB of input. It keeps the input it has seen in memory, so it bounds latency, not memory.

Dictionaries

When many small payloads share structure (JSON with a common schema, HTML templates), a dictionary known to both sides shrinks them considerably:

final dictionary = BrotliDictionary(utf8.encode(sampleRecords));

final packed = brotliEncode(record, quality: 9, dictionary: dictionary);
final restored = brotliDecode(packed, dictionary: dictionary);

Reuse one BrotliDictionary instance: its match index is built on the first encode. The decoder must be given the same bytes. Brotli streams carry no dictionary checksum, so a different dictionary decodes to different data.

Decoding untrusted input

try {
  final restored = brotliDecode(input, maxOutputSize: 10 << 20); // 10 MiB
} on BrotliException catch (e) {
  print('rejected: ${e.code.name}'); // e.g. truncatedInput, outputLimitExceeded
}

brotliDecode limits output to 1 GiB by default. BrotliException is a FormatException.

Performance

quality text encode text decode binary encode binary decode
0 1518 2915 512 1145
1 1185 3349 520 1146
5 (default) 855 3328 357 1306
9 92 2815 26 1329

Throughput in MB/s on the deterministic 1 MiB text and binary corpus of benchmark/, measured AOT on an Apple M5 (median of 5 runs). Numbers are machine-specific; run dart run benchmark/perf_history.dart to measure on your own hardware. benchmark/HISTORY.md tracks them over time.

Compatibility

  • Streams: standard RFC 7932, readable by any Brotli decoder (browsers, the brotli CLI, HTTP servers and proxies). Large-window streams (windowBits above 24) need a decoder that opts in.
  • Platforms: Dart SDK 3.13+; VM, AOT, Flutter, dart2js and dart2wasm. Web builds produce byte-identical output to the VM.

API Documentation

See the API documentation.

Source

The official source code is hosted @ GitHub.

Features and bugs

Please file feature requests and bugs at the issue tracker.

Origin

brotli_fast is inspired by brotli_neo (repository dart_brotli, MIT license): a full rewrite as a pure Dart Brotli package, with:

  • One optimized implementation: up to 1.7× faster encoding at the default quality and 2.2× at quality 9, with output byte-identical to brotli_neo's.
  • A new, simpler API: brotliEncode/brotliDecode, the brotli dart:convert codec with stream support, and BrotliDictionary.
  • Correctness fixes: valid streams that failed or decoded wrongly now decode correctly, and every quality is cross-checked against Google's reference brotli tool in CI.
  • No dependencies.

License

Apache License 2.0. Includes the RFC 7932 static dictionary and tables from google/brotli (MIT); see NOTICE.

Libraries

brotli_fast
Fast, pure Dart Brotli (RFC 7932) encoder and decoder.