Files
skills/docs/gaming/games/ecs/src/bitset/bitset.md
2026-05-29 08:20:56 +01:00

3.5 KiB

BitSet

(no submodules)

Purpose

BitSet is the foundational mask primitive used by archetypes, queries, and the component registry inside @suaveplan/ecs. Storing component membership as bits packed into Uint32Array words gives the engine cache-friendly storage and O(words) algebra for subset, superset, and intersection tests — the hot paths driving every archetype-graph transition.

The class is intentionally narrow: it offers only the operators needed by the engine itself (set, unset, has, containsAll, intersects, equals, count, clone, clear, toKey, iterator). It is universal — no node:*, browser, or React dependency — so it runs identically in Bun, Node, and the browser.

Features

  • Uint32Array-backed storage; one word per 32 bits.
  • O(words) containsAll and intersects, safe across mismatched sizes.
  • Population count (count) via the SWAR Hamming-weight trick.
  • Sorted-ascending iteration through Symbol.iterator.
  • Stable serialised key from toKey() used as the archetype-graph map key.
  • Defensive set / unset guards: out-of-range bit indices throw RangeError; the constructor rejects non-positive sizes.

Usage: Basic Example

import { BitSet } from "@suaveplan/ecs";

const mask = new BitSet(64);
mask.set(0);
mask.set(5);
mask.set(32);

console.log(mask.has(5)); // true
console.log([...mask]); // [0, 5, 32]
console.log(mask.toKey()); // "0,5,32"

Usage: Advanced Example

import { BitSet } from "@suaveplan/ecs";

const include = new BitSet(128);
const archetype = new BitSet(128);
include.set(3);
include.set(7);
for (const cid of [3, 7, 11, 18]) archetype.set(cid);

if (archetype.containsAll(include)) {
    // Archetype has every required component — yield its entities.
}

const exclude = new BitSet(128);
exclude.set(11);
if (archetype.intersects(exclude)) {
    // Skip — archetype contains a banned component.
}

API Reference

Member Description
new BitSet(size) Allocate a bitset of size bits. Throws RangeError for non-positive integer sizes.
size Read-only declared bit-width.
set(bit) / unset(bit) Flip a single bit. RangeError on out-of-range index.
has(bit) Test a bit. Returns false for out-of-range indices.
containsAll(other) true when every bit set in other is also set here. Safe across mismatched word counts.
intersects(other) true when at least one bit is set in both bitsets.
equals(other) Structural word-by-word equality.
count() Population count via SWAR.
clone() Deep copy with the same size.
clear() Reset every bit to zero.
toKey() Comma-separated string of set indices, ascending.
[Symbol.iterator]() Yield set indices in ascending order.

Implementation Notes

  • Words are accessed with >>> 5 (divide by 32) and & 31 (mod 32). Using bitwise math keeps everything in the 32-bit-integer fast path.
  • containsAll walks each word in lock-step and treats trailing words of other as zero when this is shorter — this lets the world build mask-only diffs between archetypes without forcing both sides to the same allocation size.
  • count is the classic 5-step parallel bit-population trick, fast and branch-free.
  • toKey produces a stable, comparable string used as the archetype-graph map key. It is O(size) but only paid once per archetype-create event.
  • The iterator is a generator; for (const bit of set) consumes it lazily.