@daneren2005/shared-memory-objects 1.6.1 → 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -78,7 +78,7 @@ Freshly allocated with the default options:
78
78
 
79
79
  | Structure | Used Memory |
80
80
  | ------------------------------- | ----------- |
81
- | SharedList | 16 bytes |
81
+ | SharedList | 32 bytes |
82
82
  | SharedVector | 32 bytes |
83
83
  | SharedStack | 144 bytes |
84
84
  | SharedPool | 664 bytes |
@@ -88,13 +88,14 @@ Freshly allocated with the default options:
88
88
  | SharedSpatialGrid (20x20 cells) | 8816 bytes |
89
89
  | SharedSpatialGrid (80x80 cells) | 56816 bytes |
90
90
  | SharedSpatialMap (default) | 8984 bytes |
91
+ | SharedSpatialMap (Float64) | 14984 bytes |
91
92
  | SharedSpatialMap (2048 buckets) | 23384 bytes |
92
93
 
93
94
  `SharedQuadtree`'s footprint is dominated by its fixed node array, which is allocated once up front and never grows. The default `maxLevels` of 4 is a complete tree of 341 nodes (`(4^(maxLevels+1) - 1) / 3`), so the number scales with `maxLevels`: a deeper tree resolves smaller entities but costs `~2 u32` per node. The rest is its internal `SharedPool` of entity records and the `SharedMap` that indexes entities by id, whose header blocks are inlined into the tree's own allocation (the same way `SharedPool` inlines its two stacks) so the whole spine is one contiguous block instead of three separate pointer resources. The record (id, `x/y/w/h`, node, bucket link) is stored at the configured precision, defaulting to `Float32` to match a physics engine that keeps positions in `Float32`. `Float32` stays exact as long as entity ids and the entity count both stay under 2^24 (~16.7M); pass `type: Float64Array` if you need ids or a capacity beyond that (constructing a `Float32` tree with `maxEntities` over 2^24 throws), at the cost of more memory per entity.
94
95
 
95
96
  `SharedSpatialGrid`'s footprint is likewise dominated by its fixed cell array (`cols x rows` cells at `~2 u32` each), so it scales with cell count rather than `maxLevels`: `cols = ceil(width / gridSize)` and `rows = ceil(height / gridSize)`, so a smaller `gridSize` resolves finer at the cost of more cells (the 20x20 grid above is a 1000x1000 world at `gridSize` 50; the 80x80 grid is a 4000x4000 world at `gridSize` 50). The rest is two inlined `SharedPool`s - one of entity records (the occupied cell rectangle, keyed by an inlined `SharedMap` of id -> index) and one of per-cell "slot" records (`id`, anchor cell, bucket link) - since a multi-cell entity needs one slot per cell it overlaps. `maxSlots` therefore defaults to `maxEntities * 4` to leave headroom for spanning entities. Coordinate precision follows the same `Float32`-default rule as the quadtree (ids, both pools' indices, and cell indices must stay under 2^24 for `Float32`; pass `type: Float64Array` otherwise).
96
97
 
97
- `SharedSpatialMap` starts at 8984 bytes with its default 256 buckets, about 87% less than the former fixed 8192-bucket table's 70312 bytes. When the number of allocated slots exceeds half the active bucket count, it doubles the count and rebuilds the bucket chains. Its 256-bucket pages remain at stable addresses, while their pointers live in a `SharedStack` that grows in segments. `buckets` is therefore an initial size hint rather than a permanent sizing decision; `map.buckets` reports the current count. `maxBuckets` can stop growth for a deliberately collision-heavy or memory-capped table and otherwise defaults high enough to preserve the target load through `maxSlots`. The rest is the same two inlined `SharedPool`s as the grid - one of entity records and one of per-cell slot records. `maxSlots` defaults to `maxEntities * 4`, and coordinate precision follows the same `Float32`-default rule.
98
+ `SharedSpatialMap` starts at 8984 bytes with its default 256 buckets, about 87% less than the former fixed 8192-bucket table's 70312 bytes. When the number of allocated slots exceeds half the active bucket count, it doubles the count and rebuilds the bucket chains. Its 256-bucket pages remain at stable addresses, while their pointers live in a `SharedStack` that grows in segments. `buckets` is therefore an initial size hint rather than a permanent sizing decision; `map.buckets` reports the current count. `maxBuckets` can stop growth for a deliberately collision-heavy or memory-capped table and otherwise defaults high enough to preserve the target load through `maxSlots`. The rest is the same two inlined `SharedPool`s as the grid - one of entity records and one of per-cell slot records. `maxSlots` defaults to `maxEntities * 4`, and coordinate precision follows the same `Float32`-default rule. Ids must be integers in `[0, 2^24)` for `Float32` or `[0, 2^32)` for `Float64` (the id map's keys are uint32); `insert`, `update`, and `bulkInsert` throw on anything else rather than storing an entity that could never be found or removed again.
98
99
 
99
100
  All three spatial indexes accept `bulkInsert(entities)`, where each entity has `{ id, x, y, width?, height? }`. Duplicate ids keep their last value. The method retains the normal thread-safety guarantees while reserving entity and slot ranges with one atomic operation, sizing the ID map once, and writing records directly into their pool blocks; `SharedSpatialMap` also sizes its bucket table once before linking a new batch. When an explicit entity or slot capacity is at most 16384, its pool uses one bounded contiguous chunk instead of a series of 100-record chunks. Larger and default capacities retain incremental allocation so an empty index does not reserve excessive memory.
100
101
 
@@ -17,6 +17,7 @@ export default class SharedSpatialMap {
17
17
  private tableLock;
18
18
  private readonly coordType;
19
19
  private readonly nullIndex;
20
+ private readonly idLimit;
20
21
  private bucketCount;
21
22
  private readonly maxBucketCount;
22
23
  private bucketMask;
@@ -31,6 +32,7 @@ export default class SharedSpatialMap {
31
32
  get usedMemory(): number;
32
33
  insert(id: number, x: number, y: number, width?: number, height?: number): void;
33
34
  bulkInsert(entities: Iterable<SpatialEntity>): void;
35
+ private assertValidId;
34
36
  private insertLocked;
35
37
  update(id: number, x: number, y: number, width?: number, height?: number): void;
36
38
  private updateLocked;
@@ -9,7 +9,7 @@ import g from "../shared-pool.js";
9
9
  import { addCandidate as _, candidateLimit as v, distanceSquaredToRect as y } from "./spatial-neighbors.js";
10
10
  import { getSpatialMapCapacity as ee, getSpatialPoolChunkSize as te } from "./spatial-entity.js";
11
11
  //#region src/spatial/shared-spatial-map.ts
12
- var b = 0, x = 1, S = 2, C = 3, w = 4, T = 5, E = 6, D = 8, ne = 10, O = 0, k = 1, A = 2, j = 8, M = 256, re = 255, N = 0, P = 1, F = 2, I = 3, L = 4, R = 5, z = 6, B = 7, V = 0, H = 1, U = 2, W = 3, G = 4, K = 5, q = 6, J = 7, Y = 8, X = 16777216, ie = X, ae = 4294967295, oe = 73856093, se = 19349663, ce = 50, le = 256, Z = 1, Q = 2, ue = 1e6, de = 4, fe = Float32Array, pe = class l {
12
+ var b = 0, x = 1, S = 2, C = 3, w = 4, T = 5, E = 6, D = 8, ne = 10, O = 0, k = 1, A = 2, j = 8, M = 256, re = 255, N = 0, P = 1, F = 2, I = 3, L = 4, R = 5, z = 6, B = 7, V = 0, H = 1, U = 2, W = 3, G = 4, K = 5, q = 6, J = 7, Y = 8, X = 16777216, ie = X, ae = 4294967295, oe = 4294967296, se = 73856093, ce = 19349663, le = 50, ue = 256, Z = 1, Q = 2, de = 1e6, fe = 4, pe = Float32Array, me = class l {
13
13
  static ALLOCATE_COUNT = 11;
14
14
  memory;
15
15
  firstBlock;
@@ -41,6 +41,7 @@ var b = 0, x = 1, S = 2, C = 3, w = 4, T = 5, E = 6, D = 8, ne = 10, O = 0, k =
41
41
  tableLock;
42
42
  coordType;
43
43
  nullIndex;
44
+ idLimit;
44
45
  bucketCount;
45
46
  maxBucketCount;
46
47
  bucketMask;
@@ -61,19 +62,19 @@ var b = 0, x = 1, S = 2, C = 3, w = 4, T = 5, E = 6, D = 8, ne = 10, O = 0, k =
61
62
  return this.coordType;
62
63
  }
63
64
  constructor(i, a) {
64
- if (this.memory = i, a && "firstBlock" in a) this.firstBlock = new r(i, a.firstBlock), this.coordType = this.firstBlock.data[T], this.bucketPointers = new h(i, { firstBlock: e(t(this.firstBlock.data, b), i.positionBits) }), this.entityPool = new g(i, { firstBlock: e(t(this.firstBlock.data, x), i.positionBits) }), this.slotPool = new g(i, { firstBlock: e(t(this.firstBlock.data, S), i.positionBits) }), this.idMap = new m(i, { firstBlock: e(t(this.firstBlock.data, C), i.positionBits) }), this.bucketCount = Atomics.load(this.firstBlock.data, w), this.maxBucketCount = this.firstBlock.data[ne], this.nullIndex = ge(this.coordType);
65
+ if (this.memory = i, a && "firstBlock" in a) this.firstBlock = new r(i, a.firstBlock), this.coordType = this.firstBlock.data[T], this.bucketPointers = new h(i, { firstBlock: e(t(this.firstBlock.data, b), i.positionBits) }), this.entityPool = new g(i, { firstBlock: e(t(this.firstBlock.data, x), i.positionBits) }), this.slotPool = new g(i, { firstBlock: e(t(this.firstBlock.data, S), i.positionBits) }), this.idMap = new m(i, { firstBlock: e(t(this.firstBlock.data, C), i.positionBits) }), this.bucketCount = Atomics.load(this.firstBlock.data, w), this.maxBucketCount = this.firstBlock.data[ne], this.nullIndex = _e(this.coordType);
65
66
  else {
66
- let e = a?.gridSize ?? ce;
67
- this.bucketCount = Math.min(1073741824, Math.max(1, Math.floor(a?.buckets ?? le)));
68
- let t = a?.maxEntities ?? ue, r = a?.maxSlots ?? t * de;
69
- if (this.maxBucketCount = Math.min(1073741824, Math.max(this.bucketCount, Math.floor(a?.maxBuckets ?? _e(r * 2)))), this.coordType = me(a?.type), this.nullIndex = ge(this.coordType), this.coordType === c.float32 && (t > X || r > X)) throw Error(`Float32 SharedSpatialMap supports at most ${X} entities and slots; pass type: Float64Array for more`);
67
+ let e = a?.gridSize ?? le;
68
+ this.bucketCount = Math.min(1073741824, Math.max(1, Math.floor(a?.buckets ?? ue)));
69
+ let t = a?.maxEntities ?? de, r = a?.maxSlots ?? t * fe;
70
+ if (this.maxBucketCount = Math.min(1073741824, Math.max(this.bucketCount, Math.floor(a?.maxBuckets ?? ve(r * 2)))), this.coordType = he(a?.type), this.nullIndex = _e(this.coordType), this.coordType === c.float32 && (t > X || r > X)) throw Error(`Float32 SharedSpatialMap supports at most ${X} entities and slots; pass type: Float64Array for more`);
70
71
  let o = {
71
- type: he(this.coordType),
72
+ type: ge(this.coordType),
72
73
  dataLength: Y,
73
74
  maxLength: t,
74
75
  maxChunkSize: te(t, Y, u(this.coordType), i.maxAllocationLength)
75
76
  }, s = {
76
- type: he(this.coordType),
77
+ type: ge(this.coordType),
77
78
  dataLength: B,
78
79
  maxLength: r,
79
80
  maxChunkSize: te(r, B, u(this.coordType), i.maxAllocationLength)
@@ -97,7 +98,7 @@ var b = 0, x = 1, S = 2, C = 3, w = 4, T = 5, E = 6, D = 8, ne = 10, O = 0, k =
97
98
  capacity: ee(t, i.maxAllocationLength)
98
99
  }), n(this.firstBlock.data, C, D.pointer);
99
100
  }
100
- this.bucketMask = $(this.bucketCount), this.tableLock = new Int32Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + D * Uint32Array.BYTES_PER_ELEMENT, 2);
101
+ this.idLimit = this.coordType === c.float32 ? X : oe, this.bucketMask = $(this.bucketCount), this.tableLock = new Int32Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + D * Uint32Array.BYTES_PER_ELEMENT, 2);
101
102
  let o = this.getConfigView();
102
103
  this.cellSize = o[0];
103
104
  }
@@ -120,7 +121,7 @@ var b = 0, x = 1, S = 2, C = 3, w = 4, T = 5, E = 6, D = 8, ne = 10, O = 0, k =
120
121
  }
121
122
  bulkInsert(e) {
122
123
  let t = /* @__PURE__ */ new Map();
123
- for (let n of e) t.set(n.id, n);
124
+ for (let n of e) this.assertValidId(n.id), t.set(n.id, n);
124
125
  let n = Array.from(t.values()), r = this.idMap.getAll(n.map((e) => e.id)), o = [], s = 0, c = 0;
125
126
  for (let e = 0; e < n.length; e++) if (r[e] === void 0) {
126
127
  let t = n[e];
@@ -147,8 +148,11 @@ var b = 0, x = 1, S = 2, C = 3, w = 4, T = 5, E = 6, D = 8, ne = 10, O = 0, k =
147
148
  }
148
149
  this.growBucketsForBulk();
149
150
  }
151
+ assertValidId(e) {
152
+ if (!Number.isInteger(e) || e < 0 || e >= this.idLimit) throw Error(`SharedSpatialMap id ${e} must be an integer in [0, ${this.idLimit})` + (this.coordType === c.float32 ? "; pass type: Float64Array for ids up to 2^32 - 1" : ""));
153
+ }
150
154
  insertLocked(e, t, n, r, i) {
151
- if (this.idMap.get(e) !== void 0) {
155
+ if (this.assertValidId(e), this.idMap.get(e) !== void 0) {
152
156
  this.updateLocked(e, t, n, r, i);
153
157
  return;
154
158
  }
@@ -320,7 +324,7 @@ var b = 0, x = 1, S = 2, C = 3, w = 4, T = 5, E = 6, D = 8, ne = 10, O = 0, k =
320
324
  return u * u;
321
325
  }
322
326
  bucketOf(e, t) {
323
- let n = (Math.imul(e, oe) ^ Math.imul(t, se)) >>> 0, r = this.bucketMask;
327
+ let n = (Math.imul(e, se) ^ Math.imul(t, ce)) >>> 0, r = this.bucketMask;
324
328
  return r >= 0 ? n & r : n % this.bucketCount;
325
329
  }
326
330
  bucketOffset(e) {
@@ -359,7 +363,7 @@ var b = 0, x = 1, S = 2, C = 3, w = 4, T = 5, E = 6, D = 8, ne = 10, O = 0, k =
359
363
  }
360
364
  }
361
365
  ensureBucketCapacity(e) {
362
- let t = Math.ceil(e * Q / Z), n = Math.min(_e(t), this.maxBucketCount);
366
+ let t = Math.ceil(e * Q / Z), n = Math.min(ve(t), this.maxBucketCount);
363
367
  if (!(n <= Atomics.load(this.firstBlock.data, w))) {
364
368
  o(this.tableLock);
365
369
  try {
@@ -532,24 +536,24 @@ var b = 0, x = 1, S = 2, C = 3, w = 4, T = 5, E = 6, D = 8, ne = 10, O = 0, k =
532
536
  return p(a, c), i;
533
537
  }
534
538
  };
535
- function me(e) {
536
- let t = l(e ?? fe);
539
+ function he(e) {
540
+ let t = l(e ?? pe);
537
541
  if (t !== c.float32 && t !== c.float64) throw Error("SharedSpatialMap must be Float32Array or Float64Array");
538
542
  return t;
539
543
  }
540
- function he(e) {
544
+ function ge(e) {
541
545
  return u(e) === 2 ? Float64Array : Float32Array;
542
546
  }
543
- function ge(e) {
547
+ function _e(e) {
544
548
  return e === c.float32 ? ie : ae;
545
549
  }
546
550
  function $(e) {
547
551
  return e & e - 1 ? -1 : e - 1;
548
552
  }
549
- function _e(e) {
553
+ function ve(e) {
550
554
  return 2 ** Math.ceil(Math.log2(Math.max(1, e)));
551
555
  }
552
556
  //#endregion
553
- export { pe as default };
557
+ export { me as default };
554
558
 
555
559
  //# sourceMappingURL=shared-spatial-map.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"shared-spatial-map.js","names":["AllocatedMemory","SharedMap","SharedStack","SharedPool","BUCKET_STACK_POINTER_INDEX","ENTITY_POOL_POINTER_INDEX","SLOT_POOL_POINTER_INDEX","MAP_POINTER_INDEX","BUCKET_COUNT_INDEX","TYPE_INDEX","CONFIG_INDEX","TABLE_LOCK_INDEX","MAX_BUCKET_COUNT_INDEX","BUCKET_LOCK_OFFSET","BUCKET_HEAD_OFFSET","BUCKET_SIZE","BUCKET_SEGMENT_SHIFT","BUCKETS_PER_SEGMENT","BUCKET_SEGMENT_MASK","SLOT_ID","SLOT_COL","SLOT_ROW","SLOT_ANCHOR_COL","SLOT_ANCHOR_ROW","SLOT_NEXT","SLOT_ENTITY_INDEX","SLOT_FIELDS","ENTITY_MIN_COL","ENTITY_MIN_ROW","ENTITY_MAX_COL","ENTITY_MAX_ROW","ENTITY_X","ENTITY_Y","ENTITY_WIDTH","ENTITY_HEIGHT","ENTITY_FIELDS","FLOAT32_LIMIT","FLOAT32_NULL_INDEX","FLOAT64_NULL_INDEX","HASH_PRIME_COL","HASH_PRIME_ROW","DEFAULT_GRID_SIZE","DEFAULT_BUCKET_COUNT","BUCKET_LOAD_NUMERATOR","BUCKET_LOAD_DENOMINATOR","DEFAULT_MAX_ENTITIES","DEFAULT_SLOTS_PER_ENTITY","DEFAULT_COORD_TYPE","SharedSpatialMap","memory","config","getPointer","loadRawPointer","nullIndexFor","gridSize","maxEntities","maxSlots","nextPowerOfTwo","getCoordTypeCode","ARRAY_TYPE","entityPoolConfig","getCoordTypeConstructor","getSpatialPoolChunkSize","getByteMultipler","slotPoolConfig","entityPoolAllocateCount","slotPoolAllocateCount","mapAllocateCount","allocateCount","configView","storeRawPointer","entityPoolBlock","slotPoolBlock","mapBlock","getSpatialMapCapacity","maskFor","total","pointer","id","x","y","width","height","byId","entity","entities","unique","existing","mappings","additionalSlots","newCount","i","nextEntityIndex","nextSlotIndex","minCol","minRow","maxCol","maxRow","entityIndex","block","offset","row","col","off","oldMinCol","oldMinRow","oldMaxCol","oldMaxRow","out","filter","start","qMinCol","qMinRow","qMaxCol","qMaxRow","nullIndex","bucket","bucketInts","bucketHeads","write","read","maxResults","maxDistance","maxDistanceSquared","candidates","candidate","cellIds","seenEntities","startCol","startRow","nearest","visit","distance","radius","limit","candidateLimit","top","bottom","segmentIndex","heads","keys","key","makeArrayView","cellSize","left","centerCol","right","centerRow","hash","mask","cached","ints","needed","bucketCount","newBucketCount","slotCount","target","slotIndex","slot","headIndex","before","pool","lockIndex","unlock","slotOffset","anchorCol","anchorRow","ovMinCol","ovMaxCol","ovMinRow","ovMaxRow","hasOverlap","inOverlapRow","index","entityOffset","queryCol","queryRow","addCandidate","minX","minY","distanceSquaredToRect","prev","next","code","getArrayTypeCode","type","typeCode","value"],"sources":["../../src/spatial/shared-spatial-map.ts"],"sourcesContent":["import type { SharedAllocatedMemory } from '../allocated-memory';\nimport AllocatedMemory from '../allocated-memory';\nimport type MemoryHeap from '../memory-heap';\nimport SharedPool, { type SharedPoolConfig } from '../shared-pool';\nimport SharedMap from '../shared-map';\nimport SharedStack from '../shared-stack';\nimport { lock, unlock } from '../lock/simple-lock';\nimport { readLock, readUnlock, writeLock, writeUnlock } from '../lock/read-write-lock';\nimport { getPointer, loadRawPointer, storeRawPointer } from '../utils/pointer';\nimport { ARRAY_TYPE, getArrayTypeCode, getByteMultipler, makeArrayView } from '../utils/array-type';\nimport {\n\taddCandidate, candidateLimit, distanceSquaredToRect, type NeighborCandidate, type NearestNeighbor,\n} from './spatial-neighbors';\nimport { getSpatialMapCapacity, getSpatialPoolChunkSize, type SpatialEntity } from './spatial-entity';\n\n// Unbounded spatial hash (\"spatial multimap\") built for concurrent updates from many threads. It indexes the same way\n// SharedSpatialGrid does - every entity occupies each virtual cell (of side gridSize) its rect overlaps - but it drops\n// the grid's fixed cols x rows extent so the world is unbounded: cells may sit at any (col, row), positive or negative.\n//\n// A cell (col, row) is not stored at a fixed array index (there is no finite grid to index); instead it is hashed into a\n// growable hash table of buckets and every cell that lands on a bucket shares that bucket's intrusive singly-linked list.\n// Normal operations share a table read lock. When the average slot load grows past 0.5, one writer doubles the bucket\n// count and rebuilds the chains under the exclusive side of that lock. Bucket pages and their pointer stack only grow.\n//\n// Because distinct cells collide onto one bucket, a slot must carry its own cell (col, row): a bucket walk considers only\n// slots whose cell matches the cell being queried. Each (entity, cell) membership is a \"slot\" record in a SharedPool; the\n// entity's occupied cell rectangle is stored once in a separate entity pool, keyed by an id->entityIndex SharedMap, so\n// remove(id) (which only gets an id) knows which cells to unlink from.\n//\n// search returns each entity at most once even though it spans multiple cells: every slot carries the entity's anchor\n// (top-left) cell, and a slot only emits its id when its cell is the top-left cell of the entity that intersects the query\n// (col == max(anchorCol, qMinCol) && row == max(anchorRow, qMinRow)). That cell is always one of the entity's cells and\n// inside the query, so exactly one slot emits - no Set and no per-query mutation, so concurrent queries stay safe.\n//\n// Records are stored at the configured precision. Float32 (the default) keeps everything exact as long as entity ids and\n// both pools' index spaces stay under 2^24 (~16.7M) - every id, pool index, and the NULL sentinel is Float32 exact there\n// - and cell (col, row) stay within [-2^24, 2^24]. Need more (or ids >= 2^24)? Pass type: Float64Array.\n\n// Header (firstBlock) u32 layout\nconst BUCKET_STACK_POINTER_INDEX = 0;\nconst ENTITY_POOL_POINTER_INDEX = 1;\nconst SLOT_POOL_POINTER_INDEX = 2;\nconst MAP_POINTER_INDEX = 3;\nconst BUCKET_COUNT_INDEX = 4;\nconst TYPE_INDEX = 5;\n// config view starts at slot 6 (offset 24 bytes is 8-byte aligned, so a Float64 view is valid): [gridSize]\nconst CONFIG_INDEX = 6;\nconst TABLE_LOCK_INDEX = 8;\nconst MAX_BUCKET_COUNT_INDEX = 10;\n\n// Per-bucket record: [lock, head]\nconst BUCKET_LOCK_OFFSET = 0;\nconst BUCKET_HEAD_OFFSET = 1;\nconst BUCKET_SIZE = 2;\nconst BUCKET_SEGMENT_SHIFT = 8;\nconst BUCKETS_PER_SEGMENT = 1 << BUCKET_SEGMENT_SHIFT;\nconst BUCKET_SEGMENT_MASK = BUCKETS_PER_SEGMENT - 1;\n\n// Slot record fields (one per entity-cell membership). The cell (col, row) is stored explicitly because a bucket mixes\n// slots from every cell that hashes to it, so a walk must filter to the cell it is looking at.\nconst SLOT_ID = 0;\nconst SLOT_COL = 1;\nconst SLOT_ROW = 2;\nconst SLOT_ANCHOR_COL = 3;\nconst SLOT_ANCHOR_ROW = 4;\nconst SLOT_NEXT = 5;\nconst SLOT_ENTITY_INDEX = 6;\nconst SLOT_FIELDS = 7;\n\n// Entity record fields: the occupied cell rectangle\nconst ENTITY_MIN_COL = 0;\nconst ENTITY_MIN_ROW = 1;\nconst ENTITY_MAX_COL = 2;\nconst ENTITY_MAX_ROW = 3;\nconst ENTITY_X = 4;\nconst ENTITY_Y = 5;\nconst ENTITY_WIDTH = 6;\nconst ENTITY_HEIGHT = 7;\nconst ENTITY_FIELDS = 8;\n\n// Largest exact integer range for Float32 coordinates: [0, 2^24). Ids and pool indices must stay under this to survive a\n// Float32 round-trip, so it doubles as the Float32 entity cap and the value just past every valid index.\nconst FLOAT32_LIMIT = 0x1000000;\n// End-of-list / empty-bucket sentinel, chosen per element type so it is exact in the pool's view and above every valid\n// index: 2^24 for Float32 (its first non-representable index), 0xffffffff for Float64 (well within its exact range).\nconst FLOAT32_NULL_INDEX = FLOAT32_LIMIT;\nconst FLOAT64_NULL_INDEX = 0xffffffff;\n\n// Odd primes for the spatial hash (Teschner et al.): mixing col and row through Math.imul then xor scatters neighbouring\n// cells across buckets so a query's cells rarely collide on one bucket.\nconst HASH_PRIME_COL = 73856093;\nconst HASH_PRIME_ROW = 19349663;\n\nconst DEFAULT_GRID_SIZE = 50;\nconst DEFAULT_BUCKET_COUNT = 256;\nconst BUCKET_LOAD_NUMERATOR = 1;\nconst BUCKET_LOAD_DENOMINATOR = 2;\nconst DEFAULT_MAX_ENTITIES = 1_000_000;\n// A spatial-map entity can span several cells, so the slot pool needs headroom over the entity count\nconst DEFAULT_SLOTS_PER_ENTITY = 4;\nconst DEFAULT_COORD_TYPE = Float32Array;\n\ntype CoordArray = Float32Array | Float64Array;\ntype CoordArrayConstructor = Float32ArrayConstructor | Float64ArrayConstructor;\n\nexport default class SharedSpatialMap {\n\tstatic readonly ALLOCATE_COUNT = 11;\n\n\tprivate memory: MemoryHeap;\n\tprivate firstBlock: AllocatedMemory;\n\tprivate entityPool: SharedPool<CoordArray>;\n\tprivate slotPool: SharedPool<CoordArray>;\n\tprivate idMap: SharedMap<number>;\n\tprivate bucketPointers: SharedStack;\n\n\t// Reused to hand a full record to pool.push without allocating a fresh array per operation (single writer per id)\n\tprivate entityScratch: Array<number> = [0, 0, 0, 0, 0, 0, 0, 0];\n\tprivate slotScratch: Array<number> = [0, 0, 0, 0, 0, 0, 0];\n\n\t// Each cached page is viewed as Int32 for locks and Uint32 for head indexes. Both views cover the same bytes.\n\tprivate bucketSegments: Array<Int32Array | undefined> = [];\n\tprivate bucketHeadSegments: Array<Uint32Array | undefined> = [];\n\tprivate tableLock: Int32Array;\n\n\tprivate readonly coordType: number;\n\t// Empty-bucket / end-of-list sentinel for this instance's element type (see FLOAT32_NULL_INDEX)\n\tprivate readonly nullIndex: number;\n\tprivate bucketCount: number;\n\tprivate readonly maxBucketCount: number;\n\t// bucketCount-1 when bucketCount is a power of two, else -1. Lets bucketOf mask (hash & mask) instead of taking a\n\t// variable integer modulo (hash % bucketCount) on the hot path - every insert/update/remove/query cell hashes a bucket.\n\tprivate bucketMask: number;\n\t// Virtual cell size, read once (stored at the configured precision, math done in float64)\n\tprivate readonly cellSize: number;\n\n\t// Number of entities currently tracked\n\tget size(): number {\n\t\treturn this.idMap.length;\n\t}\n\n\tget gridSize(): number {\n\t\treturn this.cellSize;\n\t}\n\tget buckets(): number {\n\t\treturn Atomics.load(this.firstBlock.data, BUCKET_COUNT_INDEX);\n\t}\n\tget maxBuckets(): number {\n\t\treturn this.maxBucketCount;\n\t}\n\n\t// Element type the records are stored in (an ARRAY_TYPE code: float32 or float64)\n\tget type(): number {\n\t\treturn this.coordType;\n\t}\n\n\tconstructor(memory: MemoryHeap, config?: SharedSpatialMapConfig | SharedSpatialMapMemory) {\n\t\tthis.memory = memory;\n\n\t\tif(config && 'firstBlock' in config) {\n\t\t\tthis.firstBlock = new AllocatedMemory(memory, config.firstBlock);\n\t\t\tthis.coordType = this.firstBlock.data[TYPE_INDEX];\n\t\t\tthis.bucketPointers = new SharedStack(memory, {\n\t\t\t\tfirstBlock: getPointer(loadRawPointer(this.firstBlock.data, BUCKET_STACK_POINTER_INDEX), memory.positionBits),\n\t\t\t});\n\t\t\tthis.entityPool = new SharedPool<CoordArray>(memory, {\n\t\t\t\tfirstBlock: getPointer(loadRawPointer(this.firstBlock.data, ENTITY_POOL_POINTER_INDEX), memory.positionBits),\n\t\t\t});\n\t\t\tthis.slotPool = new SharedPool<CoordArray>(memory, {\n\t\t\t\tfirstBlock: getPointer(loadRawPointer(this.firstBlock.data, SLOT_POOL_POINTER_INDEX), memory.positionBits),\n\t\t\t});\n\t\t\tthis.idMap = new SharedMap<number>(memory, {\n\t\t\t\tfirstBlock: getPointer(loadRawPointer(this.firstBlock.data, MAP_POINTER_INDEX), memory.positionBits),\n\t\t\t});\n\t\t\tthis.bucketCount = Atomics.load(this.firstBlock.data, BUCKET_COUNT_INDEX);\n\t\t\tthis.maxBucketCount = this.firstBlock.data[MAX_BUCKET_COUNT_INDEX];\n\t\t\tthis.nullIndex = nullIndexFor(this.coordType);\n\t\t} else {\n\t\t\tlet gridSize = config?.gridSize ?? DEFAULT_GRID_SIZE;\n\t\t\tthis.bucketCount = Math.min(0x40000000, Math.max(1, Math.floor(config?.buckets ?? DEFAULT_BUCKET_COUNT)));\n\t\t\tlet maxEntities = config?.maxEntities ?? DEFAULT_MAX_ENTITIES;\n\t\t\tlet maxSlots = config?.maxSlots ?? maxEntities * DEFAULT_SLOTS_PER_ENTITY;\n\t\t\tthis.maxBucketCount = Math.min(\n\t\t\t\t0x40000000,\n\t\t\t\tMath.max(this.bucketCount, Math.floor(config?.maxBuckets ?? nextPowerOfTwo(maxSlots * 2))),\n\t\t\t);\n\t\t\tthis.coordType = getCoordTypeCode(config?.type);\n\t\t\tthis.nullIndex = nullIndexFor(this.coordType);\n\n\t\t\tif(this.coordType === ARRAY_TYPE.float32 && (maxEntities > FLOAT32_LIMIT || maxSlots > FLOAT32_LIMIT)) {\n\t\t\t\tthrow new Error(`Float32 SharedSpatialMap supports at most ${FLOAT32_LIMIT} entities and slots; pass type: Float64Array for more`);\n\t\t\t}\n\n\t\t\t// Inline the bucket page pointers, pools, and id-map so the stable spine is one contiguous allocation.\n\t\t\tlet entityPoolConfig: SharedPoolConfig<CoordArray> = {\n\t\t\t\ttype: getCoordTypeConstructor(this.coordType),\n\t\t\t\tdataLength: ENTITY_FIELDS,\n\t\t\t\tmaxLength: maxEntities,\n\t\t\t\tmaxChunkSize: getSpatialPoolChunkSize(maxEntities, ENTITY_FIELDS, getByteMultipler(this.coordType), memory.maxAllocationLength),\n\t\t\t};\n\t\t\tlet slotPoolConfig: SharedPoolConfig<CoordArray> = {\n\t\t\t\ttype: getCoordTypeConstructor(this.coordType),\n\t\t\t\tdataLength: SLOT_FIELDS,\n\t\t\t\tmaxLength: maxSlots,\n\t\t\t\tmaxChunkSize: getSpatialPoolChunkSize(maxSlots, SLOT_FIELDS, getByteMultipler(this.coordType), memory.maxAllocationLength),\n\t\t\t};\n\t\t\tlet entityPoolAllocateCount = SharedPool.getAllocateCount(memory, entityPoolConfig);\n\t\t\tlet slotPoolAllocateCount = SharedPool.getAllocateCount(memory, slotPoolConfig);\n\t\t\tlet mapAllocateCount = SharedMap.ALLOCATE_COUNT;\n\t\t\tlet allocateCount = SharedSpatialMap.ALLOCATE_COUNT;\n\t\t\tthis.firstBlock = memory.allocUI32(allocateCount + entityPoolAllocateCount + slotPoolAllocateCount + mapAllocateCount);\n\t\t\tthis.firstBlock.data[BUCKET_COUNT_INDEX] = this.bucketCount;\n\t\t\tthis.firstBlock.data[TYPE_INDEX] = this.coordType;\n\t\t\tthis.firstBlock.data[MAX_BUCKET_COUNT_INDEX] = this.maxBucketCount;\n\n\t\t\tlet configView = this.getConfigView();\n\t\t\tconfigView[0] = gridSize;\n\n\t\t\tthis.bucketPointers = new SharedStack(memory);\n\t\t\tstoreRawPointer(this.firstBlock.data, BUCKET_STACK_POINTER_INDEX, this.bucketPointers.pointer);\n\t\t\tthis.appendBucketSegments(this.bucketCount);\n\n\t\t\tlet entityPoolBlock = this.firstBlock.getSubAllocation(allocateCount, entityPoolAllocateCount);\n\t\t\tthis.entityPool = new SharedPool<CoordArray>(memory, { firstBlock: entityPoolBlock, ...entityPoolConfig });\n\t\t\tstoreRawPointer(this.firstBlock.data, ENTITY_POOL_POINTER_INDEX, entityPoolBlock.pointer);\n\n\t\t\tlet slotPoolBlock = this.firstBlock.getSubAllocation(allocateCount + entityPoolAllocateCount, slotPoolAllocateCount);\n\t\t\tthis.slotPool = new SharedPool<CoordArray>(memory, { firstBlock: slotPoolBlock, ...slotPoolConfig });\n\t\t\tstoreRawPointer(this.firstBlock.data, SLOT_POOL_POINTER_INDEX, slotPoolBlock.pointer);\n\n\t\t\tlet mapBlock = this.firstBlock.getSubAllocation(allocateCount + entityPoolAllocateCount + slotPoolAllocateCount, mapAllocateCount);\n\t\t\tthis.idMap = new SharedMap<number>(memory, { firstBlock: mapBlock, capacity: getSpatialMapCapacity(maxEntities, memory.maxAllocationLength) });\n\t\t\tstoreRawPointer(this.firstBlock.data, MAP_POINTER_INDEX, mapBlock.pointer);\n\t\t}\n\n\t\tthis.bucketMask = maskFor(this.bucketCount);\n\t\tthis.tableLock = new Int32Array(\n\t\t\tthis.firstBlock.data.buffer,\n\t\t\tthis.firstBlock.bufferByteOffset + TABLE_LOCK_INDEX * Uint32Array.BYTES_PER_ELEMENT,\n\t\t\t2,\n\t\t);\n\n\t\tlet configView = this.getConfigView();\n\t\tthis.cellSize = configView[0];\n\n\t}\n\n\tget pointer(): number {\n\t\treturn this.firstBlock.pointer;\n\t}\n\n\tget usedMemory(): number {\n\t\tlet total = this.firstBlock.usedMemory + this.bucketPointers.usedMemory\n\t\t\t+ this.entityPool.usedMemory + this.slotPool.usedMemory + this.idMap.usedMemory;\n\t\tfor(let pointer of this.bucketPointers) {\n\t\t\ttotal += new AllocatedMemory(this.memory, getPointer(pointer, this.memory.positionBits)).usedMemory;\n\t\t}\n\t\treturn total;\n\t}\n\n\tinsert(id: number, x: number, y: number, width = 0, height = 0) {\n\t\treadLock(this.tableLock);\n\t\ttry {\n\t\t\tthis.syncBucketCount();\n\t\t\tthis.insertLocked(id, x, y, width, height);\n\t\t} finally {\n\t\t\treadUnlock(this.tableLock);\n\t\t}\n\t\tthis.growBucketsIfNeeded();\n\t}\n\n\tbulkInsert(entities: Iterable<SpatialEntity>) {\n\t\tlet byId = new Map<number, SpatialEntity>();\n\t\tfor(let entity of entities) {\n\t\t\tbyId.set(entity.id, entity);\n\t\t}\n\t\tlet unique = Array.from(byId.values());\n\t\tlet existing = this.idMap.getAll(unique.map(entity => entity.id));\n\t\tlet mappings: Array<readonly [number, number]> = [];\n\t\tlet additionalSlots = 0;\n\t\tlet newCount = 0;\n\t\tfor(let i = 0; i < unique.length; i++) {\n\t\t\tif(existing[i] === undefined) {\n\t\t\t\tlet entity = unique[i];\n\t\t\t\tnewCount++;\n\t\t\t\tadditionalSlots += (this.colOf(entity.x + (entity.width ?? 0)) - this.colOf(entity.x) + 1)\n\t\t\t\t\t* (this.rowOf(entity.y + (entity.height ?? 0)) - this.rowOf(entity.y) + 1);\n\t\t\t}\n\t\t}\n\t\tthis.ensureBucketCapacity(this.slotPool.length + additionalSlots);\n\t\tlet nextEntityIndex = this.entityPool.reserveContiguous(newCount);\n\t\tlet nextSlotIndex = this.slotPool.reserveContiguous(additionalSlots);\n\t\treadLock(this.tableLock);\n\t\ttry {\n\t\t\tthis.syncBucketCount();\n\t\t\tfor(let i = 0; i < unique.length; i++) {\n\t\t\t\tlet entity = unique[i];\n\t\t\t\tlet width = entity.width ?? 0;\n\t\t\t\tlet height = entity.height ?? 0;\n\t\t\t\tif(existing[i] !== undefined) {\n\t\t\t\t\tthis.updateLocked(entity.id, entity.x, entity.y, width, height);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tlet minCol = this.colOf(entity.x);\n\t\t\t\tlet minRow = this.rowOf(entity.y);\n\t\t\t\tlet maxCol = this.colOf(entity.x + width);\n\t\t\t\tlet maxRow = this.rowOf(entity.y + height);\n\t\t\t\tlet entityIndex = nextEntityIndex++;\n\t\t\t\tlet block = this.entityPool.blockFor(entityIndex);\n\t\t\t\tlet offset = this.entityPool.blockOffset(entityIndex);\n\t\t\t\tblock[offset + ENTITY_MIN_COL] = minCol;\n\t\t\t\tblock[offset + ENTITY_MIN_ROW] = minRow;\n\t\t\t\tblock[offset + ENTITY_MAX_COL] = maxCol;\n\t\t\t\tblock[offset + ENTITY_MAX_ROW] = maxRow;\n\t\t\t\tblock[offset + ENTITY_X] = entity.x;\n\t\t\t\tblock[offset + ENTITY_Y] = entity.y;\n\t\t\t\tblock[offset + ENTITY_WIDTH] = width;\n\t\t\t\tblock[offset + ENTITY_HEIGHT] = height;\n\t\t\t\tmappings.push([entity.id, entityIndex]);\n\t\t\t\tfor(let row = minRow; row <= maxRow; row++) {\n\t\t\t\t\tfor(let col = minCol; col <= maxCol; col++) {\n\t\t\t\t\t\tthis.linkReservedBucket(col, row, nextSlotIndex++, entity.id, entityIndex, minCol, minRow);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.idMap.setAll(mappings);\n\t\t} finally {\n\t\t\treadUnlock(this.tableLock);\n\t\t}\n\t\tthis.growBucketsForBulk();\n\t}\n\n\tprivate insertLocked(id: number, x: number, y: number, width: number, height: number) {\n\t\tif(this.idMap.get(id) !== undefined) {\n\t\t\tthis.updateLocked(id, x, y, width, height);\n\t\t\treturn;\n\t\t}\n\n\t\tlet minCol = this.colOf(x);\n\t\tlet minRow = this.rowOf(y);\n\t\tlet maxCol = this.colOf(x + width);\n\t\tlet maxRow = this.rowOf(y + height);\n\n\t\tlet entity = this.entityScratch;\n\t\tentity[ENTITY_MIN_COL] = minCol;\n\t\tentity[ENTITY_MIN_ROW] = minRow;\n\t\tentity[ENTITY_MAX_COL] = maxCol;\n\t\tentity[ENTITY_MAX_ROW] = maxRow;\n\t\tentity[ENTITY_X] = x;\n\t\tentity[ENTITY_Y] = y;\n\t\tentity[ENTITY_WIDTH] = width;\n\t\tentity[ENTITY_HEIGHT] = height;\n\t\tlet entityIndex = this.entityPool.push(entity);\n\t\tthis.idMap.set(id, entityIndex);\n\n\t\tthis.linkRange(id, entityIndex, minCol, minRow, maxCol, maxRow);\n\t}\n\n\t// Move an entity to a new position. Assumes a single writer per entity id (the standard shared-memory pattern - each\n\t// worker owns a disjoint set of entities), so this entity's record is stable until we lock its bucket(s).\n\tupdate(id: number, x: number, y: number, width = 0, height = 0) {\n\t\tlet entityIndex = this.idMap.get(id);\n\t\tif(entityIndex !== undefined) {\n\t\t\tlet block = this.entityPool.blockFor(entityIndex);\n\t\t\tlet off = this.entityPool.blockOffset(entityIndex);\n\t\t\tlet minCol = this.colOf(x);\n\t\t\tlet minRow = this.rowOf(y);\n\t\t\tlet maxCol = this.colOf(x + width);\n\t\t\tlet maxRow = this.rowOf(y + height);\n\t\t\tif(minCol === block[off + ENTITY_MIN_COL] && minRow === block[off + ENTITY_MIN_ROW]\n\t\t\t\t&& maxCol === block[off + ENTITY_MAX_COL] && maxRow === block[off + ENTITY_MAX_ROW]) {\n\t\t\t\tblock[off + ENTITY_X] = x;\n\t\t\t\tblock[off + ENTITY_Y] = y;\n\t\t\t\tblock[off + ENTITY_WIDTH] = width;\n\t\t\t\tblock[off + ENTITY_HEIGHT] = height;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\treadLock(this.tableLock);\n\t\ttry {\n\t\t\tthis.syncBucketCount();\n\t\t\tthis.updateLocked(id, x, y, width, height);\n\t\t} finally {\n\t\t\treadUnlock(this.tableLock);\n\t\t}\n\t\tthis.growBucketsIfNeeded();\n\t}\n\n\tprivate updateLocked(id: number, x: number, y: number, width: number, height: number) {\n\t\tlet entityIndex = this.idMap.get(id);\n\t\tif(entityIndex === undefined) {\n\t\t\tthis.insertLocked(id, x, y, width, height);\n\t\t\treturn;\n\t\t}\n\n\t\tlet block = this.entityPool.blockFor(entityIndex);\n\t\tlet off = this.entityPool.blockOffset(entityIndex);\n\t\tlet oldMinCol = block[off + ENTITY_MIN_COL];\n\t\tlet oldMinRow = block[off + ENTITY_MIN_ROW];\n\t\tlet oldMaxCol = block[off + ENTITY_MAX_COL];\n\t\tlet oldMaxRow = block[off + ENTITY_MAX_ROW];\n\n\t\tlet minCol = this.colOf(x);\n\t\tlet minRow = this.rowOf(y);\n\t\tlet maxCol = this.colOf(x + width);\n\t\tlet maxRow = this.rowOf(y + height);\n\n\t\t// Same cell rectangle: membership is unchanged, but neighbors still needs the new exact rectangle.\n\t\tif(minCol === oldMinCol && minRow === oldMinRow && maxCol === oldMaxCol && maxRow === oldMaxRow) {\n\t\t\tblock[off + ENTITY_X] = x;\n\t\t\tblock[off + ENTITY_Y] = y;\n\t\t\tblock[off + ENTITY_WIDTH] = width;\n\t\t\tblock[off + ENTITY_HEIGHT] = height;\n\t\t\treturn;\n\t\t}\n\n\t\tthis.relinkRange(id, entityIndex, minCol, minRow, maxCol, maxRow, oldMinCol, oldMinRow, oldMaxCol, oldMaxRow);\n\n\t\tblock[off + ENTITY_MIN_COL] = minCol;\n\t\tblock[off + ENTITY_MIN_ROW] = minRow;\n\t\tblock[off + ENTITY_MAX_COL] = maxCol;\n\t\tblock[off + ENTITY_MAX_ROW] = maxRow;\n\t\tblock[off + ENTITY_X] = x;\n\t\tblock[off + ENTITY_Y] = y;\n\t\tblock[off + ENTITY_WIDTH] = width;\n\t\tblock[off + ENTITY_HEIGHT] = height;\n\t}\n\n\tremove(id: number): boolean {\n\t\treadLock(this.tableLock);\n\t\ttry {\n\t\t\tthis.syncBucketCount();\n\t\t\treturn this.removeLocked(id);\n\t\t} finally {\n\t\t\treadUnlock(this.tableLock);\n\t\t}\n\t}\n\n\tprivate removeLocked(id: number): boolean {\n\t\tlet entityIndex = this.idMap.get(id);\n\t\tif(entityIndex === undefined) {\n\t\t\treturn false;\n\t\t}\n\n\t\tlet block = this.entityPool.blockFor(entityIndex);\n\t\tlet off = this.entityPool.blockOffset(entityIndex);\n\t\tthis.unlinkRange(id, block[off + ENTITY_MIN_COL], block[off + ENTITY_MIN_ROW], block[off + ENTITY_MAX_COL], block[off + ENTITY_MAX_ROW]);\n\n\t\tthis.entityPool.deleteIndex(entityIndex);\n\t\tthis.idMap.delete(id);\n\t\treturn true;\n\t}\n\n\thas(id: number): boolean {\n\t\treturn this.idMap.get(id) !== undefined;\n\t}\n\n\t// Broad-phase query: ids of every entity occupying a cell that overlaps the query rect. Each entity is returned at most\n\t// once (see the anchor-cell dedup in collectBucket); the caller does the narrow-phase overlap check.\n\tsearch(x: number, y: number, width: number, height: number, filter?: (id: number) => boolean): Array<number> {\n\t\tlet out: Array<number> = [];\n\t\tthis.searchInto(out, x, y, width, height, filter);\n\t\treturn out;\n\t}\n\t// Same as search but appends into a caller-owned array so hot loops can reuse it. Returns the array.\n\tsearchInto(out: Array<number>, x: number, y: number, width: number, height: number, filter?: (id: number) => boolean): Array<number> {\n\t\treadLock(this.tableLock);\n\t\ttry {\n\t\t\tthis.syncBucketCount();\n\t\t\treturn this.searchIntoLocked(out, x, y, width, height, filter);\n\t\t} finally {\n\t\t\treadUnlock(this.tableLock);\n\t\t}\n\t}\n\n\tprivate searchIntoLocked(out: Array<number>, x: number, y: number, width: number, height: number, filter?: (id: number) => boolean): Array<number> {\n\t\tlet start = out.length;\n\t\tlet qMinCol = this.colOf(x);\n\t\tlet qMinRow = this.rowOf(y);\n\t\tlet qMaxCol = this.colOf(x + width);\n\t\tlet qMaxRow = this.rowOf(y + height);\n\n\t\tlet nullIndex = this.nullIndex;\n\t\tfor(let row = qMinRow; row <= qMaxRow; row++) {\n\t\t\tfor(let col = qMinCol; col <= qMaxCol; col++) {\n\t\t\t\tlet bucket = this.bucketOf(col, row);\n\t\t\t\tlet bucketInts = this.getBucketSegment(bucket);\n\t\t\t\t// Inline the empty-bucket peek (the common case) so an empty bucket never pays for a method call - most cells in\n\t\t\t\t// a typical query hash to empty buckets, so this skip dominates query cost.\n\t\t\t\tlet offset = this.bucketOffset(bucket);\n\t\t\t\tlet bucketHeads = this.bucketHeadSegments[bucket >>> BUCKET_SEGMENT_SHIFT]!;\n\t\t\t\tif(bucketHeads[offset + BUCKET_HEAD_OFFSET] === nullIndex) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tthis.collectBucket(out, bucketInts, bucketHeads, offset, col, row, qMinCol, qMinRow);\n\t\t\t}\n\t\t}\n\t\tif(filter !== undefined) {\n\t\t\tlet write = start;\n\t\t\tfor(let read = start; read < out.length; read++) {\n\t\t\t\tlet id = out[read];\n\t\t\t\tif(filter(id)) {\n\t\t\t\t\tout[write++] = id;\n\t\t\t\t}\n\t\t\t}\n\t\t\tout.length = write;\n\t\t}\n\t\treturn out;\n\t}\n\n\tneighbors(x: number, y: number, maxResults: number, maxDistance: number, filter?: (id: number) => boolean): Array<number> {\n\t\treadLock(this.tableLock);\n\t\ttry {\n\t\t\tthis.syncBucketCount();\n\t\t\treturn this.neighborsLocked(x, y, maxResults, maxDistance, filter);\n\t\t} finally {\n\t\t\treadUnlock(this.tableLock);\n\t\t}\n\t}\n\n\tprivate neighborsLocked(x: number, y: number, maxResults: number, maxDistance: number, filter?: (id: number) => boolean): Array<number> {\n\t\tif(maxResults <= 0 || maxDistance < 0 || Number.isNaN(maxResults) || Number.isNaN(maxDistance)) {\n\t\t\treturn [];\n\t\t}\n\n\t\tmaxResults = Math.floor(maxResults);\n\t\tif(maxResults === 0) {\n\t\t\treturn [];\n\t\t}\n\t\tlet maxDistanceSquared = maxDistance * maxDistance;\n\t\tlet candidates: Array<NeighborCandidate> = [];\n\t\tif(!Number.isFinite(maxDistance)) {\n\t\t\tfor(let [id, entityIndex] of this.idMap) {\n\t\t\t\tif(filter === undefined || filter(id)) {\n\t\t\t\t\tthis.addEntityCandidate(candidates, id, entityIndex, x, y, maxResults, maxDistanceSquared);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn candidates.map(candidate => candidate.id);\n\t\t}\n\n\t\tlet cellIds: Array<number> = [];\n\t\tlet seenEntities = filter !== undefined && maxResults > 1 ? new Set<number>() : undefined;\n\t\tlet startCol = this.colOf(x);\n\t\tlet startRow = this.rowOf(y);\n\t\tlet nearest: NearestNeighbor = { id: undefined, distance: maxDistanceSquared };\n\t\tlet visit = (col: number, row: number) => {\n\t\t\tif(filter === undefined) {\n\t\t\t\tif(maxResults === 1) {\n\t\t\t\t\tthis.collectNearestBucket(nearest, col, row, x, y);\n\t\t\t\t} else {\n\t\t\t\t\tthis.collectNeighborCandidates(\n\t\t\t\t\t\tcandidates, col, row, startCol, startRow, x, y, maxResults, maxDistanceSquared,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcellIds.length = 0;\n\t\t\tthis.collectNeighborBucket(cellIds, col, row);\n\t\t\tfor(let id of cellIds) {\n\t\t\t\tif(seenEntities?.has(id)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tseenEntities?.add(id);\n\t\t\t\tlet entityIndex = this.idMap.get(id);\n\t\t\t\tif(entityIndex !== undefined && (filter === undefined || filter(id))) {\n\t\t\t\t\tif(maxResults === 1) {\n\t\t\t\t\t\tlet distance = this.entityDistanceSquared(entityIndex, x, y);\n\t\t\t\t\t\tif(distance < nearest.distance\n\t\t\t\t\t\t\t|| (distance === nearest.distance && (nearest.id === undefined || id < nearest.id))) {\n\t\t\t\t\t\t\tnearest.id = id;\n\t\t\t\t\t\t\tnearest.distance = distance;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.addEntityCandidate(candidates, id, entityIndex, x, y, maxResults, maxDistanceSquared);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\tfor(let radius = 0; ; radius++) {\n\t\t\tlet limit = maxResults === 1 ? nearest.distance : candidateLimit(candidates, maxResults, maxDistanceSquared);\n\t\t\tif(this.mapRingDistanceSquared(x, y, startCol, startRow, radius) > limit) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif(radius === 0) {\n\t\t\t\tvisit(startCol, startRow);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlet minCol = startCol - radius;\n\t\t\tlet maxCol = startCol + radius;\n\t\t\tlet top = startRow - radius;\n\t\t\tlet bottom = startRow + radius;\n\t\t\tfor(let col = minCol; col <= maxCol; col++) {\n\t\t\t\tvisit(col, top);\n\t\t\t\tvisit(col, bottom);\n\t\t\t}\n\t\t\tfor(let row = top + 1; row < bottom; row++) {\n\t\t\t\tvisit(minCol, row);\n\t\t\t\tvisit(maxCol, row);\n\t\t\t}\n\t\t}\n\n\t\treturn maxResults === 1 ? nearest.id === undefined ? [] : [nearest.id] : candidates.map(candidate => candidate.id);\n\t}\n\n\tclear() {\n\t\twriteLock(this.tableLock);\n\t\ttry {\n\t\t\tthis.syncBucketCount();\n\t\t\tfor(let segmentIndex = 0; segmentIndex < this.bucketPointers.length; segmentIndex++) {\n\t\t\t\tthis.getBucketSegment(segmentIndex << BUCKET_SEGMENT_SHIFT);\n\t\t\t\tlet heads = this.bucketHeadSegments[segmentIndex]!;\n\t\t\t\tfor(let i = BUCKET_HEAD_OFFSET; i < heads.length; i += BUCKET_SIZE) {\n\t\t\t\t\theads[i] = this.nullIndex;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.entityPool.clear();\n\t\t\tthis.slotPool.clear();\n\t\t\tlet keys: Array<number> = [];\n\t\t\tfor(let [key] of this.idMap) {\n\t\t\t\tkeys.push(key);\n\t\t\t}\n\t\t\tfor(let key of keys) {\n\t\t\t\tthis.idMap.delete(key);\n\t\t\t}\n\t\t} finally {\n\t\t\twriteUnlock(this.tableLock);\n\t\t}\n\t}\n\n\tfree() {\n\t\tfor(let pointer of this.bucketPointers) {\n\t\t\tnew AllocatedMemory(this.memory, getPointer(pointer, this.memory.positionBits)).free();\n\t\t}\n\t\tthis.bucketPointers.free();\n\t\tthis.entityPool.free();\n\t\tthis.slotPool.free();\n\t\tthis.idMap.free();\n\t\tthis.firstBlock.free();\n\t}\n\n\tgetSharedMemory(): SharedSpatialMapMemory {\n\t\treturn {\n\t\t\tfirstBlock: this.firstBlock.getSharedMemory(),\n\t\t};\n\t}\n\n\tprivate getConfigView(): CoordArray {\n\t\treturn makeArrayView(\n\t\t\tthis.coordType,\n\t\t\tthis.firstBlock.data.buffer,\n\t\t\tthis.firstBlock.bufferByteOffset + CONFIG_INDEX * Uint32Array.BYTES_PER_ELEMENT,\n\t\t\t1,\n\t\t) as CoordArray;\n\t}\n\n\tprivate colOf(x: number): number {\n\t\treturn Math.floor(x / this.cellSize);\n\t}\n\tprivate rowOf(y: number): number {\n\t\treturn Math.floor(y / this.cellSize);\n\t}\n\tprivate mapRingDistanceSquared(x: number, y: number, centerCol: number, centerRow: number, radius: number): number {\n\t\tif(radius === 0) {\n\t\t\treturn 0;\n\t\t}\n\t\tlet cellSize = this.cellSize;\n\t\tlet left = x - (centerCol - radius + 1) * cellSize;\n\t\tlet right = (centerCol + radius) * cellSize - x;\n\t\tlet top = y - (centerRow - radius + 1) * cellSize;\n\t\tlet bottom = (centerRow + radius) * cellSize - y;\n\t\tlet distance = Math.min(left, right, top, bottom);\n\t\treturn distance * distance;\n\t}\n\t// Hash a virtual cell to the current bucket count. The table read lock keeps this count stable for the operation.\n\tprivate bucketOf(col: number, row: number): number {\n\t\tlet hash = (Math.imul(col, HASH_PRIME_COL) ^ Math.imul(row, HASH_PRIME_ROW)) >>> 0;\n\t\tlet mask = this.bucketMask;\n\t\treturn mask >= 0 ? hash & mask : hash % this.bucketCount;\n\t}\n\n\tprivate bucketOffset(bucket: number): number {\n\t\treturn (bucket & BUCKET_SEGMENT_MASK) * BUCKET_SIZE;\n\t}\n\n\tprivate getBucketSegment(bucket: number): Int32Array {\n\t\tlet segmentIndex = bucket >>> BUCKET_SEGMENT_SHIFT;\n\t\tlet cached = this.bucketSegments[segmentIndex];\n\t\tif(cached !== undefined) {\n\t\t\treturn cached;\n\t\t}\n\n\t\tlet pointer = this.bucketPointers.at(segmentIndex);\n\t\tlet block = new AllocatedMemory(this.memory, getPointer(pointer, this.memory.positionBits));\n\t\t// A segment is exactly BUCKETS_PER_SEGMENT * BUCKET_SIZE u32s. Never size the view by block.data.length: a production\n\t\t// build leaves that view unbounded (it runs to the end of the buffer), so the head-clearing loops below would write\n\t\t// across every allocation that follows this block and stomp their headers.\n\t\tlet ints = new Int32Array(block.data.buffer, block.bufferByteOffset, BUCKETS_PER_SEGMENT * BUCKET_SIZE);\n\t\tthis.bucketHeadSegments[segmentIndex] = new Uint32Array(block.data.buffer, block.bufferByteOffset, BUCKETS_PER_SEGMENT * BUCKET_SIZE);\n\t\tthis.bucketSegments[segmentIndex] = ints;\n\t\treturn ints;\n\t}\n\n\tprivate appendBucketSegments(bucketCount: number) {\n\t\tlet needed = Math.ceil(bucketCount / BUCKETS_PER_SEGMENT);\n\t\twhile(this.bucketPointers.length < needed) {\n\t\t\tlet block = this.memory.allocUI32(BUCKETS_PER_SEGMENT * BUCKET_SIZE);\n\t\t\tlet heads = new Uint32Array(block.data.buffer, block.bufferByteOffset, BUCKETS_PER_SEGMENT * BUCKET_SIZE);\n\t\t\tfor(let i = BUCKET_HEAD_OFFSET; i < heads.length; i += BUCKET_SIZE) {\n\t\t\t\theads[i] = this.nullIndex;\n\t\t\t}\n\t\t\tthis.bucketPointers.push(block.pointer);\n\t\t}\n\t}\n\n\tprivate syncBucketCount() {\n\t\tlet bucketCount = Atomics.load(this.firstBlock.data, BUCKET_COUNT_INDEX);\n\t\tif(bucketCount !== this.bucketCount) {\n\t\t\tthis.bucketCount = bucketCount;\n\t\t\tthis.bucketMask = maskFor(bucketCount);\n\t\t}\n\t}\n\n\tprivate growBucketsIfNeeded() {\n\t\tlet bucketCount = Atomics.load(this.firstBlock.data, BUCKET_COUNT_INDEX);\n\t\tif(bucketCount >= this.maxBucketCount\n\t\t\t|| this.slotPool.length * BUCKET_LOAD_DENOMINATOR <= bucketCount * BUCKET_LOAD_NUMERATOR) {\n\t\t\treturn;\n\t\t}\n\n\t\twriteLock(this.tableLock);\n\t\ttry {\n\t\t\tthis.syncBucketCount();\n\t\t\tif(this.slotPool.length * BUCKET_LOAD_DENOMINATOR <= this.bucketCount * BUCKET_LOAD_NUMERATOR) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlet newBucketCount = Math.min(this.bucketCount * 2, this.maxBucketCount);\n\t\t\tif(newBucketCount === this.bucketCount) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.resizeBuckets(newBucketCount);\n\t\t} finally {\n\t\t\twriteUnlock(this.tableLock);\n\t\t}\n\t}\n\n\tprivate ensureBucketCapacity(slotCount: number) {\n\t\tlet needed = Math.ceil(slotCount * BUCKET_LOAD_DENOMINATOR / BUCKET_LOAD_NUMERATOR);\n\t\tlet target = Math.min(nextPowerOfTwo(needed), this.maxBucketCount);\n\t\tif(target <= Atomics.load(this.firstBlock.data, BUCKET_COUNT_INDEX)) {\n\t\t\treturn;\n\t\t}\n\t\twriteLock(this.tableLock);\n\t\ttry {\n\t\t\tthis.syncBucketCount();\n\t\t\tif(target > this.bucketCount) {\n\t\t\t\tthis.resizeBuckets(target);\n\t\t\t}\n\t\t} finally {\n\t\t\twriteUnlock(this.tableLock);\n\t\t}\n\t}\n\n\tprivate resizeBuckets(newBucketCount: number) {\n\t\tthis.appendBucketSegments(newBucketCount);\n\t\tfor(let segmentIndex = 0; segmentIndex < this.bucketPointers.length; segmentIndex++) {\n\t\t\tthis.getBucketSegment(segmentIndex << BUCKET_SEGMENT_SHIFT);\n\t\t\tlet heads = this.bucketHeadSegments[segmentIndex]!;\n\t\t\tfor(let i = BUCKET_HEAD_OFFSET; i < heads.length; i += BUCKET_SIZE) {\n\t\t\t\theads[i] = this.nullIndex;\n\t\t\t}\n\t\t}\n\n\t\tthis.bucketCount = newBucketCount;\n\t\tthis.bucketMask = maskFor(newBucketCount);\n\t\tfor(let [slotIndex, slot] of this.slotPool.entries()) {\n\t\t\tlet bucket = this.bucketOf(slot[SLOT_COL], slot[SLOT_ROW]);\n\t\t\tlet heads = this.bucketHeadSegments[bucket >>> BUCKET_SEGMENT_SHIFT]!;\n\t\t\tlet headIndex = this.bucketOffset(bucket) + BUCKET_HEAD_OFFSET;\n\t\t\tslot[SLOT_NEXT] = heads[headIndex];\n\t\t\theads[headIndex] = slotIndex;\n\t\t}\n\t\tAtomics.store(this.firstBlock.data, BUCKET_COUNT_INDEX, newBucketCount);\n\t}\n\n\tprivate growBucketsForBulk() {\n\t\twhile(true) {\n\t\t\tlet before = Atomics.load(this.firstBlock.data, BUCKET_COUNT_INDEX);\n\t\t\tthis.growBucketsIfNeeded();\n\t\t\tif(Atomics.load(this.firstBlock.data, BUCKET_COUNT_INDEX) === before) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Push one slot into each cell of the rectangle, carrying the cell's own (col, row) and the entity's top-left cell as\n\t// the dedup anchor. Every slot shares the id and anchor, so those are filled once before the loop.\n\tprivate linkRange(id: number, entityIndex: number, minCol: number, minRow: number, maxCol: number, maxRow: number) {\n\t\tlet slot = this.slotScratch;\n\t\tslot[SLOT_ID] = id;\n\t\tslot[SLOT_ANCHOR_COL] = minCol;\n\t\tslot[SLOT_ANCHOR_ROW] = minRow;\n\t\tslot[SLOT_NEXT] = this.nullIndex;\n\t\tslot[SLOT_ENTITY_INDEX] = entityIndex;\n\n\t\tfor(let row = minRow; row <= maxRow; row++) {\n\t\t\tfor(let col = minCol; col <= maxCol; col++) {\n\t\t\t\tthis.linkIntoBucket(col, row, slot);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Push a fresh slot for cell (col, row) - carrying the shared record in slot plus that cell's own coords - onto the\n\t// head of its bucket.\n\tprivate linkIntoBucket(col: number, row: number, slot: Array<number>) {\n\t\tslot[SLOT_COL] = col;\n\t\tslot[SLOT_ROW] = row;\n\t\tlet pool = this.slotPool;\n\t\tlet slotIndex = pool.push(slot);\n\t\tlet bucket = this.bucketOf(col, row);\n\t\tlet bucketInts = this.getBucketSegment(bucket);\n\t\tlet bucketHeads = this.bucketHeadSegments[bucket >>> BUCKET_SEGMENT_SHIFT]!;\n\t\tlet offset = this.bucketOffset(bucket);\n\t\tlet lockIndex = offset + BUCKET_LOCK_OFFSET;\n\t\tlock(bucketInts, lockIndex);\n\t\tlet headIndex = offset + BUCKET_HEAD_OFFSET;\n\t\tpool.set(slotIndex, SLOT_NEXT, bucketHeads[headIndex]);\n\t\tbucketHeads[headIndex] = slotIndex;\n\t\tunlock(bucketInts, lockIndex);\n\t}\n\n\tprivate linkReservedBucket(\n\t\tcol: number, row: number, slotIndex: number, id: number, entityIndex: number, anchorCol: number, anchorRow: number,\n\t) {\n\t\tlet pool = this.slotPool;\n\t\tlet block = pool.blockFor(slotIndex);\n\t\tlet slotOffset = pool.blockOffset(slotIndex);\n\t\tblock[slotOffset + SLOT_ID] = id;\n\t\tblock[slotOffset + SLOT_COL] = col;\n\t\tblock[slotOffset + SLOT_ROW] = row;\n\t\tblock[slotOffset + SLOT_ANCHOR_COL] = anchorCol;\n\t\tblock[slotOffset + SLOT_ANCHOR_ROW] = anchorRow;\n\t\tblock[slotOffset + SLOT_ENTITY_INDEX] = entityIndex;\n\t\tlet bucket = this.bucketOf(col, row);\n\t\tlet bucketInts = this.getBucketSegment(bucket);\n\t\tlet bucketHeads = this.bucketHeadSegments[bucket >>> BUCKET_SEGMENT_SHIFT]!;\n\t\tlet offset = this.bucketOffset(bucket);\n\t\tlet lockIndex = offset + BUCKET_LOCK_OFFSET;\n\t\tlock(bucketInts, lockIndex);\n\t\tlet headIndex = offset + BUCKET_HEAD_OFFSET;\n\t\tblock[slotOffset + SLOT_NEXT] = bucketHeads[headIndex];\n\t\tbucketHeads[headIndex] = slotIndex;\n\t\tunlock(bucketInts, lockIndex);\n\t}\n\n\t// Move an entity's slots from its old cell rectangle to a new one, touching only the cells that actually change. A cell\n\t// in both rectangles keeps its slot and just gets its anchor refreshed; new-only cells are linked before old-only cells\n\t// are unlinked, so a query racing the move never sees the entity in neither rectangle. Doing it this way (rather than\n\t// linkRange(new) + unlinkRange(old)) is what keeps overlap cells correct: unlinkFromBucket matches by id + cell and\n\t// would splice out the freshly linked slot in an overlap cell, leaving the stale one behind and corrupting the anchor.\n\tprivate relinkRange(id: number, entityIndex: number, minCol: number, minRow: number, maxCol: number, maxRow: number, oldMinCol: number, oldMinRow: number, oldMaxCol: number, oldMaxRow: number) {\n\t\tlet ovMinCol = minCol > oldMinCol ? minCol : oldMinCol;\n\t\tlet ovMaxCol = maxCol < oldMaxCol ? maxCol : oldMaxCol;\n\t\tlet ovMinRow = minRow > oldMinRow ? minRow : oldMinRow;\n\t\tlet ovMaxRow = maxRow < oldMaxRow ? maxRow : oldMaxRow;\n\t\tlet hasOverlap = ovMinCol <= ovMaxCol && ovMinRow <= ovMaxRow;\n\n\t\tlet slot = this.slotScratch;\n\t\tslot[SLOT_ID] = id;\n\t\tslot[SLOT_ANCHOR_COL] = minCol;\n\t\tslot[SLOT_ANCHOR_ROW] = minRow;\n\t\tslot[SLOT_NEXT] = this.nullIndex;\n\t\tslot[SLOT_ENTITY_INDEX] = entityIndex;\n\n\t\tfor(let row = minRow; row <= maxRow; row++) {\n\t\t\tlet inOverlapRow = hasOverlap && row >= ovMinRow && row <= ovMaxRow;\n\t\t\tfor(let col = minCol; col <= maxCol; col++) {\n\t\t\t\tif(inOverlapRow && col >= ovMinCol && col <= ovMaxCol) {\n\t\t\t\t\tthis.refreshAnchorInBucket(this.bucketOf(col, row), id, col, row, minCol, minRow);\n\t\t\t\t} else {\n\t\t\t\t\tthis.linkIntoBucket(col, row, slot);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tlet nullIndex = this.nullIndex;\n\t\tlet pool = this.slotPool;\n\t\tfor(let row = oldMinRow; row <= oldMaxRow; row++) {\n\t\t\tlet inOverlapRow = hasOverlap && row >= ovMinRow && row <= ovMaxRow;\n\t\t\tfor(let col = oldMinCol; col <= oldMaxCol; col++) {\n\t\t\t\tif(inOverlapRow && col >= ovMinCol && col <= ovMaxCol) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tlet slotIndex = this.unlinkFromBucket(this.bucketOf(col, row), id, col, row);\n\t\t\t\tif(slotIndex !== nullIndex) {\n\t\t\t\t\tpool.deleteIndex(slotIndex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Refresh the anchor on this entity's slot already linked for cell (col, row) (single writer per id, so it is present).\n\tprivate refreshAnchorInBucket(bucket: number, id: number, col: number, row: number, anchorCol: number, anchorRow: number) {\n\t\tlet nullIndex = this.nullIndex;\n\t\tlet bucketInts = this.getBucketSegment(bucket);\n\t\tlet offset = this.bucketOffset(bucket);\n\t\tlet lockIndex = offset + BUCKET_LOCK_OFFSET;\n\t\tlock(bucketInts, lockIndex);\n\t\tlet pool = this.slotPool;\n\t\tlet bucketHeads = this.bucketHeadSegments[bucket >>> BUCKET_SEGMENT_SHIFT]!;\n\t\tlet index = bucketHeads[offset + BUCKET_HEAD_OFFSET];\n\t\twhile(index !== nullIndex) {\n\t\t\tlet block = pool.blockFor(index);\n\t\t\tlet off = pool.blockOffset(index);\n\t\t\tif(block[off + SLOT_ID] === id && block[off + SLOT_COL] === col && block[off + SLOT_ROW] === row) {\n\t\t\t\tblock[off + SLOT_ANCHOR_COL] = anchorCol;\n\t\t\t\tblock[off + SLOT_ANCHOR_ROW] = anchorRow;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tindex = block[off + SLOT_NEXT];\n\t\t}\n\t\tunlock(bucketInts, lockIndex);\n\t}\n\n\t// Remove and recycle this entity's slot from each cell of the rectangle.\n\tprivate unlinkRange(id: number, minCol: number, minRow: number, maxCol: number, maxRow: number) {\n\t\tlet nullIndex = this.nullIndex;\n\t\tlet pool = this.slotPool;\n\t\tfor(let row = minRow; row <= maxRow; row++) {\n\t\t\tfor(let col = minCol; col <= maxCol; col++) {\n\t\t\t\tlet slotIndex = this.unlinkFromBucket(this.bucketOf(col, row), id, col, row);\n\t\t\t\tif(slotIndex !== nullIndex) {\n\t\t\t\t\tpool.deleteIndex(slotIndex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Caller has already peeked the head unlocked and skipped empty buckets (an empty bucket has nothing to read, so the\n\t// lock and its atomics would be pure overhead). Taking the lock only to walk a non-empty bucket preserves the snapshot\n\t// guarantee - a concurrent insert we race is simply not in this snapshot.\n\tprivate collectBucket(\n\t\tout: Array<number>, bucketInts: Int32Array, bucketHeads: Uint32Array, offset: number,\n\t\tcol: number, row: number, qMinCol: number, qMinRow: number,\n\t) {\n\t\tlet nullIndex = this.nullIndex;\n\t\tlet headIndex = offset + BUCKET_HEAD_OFFSET;\n\t\tlet lockIndex = offset + BUCKET_LOCK_OFFSET;\n\t\tlock(bucketInts, lockIndex);\n\t\tlet index = bucketHeads[headIndex];\n\t\tlet pool = this.slotPool;\n\t\twhile(index !== nullIndex) {\n\t\t\t// One chunk resolve per slot covers every field it reads\n\t\t\tlet block = pool.blockFor(index);\n\t\t\tlet off = pool.blockOffset(index);\n\t\t\t// Other cells hash to this bucket too; only slots living in the cell being queried count.\n\t\t\tif(block[off + SLOT_COL] === col && block[off + SLOT_ROW] === row) {\n\t\t\t\t// Emit only from the entity's top-left cell that intersects this query, so a multi-cell entity is returned once.\n\t\t\t\tlet anchorCol = block[off + SLOT_ANCHOR_COL];\n\t\t\t\tlet anchorRow = block[off + SLOT_ANCHOR_ROW];\n\t\t\t\tlet tlCol = anchorCol > qMinCol ? anchorCol : qMinCol;\n\t\t\t\tlet tlRow = anchorRow > qMinRow ? anchorRow : qMinRow;\n\t\t\t\tif(col === tlCol && row === tlRow) {\n\t\t\t\t\tout.push(block[off + SLOT_ID]);\n\t\t\t\t}\n\t\t\t}\n\t\t\tindex = block[off + SLOT_NEXT];\n\t\t}\n\t\tunlock(bucketInts, lockIndex);\n\t}\n\n\tprivate collectNeighborBucket(out: Array<number>, col: number, row: number) {\n\t\tlet bucket = this.bucketOf(col, row);\n\t\tlet bucketInts = this.getBucketSegment(bucket);\n\t\tlet bucketHeads = this.bucketHeadSegments[bucket >>> BUCKET_SEGMENT_SHIFT]!;\n\t\tlet offset = this.bucketOffset(bucket);\n\t\tlet headIndex = offset + BUCKET_HEAD_OFFSET;\n\t\tif(bucketHeads[headIndex] === this.nullIndex) {\n\t\t\treturn;\n\t\t}\n\t\tlet lockIndex = offset + BUCKET_LOCK_OFFSET;\n\t\tlock(bucketInts, lockIndex);\n\t\tlet index = bucketHeads[headIndex];\n\t\twhile(index !== this.nullIndex) {\n\t\t\tlet block = this.slotPool.blockFor(index);\n\t\t\tlet off = this.slotPool.blockOffset(index);\n\t\t\tif(block[off + SLOT_COL] === col && block[off + SLOT_ROW] === row) {\n\t\t\t\tout.push(block[off + SLOT_ID]);\n\t\t\t}\n\t\t\tindex = block[off + SLOT_NEXT];\n\t\t}\n\t\tunlock(bucketInts, lockIndex);\n\t}\n\n\tprivate collectNearestBucket(\n\t\tnearest: NearestNeighbor, col: number, row: number, x: number, y: number,\n\t) {\n\t\tlet bucket = this.bucketOf(col, row);\n\t\tlet bucketInts = this.getBucketSegment(bucket);\n\t\tlet bucketHeads = this.bucketHeadSegments[bucket >>> BUCKET_SEGMENT_SHIFT]!;\n\t\tlet offset = this.bucketOffset(bucket);\n\t\tlet headIndex = offset + BUCKET_HEAD_OFFSET;\n\t\tif(bucketHeads[headIndex] === this.nullIndex) {\n\t\t\treturn;\n\t\t}\n\t\tlet lockIndex = offset + BUCKET_LOCK_OFFSET;\n\t\tlock(bucketInts, lockIndex);\n\t\tlet index = bucketHeads[headIndex];\n\t\twhile(index !== this.nullIndex) {\n\t\t\tlet slot = this.slotPool.blockFor(index);\n\t\t\tlet off = this.slotPool.blockOffset(index);\n\t\t\tif(slot[off + SLOT_COL] === col && slot[off + SLOT_ROW] === row) {\n\t\t\t\tlet entityIndex = slot[off + SLOT_ENTITY_INDEX];\n\t\t\t\tlet entity = this.entityPool.blockFor(entityIndex);\n\t\t\t\tlet entityOffset = this.entityPool.blockOffset(entityIndex);\n\t\t\t\tlet id = slot[off + SLOT_ID];\n\t\t\t\tlet distance = this.entityBlockDistanceSquared(entity, entityOffset, x, y);\n\t\t\t\tif(distance < nearest.distance\n\t\t\t\t\t|| (distance === nearest.distance && (nearest.id === undefined || id < nearest.id))) {\n\t\t\t\t\tnearest.id = id;\n\t\t\t\t\tnearest.distance = distance;\n\t\t\t\t}\n\t\t\t}\n\t\t\tindex = slot[off + SLOT_NEXT];\n\t\t}\n\t\tunlock(bucketInts, lockIndex);\n\t}\n\n\tprivate collectNeighborCandidates(\n\t\tcandidates: Array<NeighborCandidate>, col: number, row: number, queryCol: number, queryRow: number,\n\t\tx: number, y: number, maxResults: number, maxDistanceSquared: number,\n\t) {\n\t\tlet bucket = this.bucketOf(col, row);\n\t\tlet bucketInts = this.getBucketSegment(bucket);\n\t\tlet bucketHeads = this.bucketHeadSegments[bucket >>> BUCKET_SEGMENT_SHIFT]!;\n\t\tlet offset = this.bucketOffset(bucket);\n\t\tlet headIndex = offset + BUCKET_HEAD_OFFSET;\n\t\tif(bucketHeads[headIndex] === this.nullIndex) {\n\t\t\treturn;\n\t\t}\n\t\tlet lockIndex = offset + BUCKET_LOCK_OFFSET;\n\t\tlock(bucketInts, lockIndex);\n\t\tlet index = bucketHeads[headIndex];\n\t\twhile(index !== this.nullIndex) {\n\t\t\tlet slot = this.slotPool.blockFor(index);\n\t\t\tlet off = this.slotPool.blockOffset(index);\n\t\t\tif(slot[off + SLOT_COL] === col && slot[off + SLOT_ROW] === row) {\n\t\t\t\tlet entityIndex = slot[off + SLOT_ENTITY_INDEX];\n\t\t\t\tlet entity = this.entityPool.blockFor(entityIndex);\n\t\t\t\tlet entityOffset = this.entityPool.blockOffset(entityIndex);\n\t\t\t\tlet minCol = entity[entityOffset + ENTITY_MIN_COL];\n\t\t\t\tlet maxCol = entity[entityOffset + ENTITY_MAX_COL];\n\t\t\t\tlet minRow = entity[entityOffset + ENTITY_MIN_ROW];\n\t\t\t\tlet maxRow = entity[entityOffset + ENTITY_MAX_ROW];\n\t\t\t\tlet nearestCol = queryCol < minCol ? minCol : queryCol > maxCol ? maxCol : queryCol;\n\t\t\t\tlet nearestRow = queryRow < minRow ? minRow : queryRow > maxRow ? maxRow : queryRow;\n\t\t\t\tif(col === nearestCol && row === nearestRow) {\n\t\t\t\t\tlet distance = this.entityBlockDistanceSquared(entity, entityOffset, x, y);\n\t\t\t\t\tif(distance <= maxDistanceSquared) {\n\t\t\t\t\t\taddCandidate(candidates, slot[off + SLOT_ID], distance, maxResults);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tindex = slot[off + SLOT_NEXT];\n\t\t}\n\t\tunlock(bucketInts, lockIndex);\n\t}\n\n\tprivate addEntityCandidate(candidates: Array<NeighborCandidate>, id: number, entityIndex: number, x: number, y: number, maxResults: number, maxDistanceSquared: number) {\n\t\tlet distance = this.entityDistanceSquared(entityIndex, x, y);\n\t\tif(distance <= maxDistanceSquared) {\n\t\t\taddCandidate(candidates, id, distance, maxResults);\n\t\t}\n\t}\n\n\tprivate entityDistanceSquared(entityIndex: number, x: number, y: number): number {\n\t\tlet entity = this.entityPool.blockFor(entityIndex);\n\t\tlet off = this.entityPool.blockOffset(entityIndex);\n\t\treturn this.entityBlockDistanceSquared(entity, off, x, y);\n\t}\n\n\tprivate entityBlockDistanceSquared(entity: CoordArray, off: number, x: number, y: number): number {\n\t\tlet minX = entity[off + ENTITY_X];\n\t\tlet minY = entity[off + ENTITY_Y];\n\t\treturn distanceSquaredToRect(\n\t\t\tx, y, minX, minY, minX + entity[off + ENTITY_WIDTH], minY + entity[off + ENTITY_HEIGHT],\n\t\t);\n\t}\n\n\t// Splice this entity's slot for exactly cell (col, row) out of the bucket and return its index (nullIndex if absent).\n\t// Matching on id and cell is required: two of an entity's own cells can hash to the same bucket, so id alone is ambiguous.\n\tprivate unlinkFromBucket(bucket: number, id: number, col: number, row: number): number {\n\t\tlet nullIndex = this.nullIndex;\n\t\tlet bucketInts = this.getBucketSegment(bucket);\n\t\tlet bucketHeads = this.bucketHeadSegments[bucket >>> BUCKET_SEGMENT_SHIFT]!;\n\t\tlet offset = this.bucketOffset(bucket);\n\t\tlet lockIndex = offset + BUCKET_LOCK_OFFSET;\n\t\tlock(bucketInts, lockIndex);\n\t\tlet headIndex = offset + BUCKET_HEAD_OFFSET;\n\t\tlet pool = this.slotPool;\n\n\t\tlet prev = nullIndex;\n\t\tlet index = bucketHeads[headIndex];\n\t\twhile(index !== nullIndex) {\n\t\t\t// One chunk resolve per slot covers every field it reads\n\t\t\tlet block = pool.blockFor(index);\n\t\t\tlet off = pool.blockOffset(index);\n\t\t\tlet next = block[off + SLOT_NEXT];\n\t\t\tif(block[off + SLOT_ID] === id && block[off + SLOT_COL] === col && block[off + SLOT_ROW] === row) {\n\t\t\t\tif(prev === nullIndex) {\n\t\t\t\t\tbucketHeads[headIndex] = next;\n\t\t\t\t} else {\n\t\t\t\t\tpool.set(prev, SLOT_NEXT, next);\n\t\t\t\t}\n\t\t\t\tunlock(bucketInts, lockIndex);\n\t\t\t\treturn index;\n\t\t\t}\n\t\t\tprev = index;\n\t\t\tindex = next;\n\t\t}\n\n\t\tunlock(bucketInts, lockIndex);\n\t\treturn nullIndex;\n\t}\n}\n\nfunction getCoordTypeCode(type: CoordArrayConstructor | undefined): number {\n\tlet code = getArrayTypeCode(type ?? DEFAULT_COORD_TYPE);\n\tif(code !== ARRAY_TYPE.float32 && code !== ARRAY_TYPE.float64) {\n\t\tthrow new Error('SharedSpatialMap must be Float32Array or Float64Array');\n\t}\n\treturn code;\n}\nfunction getCoordTypeConstructor(typeCode: number): CoordArrayConstructor {\n\treturn getByteMultipler(typeCode) === 2 ? Float64Array : Float32Array;\n}\nfunction nullIndexFor(typeCode: number): number {\n\treturn typeCode === ARRAY_TYPE.float32 ? FLOAT32_NULL_INDEX : FLOAT64_NULL_INDEX;\n}\nfunction maskFor(bucketCount: number): number {\n\treturn (bucketCount & (bucketCount - 1)) === 0 ? bucketCount - 1 : -1;\n}\nfunction nextPowerOfTwo(value: number): number {\n\treturn Math.pow(2, Math.ceil(Math.log2(Math.max(1, value))));\n}\n\ninterface SharedSpatialMapConfig {\n\tgridSize?: number\n\tbuckets?: number\n\tmaxBuckets?: number\n\tmaxEntities?: number\n\tmaxSlots?: number\n\ttype?: CoordArrayConstructor\n}\ninterface SharedSpatialMapMemory {\n\tfirstBlock: SharedAllocatedMemory\n}\n\nexport type { SharedSpatialMapConfig, SharedSpatialMapMemory, SpatialEntity };\n"],"mappings":"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"}
1
+ {"version":3,"file":"shared-spatial-map.js","names":["AllocatedMemory","SharedMap","SharedStack","SharedPool","BUCKET_STACK_POINTER_INDEX","ENTITY_POOL_POINTER_INDEX","SLOT_POOL_POINTER_INDEX","MAP_POINTER_INDEX","BUCKET_COUNT_INDEX","TYPE_INDEX","CONFIG_INDEX","TABLE_LOCK_INDEX","MAX_BUCKET_COUNT_INDEX","BUCKET_LOCK_OFFSET","BUCKET_HEAD_OFFSET","BUCKET_SIZE","BUCKET_SEGMENT_SHIFT","BUCKETS_PER_SEGMENT","BUCKET_SEGMENT_MASK","SLOT_ID","SLOT_COL","SLOT_ROW","SLOT_ANCHOR_COL","SLOT_ANCHOR_ROW","SLOT_NEXT","SLOT_ENTITY_INDEX","SLOT_FIELDS","ENTITY_MIN_COL","ENTITY_MIN_ROW","ENTITY_MAX_COL","ENTITY_MAX_ROW","ENTITY_X","ENTITY_Y","ENTITY_WIDTH","ENTITY_HEIGHT","ENTITY_FIELDS","FLOAT32_LIMIT","FLOAT32_NULL_INDEX","FLOAT64_NULL_INDEX","UINT32_LIMIT","HASH_PRIME_COL","HASH_PRIME_ROW","DEFAULT_GRID_SIZE","DEFAULT_BUCKET_COUNT","BUCKET_LOAD_NUMERATOR","BUCKET_LOAD_DENOMINATOR","DEFAULT_MAX_ENTITIES","DEFAULT_SLOTS_PER_ENTITY","DEFAULT_COORD_TYPE","SharedSpatialMap","memory","config","getPointer","loadRawPointer","nullIndexFor","gridSize","maxEntities","maxSlots","nextPowerOfTwo","getCoordTypeCode","ARRAY_TYPE","entityPoolConfig","getCoordTypeConstructor","getSpatialPoolChunkSize","getByteMultipler","slotPoolConfig","entityPoolAllocateCount","slotPoolAllocateCount","mapAllocateCount","allocateCount","configView","storeRawPointer","entityPoolBlock","slotPoolBlock","mapBlock","getSpatialMapCapacity","maskFor","total","pointer","id","x","y","width","height","byId","entity","entities","unique","existing","mappings","additionalSlots","newCount","i","nextEntityIndex","nextSlotIndex","minCol","minRow","maxCol","maxRow","entityIndex","block","offset","row","col","off","oldMinCol","oldMinRow","oldMaxCol","oldMaxRow","out","filter","start","qMinCol","qMinRow","qMaxCol","qMaxRow","nullIndex","bucket","bucketInts","bucketHeads","write","read","maxResults","maxDistance","maxDistanceSquared","candidates","candidate","cellIds","seenEntities","startCol","startRow","nearest","visit","distance","radius","limit","candidateLimit","top","bottom","segmentIndex","heads","keys","key","makeArrayView","cellSize","left","centerCol","right","centerRow","hash","mask","cached","ints","needed","bucketCount","newBucketCount","slotCount","target","slotIndex","slot","headIndex","before","pool","lockIndex","unlock","slotOffset","anchorCol","anchorRow","ovMinCol","ovMaxCol","ovMinRow","ovMaxRow","hasOverlap","inOverlapRow","index","entityOffset","queryCol","queryRow","addCandidate","minX","minY","distanceSquaredToRect","prev","next","code","getArrayTypeCode","type","typeCode","value"],"sources":["../../src/spatial/shared-spatial-map.ts"],"sourcesContent":["import type { SharedAllocatedMemory } from '../allocated-memory';\nimport AllocatedMemory from '../allocated-memory';\nimport type MemoryHeap from '../memory-heap';\nimport SharedPool, { type SharedPoolConfig } from '../shared-pool';\nimport SharedMap from '../shared-map';\nimport SharedStack from '../shared-stack';\nimport { lock, unlock } from '../lock/simple-lock';\nimport { readLock, readUnlock, writeLock, writeUnlock } from '../lock/read-write-lock';\nimport { getPointer, loadRawPointer, storeRawPointer } from '../utils/pointer';\nimport { ARRAY_TYPE, getArrayTypeCode, getByteMultipler, makeArrayView } from '../utils/array-type';\nimport {\n\taddCandidate, candidateLimit, distanceSquaredToRect, type NeighborCandidate, type NearestNeighbor,\n} from './spatial-neighbors';\nimport { getSpatialMapCapacity, getSpatialPoolChunkSize, type SpatialEntity } from './spatial-entity';\n\n// Unbounded spatial hash (\"spatial multimap\") built for concurrent updates from many threads. It indexes the same way\n// SharedSpatialGrid does - every entity occupies each virtual cell (of side gridSize) its rect overlaps - but it drops\n// the grid's fixed cols x rows extent so the world is unbounded: cells may sit at any (col, row), positive or negative.\n//\n// A cell (col, row) is not stored at a fixed array index (there is no finite grid to index); instead it is hashed into a\n// growable hash table of buckets and every cell that lands on a bucket shares that bucket's intrusive singly-linked list.\n// Normal operations share a table read lock. When the average slot load grows past 0.5, one writer doubles the bucket\n// count and rebuilds the chains under the exclusive side of that lock. Bucket pages and their pointer stack only grow.\n//\n// Because distinct cells collide onto one bucket, a slot must carry its own cell (col, row): a bucket walk considers only\n// slots whose cell matches the cell being queried. Each (entity, cell) membership is a \"slot\" record in a SharedPool; the\n// entity's occupied cell rectangle is stored once in a separate entity pool, keyed by an id->entityIndex SharedMap, so\n// remove(id) (which only gets an id) knows which cells to unlink from.\n//\n// search returns each entity at most once even though it spans multiple cells: every slot carries the entity's anchor\n// (top-left) cell, and a slot only emits its id when its cell is the top-left cell of the entity that intersects the query\n// (col == max(anchorCol, qMinCol) && row == max(anchorRow, qMinRow)). That cell is always one of the entity's cells and\n// inside the query, so exactly one slot emits - no Set and no per-query mutation, so concurrent queries stay safe.\n//\n// Records are stored at the configured precision. Float32 (the default) keeps everything exact as long as entity ids and\n// both pools' index spaces stay under 2^24 (~16.7M) - every id, pool index, and the NULL sentinel is Float32 exact there\n// - and cell (col, row) stay within [-2^24, 2^24]. Need more (or ids >= 2^24)? Pass type: Float64Array, which accepts\n// ids up to 2^32 - 1: the id->entityIndex SharedMap keys are uint32 at either precision.\n\n// Header (firstBlock) u32 layout\nconst BUCKET_STACK_POINTER_INDEX = 0;\nconst ENTITY_POOL_POINTER_INDEX = 1;\nconst SLOT_POOL_POINTER_INDEX = 2;\nconst MAP_POINTER_INDEX = 3;\nconst BUCKET_COUNT_INDEX = 4;\nconst TYPE_INDEX = 5;\n// config view starts at slot 6 (offset 24 bytes is 8-byte aligned, so a Float64 view is valid): [gridSize]\nconst CONFIG_INDEX = 6;\nconst TABLE_LOCK_INDEX = 8;\nconst MAX_BUCKET_COUNT_INDEX = 10;\n\n// Per-bucket record: [lock, head]\nconst BUCKET_LOCK_OFFSET = 0;\nconst BUCKET_HEAD_OFFSET = 1;\nconst BUCKET_SIZE = 2;\nconst BUCKET_SEGMENT_SHIFT = 8;\nconst BUCKETS_PER_SEGMENT = 1 << BUCKET_SEGMENT_SHIFT;\nconst BUCKET_SEGMENT_MASK = BUCKETS_PER_SEGMENT - 1;\n\n// Slot record fields (one per entity-cell membership). The cell (col, row) is stored explicitly because a bucket mixes\n// slots from every cell that hashes to it, so a walk must filter to the cell it is looking at.\nconst SLOT_ID = 0;\nconst SLOT_COL = 1;\nconst SLOT_ROW = 2;\nconst SLOT_ANCHOR_COL = 3;\nconst SLOT_ANCHOR_ROW = 4;\nconst SLOT_NEXT = 5;\nconst SLOT_ENTITY_INDEX = 6;\nconst SLOT_FIELDS = 7;\n\n// Entity record fields: the occupied cell rectangle\nconst ENTITY_MIN_COL = 0;\nconst ENTITY_MIN_ROW = 1;\nconst ENTITY_MAX_COL = 2;\nconst ENTITY_MAX_ROW = 3;\nconst ENTITY_X = 4;\nconst ENTITY_Y = 5;\nconst ENTITY_WIDTH = 6;\nconst ENTITY_HEIGHT = 7;\nconst ENTITY_FIELDS = 8;\n\n// Largest exact integer range for Float32 coordinates: [0, 2^24). Ids and pool indices must stay under this to survive a\n// Float32 round-trip, so it doubles as the Float32 entity cap and the value just past every valid index.\nconst FLOAT32_LIMIT = 0x1000000;\n// End-of-list / empty-bucket sentinel, chosen per element type so it is exact in the pool's view and above every valid\n// index: 2^24 for Float32 (its first non-representable index), 0xffffffff for Float64 (well within its exact range).\nconst FLOAT32_NULL_INDEX = FLOAT32_LIMIT;\nconst FLOAT64_NULL_INDEX = 0xffffffff;\nconst UINT32_LIMIT = 0x100000000;\n\n// Odd primes for the spatial hash (Teschner et al.): mixing col and row through Math.imul then xor scatters neighbouring\n// cells across buckets so a query's cells rarely collide on one bucket.\nconst HASH_PRIME_COL = 73856093;\nconst HASH_PRIME_ROW = 19349663;\n\nconst DEFAULT_GRID_SIZE = 50;\nconst DEFAULT_BUCKET_COUNT = 256;\nconst BUCKET_LOAD_NUMERATOR = 1;\nconst BUCKET_LOAD_DENOMINATOR = 2;\nconst DEFAULT_MAX_ENTITIES = 1_000_000;\n// A spatial-map entity can span several cells, so the slot pool needs headroom over the entity count\nconst DEFAULT_SLOTS_PER_ENTITY = 4;\nconst DEFAULT_COORD_TYPE = Float32Array;\n\ntype CoordArray = Float32Array | Float64Array;\ntype CoordArrayConstructor = Float32ArrayConstructor | Float64ArrayConstructor;\n\nexport default class SharedSpatialMap {\n\tstatic readonly ALLOCATE_COUNT = 11;\n\n\tprivate memory: MemoryHeap;\n\tprivate firstBlock: AllocatedMemory;\n\tprivate entityPool: SharedPool<CoordArray>;\n\tprivate slotPool: SharedPool<CoordArray>;\n\tprivate idMap: SharedMap<number>;\n\tprivate bucketPointers: SharedStack;\n\n\t// Reused to hand a full record to pool.push without allocating a fresh array per operation (single writer per id)\n\tprivate entityScratch: Array<number> = [0, 0, 0, 0, 0, 0, 0, 0];\n\tprivate slotScratch: Array<number> = [0, 0, 0, 0, 0, 0, 0];\n\n\t// Each cached page is viewed as Int32 for locks and Uint32 for head indexes. Both views cover the same bytes.\n\tprivate bucketSegments: Array<Int32Array | undefined> = [];\n\tprivate bucketHeadSegments: Array<Uint32Array | undefined> = [];\n\tprivate tableLock: Int32Array;\n\n\tprivate readonly coordType: number;\n\t// Empty-bucket / end-of-list sentinel for this instance's element type (see FLOAT32_NULL_INDEX)\n\tprivate readonly nullIndex: number;\n\t// Exclusive upper bound on ids: exact in the slot records and representable as a SharedMap key\n\tprivate readonly idLimit: number;\n\tprivate bucketCount: number;\n\tprivate readonly maxBucketCount: number;\n\t// bucketCount-1 when bucketCount is a power of two, else -1. Lets bucketOf mask (hash & mask) instead of taking a\n\t// variable integer modulo (hash % bucketCount) on the hot path - every insert/update/remove/query cell hashes a bucket.\n\tprivate bucketMask: number;\n\t// Virtual cell size, read once (stored at the configured precision, math done in float64)\n\tprivate readonly cellSize: number;\n\n\t// Number of entities currently tracked\n\tget size(): number {\n\t\treturn this.idMap.length;\n\t}\n\n\tget gridSize(): number {\n\t\treturn this.cellSize;\n\t}\n\tget buckets(): number {\n\t\treturn Atomics.load(this.firstBlock.data, BUCKET_COUNT_INDEX);\n\t}\n\tget maxBuckets(): number {\n\t\treturn this.maxBucketCount;\n\t}\n\n\t// Element type the records are stored in (an ARRAY_TYPE code: float32 or float64)\n\tget type(): number {\n\t\treturn this.coordType;\n\t}\n\n\tconstructor(memory: MemoryHeap, config?: SharedSpatialMapConfig | SharedSpatialMapMemory) {\n\t\tthis.memory = memory;\n\n\t\tif(config && 'firstBlock' in config) {\n\t\t\tthis.firstBlock = new AllocatedMemory(memory, config.firstBlock);\n\t\t\tthis.coordType = this.firstBlock.data[TYPE_INDEX];\n\t\t\tthis.bucketPointers = new SharedStack(memory, {\n\t\t\t\tfirstBlock: getPointer(loadRawPointer(this.firstBlock.data, BUCKET_STACK_POINTER_INDEX), memory.positionBits),\n\t\t\t});\n\t\t\tthis.entityPool = new SharedPool<CoordArray>(memory, {\n\t\t\t\tfirstBlock: getPointer(loadRawPointer(this.firstBlock.data, ENTITY_POOL_POINTER_INDEX), memory.positionBits),\n\t\t\t});\n\t\t\tthis.slotPool = new SharedPool<CoordArray>(memory, {\n\t\t\t\tfirstBlock: getPointer(loadRawPointer(this.firstBlock.data, SLOT_POOL_POINTER_INDEX), memory.positionBits),\n\t\t\t});\n\t\t\tthis.idMap = new SharedMap<number>(memory, {\n\t\t\t\tfirstBlock: getPointer(loadRawPointer(this.firstBlock.data, MAP_POINTER_INDEX), memory.positionBits),\n\t\t\t});\n\t\t\tthis.bucketCount = Atomics.load(this.firstBlock.data, BUCKET_COUNT_INDEX);\n\t\t\tthis.maxBucketCount = this.firstBlock.data[MAX_BUCKET_COUNT_INDEX];\n\t\t\tthis.nullIndex = nullIndexFor(this.coordType);\n\t\t} else {\n\t\t\tlet gridSize = config?.gridSize ?? DEFAULT_GRID_SIZE;\n\t\t\tthis.bucketCount = Math.min(0x40000000, Math.max(1, Math.floor(config?.buckets ?? DEFAULT_BUCKET_COUNT)));\n\t\t\tlet maxEntities = config?.maxEntities ?? DEFAULT_MAX_ENTITIES;\n\t\t\tlet maxSlots = config?.maxSlots ?? maxEntities * DEFAULT_SLOTS_PER_ENTITY;\n\t\t\tthis.maxBucketCount = Math.min(\n\t\t\t\t0x40000000,\n\t\t\t\tMath.max(this.bucketCount, Math.floor(config?.maxBuckets ?? nextPowerOfTwo(maxSlots * 2))),\n\t\t\t);\n\t\t\tthis.coordType = getCoordTypeCode(config?.type);\n\t\t\tthis.nullIndex = nullIndexFor(this.coordType);\n\n\t\t\tif(this.coordType === ARRAY_TYPE.float32 && (maxEntities > FLOAT32_LIMIT || maxSlots > FLOAT32_LIMIT)) {\n\t\t\t\tthrow new Error(`Float32 SharedSpatialMap supports at most ${FLOAT32_LIMIT} entities and slots; pass type: Float64Array for more`);\n\t\t\t}\n\n\t\t\t// Inline the bucket page pointers, pools, and id-map so the stable spine is one contiguous allocation.\n\t\t\tlet entityPoolConfig: SharedPoolConfig<CoordArray> = {\n\t\t\t\ttype: getCoordTypeConstructor(this.coordType),\n\t\t\t\tdataLength: ENTITY_FIELDS,\n\t\t\t\tmaxLength: maxEntities,\n\t\t\t\tmaxChunkSize: getSpatialPoolChunkSize(maxEntities, ENTITY_FIELDS, getByteMultipler(this.coordType), memory.maxAllocationLength),\n\t\t\t};\n\t\t\tlet slotPoolConfig: SharedPoolConfig<CoordArray> = {\n\t\t\t\ttype: getCoordTypeConstructor(this.coordType),\n\t\t\t\tdataLength: SLOT_FIELDS,\n\t\t\t\tmaxLength: maxSlots,\n\t\t\t\tmaxChunkSize: getSpatialPoolChunkSize(maxSlots, SLOT_FIELDS, getByteMultipler(this.coordType), memory.maxAllocationLength),\n\t\t\t};\n\t\t\tlet entityPoolAllocateCount = SharedPool.getAllocateCount(memory, entityPoolConfig);\n\t\t\tlet slotPoolAllocateCount = SharedPool.getAllocateCount(memory, slotPoolConfig);\n\t\t\tlet mapAllocateCount = SharedMap.ALLOCATE_COUNT;\n\t\t\tlet allocateCount = SharedSpatialMap.ALLOCATE_COUNT;\n\t\t\tthis.firstBlock = memory.allocUI32(allocateCount + entityPoolAllocateCount + slotPoolAllocateCount + mapAllocateCount);\n\t\t\tthis.firstBlock.data[BUCKET_COUNT_INDEX] = this.bucketCount;\n\t\t\tthis.firstBlock.data[TYPE_INDEX] = this.coordType;\n\t\t\tthis.firstBlock.data[MAX_BUCKET_COUNT_INDEX] = this.maxBucketCount;\n\n\t\t\tlet configView = this.getConfigView();\n\t\t\tconfigView[0] = gridSize;\n\n\t\t\tthis.bucketPointers = new SharedStack(memory);\n\t\t\tstoreRawPointer(this.firstBlock.data, BUCKET_STACK_POINTER_INDEX, this.bucketPointers.pointer);\n\t\t\tthis.appendBucketSegments(this.bucketCount);\n\n\t\t\tlet entityPoolBlock = this.firstBlock.getSubAllocation(allocateCount, entityPoolAllocateCount);\n\t\t\tthis.entityPool = new SharedPool<CoordArray>(memory, { firstBlock: entityPoolBlock, ...entityPoolConfig });\n\t\t\tstoreRawPointer(this.firstBlock.data, ENTITY_POOL_POINTER_INDEX, entityPoolBlock.pointer);\n\n\t\t\tlet slotPoolBlock = this.firstBlock.getSubAllocation(allocateCount + entityPoolAllocateCount, slotPoolAllocateCount);\n\t\t\tthis.slotPool = new SharedPool<CoordArray>(memory, { firstBlock: slotPoolBlock, ...slotPoolConfig });\n\t\t\tstoreRawPointer(this.firstBlock.data, SLOT_POOL_POINTER_INDEX, slotPoolBlock.pointer);\n\n\t\t\tlet mapBlock = this.firstBlock.getSubAllocation(allocateCount + entityPoolAllocateCount + slotPoolAllocateCount, mapAllocateCount);\n\t\t\tthis.idMap = new SharedMap<number>(memory, { firstBlock: mapBlock, capacity: getSpatialMapCapacity(maxEntities, memory.maxAllocationLength) });\n\t\t\tstoreRawPointer(this.firstBlock.data, MAP_POINTER_INDEX, mapBlock.pointer);\n\t\t}\n\n\t\tthis.idLimit = this.coordType === ARRAY_TYPE.float32 ? FLOAT32_LIMIT : UINT32_LIMIT;\n\t\tthis.bucketMask = maskFor(this.bucketCount);\n\t\tthis.tableLock = new Int32Array(\n\t\t\tthis.firstBlock.data.buffer,\n\t\t\tthis.firstBlock.bufferByteOffset + TABLE_LOCK_INDEX * Uint32Array.BYTES_PER_ELEMENT,\n\t\t\t2,\n\t\t);\n\n\t\tlet configView = this.getConfigView();\n\t\tthis.cellSize = configView[0];\n\n\t}\n\n\tget pointer(): number {\n\t\treturn this.firstBlock.pointer;\n\t}\n\n\tget usedMemory(): number {\n\t\tlet total = this.firstBlock.usedMemory + this.bucketPointers.usedMemory\n\t\t\t+ this.entityPool.usedMemory + this.slotPool.usedMemory + this.idMap.usedMemory;\n\t\tfor(let pointer of this.bucketPointers) {\n\t\t\ttotal += new AllocatedMemory(this.memory, getPointer(pointer, this.memory.positionBits)).usedMemory;\n\t\t}\n\t\treturn total;\n\t}\n\n\tinsert(id: number, x: number, y: number, width = 0, height = 0) {\n\t\treadLock(this.tableLock);\n\t\ttry {\n\t\t\tthis.syncBucketCount();\n\t\t\tthis.insertLocked(id, x, y, width, height);\n\t\t} finally {\n\t\t\treadUnlock(this.tableLock);\n\t\t}\n\t\tthis.growBucketsIfNeeded();\n\t}\n\n\tbulkInsert(entities: Iterable<SpatialEntity>) {\n\t\tlet byId = new Map<number, SpatialEntity>();\n\t\tfor(let entity of entities) {\n\t\t\tthis.assertValidId(entity.id);\n\t\t\tbyId.set(entity.id, entity);\n\t\t}\n\t\tlet unique = Array.from(byId.values());\n\t\tlet existing = this.idMap.getAll(unique.map(entity => entity.id));\n\t\tlet mappings: Array<readonly [number, number]> = [];\n\t\tlet additionalSlots = 0;\n\t\tlet newCount = 0;\n\t\tfor(let i = 0; i < unique.length; i++) {\n\t\t\tif(existing[i] === undefined) {\n\t\t\t\tlet entity = unique[i];\n\t\t\t\tnewCount++;\n\t\t\t\tadditionalSlots += (this.colOf(entity.x + (entity.width ?? 0)) - this.colOf(entity.x) + 1)\n\t\t\t\t\t* (this.rowOf(entity.y + (entity.height ?? 0)) - this.rowOf(entity.y) + 1);\n\t\t\t}\n\t\t}\n\t\tthis.ensureBucketCapacity(this.slotPool.length + additionalSlots);\n\t\tlet nextEntityIndex = this.entityPool.reserveContiguous(newCount);\n\t\tlet nextSlotIndex = this.slotPool.reserveContiguous(additionalSlots);\n\t\treadLock(this.tableLock);\n\t\ttry {\n\t\t\tthis.syncBucketCount();\n\t\t\tfor(let i = 0; i < unique.length; i++) {\n\t\t\t\tlet entity = unique[i];\n\t\t\t\tlet width = entity.width ?? 0;\n\t\t\t\tlet height = entity.height ?? 0;\n\t\t\t\tif(existing[i] !== undefined) {\n\t\t\t\t\tthis.updateLocked(entity.id, entity.x, entity.y, width, height);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tlet minCol = this.colOf(entity.x);\n\t\t\t\tlet minRow = this.rowOf(entity.y);\n\t\t\t\tlet maxCol = this.colOf(entity.x + width);\n\t\t\t\tlet maxRow = this.rowOf(entity.y + height);\n\t\t\t\tlet entityIndex = nextEntityIndex++;\n\t\t\t\tlet block = this.entityPool.blockFor(entityIndex);\n\t\t\t\tlet offset = this.entityPool.blockOffset(entityIndex);\n\t\t\t\tblock[offset + ENTITY_MIN_COL] = minCol;\n\t\t\t\tblock[offset + ENTITY_MIN_ROW] = minRow;\n\t\t\t\tblock[offset + ENTITY_MAX_COL] = maxCol;\n\t\t\t\tblock[offset + ENTITY_MAX_ROW] = maxRow;\n\t\t\t\tblock[offset + ENTITY_X] = entity.x;\n\t\t\t\tblock[offset + ENTITY_Y] = entity.y;\n\t\t\t\tblock[offset + ENTITY_WIDTH] = width;\n\t\t\t\tblock[offset + ENTITY_HEIGHT] = height;\n\t\t\t\tmappings.push([entity.id, entityIndex]);\n\t\t\t\tfor(let row = minRow; row <= maxRow; row++) {\n\t\t\t\t\tfor(let col = minCol; col <= maxCol; col++) {\n\t\t\t\t\t\tthis.linkReservedBucket(col, row, nextSlotIndex++, entity.id, entityIndex, minCol, minRow);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.idMap.setAll(mappings);\n\t\t} finally {\n\t\t\treadUnlock(this.tableLock);\n\t\t}\n\t\tthis.growBucketsForBulk();\n\t}\n\n\t// An out-of-range id would be stored but never found again, leaving its slots unremovable\n\tprivate assertValidId(id: number) {\n\t\tif(!Number.isInteger(id) || id < 0 || id >= this.idLimit) {\n\t\t\tthrow new Error(`SharedSpatialMap id ${id} must be an integer in [0, ${this.idLimit})`\n\t\t\t\t+ (this.coordType === ARRAY_TYPE.float32 ? '; pass type: Float64Array for ids up to 2^32 - 1' : ''));\n\t\t}\n\t}\n\n\tprivate insertLocked(id: number, x: number, y: number, width: number, height: number) {\n\t\tthis.assertValidId(id);\n\t\tif(this.idMap.get(id) !== undefined) {\n\t\t\tthis.updateLocked(id, x, y, width, height);\n\t\t\treturn;\n\t\t}\n\n\t\tlet minCol = this.colOf(x);\n\t\tlet minRow = this.rowOf(y);\n\t\tlet maxCol = this.colOf(x + width);\n\t\tlet maxRow = this.rowOf(y + height);\n\n\t\tlet entity = this.entityScratch;\n\t\tentity[ENTITY_MIN_COL] = minCol;\n\t\tentity[ENTITY_MIN_ROW] = minRow;\n\t\tentity[ENTITY_MAX_COL] = maxCol;\n\t\tentity[ENTITY_MAX_ROW] = maxRow;\n\t\tentity[ENTITY_X] = x;\n\t\tentity[ENTITY_Y] = y;\n\t\tentity[ENTITY_WIDTH] = width;\n\t\tentity[ENTITY_HEIGHT] = height;\n\t\tlet entityIndex = this.entityPool.push(entity);\n\t\tthis.idMap.set(id, entityIndex);\n\n\t\tthis.linkRange(id, entityIndex, minCol, minRow, maxCol, maxRow);\n\t}\n\n\t// Move an entity to a new position. Assumes a single writer per entity id (the standard shared-memory pattern - each\n\t// worker owns a disjoint set of entities), so this entity's record is stable until we lock its bucket(s).\n\tupdate(id: number, x: number, y: number, width = 0, height = 0) {\n\t\tlet entityIndex = this.idMap.get(id);\n\t\tif(entityIndex !== undefined) {\n\t\t\tlet block = this.entityPool.blockFor(entityIndex);\n\t\t\tlet off = this.entityPool.blockOffset(entityIndex);\n\t\t\tlet minCol = this.colOf(x);\n\t\t\tlet minRow = this.rowOf(y);\n\t\t\tlet maxCol = this.colOf(x + width);\n\t\t\tlet maxRow = this.rowOf(y + height);\n\t\t\tif(minCol === block[off + ENTITY_MIN_COL] && minRow === block[off + ENTITY_MIN_ROW]\n\t\t\t\t&& maxCol === block[off + ENTITY_MAX_COL] && maxRow === block[off + ENTITY_MAX_ROW]) {\n\t\t\t\tblock[off + ENTITY_X] = x;\n\t\t\t\tblock[off + ENTITY_Y] = y;\n\t\t\t\tblock[off + ENTITY_WIDTH] = width;\n\t\t\t\tblock[off + ENTITY_HEIGHT] = height;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\treadLock(this.tableLock);\n\t\ttry {\n\t\t\tthis.syncBucketCount();\n\t\t\tthis.updateLocked(id, x, y, width, height);\n\t\t} finally {\n\t\t\treadUnlock(this.tableLock);\n\t\t}\n\t\tthis.growBucketsIfNeeded();\n\t}\n\n\tprivate updateLocked(id: number, x: number, y: number, width: number, height: number) {\n\t\tlet entityIndex = this.idMap.get(id);\n\t\tif(entityIndex === undefined) {\n\t\t\tthis.insertLocked(id, x, y, width, height);\n\t\t\treturn;\n\t\t}\n\n\t\tlet block = this.entityPool.blockFor(entityIndex);\n\t\tlet off = this.entityPool.blockOffset(entityIndex);\n\t\tlet oldMinCol = block[off + ENTITY_MIN_COL];\n\t\tlet oldMinRow = block[off + ENTITY_MIN_ROW];\n\t\tlet oldMaxCol = block[off + ENTITY_MAX_COL];\n\t\tlet oldMaxRow = block[off + ENTITY_MAX_ROW];\n\n\t\tlet minCol = this.colOf(x);\n\t\tlet minRow = this.rowOf(y);\n\t\tlet maxCol = this.colOf(x + width);\n\t\tlet maxRow = this.rowOf(y + height);\n\n\t\t// Same cell rectangle: membership is unchanged, but neighbors still needs the new exact rectangle.\n\t\tif(minCol === oldMinCol && minRow === oldMinRow && maxCol === oldMaxCol && maxRow === oldMaxRow) {\n\t\t\tblock[off + ENTITY_X] = x;\n\t\t\tblock[off + ENTITY_Y] = y;\n\t\t\tblock[off + ENTITY_WIDTH] = width;\n\t\t\tblock[off + ENTITY_HEIGHT] = height;\n\t\t\treturn;\n\t\t}\n\n\t\tthis.relinkRange(id, entityIndex, minCol, minRow, maxCol, maxRow, oldMinCol, oldMinRow, oldMaxCol, oldMaxRow);\n\n\t\tblock[off + ENTITY_MIN_COL] = minCol;\n\t\tblock[off + ENTITY_MIN_ROW] = minRow;\n\t\tblock[off + ENTITY_MAX_COL] = maxCol;\n\t\tblock[off + ENTITY_MAX_ROW] = maxRow;\n\t\tblock[off + ENTITY_X] = x;\n\t\tblock[off + ENTITY_Y] = y;\n\t\tblock[off + ENTITY_WIDTH] = width;\n\t\tblock[off + ENTITY_HEIGHT] = height;\n\t}\n\n\tremove(id: number): boolean {\n\t\treadLock(this.tableLock);\n\t\ttry {\n\t\t\tthis.syncBucketCount();\n\t\t\treturn this.removeLocked(id);\n\t\t} finally {\n\t\t\treadUnlock(this.tableLock);\n\t\t}\n\t}\n\n\tprivate removeLocked(id: number): boolean {\n\t\tlet entityIndex = this.idMap.get(id);\n\t\tif(entityIndex === undefined) {\n\t\t\treturn false;\n\t\t}\n\n\t\tlet block = this.entityPool.blockFor(entityIndex);\n\t\tlet off = this.entityPool.blockOffset(entityIndex);\n\t\tthis.unlinkRange(id, block[off + ENTITY_MIN_COL], block[off + ENTITY_MIN_ROW], block[off + ENTITY_MAX_COL], block[off + ENTITY_MAX_ROW]);\n\n\t\tthis.entityPool.deleteIndex(entityIndex);\n\t\tthis.idMap.delete(id);\n\t\treturn true;\n\t}\n\n\thas(id: number): boolean {\n\t\treturn this.idMap.get(id) !== undefined;\n\t}\n\n\t// Broad-phase query: ids of every entity occupying a cell that overlaps the query rect. Each entity is returned at most\n\t// once (see the anchor-cell dedup in collectBucket); the caller does the narrow-phase overlap check.\n\tsearch(x: number, y: number, width: number, height: number, filter?: (id: number) => boolean): Array<number> {\n\t\tlet out: Array<number> = [];\n\t\tthis.searchInto(out, x, y, width, height, filter);\n\t\treturn out;\n\t}\n\t// Same as search but appends into a caller-owned array so hot loops can reuse it. Returns the array.\n\tsearchInto(out: Array<number>, x: number, y: number, width: number, height: number, filter?: (id: number) => boolean): Array<number> {\n\t\treadLock(this.tableLock);\n\t\ttry {\n\t\t\tthis.syncBucketCount();\n\t\t\treturn this.searchIntoLocked(out, x, y, width, height, filter);\n\t\t} finally {\n\t\t\treadUnlock(this.tableLock);\n\t\t}\n\t}\n\n\tprivate searchIntoLocked(out: Array<number>, x: number, y: number, width: number, height: number, filter?: (id: number) => boolean): Array<number> {\n\t\tlet start = out.length;\n\t\tlet qMinCol = this.colOf(x);\n\t\tlet qMinRow = this.rowOf(y);\n\t\tlet qMaxCol = this.colOf(x + width);\n\t\tlet qMaxRow = this.rowOf(y + height);\n\n\t\tlet nullIndex = this.nullIndex;\n\t\tfor(let row = qMinRow; row <= qMaxRow; row++) {\n\t\t\tfor(let col = qMinCol; col <= qMaxCol; col++) {\n\t\t\t\tlet bucket = this.bucketOf(col, row);\n\t\t\t\tlet bucketInts = this.getBucketSegment(bucket);\n\t\t\t\t// Inline the empty-bucket peek (the common case) so an empty bucket never pays for a method call - most cells in\n\t\t\t\t// a typical query hash to empty buckets, so this skip dominates query cost.\n\t\t\t\tlet offset = this.bucketOffset(bucket);\n\t\t\t\tlet bucketHeads = this.bucketHeadSegments[bucket >>> BUCKET_SEGMENT_SHIFT]!;\n\t\t\t\tif(bucketHeads[offset + BUCKET_HEAD_OFFSET] === nullIndex) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tthis.collectBucket(out, bucketInts, bucketHeads, offset, col, row, qMinCol, qMinRow);\n\t\t\t}\n\t\t}\n\t\tif(filter !== undefined) {\n\t\t\tlet write = start;\n\t\t\tfor(let read = start; read < out.length; read++) {\n\t\t\t\tlet id = out[read];\n\t\t\t\tif(filter(id)) {\n\t\t\t\t\tout[write++] = id;\n\t\t\t\t}\n\t\t\t}\n\t\t\tout.length = write;\n\t\t}\n\t\treturn out;\n\t}\n\n\tneighbors(x: number, y: number, maxResults: number, maxDistance: number, filter?: (id: number) => boolean): Array<number> {\n\t\treadLock(this.tableLock);\n\t\ttry {\n\t\t\tthis.syncBucketCount();\n\t\t\treturn this.neighborsLocked(x, y, maxResults, maxDistance, filter);\n\t\t} finally {\n\t\t\treadUnlock(this.tableLock);\n\t\t}\n\t}\n\n\tprivate neighborsLocked(x: number, y: number, maxResults: number, maxDistance: number, filter?: (id: number) => boolean): Array<number> {\n\t\tif(maxResults <= 0 || maxDistance < 0 || Number.isNaN(maxResults) || Number.isNaN(maxDistance)) {\n\t\t\treturn [];\n\t\t}\n\n\t\tmaxResults = Math.floor(maxResults);\n\t\tif(maxResults === 0) {\n\t\t\treturn [];\n\t\t}\n\t\tlet maxDistanceSquared = maxDistance * maxDistance;\n\t\tlet candidates: Array<NeighborCandidate> = [];\n\t\tif(!Number.isFinite(maxDistance)) {\n\t\t\tfor(let [id, entityIndex] of this.idMap) {\n\t\t\t\tif(filter === undefined || filter(id)) {\n\t\t\t\t\tthis.addEntityCandidate(candidates, id, entityIndex, x, y, maxResults, maxDistanceSquared);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn candidates.map(candidate => candidate.id);\n\t\t}\n\n\t\tlet cellIds: Array<number> = [];\n\t\tlet seenEntities = filter !== undefined && maxResults > 1 ? new Set<number>() : undefined;\n\t\tlet startCol = this.colOf(x);\n\t\tlet startRow = this.rowOf(y);\n\t\tlet nearest: NearestNeighbor = { id: undefined, distance: maxDistanceSquared };\n\t\tlet visit = (col: number, row: number) => {\n\t\t\tif(filter === undefined) {\n\t\t\t\tif(maxResults === 1) {\n\t\t\t\t\tthis.collectNearestBucket(nearest, col, row, x, y);\n\t\t\t\t} else {\n\t\t\t\t\tthis.collectNeighborCandidates(\n\t\t\t\t\t\tcandidates, col, row, startCol, startRow, x, y, maxResults, maxDistanceSquared,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcellIds.length = 0;\n\t\t\tthis.collectNeighborBucket(cellIds, col, row);\n\t\t\tfor(let id of cellIds) {\n\t\t\t\tif(seenEntities?.has(id)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tseenEntities?.add(id);\n\t\t\t\tlet entityIndex = this.idMap.get(id);\n\t\t\t\tif(entityIndex !== undefined && (filter === undefined || filter(id))) {\n\t\t\t\t\tif(maxResults === 1) {\n\t\t\t\t\t\tlet distance = this.entityDistanceSquared(entityIndex, x, y);\n\t\t\t\t\t\tif(distance < nearest.distance\n\t\t\t\t\t\t\t|| (distance === nearest.distance && (nearest.id === undefined || id < nearest.id))) {\n\t\t\t\t\t\t\tnearest.id = id;\n\t\t\t\t\t\t\tnearest.distance = distance;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.addEntityCandidate(candidates, id, entityIndex, x, y, maxResults, maxDistanceSquared);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\tfor(let radius = 0; ; radius++) {\n\t\t\tlet limit = maxResults === 1 ? nearest.distance : candidateLimit(candidates, maxResults, maxDistanceSquared);\n\t\t\tif(this.mapRingDistanceSquared(x, y, startCol, startRow, radius) > limit) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif(radius === 0) {\n\t\t\t\tvisit(startCol, startRow);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlet minCol = startCol - radius;\n\t\t\tlet maxCol = startCol + radius;\n\t\t\tlet top = startRow - radius;\n\t\t\tlet bottom = startRow + radius;\n\t\t\tfor(let col = minCol; col <= maxCol; col++) {\n\t\t\t\tvisit(col, top);\n\t\t\t\tvisit(col, bottom);\n\t\t\t}\n\t\t\tfor(let row = top + 1; row < bottom; row++) {\n\t\t\t\tvisit(minCol, row);\n\t\t\t\tvisit(maxCol, row);\n\t\t\t}\n\t\t}\n\n\t\treturn maxResults === 1 ? nearest.id === undefined ? [] : [nearest.id] : candidates.map(candidate => candidate.id);\n\t}\n\n\tclear() {\n\t\twriteLock(this.tableLock);\n\t\ttry {\n\t\t\tthis.syncBucketCount();\n\t\t\tfor(let segmentIndex = 0; segmentIndex < this.bucketPointers.length; segmentIndex++) {\n\t\t\t\tthis.getBucketSegment(segmentIndex << BUCKET_SEGMENT_SHIFT);\n\t\t\t\tlet heads = this.bucketHeadSegments[segmentIndex]!;\n\t\t\t\tfor(let i = BUCKET_HEAD_OFFSET; i < heads.length; i += BUCKET_SIZE) {\n\t\t\t\t\theads[i] = this.nullIndex;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.entityPool.clear();\n\t\t\tthis.slotPool.clear();\n\t\t\tlet keys: Array<number> = [];\n\t\t\tfor(let [key] of this.idMap) {\n\t\t\t\tkeys.push(key);\n\t\t\t}\n\t\t\tfor(let key of keys) {\n\t\t\t\tthis.idMap.delete(key);\n\t\t\t}\n\t\t} finally {\n\t\t\twriteUnlock(this.tableLock);\n\t\t}\n\t}\n\n\tfree() {\n\t\tfor(let pointer of this.bucketPointers) {\n\t\t\tnew AllocatedMemory(this.memory, getPointer(pointer, this.memory.positionBits)).free();\n\t\t}\n\t\tthis.bucketPointers.free();\n\t\tthis.entityPool.free();\n\t\tthis.slotPool.free();\n\t\tthis.idMap.free();\n\t\tthis.firstBlock.free();\n\t}\n\n\tgetSharedMemory(): SharedSpatialMapMemory {\n\t\treturn {\n\t\t\tfirstBlock: this.firstBlock.getSharedMemory(),\n\t\t};\n\t}\n\n\tprivate getConfigView(): CoordArray {\n\t\treturn makeArrayView(\n\t\t\tthis.coordType,\n\t\t\tthis.firstBlock.data.buffer,\n\t\t\tthis.firstBlock.bufferByteOffset + CONFIG_INDEX * Uint32Array.BYTES_PER_ELEMENT,\n\t\t\t1,\n\t\t) as CoordArray;\n\t}\n\n\tprivate colOf(x: number): number {\n\t\treturn Math.floor(x / this.cellSize);\n\t}\n\tprivate rowOf(y: number): number {\n\t\treturn Math.floor(y / this.cellSize);\n\t}\n\tprivate mapRingDistanceSquared(x: number, y: number, centerCol: number, centerRow: number, radius: number): number {\n\t\tif(radius === 0) {\n\t\t\treturn 0;\n\t\t}\n\t\tlet cellSize = this.cellSize;\n\t\tlet left = x - (centerCol - radius + 1) * cellSize;\n\t\tlet right = (centerCol + radius) * cellSize - x;\n\t\tlet top = y - (centerRow - radius + 1) * cellSize;\n\t\tlet bottom = (centerRow + radius) * cellSize - y;\n\t\tlet distance = Math.min(left, right, top, bottom);\n\t\treturn distance * distance;\n\t}\n\t// Hash a virtual cell to the current bucket count. The table read lock keeps this count stable for the operation.\n\tprivate bucketOf(col: number, row: number): number {\n\t\tlet hash = (Math.imul(col, HASH_PRIME_COL) ^ Math.imul(row, HASH_PRIME_ROW)) >>> 0;\n\t\tlet mask = this.bucketMask;\n\t\treturn mask >= 0 ? hash & mask : hash % this.bucketCount;\n\t}\n\n\tprivate bucketOffset(bucket: number): number {\n\t\treturn (bucket & BUCKET_SEGMENT_MASK) * BUCKET_SIZE;\n\t}\n\n\tprivate getBucketSegment(bucket: number): Int32Array {\n\t\tlet segmentIndex = bucket >>> BUCKET_SEGMENT_SHIFT;\n\t\tlet cached = this.bucketSegments[segmentIndex];\n\t\tif(cached !== undefined) {\n\t\t\treturn cached;\n\t\t}\n\n\t\tlet pointer = this.bucketPointers.at(segmentIndex);\n\t\tlet block = new AllocatedMemory(this.memory, getPointer(pointer, this.memory.positionBits));\n\t\t// A segment is exactly BUCKETS_PER_SEGMENT * BUCKET_SIZE u32s. Never size the view by block.data.length: a production\n\t\t// build leaves that view unbounded (it runs to the end of the buffer), so the head-clearing loops below would write\n\t\t// across every allocation that follows this block and stomp their headers.\n\t\tlet ints = new Int32Array(block.data.buffer, block.bufferByteOffset, BUCKETS_PER_SEGMENT * BUCKET_SIZE);\n\t\tthis.bucketHeadSegments[segmentIndex] = new Uint32Array(block.data.buffer, block.bufferByteOffset, BUCKETS_PER_SEGMENT * BUCKET_SIZE);\n\t\tthis.bucketSegments[segmentIndex] = ints;\n\t\treturn ints;\n\t}\n\n\tprivate appendBucketSegments(bucketCount: number) {\n\t\tlet needed = Math.ceil(bucketCount / BUCKETS_PER_SEGMENT);\n\t\twhile(this.bucketPointers.length < needed) {\n\t\t\tlet block = this.memory.allocUI32(BUCKETS_PER_SEGMENT * BUCKET_SIZE);\n\t\t\tlet heads = new Uint32Array(block.data.buffer, block.bufferByteOffset, BUCKETS_PER_SEGMENT * BUCKET_SIZE);\n\t\t\tfor(let i = BUCKET_HEAD_OFFSET; i < heads.length; i += BUCKET_SIZE) {\n\t\t\t\theads[i] = this.nullIndex;\n\t\t\t}\n\t\t\tthis.bucketPointers.push(block.pointer);\n\t\t}\n\t}\n\n\tprivate syncBucketCount() {\n\t\tlet bucketCount = Atomics.load(this.firstBlock.data, BUCKET_COUNT_INDEX);\n\t\tif(bucketCount !== this.bucketCount) {\n\t\t\tthis.bucketCount = bucketCount;\n\t\t\tthis.bucketMask = maskFor(bucketCount);\n\t\t}\n\t}\n\n\tprivate growBucketsIfNeeded() {\n\t\tlet bucketCount = Atomics.load(this.firstBlock.data, BUCKET_COUNT_INDEX);\n\t\tif(bucketCount >= this.maxBucketCount\n\t\t\t|| this.slotPool.length * BUCKET_LOAD_DENOMINATOR <= bucketCount * BUCKET_LOAD_NUMERATOR) {\n\t\t\treturn;\n\t\t}\n\n\t\twriteLock(this.tableLock);\n\t\ttry {\n\t\t\tthis.syncBucketCount();\n\t\t\tif(this.slotPool.length * BUCKET_LOAD_DENOMINATOR <= this.bucketCount * BUCKET_LOAD_NUMERATOR) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlet newBucketCount = Math.min(this.bucketCount * 2, this.maxBucketCount);\n\t\t\tif(newBucketCount === this.bucketCount) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.resizeBuckets(newBucketCount);\n\t\t} finally {\n\t\t\twriteUnlock(this.tableLock);\n\t\t}\n\t}\n\n\tprivate ensureBucketCapacity(slotCount: number) {\n\t\tlet needed = Math.ceil(slotCount * BUCKET_LOAD_DENOMINATOR / BUCKET_LOAD_NUMERATOR);\n\t\tlet target = Math.min(nextPowerOfTwo(needed), this.maxBucketCount);\n\t\tif(target <= Atomics.load(this.firstBlock.data, BUCKET_COUNT_INDEX)) {\n\t\t\treturn;\n\t\t}\n\t\twriteLock(this.tableLock);\n\t\ttry {\n\t\t\tthis.syncBucketCount();\n\t\t\tif(target > this.bucketCount) {\n\t\t\t\tthis.resizeBuckets(target);\n\t\t\t}\n\t\t} finally {\n\t\t\twriteUnlock(this.tableLock);\n\t\t}\n\t}\n\n\tprivate resizeBuckets(newBucketCount: number) {\n\t\tthis.appendBucketSegments(newBucketCount);\n\t\tfor(let segmentIndex = 0; segmentIndex < this.bucketPointers.length; segmentIndex++) {\n\t\t\tthis.getBucketSegment(segmentIndex << BUCKET_SEGMENT_SHIFT);\n\t\t\tlet heads = this.bucketHeadSegments[segmentIndex]!;\n\t\t\tfor(let i = BUCKET_HEAD_OFFSET; i < heads.length; i += BUCKET_SIZE) {\n\t\t\t\theads[i] = this.nullIndex;\n\t\t\t}\n\t\t}\n\n\t\tthis.bucketCount = newBucketCount;\n\t\tthis.bucketMask = maskFor(newBucketCount);\n\t\tfor(let [slotIndex, slot] of this.slotPool.entries()) {\n\t\t\tlet bucket = this.bucketOf(slot[SLOT_COL], slot[SLOT_ROW]);\n\t\t\tlet heads = this.bucketHeadSegments[bucket >>> BUCKET_SEGMENT_SHIFT]!;\n\t\t\tlet headIndex = this.bucketOffset(bucket) + BUCKET_HEAD_OFFSET;\n\t\t\tslot[SLOT_NEXT] = heads[headIndex];\n\t\t\theads[headIndex] = slotIndex;\n\t\t}\n\t\tAtomics.store(this.firstBlock.data, BUCKET_COUNT_INDEX, newBucketCount);\n\t}\n\n\tprivate growBucketsForBulk() {\n\t\twhile(true) {\n\t\t\tlet before = Atomics.load(this.firstBlock.data, BUCKET_COUNT_INDEX);\n\t\t\tthis.growBucketsIfNeeded();\n\t\t\tif(Atomics.load(this.firstBlock.data, BUCKET_COUNT_INDEX) === before) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Push one slot into each cell of the rectangle, carrying the cell's own (col, row) and the entity's top-left cell as\n\t// the dedup anchor. Every slot shares the id and anchor, so those are filled once before the loop.\n\tprivate linkRange(id: number, entityIndex: number, minCol: number, minRow: number, maxCol: number, maxRow: number) {\n\t\tlet slot = this.slotScratch;\n\t\tslot[SLOT_ID] = id;\n\t\tslot[SLOT_ANCHOR_COL] = minCol;\n\t\tslot[SLOT_ANCHOR_ROW] = minRow;\n\t\tslot[SLOT_NEXT] = this.nullIndex;\n\t\tslot[SLOT_ENTITY_INDEX] = entityIndex;\n\n\t\tfor(let row = minRow; row <= maxRow; row++) {\n\t\t\tfor(let col = minCol; col <= maxCol; col++) {\n\t\t\t\tthis.linkIntoBucket(col, row, slot);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Push a fresh slot for cell (col, row) - carrying the shared record in slot plus that cell's own coords - onto the\n\t// head of its bucket.\n\tprivate linkIntoBucket(col: number, row: number, slot: Array<number>) {\n\t\tslot[SLOT_COL] = col;\n\t\tslot[SLOT_ROW] = row;\n\t\tlet pool = this.slotPool;\n\t\tlet slotIndex = pool.push(slot);\n\t\tlet bucket = this.bucketOf(col, row);\n\t\tlet bucketInts = this.getBucketSegment(bucket);\n\t\tlet bucketHeads = this.bucketHeadSegments[bucket >>> BUCKET_SEGMENT_SHIFT]!;\n\t\tlet offset = this.bucketOffset(bucket);\n\t\tlet lockIndex = offset + BUCKET_LOCK_OFFSET;\n\t\tlock(bucketInts, lockIndex);\n\t\tlet headIndex = offset + BUCKET_HEAD_OFFSET;\n\t\tpool.set(slotIndex, SLOT_NEXT, bucketHeads[headIndex]);\n\t\tbucketHeads[headIndex] = slotIndex;\n\t\tunlock(bucketInts, lockIndex);\n\t}\n\n\tprivate linkReservedBucket(\n\t\tcol: number, row: number, slotIndex: number, id: number, entityIndex: number, anchorCol: number, anchorRow: number,\n\t) {\n\t\tlet pool = this.slotPool;\n\t\tlet block = pool.blockFor(slotIndex);\n\t\tlet slotOffset = pool.blockOffset(slotIndex);\n\t\tblock[slotOffset + SLOT_ID] = id;\n\t\tblock[slotOffset + SLOT_COL] = col;\n\t\tblock[slotOffset + SLOT_ROW] = row;\n\t\tblock[slotOffset + SLOT_ANCHOR_COL] = anchorCol;\n\t\tblock[slotOffset + SLOT_ANCHOR_ROW] = anchorRow;\n\t\tblock[slotOffset + SLOT_ENTITY_INDEX] = entityIndex;\n\t\tlet bucket = this.bucketOf(col, row);\n\t\tlet bucketInts = this.getBucketSegment(bucket);\n\t\tlet bucketHeads = this.bucketHeadSegments[bucket >>> BUCKET_SEGMENT_SHIFT]!;\n\t\tlet offset = this.bucketOffset(bucket);\n\t\tlet lockIndex = offset + BUCKET_LOCK_OFFSET;\n\t\tlock(bucketInts, lockIndex);\n\t\tlet headIndex = offset + BUCKET_HEAD_OFFSET;\n\t\tblock[slotOffset + SLOT_NEXT] = bucketHeads[headIndex];\n\t\tbucketHeads[headIndex] = slotIndex;\n\t\tunlock(bucketInts, lockIndex);\n\t}\n\n\t// Move an entity's slots from its old cell rectangle to a new one, touching only the cells that actually change. A cell\n\t// in both rectangles keeps its slot and just gets its anchor refreshed; new-only cells are linked before old-only cells\n\t// are unlinked, so a query racing the move never sees the entity in neither rectangle. Doing it this way (rather than\n\t// linkRange(new) + unlinkRange(old)) is what keeps overlap cells correct: unlinkFromBucket matches by id + cell and\n\t// would splice out the freshly linked slot in an overlap cell, leaving the stale one behind and corrupting the anchor.\n\tprivate relinkRange(id: number, entityIndex: number, minCol: number, minRow: number, maxCol: number, maxRow: number, oldMinCol: number, oldMinRow: number, oldMaxCol: number, oldMaxRow: number) {\n\t\tlet ovMinCol = minCol > oldMinCol ? minCol : oldMinCol;\n\t\tlet ovMaxCol = maxCol < oldMaxCol ? maxCol : oldMaxCol;\n\t\tlet ovMinRow = minRow > oldMinRow ? minRow : oldMinRow;\n\t\tlet ovMaxRow = maxRow < oldMaxRow ? maxRow : oldMaxRow;\n\t\tlet hasOverlap = ovMinCol <= ovMaxCol && ovMinRow <= ovMaxRow;\n\n\t\tlet slot = this.slotScratch;\n\t\tslot[SLOT_ID] = id;\n\t\tslot[SLOT_ANCHOR_COL] = minCol;\n\t\tslot[SLOT_ANCHOR_ROW] = minRow;\n\t\tslot[SLOT_NEXT] = this.nullIndex;\n\t\tslot[SLOT_ENTITY_INDEX] = entityIndex;\n\n\t\tfor(let row = minRow; row <= maxRow; row++) {\n\t\t\tlet inOverlapRow = hasOverlap && row >= ovMinRow && row <= ovMaxRow;\n\t\t\tfor(let col = minCol; col <= maxCol; col++) {\n\t\t\t\tif(inOverlapRow && col >= ovMinCol && col <= ovMaxCol) {\n\t\t\t\t\tthis.refreshAnchorInBucket(this.bucketOf(col, row), id, col, row, minCol, minRow);\n\t\t\t\t} else {\n\t\t\t\t\tthis.linkIntoBucket(col, row, slot);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tlet nullIndex = this.nullIndex;\n\t\tlet pool = this.slotPool;\n\t\tfor(let row = oldMinRow; row <= oldMaxRow; row++) {\n\t\t\tlet inOverlapRow = hasOverlap && row >= ovMinRow && row <= ovMaxRow;\n\t\t\tfor(let col = oldMinCol; col <= oldMaxCol; col++) {\n\t\t\t\tif(inOverlapRow && col >= ovMinCol && col <= ovMaxCol) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tlet slotIndex = this.unlinkFromBucket(this.bucketOf(col, row), id, col, row);\n\t\t\t\tif(slotIndex !== nullIndex) {\n\t\t\t\t\tpool.deleteIndex(slotIndex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Refresh the anchor on this entity's slot already linked for cell (col, row) (single writer per id, so it is present).\n\tprivate refreshAnchorInBucket(bucket: number, id: number, col: number, row: number, anchorCol: number, anchorRow: number) {\n\t\tlet nullIndex = this.nullIndex;\n\t\tlet bucketInts = this.getBucketSegment(bucket);\n\t\tlet offset = this.bucketOffset(bucket);\n\t\tlet lockIndex = offset + BUCKET_LOCK_OFFSET;\n\t\tlock(bucketInts, lockIndex);\n\t\tlet pool = this.slotPool;\n\t\tlet bucketHeads = this.bucketHeadSegments[bucket >>> BUCKET_SEGMENT_SHIFT]!;\n\t\tlet index = bucketHeads[offset + BUCKET_HEAD_OFFSET];\n\t\twhile(index !== nullIndex) {\n\t\t\tlet block = pool.blockFor(index);\n\t\t\tlet off = pool.blockOffset(index);\n\t\t\tif(block[off + SLOT_ID] === id && block[off + SLOT_COL] === col && block[off + SLOT_ROW] === row) {\n\t\t\t\tblock[off + SLOT_ANCHOR_COL] = anchorCol;\n\t\t\t\tblock[off + SLOT_ANCHOR_ROW] = anchorRow;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tindex = block[off + SLOT_NEXT];\n\t\t}\n\t\tunlock(bucketInts, lockIndex);\n\t}\n\n\t// Remove and recycle this entity's slot from each cell of the rectangle.\n\tprivate unlinkRange(id: number, minCol: number, minRow: number, maxCol: number, maxRow: number) {\n\t\tlet nullIndex = this.nullIndex;\n\t\tlet pool = this.slotPool;\n\t\tfor(let row = minRow; row <= maxRow; row++) {\n\t\t\tfor(let col = minCol; col <= maxCol; col++) {\n\t\t\t\tlet slotIndex = this.unlinkFromBucket(this.bucketOf(col, row), id, col, row);\n\t\t\t\tif(slotIndex !== nullIndex) {\n\t\t\t\t\tpool.deleteIndex(slotIndex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Caller has already peeked the head unlocked and skipped empty buckets (an empty bucket has nothing to read, so the\n\t// lock and its atomics would be pure overhead). Taking the lock only to walk a non-empty bucket preserves the snapshot\n\t// guarantee - a concurrent insert we race is simply not in this snapshot.\n\tprivate collectBucket(\n\t\tout: Array<number>, bucketInts: Int32Array, bucketHeads: Uint32Array, offset: number,\n\t\tcol: number, row: number, qMinCol: number, qMinRow: number,\n\t) {\n\t\tlet nullIndex = this.nullIndex;\n\t\tlet headIndex = offset + BUCKET_HEAD_OFFSET;\n\t\tlet lockIndex = offset + BUCKET_LOCK_OFFSET;\n\t\tlock(bucketInts, lockIndex);\n\t\tlet index = bucketHeads[headIndex];\n\t\tlet pool = this.slotPool;\n\t\twhile(index !== nullIndex) {\n\t\t\t// One chunk resolve per slot covers every field it reads\n\t\t\tlet block = pool.blockFor(index);\n\t\t\tlet off = pool.blockOffset(index);\n\t\t\t// Other cells hash to this bucket too; only slots living in the cell being queried count.\n\t\t\tif(block[off + SLOT_COL] === col && block[off + SLOT_ROW] === row) {\n\t\t\t\t// Emit only from the entity's top-left cell that intersects this query, so a multi-cell entity is returned once.\n\t\t\t\tlet anchorCol = block[off + SLOT_ANCHOR_COL];\n\t\t\t\tlet anchorRow = block[off + SLOT_ANCHOR_ROW];\n\t\t\t\tlet tlCol = anchorCol > qMinCol ? anchorCol : qMinCol;\n\t\t\t\tlet tlRow = anchorRow > qMinRow ? anchorRow : qMinRow;\n\t\t\t\tif(col === tlCol && row === tlRow) {\n\t\t\t\t\tout.push(block[off + SLOT_ID]);\n\t\t\t\t}\n\t\t\t}\n\t\t\tindex = block[off + SLOT_NEXT];\n\t\t}\n\t\tunlock(bucketInts, lockIndex);\n\t}\n\n\tprivate collectNeighborBucket(out: Array<number>, col: number, row: number) {\n\t\tlet bucket = this.bucketOf(col, row);\n\t\tlet bucketInts = this.getBucketSegment(bucket);\n\t\tlet bucketHeads = this.bucketHeadSegments[bucket >>> BUCKET_SEGMENT_SHIFT]!;\n\t\tlet offset = this.bucketOffset(bucket);\n\t\tlet headIndex = offset + BUCKET_HEAD_OFFSET;\n\t\tif(bucketHeads[headIndex] === this.nullIndex) {\n\t\t\treturn;\n\t\t}\n\t\tlet lockIndex = offset + BUCKET_LOCK_OFFSET;\n\t\tlock(bucketInts, lockIndex);\n\t\tlet index = bucketHeads[headIndex];\n\t\twhile(index !== this.nullIndex) {\n\t\t\tlet block = this.slotPool.blockFor(index);\n\t\t\tlet off = this.slotPool.blockOffset(index);\n\t\t\tif(block[off + SLOT_COL] === col && block[off + SLOT_ROW] === row) {\n\t\t\t\tout.push(block[off + SLOT_ID]);\n\t\t\t}\n\t\t\tindex = block[off + SLOT_NEXT];\n\t\t}\n\t\tunlock(bucketInts, lockIndex);\n\t}\n\n\tprivate collectNearestBucket(\n\t\tnearest: NearestNeighbor, col: number, row: number, x: number, y: number,\n\t) {\n\t\tlet bucket = this.bucketOf(col, row);\n\t\tlet bucketInts = this.getBucketSegment(bucket);\n\t\tlet bucketHeads = this.bucketHeadSegments[bucket >>> BUCKET_SEGMENT_SHIFT]!;\n\t\tlet offset = this.bucketOffset(bucket);\n\t\tlet headIndex = offset + BUCKET_HEAD_OFFSET;\n\t\tif(bucketHeads[headIndex] === this.nullIndex) {\n\t\t\treturn;\n\t\t}\n\t\tlet lockIndex = offset + BUCKET_LOCK_OFFSET;\n\t\tlock(bucketInts, lockIndex);\n\t\tlet index = bucketHeads[headIndex];\n\t\twhile(index !== this.nullIndex) {\n\t\t\tlet slot = this.slotPool.blockFor(index);\n\t\t\tlet off = this.slotPool.blockOffset(index);\n\t\t\tif(slot[off + SLOT_COL] === col && slot[off + SLOT_ROW] === row) {\n\t\t\t\tlet entityIndex = slot[off + SLOT_ENTITY_INDEX];\n\t\t\t\tlet entity = this.entityPool.blockFor(entityIndex);\n\t\t\t\tlet entityOffset = this.entityPool.blockOffset(entityIndex);\n\t\t\t\tlet id = slot[off + SLOT_ID];\n\t\t\t\tlet distance = this.entityBlockDistanceSquared(entity, entityOffset, x, y);\n\t\t\t\tif(distance < nearest.distance\n\t\t\t\t\t|| (distance === nearest.distance && (nearest.id === undefined || id < nearest.id))) {\n\t\t\t\t\tnearest.id = id;\n\t\t\t\t\tnearest.distance = distance;\n\t\t\t\t}\n\t\t\t}\n\t\t\tindex = slot[off + SLOT_NEXT];\n\t\t}\n\t\tunlock(bucketInts, lockIndex);\n\t}\n\n\tprivate collectNeighborCandidates(\n\t\tcandidates: Array<NeighborCandidate>, col: number, row: number, queryCol: number, queryRow: number,\n\t\tx: number, y: number, maxResults: number, maxDistanceSquared: number,\n\t) {\n\t\tlet bucket = this.bucketOf(col, row);\n\t\tlet bucketInts = this.getBucketSegment(bucket);\n\t\tlet bucketHeads = this.bucketHeadSegments[bucket >>> BUCKET_SEGMENT_SHIFT]!;\n\t\tlet offset = this.bucketOffset(bucket);\n\t\tlet headIndex = offset + BUCKET_HEAD_OFFSET;\n\t\tif(bucketHeads[headIndex] === this.nullIndex) {\n\t\t\treturn;\n\t\t}\n\t\tlet lockIndex = offset + BUCKET_LOCK_OFFSET;\n\t\tlock(bucketInts, lockIndex);\n\t\tlet index = bucketHeads[headIndex];\n\t\twhile(index !== this.nullIndex) {\n\t\t\tlet slot = this.slotPool.blockFor(index);\n\t\t\tlet off = this.slotPool.blockOffset(index);\n\t\t\tif(slot[off + SLOT_COL] === col && slot[off + SLOT_ROW] === row) {\n\t\t\t\tlet entityIndex = slot[off + SLOT_ENTITY_INDEX];\n\t\t\t\tlet entity = this.entityPool.blockFor(entityIndex);\n\t\t\t\tlet entityOffset = this.entityPool.blockOffset(entityIndex);\n\t\t\t\tlet minCol = entity[entityOffset + ENTITY_MIN_COL];\n\t\t\t\tlet maxCol = entity[entityOffset + ENTITY_MAX_COL];\n\t\t\t\tlet minRow = entity[entityOffset + ENTITY_MIN_ROW];\n\t\t\t\tlet maxRow = entity[entityOffset + ENTITY_MAX_ROW];\n\t\t\t\tlet nearestCol = queryCol < minCol ? minCol : queryCol > maxCol ? maxCol : queryCol;\n\t\t\t\tlet nearestRow = queryRow < minRow ? minRow : queryRow > maxRow ? maxRow : queryRow;\n\t\t\t\tif(col === nearestCol && row === nearestRow) {\n\t\t\t\t\tlet distance = this.entityBlockDistanceSquared(entity, entityOffset, x, y);\n\t\t\t\t\tif(distance <= maxDistanceSquared) {\n\t\t\t\t\t\taddCandidate(candidates, slot[off + SLOT_ID], distance, maxResults);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tindex = slot[off + SLOT_NEXT];\n\t\t}\n\t\tunlock(bucketInts, lockIndex);\n\t}\n\n\tprivate addEntityCandidate(candidates: Array<NeighborCandidate>, id: number, entityIndex: number, x: number, y: number, maxResults: number, maxDistanceSquared: number) {\n\t\tlet distance = this.entityDistanceSquared(entityIndex, x, y);\n\t\tif(distance <= maxDistanceSquared) {\n\t\t\taddCandidate(candidates, id, distance, maxResults);\n\t\t}\n\t}\n\n\tprivate entityDistanceSquared(entityIndex: number, x: number, y: number): number {\n\t\tlet entity = this.entityPool.blockFor(entityIndex);\n\t\tlet off = this.entityPool.blockOffset(entityIndex);\n\t\treturn this.entityBlockDistanceSquared(entity, off, x, y);\n\t}\n\n\tprivate entityBlockDistanceSquared(entity: CoordArray, off: number, x: number, y: number): number {\n\t\tlet minX = entity[off + ENTITY_X];\n\t\tlet minY = entity[off + ENTITY_Y];\n\t\treturn distanceSquaredToRect(\n\t\t\tx, y, minX, minY, minX + entity[off + ENTITY_WIDTH], minY + entity[off + ENTITY_HEIGHT],\n\t\t);\n\t}\n\n\t// Splice this entity's slot for exactly cell (col, row) out of the bucket and return its index (nullIndex if absent).\n\t// Matching on id and cell is required: two of an entity's own cells can hash to the same bucket, so id alone is ambiguous.\n\tprivate unlinkFromBucket(bucket: number, id: number, col: number, row: number): number {\n\t\tlet nullIndex = this.nullIndex;\n\t\tlet bucketInts = this.getBucketSegment(bucket);\n\t\tlet bucketHeads = this.bucketHeadSegments[bucket >>> BUCKET_SEGMENT_SHIFT]!;\n\t\tlet offset = this.bucketOffset(bucket);\n\t\tlet lockIndex = offset + BUCKET_LOCK_OFFSET;\n\t\tlock(bucketInts, lockIndex);\n\t\tlet headIndex = offset + BUCKET_HEAD_OFFSET;\n\t\tlet pool = this.slotPool;\n\n\t\tlet prev = nullIndex;\n\t\tlet index = bucketHeads[headIndex];\n\t\twhile(index !== nullIndex) {\n\t\t\t// One chunk resolve per slot covers every field it reads\n\t\t\tlet block = pool.blockFor(index);\n\t\t\tlet off = pool.blockOffset(index);\n\t\t\tlet next = block[off + SLOT_NEXT];\n\t\t\tif(block[off + SLOT_ID] === id && block[off + SLOT_COL] === col && block[off + SLOT_ROW] === row) {\n\t\t\t\tif(prev === nullIndex) {\n\t\t\t\t\tbucketHeads[headIndex] = next;\n\t\t\t\t} else {\n\t\t\t\t\tpool.set(prev, SLOT_NEXT, next);\n\t\t\t\t}\n\t\t\t\tunlock(bucketInts, lockIndex);\n\t\t\t\treturn index;\n\t\t\t}\n\t\t\tprev = index;\n\t\t\tindex = next;\n\t\t}\n\n\t\tunlock(bucketInts, lockIndex);\n\t\treturn nullIndex;\n\t}\n}\n\nfunction getCoordTypeCode(type: CoordArrayConstructor | undefined): number {\n\tlet code = getArrayTypeCode(type ?? DEFAULT_COORD_TYPE);\n\tif(code !== ARRAY_TYPE.float32 && code !== ARRAY_TYPE.float64) {\n\t\tthrow new Error('SharedSpatialMap must be Float32Array or Float64Array');\n\t}\n\treturn code;\n}\nfunction getCoordTypeConstructor(typeCode: number): CoordArrayConstructor {\n\treturn getByteMultipler(typeCode) === 2 ? Float64Array : Float32Array;\n}\nfunction nullIndexFor(typeCode: number): number {\n\treturn typeCode === ARRAY_TYPE.float32 ? FLOAT32_NULL_INDEX : FLOAT64_NULL_INDEX;\n}\nfunction maskFor(bucketCount: number): number {\n\treturn (bucketCount & (bucketCount - 1)) === 0 ? bucketCount - 1 : -1;\n}\nfunction nextPowerOfTwo(value: number): number {\n\treturn Math.pow(2, Math.ceil(Math.log2(Math.max(1, value))));\n}\n\ninterface SharedSpatialMapConfig {\n\tgridSize?: number\n\tbuckets?: number\n\tmaxBuckets?: number\n\tmaxEntities?: number\n\tmaxSlots?: number\n\ttype?: CoordArrayConstructor\n}\ninterface SharedSpatialMapMemory {\n\tfirstBlock: SharedAllocatedMemory\n}\n\nexport type { SharedSpatialMapConfig, SharedSpatialMapMemory, SpatialEntity };\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@daneren2005/shared-memory-objects",
3
- "version": "1.6.1",
3
+ "version": "1.6.2",
4
4
  "author": "daneren2005@gmail.com",
5
5
  "description": "Creating objects with a SharedArrayBuffer",
6
6
  "homepage": "https://github.com/daneren2005/shared-memory-objects#readme",