DEFLATE compression (RFC 1951) — LZ77 + Huffman coding. Internal engine used by
gzip,zlib, andzip.
Module deflate | Source packages/front/fw/src/io/compress/deflate.js | Deps bitstream, huffman, lz77 | Worker-safe yes
Resolve
const deflate = runtime.resolve('deflate');
// Returns: { deflateSync, inflateSync, deflate, inflate, DeflateStream, InflateStream }
API
Synchrone
const compressed = deflate.deflateSync(uint8Array, opts?); // → Uint8Array
const decompressed = deflate.inflateSync(compressed, opts?); // → Uint8Array
Asynchrone
const compressed = await deflate.deflate(uint8Array, opts?); // → Uint8Array
const decompressed = await deflate.inflate(compressed, opts?); // → Uint8Array
The async API uses queueMicrotask — it does not block the thread but is not multi-threaded.
Streaming — compression (deflate)
const chunks = [];
const stream = new deflate.DeflateStream(opts?, (chunk) => chunks.push(chunk));
stream.push(data1);
stream.push(data2);
stream.flush(); // flushes the buffer without finalizing
stream.push(lastChunk, true); // finalizes the stream
Streaming — decompression (inflate)
const output = [];
const stream = new deflate.InflateStream(opts?, (chunk) => output.push(chunk));
stream.push(compressedChunk);
stream.push(lastChunk, true);
Options
Compression
| Option | Type | Description |
|---|---|---|
level |
0–9 |
Compression level (0 = store, 9 = max). Default: 6 |
mem |
0–12 |
Hash-table width for the LZ77 scan, min(22, 12 + mem) bits (not a window/search-depth size). Default: unset — width tracks input size, clamp(ceil(log2(inputLength)), 12, 20) |
lazy |
boolean |
Lazy matching in the LZ77 scan: a better ratio (about −0.7 % bytes at levels 8–9) for a slower scan at levels 4–9. Default: false — the fast path. See Design › Encoder |
dictionary |
Uint8Array |
Pre-compression dictionary |
The same options reach deflate through gzip, zlib and zip, so
gzipSync(data, { level: 9, lazy: true }) is the maximum-ratio request.
Decompression
| Option | Type | Description |
|---|---|---|
out |
Uint8Array |
Pre-allocated output buffer |
dictionary |
Uint8Array |
Dictionary matching the compression |
Error codes
Every error thrown by this module is an Error carrying a numeric e.code.
The table lists the complete set — verified against the fail() call sites in
deflate.js.
| Code | Message | Raised when |
|---|---|---|
| 0 | unexpected EOF |
the input ends inside a block header, a symbol, or a stored body (one-shot decoding, or a stream closed with final = true) |
| 1 | invalid block type |
BTYPE = 3 (RFC 1951 §3.2.3 reserved value) |
| 2 | invalid length/literal |
a literal/length code has no entry in the active table, or a length symbol above 285 is read |
| 3 | invalid distance |
a distance code has no entry, a distance symbol above 29 is read, or the distance reaches back before the start of the output (dictionary included) |
| 4 | stream finished |
push() after the chunk marked final |
| 5 | no stream handler |
push() on a stream constructed without ondata |
| 8 | invalid data |
a malformed dynamic header, a stored-block LEN/NLEN mismatch, or an overflow of the caller-provided out buffer (the message states which) |
Codes 12 and 13 belong to gzip and zip; deflate
never emits them.
Design
Decoder
The decoder (inflateSync, inflate, InflateStream) is written from
RFC 1951 and shares its derived tables with the encoder below.
Tables. Nothing is transcribed by hand. LENGTH_EXTRA / LENGTH_BASE
(symbols 257–285) and DIST_EXTRA / DIST_BASE (symbols 0–29) are derived at
factory time from the RFC §3.2.5 formulas — extra(i) = i < 8 || i === 28 ? 0 : (i - 4) >> 2 for lengths, extra(i) = i < 4 ? 0 : (i - 2) >> 1 for
distances — with the bases accumulated from 3 and 1 respectively and the
single special case LENGTH_BASE[28] = 258. CODE_LENGTH_ORDER (§3.2.7) and
the fixed code lengths (§3.2.6: 8/9/7/8 for the literal code, a flat 5 for the
30 usable distance codes) follow the same rule. The decode tables come from
the shared huffman.buildMap(lengths, maxBits, 1) primitive: an entry is
(symbol << 4) | codeLength, indexed by the next maxBits bits read
LSB-first, and entry 0 means "no code here". deflate.test.js re-derives
each table from the RFC formulas, compares it with the RFC tables transcribed
literally, and decodes a hand-assembled vector per length and per distance
symbol at both ends of its range.
Block state machine. A decoder state — bitPos, finalBlock, phase,
storedRemaining, the two tables and their widths, out / outLen /
emitted — moves between four phases: HEADER reads BFINAL + BTYPE and
the per-type preamble (stored LEN/NLEN, the fixed tables, or the dynamic
header of §3.2.7); STORED copies the body of a BTYPE = 0 block, resuming a
partial copy; CODES decodes literal/length and distance symbols until
end-of-block; DONE is reached when the final block ends.
Resumability. bitstream.readBits pads reads past the end of the buffer
with zeros, so an overrun is detected by POSITION, never by an exception:
bitPos is snapshotted before each block header and before each symbol, and a
read that lands past input.length * 8 — or a table lookup that returns entry
0 while fewer than maxBits bits remain — restores the snapshot. In
one-shot mode that condition fails with code 0; in stream mode it returns
"need input", InflateStream keeps the bytes from the last consumed byte
boundary, and the next push re-reads from the snapshot (a partially read
dynamic header is simply re-parsed, which is bounded by the header size).
After each push only the last 32 KiB of output are retained as the
back-reference window.
Output growth. Without opts.out the buffer starts at
max(64 KiB, 3 × input.length) and doubles on demand; the result is a
copy trimmed to the exact length. With opts.out the decoder writes directly
into the caller's buffer, returns a view into it, and raises code 8 rather
than growing. opts.dictionary primes the output with the dictionary's last
32 KiB so back-references into it are ordinary in-buffer copies; combined with
opts.out, decoding runs in an internal buffer and the payload alone is
copied across at the end.
Known limitation. An over-subscribed or incomplete dynamic Huffman code is not validated when the header is read. Such a stream is not silently accepted: decoding proceeds and ends in code 2, code 3 or code 8 as soon as a hole in the table is hit.
Encoder
The compressor (deflateSync, deflate, DeflateStream) is written from
RFC 1951 as well. Its matching pass is the shared lz77 module —
deflate owns only the DEFLATE-specific parts: the symbol mapping of §3.2.5,
the block-type decision, and the canonical Huffman coding of §3.2.6/§3.2.7.
Matching. lz77.encodeTokens is called once per compression unit with
windowBits: 15, minMatch: 3, maxMatch: 258 and an injected hashFn —
the 3-byte Fibonacci hash
imul(d[i] | d[i+1] << 8 | d[i+2] << 16, 0x9E3779B1) >>> (32 - hashBits),
i.e. exactly the bytes that define a minimum-length match, and with the
chainSkip walk on. Search effort per level, as shipped (zlib's shape for
chainDepth / niceLength / goodLength / maxLazy):
| Level | chainDepth |
niceLength |
goodLength |
maxLazy |
|---|---|---|---|---|
| 0 | — | — | — | — (stored blocks only, no lz77 call) |
| 1 | 4 | 8 | — | — |
| 2 | 8 | 16 | — | — |
| 3 | 16 | 32 | — | — |
| 4 | 16 | 16 | 4 | 4 |
| 5 | 32 | 32 | 8 | 16 |
| 6 | 128 | 128 | 8 | 16 |
| 7 | 256 | 128 | 8 | 32 |
| 8 | 1024 | 258 | 32 | 128 |
| 9 | 4096 | 258 | 32 | 258 |
level defaults to 6 and is clamped to 0..9. No level turns lazy
matching on: goodLength / maxLazy (levels 1–3: the lz77 defaults)
only act under the lazy option below.
Lazy matching is an option, not a level. { lazy: true } enables
lz77's lazy probe at every level ≥ 1 — the same engine with one more
probe per match, throttled by the level's goodLength / maxLazy. It is
what buys the ratio (−0.67 % bytes at levels 8–9 on the bench corpora, and
better than zlib on the text corpus) and what costs the time (+18 % at
level 8, +90 % on the JSON corpus). The default stays the fast
configuration because that is the one that meets the batch's < +5 %
time gate at every level. Measured on the option-vs-separate-path
question: the lazy test is taken once per match, on a hoisted local,
and an interleaved A/B of the shipped scanner against the same file with
the lazy branch deleted gives an aggregate Δ of +0.1 % / +1.5 % / +0.5 %
(levels 1 / 4 / 8) in one load order and −1.1 % / +0.2 % / −1.0 % in the
other — noise, so one loop and one option rather than two scan functions.
mem → hashBits. With mem given, the hash table is
min(22, 12 + mem) bits wide; without it the width tracks the input size,
clamp(ceil(log2(inputLength)), 12, 20).
Blocks and cost. The scan of a compression unit is one
lz77.encodeTokens call whose packed (len | 0, dist | byte) pairs are
read in place: a block is a window [from, to) of 16 384 tokens over that
buffer, tallied in one pass into the literal/length and distance
frequencies and the total of extra bits, then written; the tail block
carries BFINAL. The pairs are never copied into a second layout. For each
block three exact bit costs are computed and the cheapest wins:
| Type | Cost |
|---|---|
| stored (§3.2.4) | 8 × (5 + n) + align, only when n ≤ 65535 |
| fixed (§3.2.6) | 3 + Σ litFreq·fixedLitLen + Σ distFreq·5 + extraBits |
| dynamic (§3.2.7) | 3 + 14 + 3·HCLEN + Σ clFreq·clLen + clExtra + Σ litFreq·litLen + Σ distFreq·distLen + extraBits |
Trees come from huffman.buildTree(freqs, 15) (7 for the code-length code)
and codes from huffman.buildMap(lengths, maxBits, 0), which are already
bit-reversed for bitstream.writeBits. A block with no distance token is
written with HDIST = 1 and a single distance length of zero, as §3.2.7
allows. The dynamic form is skipped when the literal/length or code-length
code would hold a single symbol: that code is incomplete, which §3.2.7
tolerates only for distances. An empty input is therefore one empty final
fixed block — the two bytes 03 00.
BLOCK_TOKENS stays at 16 384: huffman.buildTree uses a frequency
sentinel of 25 001, so a block's symbol total must stay below it.
Memory. The token buffer is the encoder's working set: deflateSync
on N bytes holds an Int32Array of 2 × N entries — 8 bytes per input
byte — until it returns (up to 8 MiB of it is kept between calls as a
scratch buffer; larger ones are transient). DeflateStream compresses in
64 KiB units and stays bounded. A resumable scanner filling a fixed
BLOCK_TOKENS buffer would remove this and is the recorded follow-up.
Streaming. DeflateStream keeps the last 32 KiB of input as a window and
buffers pushes until 64 KiB or final, then compresses window ++ pending
with start = window.length. Whole bytes go out through ondata; a trailing
partial byte stays in the carry and opens the next unit, so flush() never
forces byte alignment. The contract is that the concatenation of every
ondata chunk is one valid raw DEFLATE stream — where the chunk boundaries
fall is not part of it, and they differ from the pre-batch engine's.
Benchmark
Final gate record on the merged tree, full
baseline sha b10c8a6da43e8c598645d097a678c38249ddebbc (git merge-base master HEAD), with the batch's exact command:
bun packages/front/fw/tools/bench/deflate-levels.js \
--baseline b10c8a6da43e8c598645d097a678c38249ddebbc --reps 7 --warmup 3 \
--json bench-final.json
Host: win32 / 13th Gen Intel(R) Core(TM) i7-13700KF / Bun 1.3.13.
Six deterministic corpora (text = the RFC 7932 dictionary blob,
source = brotli.js, json, random, repeat, small = 200 × 2 KiB
calls), levels 1–9, median of 7 timed reps after 3 warm-ups. Artifact
of the benchmark run (not shipped), generated
2026-09-05T10:53:48.246Z. Every output of both engines round-trips
through our inflateSync, and the fw test suite cross-checks the same
corpora against node:zlib. Δ is new against old; a negative Δtime
is faster. The ruled gate is Δtime < +5 % per level, with a proposed
ratio tolerance of Δratio ≤ +1 %.
Default configuration (shipped table, lazy off)
| Level | Old ms | New ms | Δtime % | Old bytes | New bytes | Δratio % | Verdict |
|---|---|---|---|---|---|---|---|
| 1 | 27.01 | 22.86 | −15.3 % | 577 206 | 574 443 | −0.48 % | PASS |
| 2 | 26.15 | 23.03 | −11.9 % | 567 921 | 567 269 | −0.11 % | PASS |
| 3 | 26.73 | 22.38 | −16.3 % | 564 084 | 562 905 | −0.21 % | PASS |
| 4 | 29.23 | 26.24 | −10.2 % | 561 392 | 564 059 | +0.48 % | PASS |
| 5 | 28.20 | 22.84 | −19.0 % | 560 089 | 560 350 | +0.05 % | PASS |
| 6 | 31.48 | 25.10 | −20.2 % | 557 944 | 558 319 | +0.07 % | PASS |
| 7 | 33.80 | 26.66 | −21.1 % | 557 528 | 557 952 | +0.08 % | PASS |
| 8 | 35.16 | 27.51 | −21.8 % | 557 337 | 557 772 | +0.08 % | PASS |
| 9 | 40.24 | 28.13 | −30.1 % | 557 334 | 557 766 | +0.08 % | PASS |
All nine levels PASS both thresholds: Δtime is negative (faster) at
every level and Δratio stays within ±1 % (better than the baseline at
levels 1–3, at parity elsewhere — the largest regression is +0.48 % at
level 4). Per corpus, the persistent residual is the incompressible
random corpus (+13 … +53 % in this run — the known fixed per-position
cost of the generic scanner over an engine that inlines its probe, the
analysis' fix 7, deliberately not taken); repeat is the biggest winner
at −46 … −56 %.
Measurement caveat. Individual per-corpus cells of this sequential
harness move by up to ±30–50 % between runs on this host, in both
directions — the aggregate per level, not any single cell, is what the
gate reads. The text corpus at level 4 is a reproducible instance of
this (this run: old 2.79 ms / new 3.82 ms, a cell Δtime of +37 %, while
the level's aggregate is still −10.2 %): an earlier interleaved-repetition
investigation traced it to a JIT-state carry-over that appears only when the repeat
corpus runs immediately before text in the same process at level 4,
disappears when level 4 runs alone or in a directory-to-directory
interleaved comparison — there this engine is byte-identical to the
analysis' p6nl prototype at every cell and within ±1 % of its time — and
is unrelated to the engine change. Read the aggregates, not the cells; a
finer per-corpus verdict needs interleaved A/B repetitions (backlog item
proposed in the perf-landing session, not yet filed as this task's
perimeter excludes the bench tool).
{ lazy: true } (same engine, lazy matching on) — supplementary, not gated
Measured separately by the perf-landing session at --warmup 15 --reps 11
(not re-run by this task: the batch gate covers only the default
configuration above).
| Level | Old ms | New ms | Δtime | Old bytes | New bytes | Δbytes |
|---|---|---|---|---|---|---|
| 1 | 25.03 | 21.40 | −14.5 % | 577 206 | 570 076 | −1.24 % |
| 2 | 30.04 | 23.15 | −22.9 % | 567 921 | 561 810 | −1.08 % |
| 3 | 26.82 | 26.02 | −3.0 % | 564 084 | 557 766 | −1.12 % |
| 4 | 27.75 | 24.22 | −12.7 % | 561 392 | 563 035 | +0.29 % |
| 5 | 28.43 | 25.71 | −9.6 % | 560 089 | 556 986 | −0.55 % |
| 6 | 31.13 | 28.18 | −9.5 % | 557 944 | 555 149 | −0.50 % |
| 7 | 32.67 | 31.93 | −2.3 % | 557 528 | 554 208 | −0.60 % |
| 8 | 35.61 | 43.72 | +22.8 % | 557 337 | 553 585 | −0.67 % |
| 9 | 40.61 | 46.73 | +15.1 % | 557 334 | 553 579 | −0.67 % |
Lazy matching is where the ratio comes from — and where the time goes at
the two top levels: json +85 … +91 %, text +38 … +40 %, source
+6 … −13 %, small −9 … −14 %. At level 9 with lazy the text corpus
compresses to 58 286 bytes, below zlib's 59 319 at its level 9; at
level 4 the zlib-shaped table (maxLazy 4) gives up ratio on json
(+4.5 %) for a −13 % time, the trade the analysis flagged and the owner
accepted.
Artifacts of the lazy run: the default-configuration output and
the lazy-configuration output (not shipped). Re-run with
bun packages/front/fw/tools/bench/deflate-levels.js --baseline b10c8a6da43e8c598645d097a678c38249ddebbc --warmup 15 --reps 11 [--opts '{"lazy":true}'].
The gate table above (default configuration) was re-run for this record
with the batch's own --reps 7 --warmup 3; artifact
of the gate run (not shipped).
Clean-room huffman/bitstream rewrite (2026-09)
deflate.js itself is unchanged by the clean-room rewrite; this record
measures the effect on the deflate codec of the underlying
huffman/bitstream clean-room rewrite (see huffman,
bitstream) plus the perf pass landed on huffman.js
(two-queue fast path ahead of package-merge). Same six corpora and levels
1-9, --reps 7 --warmup 3, baseline sha
d47f8fc7a68809f519fe7d36ff2b0ba88bcacdbd (git merge-base master HEAD
taken before the rewrite). Host win32 / 13th Gen Intel(R) Core(TM) i7-13700KF / Bun 1.3.13. Generated 2026-09-24T20:49:44.085Z
(gitignored bench artifact). Rule: Δenc < +5 %, Δdec < +5 % per level
and per corpus; Δbytes ≤ 0.
| Level | Corpus | Old enc ms | New enc ms | Δenc % | Old dec ms | New dec ms | Δdec % | Old B | New B | Δbytes % | Verdict |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | text | 2.85 | 2.36 | -17.4 | 1.01 | 0.97 | -3.6 | 62495 | 62495 | 0.00 | PASS |
| 1 | source | 1.84 | 1.83 | -0.4 | 0.71 | 0.66 | -7.7 | 40427 | 40427 | 0.00 | PASS |
| 1 | json | 2.50 | 2.44 | -2.2 | 1.02 | 0.78 | -23.0 | 55435 | 55435 | 0.00 | PASS |
| 1 | random | 4.76 | 4.29 | -10.0 | 0.08 | 0.08 | 6.2 | 262224 | 262224 | 0.00 | FAIL (dec) |
| 1 | repeat | 0.88 | 0.86 | -1.5 | 0.26 | 0.24 | -7.7 | 1047 | 1047 | 0.00 | PASS |
| 1 | small | 10.17 | 9.93 | -2.4 | 4.52 | 4.46 | -1.3 | 153607 | 153607 | 0.00 | PASS |
| 2 | text | 3.72 | 3.89 | 4.5 | 0.70 | 0.70 | -0.9 | 60878 | 60878 | 0.00 | PASS |
| 2 | source | 2.16 | 2.13 | -1.4 | 0.60 | 0.58 | -3.6 | 38671 | 38671 | 0.00 | PASS |
| 2 | json | 3.30 | 3.27 | -1.1 | 0.71 | 0.68 | -5.4 | 53329 | 53329 | 0.00 | PASS |
| 2 | random | 4.36 | 4.48 | 2.7 | 0.07 | 0.06 | -0.8 | 262224 | 262224 | 0.00 | PASS |
| 2 | repeat | 0.86 | 0.85 | -1.5 | 0.23 | 0.27 | 16.4 | 1047 | 1047 | 0.00 | FAIL (dec) |
| 2 | small | 8.98 | 8.74 | -2.7 | 4.08 | 3.96 | -3.0 | 151958 | 151958 | 0.00 | PASS |
| 3 | text | 2.56 | 2.81 | 9.6 | 0.65 | 0.64 | -1.6 | 60040 | 60040 | 0.00 | FAIL (enc) |
| 3 | source | 2.14 | 2.12 | -1.2 | 0.53 | 0.51 | -3.0 | 37669 | 37669 | 0.00 | PASS |
| 3 | json | 3.31 | 3.22 | -2.9 | 0.74 | 0.70 | -5.1 | 51783 | 51783 | 0.00 | PASS |
| 3 | random | 4.56 | 4.22 | -7.5 | 0.07 | 0.06 | -8.5 | 262224 | 262224 | 0.00 | PASS |
| 3 | repeat | 0.86 | 0.86 | 0.5 | 0.21 | 0.22 | 3.5 | 1047 | 1047 | 0.00 | PASS |
| 3 | small | 9.28 | 8.86 | -4.5 | 4.00 | 3.18 | -20.5 | 150890 | 150890 | 0.00 | PASS |
| 4 | text | 3.96 | 3.94 | -0.6 | 0.66 | 0.65 | -1.6 | 60041 | 60041 | 0.00 | PASS |
| 4 | source | 3.41 | 3.38 | -0.8 | 0.56 | 0.54 | -2.3 | 38117 | 38117 | 0.00 | PASS |
| 4 | json | 5.65 | 5.46 | -3.4 | 0.73 | 0.71 | -2.3 | 51779 | 51779 | 0.00 | PASS |
| 4 | random | 5.23 | 5.40 | 3.1 | 0.06 | 0.07 | 1.4 | 262224 | 262224 | 0.00 | PASS |
| 4 | repeat | 1.46 | 1.43 | -1.7 | 0.21 | 0.23 | 11.5 | 1047 | 1047 | 0.00 | FAIL (dec) |
| 4 | small | 9.36 | 7.95 | -15.0 | 5.03 | 5.18 | 2.9 | 151652 | 151652 | 0.00 | PASS |
| 5 | text | 2.86 | 2.83 | -1.0 | 0.72 | 0.70 | -1.7 | 59678 | 59678 | 0.00 | PASS |
| 5 | source | 2.39 | 2.37 | -0.6 | 0.60 | 0.60 | 0.2 | 37242 | 37242 | 0.00 | PASS |
| 5 | json | 3.92 | 3.90 | -0.5 | 0.74 | 0.67 | -9.7 | 50296 | 50296 | 0.00 | PASS |
| 5 | random | 4.53 | 4.35 | -4.0 | 0.06 | 0.06 | -1.3 | 262224 | 262224 | 0.00 | PASS |
| 5 | repeat | 0.84 | 0.84 | 0.2 | 0.21 | 0.21 | 0.2 | 1047 | 1047 | 0.00 | PASS |
| 5 | small | 9.98 | 8.26 | -17.2 | 4.36 | 5.35 | 22.7 | 150639 | 150639 | 0.00 | FAIL (dec) |
| 6 | text | 3.00 | 2.95 | -1.5 | 0.69 | 0.64 | -7.3 | 59597 | 59597 | 0.00 | PASS |
| 6 | source | 2.62 | 2.55 | -2.5 | 0.53 | 0.52 | -1.4 | 36870 | 36870 | 0.00 | PASS |
| 6 | json | 5.30 | 5.17 | -2.5 | 0.73 | 0.69 | -4.7 | 49048 | 49048 | 0.00 | PASS |
| 6 | random | 4.52 | 4.22 | -6.5 | 0.06 | 0.07 | 16.8 | 262224 | 262224 | 0.00 | FAIL (dec) |
| 6 | repeat | 0.92 | 0.84 | -8.9 | 0.20 | 0.21 | 2.9 | 1047 | 1047 | 0.00 | PASS |
| 6 | small | 10.45 | 8.56 | -18.1 | 4.10 | 4.16 | 1.6 | 150312 | 150312 | 0.00 | PASS |
| 7 | text | 2.92 | 2.94 | 0.6 | 0.69 | 0.66 | -5.0 | 59591 | 59591 | 0.00 | PASS |
| 7 | source | 2.81 | 2.77 | -1.4 | 0.51 | 0.49 | -3.0 | 36815 | 36815 | 0.00 | PASS |
| 7 | json | 5.73 | 6.03 | 5.1 | 0.72 | 0.69 | -4.5 | 48850 | 48850 | 0.00 | FAIL (enc) |
| 7 | random | 4.61 | 4.31 | -6.6 | 0.06 | 0.07 | 5.0 | 262224 | 262224 | 0.00 | PASS |
| 7 | repeat | 0.86 | 0.85 | -0.3 | 0.20 | 0.20 | 3.8 | 1047 | 1047 | 0.00 | PASS |
| 7 | small | 9.50 | 8.34 | -12.2 | 5.16 | 4.18 | -19.0 | 150232 | 150232 | 0.00 | PASS |
| 8 | text | 3.01 | 3.00 | -0.6 | 0.69 | 0.64 | -6.8 | 59590 | 59590 | 0.00 | PASS |
| 8 | source | 3.05 | 3.05 | -0.2 | 0.57 | 0.52 | -8.7 | 36768 | 36768 | 0.00 | PASS |
| 8 | json | 6.79 | 6.85 | 0.8 | 0.75 | 0.70 | -7.0 | 48755 | 48755 | 0.00 | PASS |
| 8 | random | 4.44 | 4.20 | -5.4 | 0.06 | 0.06 | -0.2 | 262224 | 262224 | 0.00 | PASS |
| 8 | repeat | 0.88 | 0.90 | 2.7 | 0.20 | 0.22 | 5.9 | 1047 | 1047 | 0.00 | FAIL (dec) |
| 8 | small | 10.25 | 9.32 | -9.1 | 3.52 | 4.72 | 34.1 | 150199 | 150199 | 0.00 | FAIL (dec) |
| 9 | text | 2.99 | 3.16 | 5.7 | 0.67 | 0.66 | -1.6 | 59590 | 59590 | 0.00 | FAIL (enc) |
| 9 | source | 3.21 | 3.19 | -0.7 | 0.55 | 0.50 | -7.9 | 36763 | 36763 | 0.00 | PASS |
| 9 | json | 6.72 | 6.78 | 0.8 | 0.76 | 0.76 | -0.2 | 48755 | 48755 | 0.00 | PASS |
| 9 | random | 4.78 | 4.50 | -5.9 | 0.07 | 0.06 | -14.4 | 262224 | 262224 | 0.00 | PASS |
| 9 | repeat | 0.85 | 0.84 | -1.0 | 0.20 | 0.21 | 9.0 | 1047 | 1047 | 0.00 | FAIL (dec) |
| 9 | small | 9.45 | 8.94 | -5.4 | 4.77 | 3.81 | -20.2 | 150199 | 150199 | 0.00 | PASS |
43/54 PASS, 11/54 FAIL. Δbytes is +0.000 % on every one of the 54
cells (the deflate codec's own bit-packing is untouched; only the
huffman/bitstream primitives underneath it changed). Every red cell is
a time cell, and it is the harness's own measurement noise: a later
measurement ruling accepted these cells as the measured floor, based on a
live-vs-live identical-code A/A run at the same parameters that puts
10-21 of these 54 cells past |5 %|, while at --reps 15 all
sub-millisecond and GC-heavy cells stabilise. The residual cells above are
accepted as the measurement floor, not a regression — the same acceptance
applies to brotli.md, lz4.md, huffman.md and bitstream.md.
Provenance
In-house implementation from RFC 1951; the earlier fflate-derived
implementation has since been replaced. The LZ77 matching pass is the
shared lz77 module.
Example
const deflate = runtime.resolve('deflate');
const data = new TextEncoder().encode('Hello World '.repeat(500));
// Sync
const compressed = deflate.deflateSync(data, { level: 6 });
const decompressed = deflate.inflateSync(compressed);
// With dictionary (better compression for similar data)
const dict = new TextEncoder().encode('Hello World ');
const c = deflate.deflateSync(data, { level: 9, dictionary: dict });
const d = deflate.inflateSync(c, { dictionary: dict });
Worker Usage
const worker = fw.createWorker(
function ({ libs, args }) {
const deflate = libs.deflate;
const compressed = deflate.deflateSync(args.data, { level: 6 });
self.postMessage(compressed, [compressed.buffer]);
},
{ dependencies: ['deflate'], args: { data: new Uint8Array(largeData) } }
);
Notes
- This module is the low-level primitive. For common usage, prefer gzip or zlib which add a header + checksum.
- The
DeflateStream/InflateStreamstreams are reused internally bygzipandzlib. - Both halves implement RFC 1951 §3.2.3–§3.2.7 directly — see Design › Decoder for the table derivation, the block state machine and the resumability rule, and Design › Encoder for the level table, the
lazyopt-in, the block-cost rule and the streaming chunk contract; Benchmark carries the measured record against the pre-batch engine.