@daneren2005/shared-memory-objects 1.6.1 → 1.6.3
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 +3 -2
- package/dist/allocated-memory.js +1 -2
- package/dist/cached-item-list.js +1 -2
- package/dist/constant-string.js +13 -14
- package/dist/local-pool.js +5 -6
- package/dist/lock/read-write-lock.js +0 -1
- package/dist/lock/simple-lock.js +0 -1
- package/dist/memory-buffer.js +1 -2
- package/dist/memory-heap.js +24 -25
- package/dist/serialize-object.js +4 -5
- package/dist/shared-list.js +43 -44
- package/dist/shared-map.js +74 -75
- package/dist/shared-pointer-list.js +15 -16
- package/dist/shared-pool.d.ts +1 -0
- package/dist/shared-pool.js +36 -32
- package/dist/shared-pool.js.map +1 -1
- package/dist/shared-stack.js +33 -34
- package/dist/shared-string.js +19 -20
- package/dist/shared-vector.js +23 -24
- package/dist/spatial/shared-quadtree.js +81 -82
- package/dist/spatial/shared-quadtree.js.map +1 -1
- package/dist/spatial/shared-spatial-grid.js +104 -110
- package/dist/spatial/shared-spatial-grid.js.map +1 -1
- package/dist/spatial/shared-spatial-map.d.ts +2 -0
- package/dist/spatial/shared-spatial-map.js +205 -202
- package/dist/spatial/shared-spatial-map.js.map +1 -1
- package/dist/spatial/spatial-entity.js +1 -2
- package/dist/spatial/spatial-neighbors.js +1 -2
- package/dist/utils/16-from-32-array.js +1 -2
- package/dist/utils/16-from-32-float.js +1 -2
- package/dist/utils/16-from-32-int.js +1 -2
- package/dist/utils/16-from-32-uint.js +1 -2
- package/dist/utils/16-from-64-array.js +1 -2
- package/dist/utils/16-from-64-float.js +1 -2
- package/dist/utils/16-from-64-int.js +1 -2
- package/dist/utils/16-from-64-uint.js +1 -2
- package/dist/utils/32-from-64-float.js +1 -2
- package/dist/utils/32-from-64-int.js +1 -2
- package/dist/utils/32-from-64-uint.js +1 -2
- package/dist/utils/array-type.js +1 -2
- package/dist/utils/atomic-math.js +1 -2
- package/dist/utils/float32-atomics.js +1 -2
- package/dist/utils/float64-atomics.js +1 -2
- package/dist/utils/hash.js +1 -2
- package/dist/utils/pointer.js +1 -2
- package/dist/utils/typedarray.js +1 -2
- package/package.json +11 -11
|
@@ -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","SharedPool","SharedMap","SharedStack","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","pool","recycled","length","chunkSize","base","end","slotIndex","slotOffset","headIndex","before","slot","lockIndex","unlock","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\t// The table write lock excludes every writer, so the length snapshot in the mask holds. Recycled slots are in no\n\t\t// chain and carry a stale SLOT_NEXT, so they must be skipped.\n\t\tlet pool = this.slotPool;\n\t\tlet recycled = pool.getRecycledMask();\n\t\tlet length = recycled.length;\n\t\tlet chunkSize = pool.maxChunkSize;\n\t\tfor(let base = 0; base < length; base += chunkSize) {\n\t\t\tlet block = pool.blockFor(base);\n\t\t\tlet end = Math.min(base + chunkSize, length);\n\t\t\tfor(let slotIndex = base; slotIndex < end; slotIndex++) {\n\t\t\t\tif(recycled[slotIndex] === 1) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tlet slotOffset = (slotIndex - base) * SLOT_FIELDS;\n\t\t\t\tlet bucket = this.bucketOf(block[slotOffset + SLOT_COL], block[slotOffset + SLOT_ROW]);\n\t\t\t\tlet heads = this.bucketHeadSegments[bucket >>> BUCKET_SEGMENT_SHIFT]!;\n\t\t\t\tlet headIndex = this.bucketOffset(bucket) + BUCKET_HEAD_OFFSET;\n\t\t\t\tblock[slotOffset + SLOT_NEXT] = heads[headIndex];\n\t\t\t\theads[headIndex] = slotIndex;\n\t\t\t}\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,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/spatial/spatial-entity.ts
|
|
2
2
|
var e = 16384;
|
|
3
3
|
function t(t, n, r, i) {
|
|
4
4
|
if (t < 100 || t > e) return 100;
|
|
@@ -13,7 +13,6 @@ function n(t, n) {
|
|
|
13
13
|
for (; a * 2 * 3 <= n;) a *= 2;
|
|
14
14
|
return Math.min(i, a);
|
|
15
15
|
}
|
|
16
|
-
//#endregion
|
|
17
16
|
export { n as getSpatialMapCapacity, t as getSpatialPoolChunkSize };
|
|
18
17
|
|
|
19
18
|
//# sourceMappingURL=spatial-entity.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/spatial/spatial-neighbors.ts
|
|
2
2
|
var e = class {
|
|
3
3
|
values = [];
|
|
4
4
|
priorities = [];
|
|
@@ -49,7 +49,6 @@ function n(e, t, n, r) {
|
|
|
49
49
|
function r(e, t, n) {
|
|
50
50
|
return e.length === t ? Math.min(n, e[e.length - 1].distance) : n;
|
|
51
51
|
}
|
|
52
|
-
//#endregion
|
|
53
52
|
export { e as MinPriorityQueue, n as addCandidate, r as candidateLimit, t as distanceSquaredToRect };
|
|
54
53
|
|
|
55
54
|
//# sourceMappingURL=spatial-neighbors.js.map
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export { e as convert16To32, t as convert32To16, n as load16From32, r as store16In32 };
|
|
1
|
+
export { convert16To32Uint as convert16To32, convert32To16Uint as convert32To16, load16From32Uint as load16From32, store16In32Uint as store16In32 } from "./16-from-32-uint.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/utils/16-from-32-float.ts
|
|
2
2
|
var e = /* @__PURE__ */ new ArrayBuffer(4), t = new Float32Array(e), n = new Int32Array(e), r = new Float16Array(e);
|
|
3
3
|
function i(e, t) {
|
|
4
4
|
let i = new Int32Array(e.buffer, e.byteOffset, e.length);
|
|
@@ -14,7 +14,6 @@ function o(e) {
|
|
|
14
14
|
function s(e, n) {
|
|
15
15
|
return r[0] = e, r[1] = n, t[0];
|
|
16
16
|
}
|
|
17
|
-
//#endregion
|
|
18
17
|
export { s as convert16To32Float, o as convert32To16Float, i as load16From32Float, a as store16In32Float };
|
|
19
18
|
|
|
20
19
|
//# sourceMappingURL=16-from-32-float.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/utils/16-from-32-int.ts
|
|
2
2
|
var e = /* @__PURE__ */ new ArrayBuffer(4), t = new Int32Array(e), n = new Int16Array(e);
|
|
3
3
|
function r(e, t) {
|
|
4
4
|
return a(Atomics.load(e, t));
|
|
@@ -12,7 +12,6 @@ function a(e) {
|
|
|
12
12
|
function o(e, r) {
|
|
13
13
|
return n[0] = e, n[1] = r, t[0];
|
|
14
14
|
}
|
|
15
|
-
//#endregion
|
|
16
15
|
export { o as convert16To32Int, a as convert32To16Int, r as load16From32Int, i as store16In32Int };
|
|
17
16
|
|
|
18
17
|
//# sourceMappingURL=16-from-32-int.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/utils/16-from-32-uint.ts
|
|
2
2
|
var e = /* @__PURE__ */ new ArrayBuffer(4), t = new Uint32Array(e), n = new Uint16Array(e);
|
|
3
3
|
function r(e, t) {
|
|
4
4
|
return a(Atomics.load(e, t));
|
|
@@ -12,7 +12,6 @@ function a(e) {
|
|
|
12
12
|
function o(e, r) {
|
|
13
13
|
return n[0] = e, n[1] = r, t[0];
|
|
14
14
|
}
|
|
15
|
-
//#endregion
|
|
16
15
|
export { o as convert16To32Uint, a as convert32To16Uint, r as load16From32Uint, i as store16In32Uint };
|
|
17
16
|
|
|
18
17
|
//# sourceMappingURL=16-from-32-uint.js.map
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export { e as load16From64, t as store16In64 };
|
|
1
|
+
export { load16From64Uint as load16From64, store16In64Uint as store16In64 } from "./16-from-64-uint.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/utils/16-from-64-float.ts
|
|
2
2
|
var e = /* @__PURE__ */ new ArrayBuffer(8), t = new Float64Array(e), n = new BigInt64Array(e), r = new Float16Array(e);
|
|
3
3
|
function i(e, t) {
|
|
4
4
|
let i = new BigInt64Array(e.buffer, e.byteOffset, e.length);
|
|
@@ -24,7 +24,6 @@ function o(e) {
|
|
|
24
24
|
function s(e, n, i, a = 0) {
|
|
25
25
|
return r[0] = e, r[1] = n, r[2] = i, r[3] = a, t[0];
|
|
26
26
|
}
|
|
27
|
-
//#endregion
|
|
28
27
|
export { s as convert16To64Float, o as convert64To16Float, i as load16From64Float, a as store16In64Float };
|
|
29
28
|
|
|
30
29
|
//# sourceMappingURL=16-from-64-float.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/utils/16-from-64-int.ts
|
|
2
2
|
var e = /* @__PURE__ */ new ArrayBuffer(8), t = new BigInt64Array(e), n = new Int16Array(e);
|
|
3
3
|
function r(e, t) {
|
|
4
4
|
return a(Atomics.load(e, t));
|
|
@@ -17,7 +17,6 @@ function a(e) {
|
|
|
17
17
|
function o(e, r, i, a = 0) {
|
|
18
18
|
return n[0] = e, n[1] = r, n[2] = i, n[3] = a, t[0];
|
|
19
19
|
}
|
|
20
|
-
//#endregion
|
|
21
20
|
export { o as convert16To64Int, a as convert64To16Int, r as load16From64Int, i as store16In64Int };
|
|
22
21
|
|
|
23
22
|
//# sourceMappingURL=16-from-64-int.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/utils/16-from-64-uint.ts
|
|
2
2
|
var e = /* @__PURE__ */ new ArrayBuffer(8), t = new BigUint64Array(e), n = new Uint16Array(e);
|
|
3
3
|
function r(e, t) {
|
|
4
4
|
return a(Atomics.load(e, t));
|
|
@@ -17,7 +17,6 @@ function a(e) {
|
|
|
17
17
|
function o(e, r, i, a = 0) {
|
|
18
18
|
return n[0] = e, n[1] = r, n[2] = i, n[3] = a, t[0];
|
|
19
19
|
}
|
|
20
|
-
//#endregion
|
|
21
20
|
export { o as convert16To64Uint, a as convert64To16Uint, r as load16From64Uint, i as store16In64Uint };
|
|
22
21
|
|
|
23
22
|
//# sourceMappingURL=16-from-64-uint.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/utils/32-from-64-float.ts
|
|
2
2
|
var e = /* @__PURE__ */ new ArrayBuffer(8), t = new Float64Array(e), n = new Float32Array(e), r = new BigInt64Array(e);
|
|
3
3
|
function i(e, t) {
|
|
4
4
|
let i = new BigInt64Array(e.buffer, e.byteOffset, e.length);
|
|
@@ -14,7 +14,6 @@ function o(e) {
|
|
|
14
14
|
function s(e, r) {
|
|
15
15
|
return n[0] = e, n[1] = r, t[0];
|
|
16
16
|
}
|
|
17
|
-
//#endregion
|
|
18
17
|
export { s as convert32To64Float, o as convert64To32Float, i as load32From64Float, a as store32In64Float };
|
|
19
18
|
|
|
20
19
|
//# sourceMappingURL=32-from-64-float.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/utils/32-from-64-int.ts
|
|
2
2
|
var e = /* @__PURE__ */ new ArrayBuffer(8), t = new BigInt64Array(e), n = new Int32Array(e);
|
|
3
3
|
function r(e, t) {
|
|
4
4
|
return a(Atomics.load(e, t));
|
|
@@ -12,7 +12,6 @@ function a(e) {
|
|
|
12
12
|
function o(e, r) {
|
|
13
13
|
return n[0] = e, n[1] = r, t[0];
|
|
14
14
|
}
|
|
15
|
-
//#endregion
|
|
16
15
|
export { o as convert32To64Int, a as convert64To32Int, r as load32From64Int, i as store32In64Int };
|
|
17
16
|
|
|
18
17
|
//# sourceMappingURL=32-from-64-int.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/utils/32-from-64-uint.ts
|
|
2
2
|
var e = /* @__PURE__ */ new ArrayBuffer(8), t = new BigUint64Array(e), n = new Uint32Array(e);
|
|
3
3
|
function r(e, t) {
|
|
4
4
|
return a(Atomics.load(e, t));
|
|
@@ -12,7 +12,6 @@ function a(e) {
|
|
|
12
12
|
function o(e, r) {
|
|
13
13
|
return n[0] = e, n[1] = r, t[0];
|
|
14
14
|
}
|
|
15
|
-
//#endregion
|
|
16
15
|
export { o as convert32To64Uint, a as convert64To32Uint, r as load32From64Uint, i as store32In64Uint };
|
|
17
16
|
|
|
18
17
|
//# sourceMappingURL=32-from-64-uint.js.map
|
package/dist/utils/array-type.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/utils/array-type.ts
|
|
2
2
|
var e = {
|
|
3
3
|
uint32: 0,
|
|
4
4
|
int32: 1,
|
|
@@ -30,7 +30,6 @@ function a(t, n, r, i) {
|
|
|
30
30
|
default: throw Error(`Unknown data block type ${t}`);
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
-
//#endregion
|
|
34
33
|
export { e as ARRAY_TYPE, t as getArrayTypeCode, n as getByteMultipler, r as isBigIntType, i as isFloatType, a as makeArrayView };
|
|
35
34
|
|
|
36
35
|
//# sourceMappingURL=array-type.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { compareExchangeFloat32 as e, loadFloat32 as t } from "./float32-atomics.js";
|
|
2
|
-
|
|
2
|
+
// src/utils/atomic-math.ts
|
|
3
3
|
function n(e, t, n, r) {
|
|
4
4
|
let i, a;
|
|
5
5
|
do
|
|
@@ -28,7 +28,6 @@ function a(n, r, i, a) {
|
|
|
28
28
|
while (e(n, r, o, s) !== o);
|
|
29
29
|
return s;
|
|
30
30
|
}
|
|
31
|
-
//#endregion
|
|
32
31
|
export { i as addAtomicFloat32, n as addAtomicInt, a as subtractAtomicFloat, r as subtractAtomicInt };
|
|
33
32
|
|
|
34
33
|
//# sourceMappingURL=atomic-math.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/utils/float32-atomics.ts
|
|
2
2
|
var e = /* @__PURE__ */ new ArrayBuffer(4), t = new Float32Array(e), n = new Int32Array(e);
|
|
3
3
|
function r(e, t) {
|
|
4
4
|
return e instanceof Float32Array && (e = new Int32Array(e.buffer, e.byteOffset, e.length)), a(Atomics.load(e, t));
|
|
@@ -18,7 +18,6 @@ function s(e, t, n) {
|
|
|
18
18
|
function c(e, t, n, r) {
|
|
19
19
|
return e instanceof Float32Array && (e = new Int32Array(e.buffer, e.byteOffset, e.length)), a(Atomics.compareExchange(e, t, o(n), o(r)));
|
|
20
20
|
}
|
|
21
|
-
//#endregion
|
|
22
21
|
export { c as compareExchangeFloat32, o as convertFloat32ToInt32, a as convertInt32ToFloat32, s as exchangeFloat32, r as loadFloat32, i as storeFloat32 };
|
|
23
22
|
|
|
24
23
|
//# sourceMappingURL=float32-atomics.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/utils/float64-atomics.ts
|
|
2
2
|
var e = /* @__PURE__ */ new ArrayBuffer(8), t = new Float64Array(e), n = new BigInt64Array(e);
|
|
3
3
|
function r(e, t) {
|
|
4
4
|
return e instanceof Float64Array && (e = new BigInt64Array(e.buffer, e.byteOffset, e.length)), a(Atomics.load(e, t));
|
|
@@ -18,7 +18,6 @@ function s(e, t, n) {
|
|
|
18
18
|
function c(e, t, n, r) {
|
|
19
19
|
return e instanceof Float64Array && (e = new BigInt64Array(e.buffer, e.byteOffset, e.length)), a(Atomics.compareExchange(e, t, o(n), o(r)));
|
|
20
20
|
}
|
|
21
|
-
//#endregion
|
|
22
21
|
export { c as compareExchangeFloat64, o as convertFloat64ToInt64, a as convertInt64ToFloat64, s as exchangeFloat64, r as loadFloat64, i as storeFloat64 };
|
|
23
22
|
|
|
24
23
|
//# sourceMappingURL=float64-atomics.js.map
|