PURPOSE
Object pool for Set<any> instances used as bullet hit-tracking collections. Eliminates per-bullet new Set() allocations by recycling cleared Sets between bullet lifetimes. Targets GC pressure caused by Set’s internal hash-table allocations, which are more expensive than plain-object nursery churn at the engine’s bullet throughput.
OWNS
_pool: module-levelSet<any>[]array holding released, cleared Sets ready for reuse.- Hard cap of 500 entries on
_pool.lengthto prevent unbounded growth when bullet population permanently shrinks. - The “guaranteed empty on acquire” invariant: callers always receive a Set with no entries.
READS FROM
_pool.lengthto decide whether to pop a recycled Set or allocate a new one.- The
setargument passed toreleaseSet(null/undefined-tolerant).
PUSHES TO
_pool(push on release, pop on acquire).- Returned Set reference to the caller (assigned to
bullet.hitsat every call site).
DOES NOT
- Does not track which Sets are currently in use (no leak detection).
- Does not validate that a released Set is not still referenced elsewhere.
- Does not pool any collection type other than
Set. - Does not warm the pool — first acquires always allocate.
- Does not shrink the pool below the cap once it has grown.
- Does not log, telemetry-report, or expose pool statistics.
- Does not clear the Set on acquire — relies on
releaseSethaving cleared it before return.
Signals
None. Pure synchronous functions with no event emission, no store writes, no telemetry hooks.
Entry points
acquireSet(): Set<any>— returns either_pool.pop()or a freshly constructednew Set().releaseSet(set: Set<any> | null | undefined): void— callsset.clear()then pushes to_poolif under the cap; no-op whensetis falsy.
Pattern notes
- Callers are bullet-creation sites in
engine/weapons/bullets.ts,engine/weapons/weapons.ts, andengine/world/artifacts.ts, each assigning the acquired Set tobullet.hits. - Release happens in
engine/bridge.tsat the bullet-cleanup site when a bullet dies, callingreleaseSet(b.hits). - Acquire/release must be paired per bullet lifetime. Holding a released Set reference is a contract violation — the same Set may be handed to another bullet on the next
acquireSetcall. - The 500-entry cap is a one-way ratchet: it bounds peak retained memory but never releases pooled Sets back to GC once allocated.
- LIFO (stack) ordering via
push/popis intentional — keeps recently used Sets warm in CPU cache. - Typed as
Set<any>rather than a generic parameter; bullet hit-tracking stores enemy IDs but the pool is type-erased at the boundary.