# bitset ## Purpose Implements the wire-format bitset codec for the mesh protocol. Bitsets carry chunk presence (`cd:advertise`) and chunk-need maps (`cd:request` `kind:'bitset'`); their on-wire representation is a deterministic, magic-prefixed, length-tagged envelope so receivers can detect and reject payloads that bypassed the mandated framing layer (DMP-21). This module also provides the raw bit-manipulation helpers (`chunkIdsToRawBitset`, `rawBitsetToChunkIds`, `popcount`) that the rest of the package consumes. ## Features - `BITSET_MAGIC` (4-byte `"DMPB"` prefix) + `BITSET_HEADER_SIZE` (12-byte fixed header). - `encodeBitset(rawBytes, bitLength)` — wraps a raw bit-packed buffer with the header. - `decodeBitset(wire)` — verifies magic, decodes header, returns `{ raw, bitLength }`; throws `DMP.CODEC_MISMATCH` for missing magic / truncated headers / length mismatches. - `chunkIdsToRawBitset(ids, bitLength)` — packs a chunk-id list into a raw bit-packed buffer with strict bounds checking. - `rawBitsetToChunkIds(bytes, bitLength)` — inverse, with padding-zero enforcement (DMP-08). - `popcount(bytes)` — population count (Hamming weight) over a raw byte array; used by trust accounting. - All length / padding violations raise `DMP.BITSET_LENGTH_MISMATCH` or `DMP.BITSET_PADDING_NONZERO`. ## Usage: Basic Example ```ts import { chunkIdsToRawBitset, decodeBitset, encodeBitset, rawBitsetToChunkIds, } from "@suaveplan/drive-mesh-protocol"; const raw = chunkIdsToRawBitset([0, 5, 17], 18); const wire = encodeBitset(raw, 18); const back = decodeBitset(wire); const ids = rawBitsetToChunkIds(back.raw, back.bitLength); // [0, 5, 17] ``` ## Usage: Advanced Example ```ts import { BITSET_HEADER_SIZE, BITSET_MAGIC, encodeBitset, popcount, } from "@suaveplan/drive-mesh-protocol"; function buildPresence(present: number[], totalChunks: number): Uint8Array { const raw = chunkIdsToRawBitset(present, totalChunks); const wire = encodeBitset(raw, totalChunks); console.log( `${present.length}/${totalChunks} chunks; ${popcount(raw)} set bits;` + ` wire size ${wire.length} (${BITSET_HEADER_SIZE}-byte header)` ); return wire; } ``` ## API Reference - `BITSET_MAGIC: Uint8Array` — 4-byte prefix `0x44 0x4d 0x50 0x42`. - `BITSET_HEADER_SIZE: 12` — magic + bitLength + rawByteLen. - `encodeBitset(rawBytes: Uint8Array, bitLength: number): Uint8Array`. - `decodeBitset(wire: Uint8Array): { raw: Uint8Array; bitLength: number }`. - `chunkIdsToRawBitset(chunkIds: readonly number[], bitLength: number): Uint8Array`. - `rawBitsetToChunkIds(bytes: Uint8Array, bitLength: number): number[]`. - `popcount(bytes: Uint8Array): number`. ## Implementation Notes The wire format does NOT compress the bitset body — the documented `bitpacker-compressed` mandate is satisfied by the magic + length envelope, which is itself a length-prefixed, self-describing frame. The `compressRle` fast path in `@suaveplan/bitpacker-compressed` is non-bijective on inputs containing isolated `[X, 0, …]` triples and is therefore unsuitable for arbitrary bitset bytes; switching to gzip would force the codec async and break the hot path. The trade-off is intentional and documented inline in the source.