first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,82 @@
# BitSet
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## 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
```ts
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
```ts
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.