This package provides a blazing fast TypeScript implementation of the Brotli decompression algorithm, suitable for use in any ES2015+ environment. It offers a performant, portable, and reliable alternative to existing solutions, with support for custom dictionaries and a small footprint.
Benchmarks show it to be nearly as fast as WebAssembly-based decoders, and significantly faster than JavaScript-only alternatives.
deno add jsr:@nick/brocha
npx jsr add @nick/brocha
bunx jsr add @nick/brocha
pnpm dlx jsr add @nick/brocha
yarn add @nick/brocha
Mirrored on NPM as brocha
:
npm i brocha
import { decompress } from "@nick/brocha";
const response = await fetch("file:///compressed.br");
const compressedData = new Uint8Array(await response.arrayBuffer());
const decompressedData = decompress(compressedData);
console.log(
`Decompressed ${compressedData.length}B -> ${decompressedData.length}B`,
);
Tip
The decompress
function is ready to use immediately upon import. Simply pass
in your Brotli-compressed data and receive the decompressed Uint8Array
.
Decompresses Brotli-encoded data into a Uint8Array
.
decompress(input: BufferSource, options?: BrotliDecodeOptions): Uint8Array;
input
: The Brotli-compressedBufferSource
to be decoded.options
: Optional decompression options, allowing you to use a custom dictionary.
Uint8Array
: The decompressed data.
Decompressing a Brotli-compressed file:
import { decompress } from "@nick/brocha";
import * as fs from "node:fs";
const compressed = fs.readFileSync("data.br");
const decompressed = decompress(compressed);
const inputKB = (compressed.byteLength / 1024).toFixed(0);
const outputKB = (decompressed.byteLength / 1024).toFixed(0);
console.log(
`${inputKB}K → ${outputKB}K (+${(outputKB / inputKB).toFixed(2)}x)`,
);
// Example log: "115K → 483K (+4.20x)"
Using a custom dictionary:
import { decompress } from "@nick/brocha";
const compressedData = /* Brotli-compressed data */;
const dictionary = new Uint8Array([/* custom dictionary bytes */]);
const options = { customDictionary: dictionary };
const decompressedData = decompress(compressedData, options);
console.log(decompressedData);
Options to customize the behavior of the Brotli decompression process.
interface BrotliDecodeOptions {
customDictionary?: BufferSource | null;
}
Custom dictionary to use for the Brotli decompression.
null
(uses the default dictionary)
The dictionary must be a valid Brotli dictionary that exactly matches the one used when the input data was compressed. Otherwise, the decompressor will either throw an exception or corrupted data will be returned.
This package is designed to be lightweight and fast, with a focus on performance and efficiency. The following benchmarks were run on a 2021 MacBook Pro with an M1 Pro chip using Deno 2.1.2.
The results demonstrate the performance of this package compared to other popular Brotli decompression tools, suggesting it is a viable alternative to existing WebAssembly-based solutions.
- Performance is mostly on par with WebAssembly decoders like brotli-wasm, which typically show small speed advantages of ~15-20% over this package.
- Compared to other pure-JS implementations (specifically npm:brotli),
brocha
consistently clocks speeds ~1.75x faster across all benchmarks. - Performance is about 35-50% that of the native Node.js
node:zlib
module.
Note
The native node:zlib
module is written in C++ and is highly optimized for
performance. While "~2.5x slower" sounds like a poor result, it's actually
quite fast for a pure JavaScript implementation, which will never be able to
match the performance of a native module.
> deno bench -A --no-check
benchmark time/iter (avg) iter/s (min … max) p75 p99 p995
--------------------- ----------------------------- --------------------- --------------------------
group basic json (6.5 KB -> 27.5 KB)
jsr:@nick/brocha 399.0 µs 2,506 (324.8 µs … 3.1 ms) 381.8 µs 1.6 ms 2.1 ms
npm:brotli 666.1 µs 1,501 (553.7 µs … 3.0 ms) 626.2 µs 1.9 ms 2.3 ms
npm:brotli-wasm 322.5 µs 3,101 (265.4 µs … 7.0 ms) 276.6 µs 1.7 ms 2.5 ms
npm:brotli-dec-wasm 306.7 µs 3,261 (279.2 µs … 4.7 ms) 288.3 µs 915.1 µs 1.6 ms
node:zlib 144.5 µs 6,921 (136.5 µs … 1.8 ms) 141.8 µs 194.1 µs 245.7 µs
summary
jsr:@nick/brocha
2.76x slower than node:zlib
1.30x slower than npm:brotli-dec-wasm
1.24x slower than npm:brotli-wasm
1.67x faster than npm:brotli
group dprint-plugin-graphql.wasm (147 KB -> 768 KB)
jsr:@nick/brocha 6.5 ms 154.0 ( 6.0 ms … 7.7 ms) 6.7 ms 7.7 ms 7.7 ms
npm:brotli 12.2 ms 82.1 ( 10.6 ms … 57.6 ms) 11.3 ms 57.6 ms 57.6 ms
npm:brotli-wasm 6.5 ms 153.3 ( 5.9 ms … 31.6 ms) 6.1 ms 31.6 ms 31.6 ms
npm:brotli-dec-wasm 6.3 ms 158.8 ( 6.2 ms … 7.1 ms) 6.3 ms 7.1 ms 7.1 ms
node:zlib 3.4 ms 292.2 ( 3.2 ms … 4.6 ms) 3.5 ms 4.2 ms 4.6 ms
summary
jsr:@nick/brocha
1.90x slower than node:zlib
1.03x slower than npm:brotli-dec-wasm
1.00x faster than npm:brotli-wasm
1.88x faster than npm:brotli
group dprint-plugin-jupyter.wasm (354 KB -> 1.68 MB)
jsr:@nick/brocha 14.4 ms 69.6 ( 13.8 ms … 15.1 ms) 14.5 ms 15.1 ms 15.1 ms
npm:brotli 26.2 ms 38.1 ( 25.3 ms … 30.1 ms) 26.7 ms 30.1 ms 30.1 ms
npm:brotli-wasm 13.9 ms 71.8 ( 13.6 ms … 15.4 ms) 14.0 ms 15.4 ms 15.4 ms
npm:brotli-dec-wasm 14.8 ms 67.4 ( 14.4 ms … 16.6 ms) 14.9 ms 16.6 ms 16.6 ms
node:zlib 8.8 ms 114.2 ( 8.5 ms … 10.1 ms) 8.8 ms 10.1 ms 10.1 ms
summary
jsr:@nick/brocha
1.64x slower than node:zlib
1.03x slower than npm:brotli-wasm
1.03x faster than npm:brotli-dec-wasm
1.82x faster than npm:brotli
group dprint-plugin-typescript.wasm (746 KB -> 4.01 MB)
jsr:@nick/brocha 36.5 ms 27.4 ( 33.3 ms … 41.5 ms) 37.8 ms 41.5 ms 41.5 ms
npm:brotli 63.4 ms 15.8 ( 59.6 ms … 76.9 ms) 64.4 ms 76.9 ms 76.9 ms
npm:brotli-wasm 31.6 ms 31.6 ( 30.8 ms … 32.4 ms) 31.9 ms 32.4 ms 32.4 ms
npm:brotli-dec-wasm 33.8 ms 29.6 ( 33.0 ms … 34.4 ms) 34.0 ms 34.4 ms 34.4 ms
node:zlib 18.6 ms 53.8 ( 17.9 ms … 19.6 ms) 18.8 ms 19.6 ms 19.6 ms
summary
jsr:@nick/brocha
1.96x slower than node:zlib
1.16x slower than npm:brotli-wasm
1.08x slower than npm:brotli-dec-wasm
1.74x faster than npm:brotli
group ten megs of lipsum (1.71 MB -> 9.77 MB)
jsr:@nick/brocha 65.3 ms 15.3 ( 63.3 ms … 69.5 ms) 65.6 ms 69.5 ms 69.5 ms
npm:brotli 117.3 ms 8.5 (111.7 ms … 122.4 ms) 119.8 ms 122.4 ms 122.4 ms
npm:brotli-wasm 49.1 ms 20.4 ( 46.3 ms … 51.7 ms) 49.7 ms 51.7 ms 51.7 ms
npm:brotli-dec-wasm 56.4 ms 17.7 ( 52.5 ms … 76.7 ms) 56.6 ms 76.7 ms 76.7 ms
node:zlib 33.1 ms 30.2 ( 29.7 ms … 91.5 ms) 31.0 ms 91.5 ms 91.5 ms
summary
jsr:@nick/brocha
1.97x slower than node:zlib
1.33x slower than npm:brotli-wasm
1.16x slower than npm:brotli-dec-wasm
1.80x faster than npm:brotli
The
decompress
function from this package is the baseline, with some other popular Brotli decompression tools for comparison.
This project was adapted from the original brotli source code, developed by the Google Brotli team and licensed under the MIT license.