LSB-first bit-stream reader/writer over
Uint8Array+ 15-bit reverse table.
Module bitstream | Source packages/front/fw/src/io/compress/bitstream.js | Deps none | Worker-safe yes
Primitive shared by codecs whose bit layout follows DEFLATE/Brotli
conventions: little-endian packing within each byte, least-significant bit
first. Also exposes the rev 15-bit bit-reversal table used by canonical
Huffman decoding.
Resolve
const bs = runtime.resolve('bitstream');
// { readBits, readBits16, writeBits, writeBits16, byteOffset, slice, max, rev }
API
| Method | Signature | Returns |
|---|---|---|
readBits |
(buf: Uint8Array, bitPos: number, mask: number) => number |
up to 16 bits at bitPos, masked |
readBits16 |
(buf: Uint8Array, bitPos: number) => number |
up to 24 bits at bitPos, not masked |
writeBits |
(buf: Uint8Array, bitPos: number, v: number) => void |
writes up to 16 bits (OR-into) |
writeBits16 |
(buf: Uint8Array, bitPos: number, v: number) => void |
writes up to 24 bits (OR-into) |
byteOffset |
(bitPos: number) => number |
ceil(bitPos / 8) |
slice |
(buf: Uint8Array, start?, end?) => Uint8Array |
copied sub-array (not a view) |
max |
(arr: ArrayLike<number>) => number |
maximum |
rev |
Uint16Array(32768) |
rev[i] = bit-reverse of i in 15 bits |
readBits(buf, bitPos, mask)
Reads up to 16 bits starting at bit position bitPos (absolute index from
the beginning of buf). The mask must be pre-computed by the caller as
(1 << nbits) - 1. The explicit pattern allows the mask to be hoisted out
of hot loops.
writeBits(buf, bitPos, v)
OR-into: destination bits must be zero before the call. This is the default
for a freshly allocated Uint8Array, which is the expected pattern
(pre-allocated output buffer, sequential writes).
rev
15-bit bit-reversal table: rev[i] is i read in mirror over 15 bits,
the result right-shifted by one bit to fit in 15 useful bits. Used by
canonical Huffman decoders to index a table by the maxBits low-order
bits of the stream.
Examples
Write/read a 9-bit code
const bs = runtime.resolve('bitstream');
const out = new Uint8Array(4);
bs.writeBits(out, 5, 0x1A5); // 9 bits at position 5
const v = bs.readBits(out, 5, (1 << 9) - 1); // 0x1A5
Build a mask from a bit count
const nbits = 11;
const mask = (1 << nbits) - 1;
bs.readBits(buf, p, mask); // reads 11 bits
Worker Usage
const worker = fw.createWorker(
function ({ libs, args }) {
const bs = libs.bitstream;
const buf = args[0];
self.postMessage(bs.readBits(buf, 0, 0xFFFF));
},
{ dependencies: ['bitstream'], args: [new Uint8Array([0xCD, 0xAB])] }
);
Notes
readBitsalways reads 2 consecutive bytes starting at bytefloor(bitPos / 8)(notbyteOffset(bitPos), which rounds up); an index past the end of the buffer reads as zero, never throws.readBits16reads 3 consecutive bytes and does not mask — the caller must apply its own mask.writeBits/writeBits16areOR-into, not destructive writes: a second call on the same bits produces corrupt data.- The
revtable is built once per factory instance (~64 KB) — share the instance across consumer modules.
Benchmark
Micro-cell bitstream.rw, tools/bench/codec-levels.js (one pass writing
1 << 20 values of widths 1-16 then reading them back, corpus-independent),
--reps 7 --warmup 3, baseline sha d47f8fc7a68809f519fe7d36ff2b0ba88bcacdbd.
Host win32 / 13th Gen Intel(R) Core(TM) i7-13700KF / Bun 1.3.13.
Generated 2026-09-24T20:50:02.210Z (gitignored bench artifact). Rule:
Δenc < +5 %; gated on time only (no dec split, no bytes cell —
bitstream.js is byte-for-byte unchanged by the clean-room rewrite, see
huffman § Design).
| Cell | Old enc ms | New enc ms | Δenc % | Verdict |
|---|---|---|---|---|
| bitstream.rw | 7.79 | 8.55 | 9.8 | FAIL (enc) |
0/1 PASS. bitstream.js did not change in the clean-room rewrite
(confirmed byte-for-byte by the module's own perf-pass diff), so this is
measurement noise, not a regression: a later measurement ruling recorded
that an A/A run of two byte-identical copies of the module measures the
same rw path at ±10-15 % between copies in the shared-process harness,
while running one engine per process the same path measures 3.25 vs 3.25
ms (equal). The cell is accepted as the harness's own noise floor.
Provenance
In-house implementation written from RFC 1951 §3.1.1 (bit packing) and RFC 7932 §1.5; the earlier fflate-derived implementation was replaced by a clean-room rewrite (2026-09).