@daneren2005/shared-memory-objects 1.6.0 → 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +3 -2
  2. package/dist/allocated-memory.js.map +1 -1
  3. package/dist/cached-item-list.js.map +1 -1
  4. package/dist/constant-string.js.map +1 -1
  5. package/dist/local-pool.js.map +1 -1
  6. package/dist/lock/read-write-lock.js.map +1 -1
  7. package/dist/lock/simple-lock.js.map +1 -1
  8. package/dist/memory-buffer.js.map +1 -1
  9. package/dist/memory-heap.js.map +1 -1
  10. package/dist/serialize-object.js.map +1 -1
  11. package/dist/shared-list.js.map +1 -1
  12. package/dist/shared-map.d.ts +3 -0
  13. package/dist/shared-map.js +42 -24
  14. package/dist/shared-map.js.map +1 -1
  15. package/dist/shared-pointer-list.js.map +1 -1
  16. package/dist/shared-pool.js.map +1 -1
  17. package/dist/shared-stack.js.map +1 -1
  18. package/dist/shared-string.js.map +1 -1
  19. package/dist/shared-vector.js.map +1 -1
  20. package/dist/spatial/shared-quadtree.js.map +1 -1
  21. package/dist/spatial/shared-spatial-grid.js.map +1 -1
  22. package/dist/spatial/shared-spatial-map.d.ts +2 -0
  23. package/dist/spatial/shared-spatial-map.js +23 -19
  24. package/dist/spatial/shared-spatial-map.js.map +1 -1
  25. package/dist/spatial/spatial-entity.js.map +1 -1
  26. package/dist/spatial/spatial-neighbors.js.map +1 -1
  27. package/dist/utils/16-from-32-float.js.map +1 -1
  28. package/dist/utils/16-from-32-int.js.map +1 -1
  29. package/dist/utils/16-from-32-uint.js.map +1 -1
  30. package/dist/utils/16-from-64-float.js.map +1 -1
  31. package/dist/utils/16-from-64-int.js.map +1 -1
  32. package/dist/utils/16-from-64-uint.js.map +1 -1
  33. package/dist/utils/32-from-64-float.js.map +1 -1
  34. package/dist/utils/32-from-64-int.js.map +1 -1
  35. package/dist/utils/32-from-64-uint.js.map +1 -1
  36. package/dist/utils/array-type.js.map +1 -1
  37. package/dist/utils/atomic-math.js.map +1 -1
  38. package/dist/utils/float32-atomics.js.map +1 -1
  39. package/dist/utils/float64-atomics.js.map +1 -1
  40. package/dist/utils/hash.js.map +1 -1
  41. package/dist/utils/pointer.js.map +1 -1
  42. package/dist/utils/typedarray.js.map +1 -1
  43. package/package.json +8 -8
@@ -1 +1 @@
1
- {"version":3,"file":"shared-stack.js","names":[],"sources":["../src/shared-stack.ts"],"sourcesContent":["import type { SharedAllocatedMemory } from './allocated-memory';\nimport AllocatedMemory from './allocated-memory';\nimport { lock, unlock } from './lock/simple-lock';\nimport type MemoryHeap from './memory-heap';\nimport { getPointer, storePointer } from './utils/pointer';\nimport { getArrayTypeCode, getByteMultipler, makeArrayView, type NumericArray, type NumericArrayIO, type TypedArrayConstructor } from './utils/array-type';\n\nconst LENGTH_INDEX = 0;\nconst TYPE_INDEX = 1;\nconst BASE_SEGMENT_LENGTH_INDEX = 2;\nconst MAX_SEGMENTS_INDEX = 3;\nconst MAX_SEGMENT_LENGTH_INDEX = 4;\nconst SEGMENT_COUNT_INDEX = 5;\nconst GROW_LOCK_INDEX = 6;\nconst SEGMENT_START_INDEX = 7;\n\nconst DEFAULT_BASE_SEGMENT_LENGTH = 4;\n// Default segment budget. With uncapped power-of-two growth these 20 segments spanned 4,194,300 slots, but that ceiling\n// was never reachable: past segment 14 a segment's block outgrows a 1MB buffer and allocation throws. Capping segments at\n// what a buffer holds, the honest capacity of 20 segments is getCapacity(20, base, cap) instead.\nconst DEFAULT_MAX_SEGMENTS = 20;\n\n// The LENGTH word packs an ABA version counter (high bits) with the live count (low LEN_BITS). Both push and pop bump\n// the version on every successful CAS, so the CAS fails on ANY intervening op - the count can never ABA back to the\n// same value with a slot at a different generation. 22 count bits cover the default max length (4,194,300); the\n// remaining 10 version bits (1024) only need to outlast a single thread's load->CAS window.\nconst LEN_BITS = 22;\nconst LEN_MASK = (1 << LEN_BITS) - 1;\nconst MAX_COUNT = LEN_MASK - 1;\n// Adding this to the packed word bumps the version (high bits) by one. Combined with +1/-1 for the count it repacks the\n// whole word in a single add: the count is guarded from overflowing into the version bits, and a version overflow just\n// wraps harmlessly when the value is coerced back to Uint32 on the atomic store.\nconst VERSION_STEP = 1 << LEN_BITS;\n\n// Per-slot publish handshake. Each data slot has a companion sequence number: even means the slot is empty (free to\n// fill) at some lap, odd means the value is published (safe to read). It only ever increases. push/pop read the slot's\n// sequence and bail out (retry) unless it has the parity they need, then CAS the length; because the versioned CAS\n// rejects any intervening op, the sequence read stays valid all the way through the publish store below.\n\nexport default class SharedStack<T extends NumericArray = Uint32Array> implements Iterable<T[number]> {\n\tstatic readonly BASE_ALLOCATE_COUNT = 8;\n\tstatic readonly DEFAULT_ALLOCATE_COUNT = this.BASE_ALLOCATE_COUNT + DEFAULT_MAX_SEGMENTS;\n\n\t// Largest power-of-two segment length whose block (segmentLength data slots plus an equal run of publish sequences)\n\t// still fits in a single buffer. Segments double until they reach this, then stay flat so a segment can always be\n\t// allocated in one contiguous block. maxAllocationLength is MemoryHeap.maxAllocationLength. byteMultipler is the u32\n\t// units per data slot (2 for 64-bit types), so a length-L segment's block is L * byteMultipler data u32s + L sequence u32s.\n\tstatic getMaxSegmentLength(maxAllocationLength: number, baseSegmentLength = DEFAULT_BASE_SEGMENT_LENGTH, byteMultipler = 1): number {\n\t\tlet segmentLength = baseSegmentLength;\n\t\t// A length-L segment needs a block of L * (byteMultipler + 1) u32s, so the next double fits while\n\t\t// 2L * (byteMultipler + 1) <= maxAllocationLength\n\t\twhile(segmentLength * 2 * (byteMultipler + 1) <= maxAllocationLength) {\n\t\t\tsegmentLength *= 2;\n\t\t}\n\t\treturn segmentLength;\n\t}\n\n\t// Smallest segment count whose summed capacity covers maxLength. Segments double from baseSegmentLength but never grow\n\t// past maxSegmentLength, so once capped they each add a flat maxSegmentLength. Looped rather than solved in closed form\n\t// to avoid float rounding landing a segment short or over.\n\tstatic getMaxSegments(maxLength: number, baseSegmentLength = DEFAULT_BASE_SEGMENT_LENGTH, maxSegmentLength = Infinity): number {\n\t\tlet segments = 0;\n\t\tlet capacity = 0;\n\t\tlet segmentLength = baseSegmentLength;\n\t\twhile(capacity < maxLength) {\n\t\t\tcapacity += segmentLength;\n\t\t\tsegments++;\n\t\t\tsegmentLength = Math.min(segmentLength * 2, maxSegmentLength);\n\t\t}\n\t\treturn segments;\n\t}\n\t// Total capacity of the first `segments` segments under the same doubling-then-capped growth as getMaxSegments\n\tstatic getCapacity(segments: number, baseSegmentLength = DEFAULT_BASE_SEGMENT_LENGTH, maxSegmentLength = Infinity): number {\n\t\tlet capacity = 0;\n\t\tlet segmentLength = baseSegmentLength;\n\t\tfor(let i = 0; i < segments; i++) {\n\t\t\tcapacity += segmentLength;\n\t\t\tsegmentLength = Math.min(segmentLength * 2, maxSegmentLength);\n\t\t}\n\t\treturn capacity;\n\t}\n\tstatic getAllocateCount(maxLength: number, baseSegmentLength = DEFAULT_BASE_SEGMENT_LENGTH, maxSegmentLength = Infinity): number {\n\t\treturn this.BASE_ALLOCATE_COUNT + this.getMaxSegments(maxLength, baseSegmentLength, maxSegmentLength);\n\t}\n\tprivate memory: MemoryHeap;\n\n\t// Length, Type, Base Segment Length, Max Segments, Max Segment Length, Segment Count, Grow Lock, Segments\n\tprivate firstBlock: AllocatedMemory;\n\tprivate uint16Array: Uint16Array;\n\tprivate growLock: Int32Array;\n\treadonly maxLength: number;\n\n\t// False when firstBlock is a slice of another structure's memory (e.g. inlined in SharedPool); that owner frees it\n\tprivate ownsFirstBlock: boolean;\n\n\tprivate cachedSegments: T[] = [];\n\t// Parallel to cachedSegments: the per-slot publish sequences for each slot of that segment. Populated together with\n\t// the data view in getSegment, so whenever cachedSegments[i] is set cachedSequences[i] is too.\n\tprivate cachedSequences: Int32Array[] = [];\n\tprivate cachedBaseSegmentLength = 0;\n\t// Segment growth caps at cachedMaxSegmentLength. Segments 0..(cachedCapSegmentCount - 1) double geometrically and cover\n\t// the first cachedGeometricCapacity slots; every segment beyond that is a flat cachedMaxSegmentLength. Indexing splits\n\t// on cachedGeometricCapacity: below it the fast log2 mapping, above it a plain linear division into capped segments.\n\tprivate cachedMaxSegmentLength = 0;\n\tprivate cachedCapSegmentCount = 0;\n\tprivate cachedGeometricCapacity = 0;\n\t// u32 units per data slot: 1 for 32-bit types, 2 for 64-bit (float64/int64/uint64). A segment block is\n\t// segmentLength * cachedByteMultipler data u32s followed by segmentLength sequence u32s.\n\tprivate cachedByteMultipler = 1;\n\n\tget length(): number {\n\t\treturn Atomics.load(this.firstBlock.data, LENGTH_INDEX) & LEN_MASK;\n\t}\n\tget segmentCount(): number {\n\t\treturn Atomics.load(this.firstBlock.data, SEGMENT_COUNT_INDEX);\n\t}\n\tprivate set segmentCount(value: number) {\n\t\tthis.firstBlock.data[SEGMENT_COUNT_INDEX] = value;\n\t}\n\t\n\tget type(): number {\n\t\treturn this.uint16Array[0];\n\t}\n\tprivate set type(value: number) {\n\t\tAtomics.store(this.uint16Array, 0, value);\n\t}\n\n\tget baseSegmentLength() {\n\t\treturn this.firstBlock.data[BASE_SEGMENT_LENGTH_INDEX];\n\t}\n\tprivate set baseSegmentLength(value: number) {\n\t\tthis.firstBlock.data[BASE_SEGMENT_LENGTH_INDEX] = value;\n\t}\n\tget maxSegments() {\n\t\treturn this.firstBlock.data[MAX_SEGMENTS_INDEX];\n\t}\n\tprivate set maxSegments(value: number) {\n\t\tthis.firstBlock.data[MAX_SEGMENTS_INDEX] = value;\n\t}\n\tget maxSegmentLength() {\n\t\treturn this.firstBlock.data[MAX_SEGMENT_LENGTH_INDEX];\n\t}\n\tprivate set maxSegmentLength(value: number) {\n\t\tthis.firstBlock.data[MAX_SEGMENT_LENGTH_INDEX] = value;\n\t}\n\n\tget pointer() {\n\t\treturn this.firstBlock.pointer;\n\t}\n\n\t// Own internal memory plus every allocated segment. The firstBlock is excluded when inlined in another structure\n\t// (e.g. SharedPool) since that owner already counts it as part of its own internal memory.\n\tget usedMemory(): number {\n\t\tlet total = this.ownsFirstBlock ? this.firstBlock.usedMemory : 0;\n\t\tfor(let i = 0; i < this.segmentCount; i++) {\n\t\t\tlet pointer = getPointer(Atomics.load(this.firstBlock.data, SEGMENT_START_INDEX + i), this.memory.positionBits);\n\t\t\ttotal += new AllocatedMemory(this.memory, pointer).usedMemory;\n\t\t}\n\t\treturn total;\n\t}\n\n\tconstructor(memory: MemoryHeap, config?: SharedStackConfig<T> | SharedStackMemory | SharedStackMemory & SharedStackConfig<T>) {\n\t\tthis.memory = memory;\n\n\t\tif(config && 'firstBlock' in config) {\n\t\t\t// An AllocatedMemory instance is a caller-reserved slice (e.g. inlined in SharedPool); a plain pointer is the\n\t\t\t// serialized clone path. Taking the instance directly keeps us off the pointer-resolving construction branch.\n\t\t\tif(config.firstBlock instanceof AllocatedMemory) {\n\t\t\t\tthis.firstBlock = config.firstBlock;\n\t\t\t} else {\n\t\t\t\tconst maxSegments = new Uint32Array(\n\t\t\t\t\tmemory.buffers[config.firstBlock.bufferPosition].buf,\n\t\t\t\t\tconfig.firstBlock.bufferByteOffset + MAX_SEGMENTS_INDEX * Uint32Array.BYTES_PER_ELEMENT,\n\t\t\t\t\t1,\n\t\t\t\t)[0];\n\t\t\t\tthis.firstBlock = new AllocatedMemory(memory, {\n\t\t\t\t\tlength: SharedStack.BASE_ALLOCATE_COUNT + maxSegments,\n\t\t\t\t\t...config.firstBlock,\n\t\t\t\t});\n\t\t\t}\n\t\t\tthis.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + TYPE_INDEX * Uint32Array.BYTES_PER_ELEMENT, 2);\n\n\t\t\t// Memory passed together with config means the caller reserved the firstBlock slot for us to initialize in place\n\t\t\tif('type' in config || 'baseSegmentLength' in config || 'maxLength' in config) {\n\t\t\t\tthis.initialize(config);\n\t\t\t\tthis.ownsFirstBlock = false;\n\t\t\t} else {\n\t\t\t\tthis.ownsFirstBlock = true;\n\t\t\t}\n\t\t} else {\n\t\t\tlet byteMultipler = getByteMultipler(getArrayTypeCode(config?.type ?? Uint32Array));\n\t\t\tlet baseSegmentLength = config?.baseSegmentLength ?? DEFAULT_BASE_SEGMENT_LENGTH;\n\t\t\tlet maxSegmentLength = this.resolveMaxSegmentLength(baseSegmentLength, config?.maxSegmentLength, byteMultipler);\n\t\t\tlet maxSegments = this.resolveMaxSegments(config?.maxLength, baseSegmentLength, maxSegmentLength);\n\n\t\t\tthis.firstBlock = memory.allocUI32(SharedStack.BASE_ALLOCATE_COUNT + maxSegments);\n\t\t\tthis.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + TYPE_INDEX * Uint32Array.BYTES_PER_ELEMENT, 2);\n\t\t\tthis.ownsFirstBlock = true;\n\n\t\t\tthis.initialize(config);\n\t\t}\n\n\t\tthis.growLock = new Int32Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + GROW_LOCK_INDEX * Uint32Array.BYTES_PER_ELEMENT, 1);\n\t\tthis.cachedByteMultipler = getByteMultipler(this.type);\n\t\tthis.cachedBaseSegmentLength = this.baseSegmentLength;\n\t\tthis.cachedMaxSegmentLength = this.maxSegmentLength;\n\t\t// Segments 0..(K-1) double geometrically up to the cap, where K = log2(cap / base) + 1\n\t\tthis.cachedCapSegmentCount = 32 - Math.clz32(((this.cachedMaxSegmentLength / this.cachedBaseSegmentLength) | 0));\n\t\tthis.cachedGeometricCapacity = this.cachedBaseSegmentLength * ((1 << this.cachedCapSegmentCount) - 1);\n\t\tthis.maxLength = SharedStack.getCapacity(this.maxSegments, this.cachedBaseSegmentLength, this.cachedMaxSegmentLength);\n\t\tif(this.maxLength > MAX_COUNT) {\n\t\t\t// The length word reserves LEN_BITS for the count and the rest for the ABA version tag\n\t\t\tthrow new Error(`SharedStack maxLength ${this.maxLength} exceeds the ${MAX_COUNT} the versioned length counter can address`);\n\t\t}\n\t}\n\n\t// Cap on segment length: the largest a single buffer can hold, optionally lowered to a caller-requested value (floored\n\t// to a power-of-two multiple of the base). Beyond this segments stop doubling and stay flat so each still fits a buffer.\n\tprivate resolveMaxSegmentLength(baseSegmentLength: number, requested?: number, byteMultipler = 1): number {\n\t\tlet cap = SharedStack.getMaxSegmentLength(this.memory.maxAllocationLength, baseSegmentLength, byteMultipler);\n\t\tif(requested) {\n\t\t\tlet floored = baseSegmentLength;\n\t\t\twhile(floored * 2 <= requested) {\n\t\t\t\tfloored *= 2;\n\t\t\t}\n\t\t\tcap = Math.min(cap, floored);\n\t\t}\n\t\treturn cap;\n\t}\n\n\t// Segments needed to cover maxLength (or the default budget when none is given), but never more than the segment-pointer\n\t// spine can hold in a single buffer. Once capped, segments no longer double, so a huge maxLength on a small buffer is\n\t// honestly bounded here instead of blowing up later when the spine (or a segment) can't be allocated.\n\tprivate resolveMaxSegments(maxLength: number | undefined, baseSegmentLength: number, maxSegmentLength: number): number {\n\t\tlet needed = maxLength === undefined\n\t\t\t? DEFAULT_MAX_SEGMENTS\n\t\t\t: SharedStack.getMaxSegments(maxLength, baseSegmentLength, maxSegmentLength);\n\t\tlet spineLimit = this.memory.maxAllocationLength - SharedStack.BASE_ALLOCATE_COUNT;\n\t\treturn Math.min(needed, spineLimit);\n\t}\n\n\tprivate initialize(config?: SharedStackConfig<T>) {\n\t\tlet typeCode = getArrayTypeCode(config?.type ?? Uint32Array);\n\t\tlet byteMultipler = getByteMultipler(typeCode);\n\t\tlet baseSegmentLength = config?.baseSegmentLength ?? DEFAULT_BASE_SEGMENT_LENGTH;\n\t\tlet maxSegmentLength = this.resolveMaxSegmentLength(baseSegmentLength, config?.maxSegmentLength, byteMultipler);\n\t\t// Each segment block holds the data slots (byteMultipler u32s each) followed by one publish sequence u32 per slot\n\t\tlet firstSegmentBlock = this.memory.allocUI32(baseSegmentLength * (byteMultipler + 1));\n\t\tstorePointer(this.firstBlock.data, SEGMENT_START_INDEX, firstSegmentBlock.bufferPosition, firstSegmentBlock.bufferByteOffset, this.memory.positionBits);\n\t\tthis.baseSegmentLength = baseSegmentLength;\n\t\tthis.maxSegmentLength = maxSegmentLength;\n\t\tthis.maxSegments = this.resolveMaxSegments(config?.maxLength, baseSegmentLength, maxSegmentLength);\n\t\tthis.segmentCount = 1;\n\t\tthis.type = typeCode;\n\t}\n\n\tat(index: number): T[number] {\n\t\tlet length = this.length;\n\t\tif(index >= length || index < 0) {\n\t\t\tthrow new Error(`${index} is out of bounds ${length}`);\n\t\t}\n\n\t\tlet geometricCapacity = this.cachedGeometricCapacity;\n\t\tlet segmentIndex: number;\n\t\tlet localIndex: number;\n\t\tif(index < geometricCapacity) {\n\t\t\tlet baseSegmentLength = this.cachedBaseSegmentLength;\n\t\t\tsegmentIndex = 31 - Math.clz32(((index / baseSegmentLength) | 0) + 1);\n\t\t\tlocalIndex = index - baseSegmentLength * ((1 << segmentIndex) - 1);\n\t\t} else {\n\t\t\tlet maxSegmentLength = this.cachedMaxSegmentLength;\n\t\t\tlet offset = index - geometricCapacity;\n\t\t\tlet extra = (offset / maxSegmentLength) | 0;\n\t\t\tsegmentIndex = this.cachedCapSegmentCount + extra;\n\t\t\tlocalIndex = offset - extra * maxSegmentLength;\n\t\t}\n\t\tlet segment = this.cachedSegments[segmentIndex] ?? this.getSegment(segmentIndex);\n\t\t// push() bumps the length before it stores the value and publishes the slot, so an index already counted in length\n\t\t// can still be mid-publish. Wait for the slot's sequence to go odd (published) before reading, otherwise we hand\n\t\t// back a slot whose value hasn't landed yet - for the pool's chunk pointers that means decoding a zeroed pointer.\n\t\tlet sequences = this.cachedSequences[segmentIndex];\n\t\twhile((Atomics.load(sequences, localIndex) & 1) === 0) { /* spin: value is being published on another thread */ }\n\t\treturn (segment as NumericArrayIO)[localIndex];\n\t}\n\n\tpush(value: T[number]): number {\n\t\tlet data = this.firstBlock.data;\n\t\tlet geometricCapacity = this.cachedGeometricCapacity;\n\t\t// eslint-disable-next-line no-constant-condition\n\t\twhile(true) {\n\t\t\t// Plain (relaxed) read is enough: it only seeds the versioned CAS below, which rejects any stale value\n\t\t\tlet packed = data[LENGTH_INDEX];\n\t\t\tlet newIndex = packed & LEN_MASK;\n\t\t\tif(newIndex >= this.maxLength) {\n\t\t\t\tthrow new Error(`${newIndex + 1} is out of bounds ${this.maxLength}`);\n\t\t\t}\n\n\t\t\tlet segmentIndex: number;\n\t\t\tlet localIndex: number;\n\t\t\tif(newIndex < geometricCapacity) {\n\t\t\t\tlet baseSegmentLength = this.cachedBaseSegmentLength;\n\t\t\t\tsegmentIndex = 31 - Math.clz32(((newIndex / baseSegmentLength) | 0) + 1);\n\t\t\t\tlocalIndex = newIndex - baseSegmentLength * ((1 << segmentIndex) - 1);\n\t\t\t} else {\n\t\t\t\tlet maxSegmentLength = this.cachedMaxSegmentLength;\n\t\t\t\tlet offset = newIndex - geometricCapacity;\n\t\t\t\tlet extra = (offset / maxSegmentLength) | 0;\n\t\t\t\tsegmentIndex = this.cachedCapSegmentCount + extra;\n\t\t\t\tlocalIndex = offset - extra * maxSegmentLength;\n\t\t\t}\n\t\t\tlet segment = this.cachedSegments[segmentIndex] ?? this.getSegment(segmentIndex);\n\t\t\tlet sequences = this.cachedSequences[segmentIndex];\n\n\t\t\t// The slot must be empty (even sequence) to fill. Odd means a popper hasn't finished emptying it yet - retry\n\t\t\tlet sequence = Atomics.load(sequences, localIndex);\n\t\t\tif((sequence & 1) !== 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// Take the slot only if the whole versioned length word is unchanged. The version bump makes this fail on any\n\t\t\t// intervening op, so a win guarantees nothing touched the top - and our sequence read above is still valid\n\t\t\tif(Atomics.compareExchange(data, LENGTH_INDEX, packed, packed + VERSION_STEP + 1) !== packed) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t(segment as NumericArrayIO)[localIndex] = value;\n\t\t\t// Publish: bump to odd (full). Safe as a plain store - no other op can claim this slot until the length comes\n\t\t\t// back down to it, which can't happen (its popper is gated on an odd sequence) until this store lands\n\t\t\tAtomics.store(sequences, localIndex, sequence + 1);\n\t\t\treturn newIndex;\n\t\t}\n\t}\n\n\tpop(): T[number] | undefined {\n\t\tlet data = this.firstBlock.data;\n\t\tlet geometricCapacity = this.cachedGeometricCapacity;\n\t\t// eslint-disable-next-line no-constant-condition\n\t\twhile(true) {\n\t\t\t// Plain (relaxed) read is enough: it only seeds the versioned CAS below, which rejects any stale value\n\t\t\tlet packed = data[LENGTH_INDEX];\n\t\t\tlet length = packed & LEN_MASK;\n\t\t\tif(length <= 0) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tlet oldIndex = length - 1;\n\n\t\t\tlet segmentIndex: number;\n\t\t\tlet localIndex: number;\n\t\t\tif(oldIndex < geometricCapacity) {\n\t\t\t\tlet baseSegmentLength = this.cachedBaseSegmentLength;\n\t\t\t\tsegmentIndex = 31 - Math.clz32(((oldIndex / baseSegmentLength) | 0) + 1);\n\t\t\t\tlocalIndex = oldIndex - baseSegmentLength * ((1 << segmentIndex) - 1);\n\t\t\t} else {\n\t\t\t\tlet maxSegmentLength = this.cachedMaxSegmentLength;\n\t\t\t\tlet offset = oldIndex - geometricCapacity;\n\t\t\t\tlet extra = (offset / maxSegmentLength) | 0;\n\t\t\t\tsegmentIndex = this.cachedCapSegmentCount + extra;\n\t\t\t\tlocalIndex = offset - extra * maxSegmentLength;\n\t\t\t}\n\t\t\tlet segment = this.cachedSegments[segmentIndex] ?? this.getSegment(segmentIndex);\n\t\t\tlet sequences = this.cachedSequences[segmentIndex];\n\n\t\t\t// The slot must be full (odd sequence) to read. Even means a pusher hasn't finished publishing it yet - retry\n\t\t\tlet sequence = Atomics.load(sequences, localIndex);\n\t\t\tif((sequence & 1) === 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif(Atomics.compareExchange(data, LENGTH_INDEX, packed, packed + VERSION_STEP - 1) !== packed) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet value = (segment as NumericArrayIO)[localIndex] as T[number];\n\t\t\t// Release the slot: bump to even (empty) so a later push may reuse it\n\t\t\tAtomics.store(sequences, localIndex, sequence + 1);\n\t\t\treturn value;\n\t\t}\n\t}\n\n\tclear() {\n\t\t// Mark every occupied slot empty (bump its odd sequence to the next even) before dropping the length, otherwise a\n\t\t// later push reusing the slot would spin forever on a stale full sequence. Not safe to call concurrently with push/pop.\n\t\tlet baseSegmentLength = this.cachedBaseSegmentLength;\n\t\tlet geometricCapacity = this.cachedGeometricCapacity;\n\t\tlet maxSegmentLength = this.cachedMaxSegmentLength;\n\t\tlet oldLength = Atomics.load(this.firstBlock.data, LENGTH_INDEX) & LEN_MASK;\n\t\tfor(let i = 0; i < oldLength; i++) {\n\t\t\tlet segmentIndex: number;\n\t\t\tlet localIndex: number;\n\t\t\tif(i < geometricCapacity) {\n\t\t\t\tsegmentIndex = 31 - Math.clz32(((i / baseSegmentLength) | 0) + 1);\n\t\t\t\tlocalIndex = i - baseSegmentLength * ((1 << segmentIndex) - 1);\n\t\t\t} else {\n\t\t\t\tlet offset = i - geometricCapacity;\n\t\t\t\tlet extra = (offset / maxSegmentLength) | 0;\n\t\t\t\tsegmentIndex = this.cachedCapSegmentCount + extra;\n\t\t\t\tlocalIndex = offset - extra * maxSegmentLength;\n\t\t\t}\n\t\t\tlet sequences = this.cachedSequences[segmentIndex] ?? (this.getSegment(segmentIndex), this.cachedSequences[segmentIndex]);\n\t\t\tlet sequence = Atomics.load(sequences, localIndex);\n\t\t\tif((sequence & 1) !== 0) {\n\t\t\t\tAtomics.store(sequences, localIndex, sequence + 1);\n\t\t\t}\n\t\t}\n\t\tAtomics.store(this.firstBlock.data, LENGTH_INDEX, 0);\n\t}\n\n\t*[Symbol.iterator]() {\n\t\tlet totalDataEntries = this.length;\n\n\t\tlet maxSegmentLength = this.cachedMaxSegmentLength;\n\t\tlet segmentLength = this.baseSegmentLength;\n\t\tlet segmentIndex = 0;\n\t\tlet segment = this.getSegment(segmentIndex);\n\t\tlet segmentDataIndex = 0;\n\t\tfor(let i = 0; i < totalDataEntries; i++) {\n\t\t\tyield (segment as NumericArrayIO)[segmentDataIndex] as T[number];\n\t\t\tsegmentDataIndex++;\n\t\t\tif(segmentDataIndex >= segmentLength) {\n\t\t\t\tsegmentIndex++;\n\t\t\t\tsegment = this.getSegment(segmentIndex);\n\t\t\t\tsegmentDataIndex = 0;\n\t\t\t\tsegmentLength = Math.min(segmentLength * 2, maxSegmentLength);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate getSegment(segmentIndex: number): T {\n\t\tif(this.cachedSegments[segmentIndex]) {\n\t\t\treturn this.cachedSegments[segmentIndex];\n\t\t}\n\n\t\t// Geometric while below the cap, then flat so the block always fits a single buffer\n\t\tlet segmentLength = segmentIndex < this.cachedCapSegmentCount\n\t\t\t? this.cachedBaseSegmentLength << segmentIndex\n\t\t\t: this.cachedMaxSegmentLength;\n\t\tlet segmentDataBlock: AllocatedMemory | undefined;\n\t\tif(segmentIndex >= this.segmentCount) {\n\t\t\t// Lock when growing so another thread doesn't also setup the same segment\n\t\t\tlock(this.growLock);\n\t\t\ttry {\n\t\t\t\t// Re-check under the lock: another thread may have already appended the segment while we were waiting\n\t\t\t\tif(segmentIndex >= this.segmentCount) {\n\t\t\t\t\t// Data slots (byteMultipler u32s each) followed by one publish sequence u32 per slot (calloc-zeroed = 0 = empty)\n\t\t\t\t\tsegmentDataBlock = this.memory.allocUI32(segmentLength * (this.cachedByteMultipler + 1));\n\t\t\t\t\tstorePointer(this.firstBlock.data, SEGMENT_START_INDEX + segmentIndex, segmentDataBlock.bufferPosition, segmentDataBlock.bufferByteOffset, this.memory.positionBits);\n\t\t\t\t\tAtomics.add(this.firstBlock.data, SEGMENT_COUNT_INDEX, 1);\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tunlock(this.growLock);\n\t\t\t}\n\t\t}\n\n\t\tif(!segmentDataBlock) {\n\t\t\tlet pointerNumber = Atomics.load(this.firstBlock.data, SEGMENT_START_INDEX + segmentIndex);\n\t\t\tlet pointer = getPointer(pointerNumber, this.memory.positionBits);\n\t\t\tsegmentDataBlock = new AllocatedMemory(this.memory, pointer);\n\t\t}\n\n\t\tlet data = makeArrayView(this.type, segmentDataBlock.data.buffer, segmentDataBlock.bufferByteOffset, segmentLength) as T;\n\t\t// Publish sequences live after the data region (segmentLength * byteMultipler u32s), always as Int32 regardless of type\n\t\tlet sequenceByteOffset = segmentDataBlock.bufferByteOffset + segmentLength * this.cachedByteMultipler * Uint32Array.BYTES_PER_ELEMENT;\n\t\tthis.cachedSequences[segmentIndex] = new Int32Array(segmentDataBlock.data.buffer, sequenceByteOffset, segmentLength);\n\t\tthis.cachedSegments[segmentIndex] = data;\n\n\t\treturn data;\n\t}\n\n\tfree() {\n\t\tfor(let i = 0; i < this.segmentCount; i++) {\n\t\t\tlet pointerNumber = Atomics.load(this.firstBlock.data, SEGMENT_START_INDEX + i);\n\t\t\tlet pointer = getPointer(pointerNumber, this.memory.positionBits);\n\t\t\tlet rawData = new AllocatedMemory(this.memory, pointer);\n\t\t\trawData.free();\n\t\t}\n\t\tif(this.ownsFirstBlock) {\n\t\t\tthis.firstBlock.free();\n\t\t}\n\t}\n\n\tgetSharedMemory(): SharedStackMemory {\n\t\treturn {\n\t\t\tfirstBlock: this.firstBlock.getSharedMemory(),\n\t\t};\n\t}\n}\n\ninterface SharedStackConfig<T extends NumericArray> {\n\ttype?: TypedArrayConstructor<T>\n\n\tbaseSegmentLength?: number\n\tmaxLength?: number\n\t// Cap on how large a single segment may grow. Defaults to the largest a buffer can hold; a smaller value is floored to\n\t// a power-of-two multiple of baseSegmentLength. Beyond the cap segments stay flat instead of doubling.\n\tmaxSegmentLength?: number\n}\ninterface SharedStackMemory {\n\tfirstBlock: SharedAllocatedMemory | AllocatedMemory\n}\n\nexport type { SharedStackConfig, SharedStackMemory };"],"mappings":"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"}
1
+ {"version":3,"file":"shared-stack.js","names":["AllocatedMemory","LENGTH_INDEX","TYPE_INDEX","BASE_SEGMENT_LENGTH_INDEX","MAX_SEGMENTS_INDEX","MAX_SEGMENT_LENGTH_INDEX","SEGMENT_COUNT_INDEX","GROW_LOCK_INDEX","SEGMENT_START_INDEX","DEFAULT_BASE_SEGMENT_LENGTH","DEFAULT_MAX_SEGMENTS","LEN_BITS","LEN_MASK","MAX_COUNT","VERSION_STEP","SharedStack","segmentLength","baseSegmentLength","byteMultipler","maxAllocationLength","segments","capacity","maxLength","maxSegmentLength","i","value","total","pointer","getPointer","memory","config","maxSegments","getByteMultipler","getArrayTypeCode","cap","requested","floored","needed","spineLimit","typeCode","firstSegmentBlock","storePointer","length","index","geometricCapacity","segmentIndex","localIndex","offset","extra","segment","sequences","data","packed","newIndex","sequence","oldIndex","oldLength","totalDataEntries","segmentDataIndex","segmentDataBlock","pointerNumber","makeArrayView","sequenceByteOffset"],"sources":["../src/shared-stack.ts"],"sourcesContent":["import type { SharedAllocatedMemory } from './allocated-memory';\nimport AllocatedMemory from './allocated-memory';\nimport { lock, unlock } from './lock/simple-lock';\nimport type MemoryHeap from './memory-heap';\nimport { getPointer, storePointer } from './utils/pointer';\nimport { getArrayTypeCode, getByteMultipler, makeArrayView, type NumericArray, type NumericArrayIO, type TypedArrayConstructor } from './utils/array-type';\n\nconst LENGTH_INDEX = 0;\nconst TYPE_INDEX = 1;\nconst BASE_SEGMENT_LENGTH_INDEX = 2;\nconst MAX_SEGMENTS_INDEX = 3;\nconst MAX_SEGMENT_LENGTH_INDEX = 4;\nconst SEGMENT_COUNT_INDEX = 5;\nconst GROW_LOCK_INDEX = 6;\nconst SEGMENT_START_INDEX = 7;\n\nconst DEFAULT_BASE_SEGMENT_LENGTH = 4;\n// Default segment budget. With uncapped power-of-two growth these 20 segments spanned 4,194,300 slots, but that ceiling\n// was never reachable: past segment 14 a segment's block outgrows a 1MB buffer and allocation throws. Capping segments at\n// what a buffer holds, the honest capacity of 20 segments is getCapacity(20, base, cap) instead.\nconst DEFAULT_MAX_SEGMENTS = 20;\n\n// The LENGTH word packs an ABA version counter (high bits) with the live count (low LEN_BITS). Both push and pop bump\n// the version on every successful CAS, so the CAS fails on ANY intervening op - the count can never ABA back to the\n// same value with a slot at a different generation. 22 count bits cover the default max length (4,194,300); the\n// remaining 10 version bits (1024) only need to outlast a single thread's load->CAS window.\nconst LEN_BITS = 22;\nconst LEN_MASK = (1 << LEN_BITS) - 1;\nconst MAX_COUNT = LEN_MASK - 1;\n// Adding this to the packed word bumps the version (high bits) by one. Combined with +1/-1 for the count it repacks the\n// whole word in a single add: the count is guarded from overflowing into the version bits, and a version overflow just\n// wraps harmlessly when the value is coerced back to Uint32 on the atomic store.\nconst VERSION_STEP = 1 << LEN_BITS;\n\n// Per-slot publish handshake. Each data slot has a companion sequence number: even means the slot is empty (free to\n// fill) at some lap, odd means the value is published (safe to read). It only ever increases. push/pop read the slot's\n// sequence and bail out (retry) unless it has the parity they need, then CAS the length; because the versioned CAS\n// rejects any intervening op, the sequence read stays valid all the way through the publish store below.\n\nexport default class SharedStack<T extends NumericArray = Uint32Array> implements Iterable<T[number]> {\n\tstatic readonly BASE_ALLOCATE_COUNT = 8;\n\tstatic readonly DEFAULT_ALLOCATE_COUNT = this.BASE_ALLOCATE_COUNT + DEFAULT_MAX_SEGMENTS;\n\n\t// Largest power-of-two segment length whose block (segmentLength data slots plus an equal run of publish sequences)\n\t// still fits in a single buffer. Segments double until they reach this, then stay flat so a segment can always be\n\t// allocated in one contiguous block. maxAllocationLength is MemoryHeap.maxAllocationLength. byteMultipler is the u32\n\t// units per data slot (2 for 64-bit types), so a length-L segment's block is L * byteMultipler data u32s + L sequence u32s.\n\tstatic getMaxSegmentLength(maxAllocationLength: number, baseSegmentLength = DEFAULT_BASE_SEGMENT_LENGTH, byteMultipler = 1): number {\n\t\tlet segmentLength = baseSegmentLength;\n\t\t// A length-L segment needs a block of L * (byteMultipler + 1) u32s, so the next double fits while\n\t\t// 2L * (byteMultipler + 1) <= maxAllocationLength\n\t\twhile(segmentLength * 2 * (byteMultipler + 1) <= maxAllocationLength) {\n\t\t\tsegmentLength *= 2;\n\t\t}\n\t\treturn segmentLength;\n\t}\n\n\t// Smallest segment count whose summed capacity covers maxLength. Segments double from baseSegmentLength but never grow\n\t// past maxSegmentLength, so once capped they each add a flat maxSegmentLength. Looped rather than solved in closed form\n\t// to avoid float rounding landing a segment short or over.\n\tstatic getMaxSegments(maxLength: number, baseSegmentLength = DEFAULT_BASE_SEGMENT_LENGTH, maxSegmentLength = Infinity): number {\n\t\tlet segments = 0;\n\t\tlet capacity = 0;\n\t\tlet segmentLength = baseSegmentLength;\n\t\twhile(capacity < maxLength) {\n\t\t\tcapacity += segmentLength;\n\t\t\tsegments++;\n\t\t\tsegmentLength = Math.min(segmentLength * 2, maxSegmentLength);\n\t\t}\n\t\treturn segments;\n\t}\n\t// Total capacity of the first `segments` segments under the same doubling-then-capped growth as getMaxSegments\n\tstatic getCapacity(segments: number, baseSegmentLength = DEFAULT_BASE_SEGMENT_LENGTH, maxSegmentLength = Infinity): number {\n\t\tlet capacity = 0;\n\t\tlet segmentLength = baseSegmentLength;\n\t\tfor(let i = 0; i < segments; i++) {\n\t\t\tcapacity += segmentLength;\n\t\t\tsegmentLength = Math.min(segmentLength * 2, maxSegmentLength);\n\t\t}\n\t\treturn capacity;\n\t}\n\tstatic getAllocateCount(maxLength: number, baseSegmentLength = DEFAULT_BASE_SEGMENT_LENGTH, maxSegmentLength = Infinity): number {\n\t\treturn this.BASE_ALLOCATE_COUNT + this.getMaxSegments(maxLength, baseSegmentLength, maxSegmentLength);\n\t}\n\tprivate memory: MemoryHeap;\n\n\t// Length, Type, Base Segment Length, Max Segments, Max Segment Length, Segment Count, Grow Lock, Segments\n\tprivate firstBlock: AllocatedMemory;\n\tprivate uint16Array: Uint16Array;\n\tprivate growLock: Int32Array;\n\treadonly maxLength: number;\n\n\t// False when firstBlock is a slice of another structure's memory (e.g. inlined in SharedPool); that owner frees it\n\tprivate ownsFirstBlock: boolean;\n\n\tprivate cachedSegments: T[] = [];\n\t// Parallel to cachedSegments: the per-slot publish sequences for each slot of that segment. Populated together with\n\t// the data view in getSegment, so whenever cachedSegments[i] is set cachedSequences[i] is too.\n\tprivate cachedSequences: Int32Array[] = [];\n\tprivate cachedBaseSegmentLength = 0;\n\t// Segment growth caps at cachedMaxSegmentLength. Segments 0..(cachedCapSegmentCount - 1) double geometrically and cover\n\t// the first cachedGeometricCapacity slots; every segment beyond that is a flat cachedMaxSegmentLength. Indexing splits\n\t// on cachedGeometricCapacity: below it the fast log2 mapping, above it a plain linear division into capped segments.\n\tprivate cachedMaxSegmentLength = 0;\n\tprivate cachedCapSegmentCount = 0;\n\tprivate cachedGeometricCapacity = 0;\n\t// u32 units per data slot: 1 for 32-bit types, 2 for 64-bit (float64/int64/uint64). A segment block is\n\t// segmentLength * cachedByteMultipler data u32s followed by segmentLength sequence u32s.\n\tprivate cachedByteMultipler = 1;\n\n\tget length(): number {\n\t\treturn Atomics.load(this.firstBlock.data, LENGTH_INDEX) & LEN_MASK;\n\t}\n\tget segmentCount(): number {\n\t\treturn Atomics.load(this.firstBlock.data, SEGMENT_COUNT_INDEX);\n\t}\n\tprivate set segmentCount(value: number) {\n\t\tthis.firstBlock.data[SEGMENT_COUNT_INDEX] = value;\n\t}\n\t\n\tget type(): number {\n\t\treturn this.uint16Array[0];\n\t}\n\tprivate set type(value: number) {\n\t\tAtomics.store(this.uint16Array, 0, value);\n\t}\n\n\tget baseSegmentLength() {\n\t\treturn this.firstBlock.data[BASE_SEGMENT_LENGTH_INDEX];\n\t}\n\tprivate set baseSegmentLength(value: number) {\n\t\tthis.firstBlock.data[BASE_SEGMENT_LENGTH_INDEX] = value;\n\t}\n\tget maxSegments() {\n\t\treturn this.firstBlock.data[MAX_SEGMENTS_INDEX];\n\t}\n\tprivate set maxSegments(value: number) {\n\t\tthis.firstBlock.data[MAX_SEGMENTS_INDEX] = value;\n\t}\n\tget maxSegmentLength() {\n\t\treturn this.firstBlock.data[MAX_SEGMENT_LENGTH_INDEX];\n\t}\n\tprivate set maxSegmentLength(value: number) {\n\t\tthis.firstBlock.data[MAX_SEGMENT_LENGTH_INDEX] = value;\n\t}\n\n\tget pointer() {\n\t\treturn this.firstBlock.pointer;\n\t}\n\n\t// Own internal memory plus every allocated segment. The firstBlock is excluded when inlined in another structure\n\t// (e.g. SharedPool) since that owner already counts it as part of its own internal memory.\n\tget usedMemory(): number {\n\t\tlet total = this.ownsFirstBlock ? this.firstBlock.usedMemory : 0;\n\t\tfor(let i = 0; i < this.segmentCount; i++) {\n\t\t\tlet pointer = getPointer(Atomics.load(this.firstBlock.data, SEGMENT_START_INDEX + i), this.memory.positionBits);\n\t\t\ttotal += new AllocatedMemory(this.memory, pointer).usedMemory;\n\t\t}\n\t\treturn total;\n\t}\n\n\tconstructor(memory: MemoryHeap, config?: SharedStackConfig<T> | SharedStackMemory | SharedStackMemory & SharedStackConfig<T>) {\n\t\tthis.memory = memory;\n\n\t\tif(config && 'firstBlock' in config) {\n\t\t\t// An AllocatedMemory instance is a caller-reserved slice (e.g. inlined in SharedPool); a plain pointer is the\n\t\t\t// serialized clone path. Taking the instance directly keeps us off the pointer-resolving construction branch.\n\t\t\tif(config.firstBlock instanceof AllocatedMemory) {\n\t\t\t\tthis.firstBlock = config.firstBlock;\n\t\t\t} else {\n\t\t\t\tconst maxSegments = new Uint32Array(\n\t\t\t\t\tmemory.buffers[config.firstBlock.bufferPosition].buf,\n\t\t\t\t\tconfig.firstBlock.bufferByteOffset + MAX_SEGMENTS_INDEX * Uint32Array.BYTES_PER_ELEMENT,\n\t\t\t\t\t1,\n\t\t\t\t)[0];\n\t\t\t\tthis.firstBlock = new AllocatedMemory(memory, {\n\t\t\t\t\tlength: SharedStack.BASE_ALLOCATE_COUNT + maxSegments,\n\t\t\t\t\t...config.firstBlock,\n\t\t\t\t});\n\t\t\t}\n\t\t\tthis.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + TYPE_INDEX * Uint32Array.BYTES_PER_ELEMENT, 2);\n\n\t\t\t// Memory passed together with config means the caller reserved the firstBlock slot for us to initialize in place\n\t\t\tif('type' in config || 'baseSegmentLength' in config || 'maxLength' in config) {\n\t\t\t\tthis.initialize(config);\n\t\t\t\tthis.ownsFirstBlock = false;\n\t\t\t} else {\n\t\t\t\tthis.ownsFirstBlock = true;\n\t\t\t}\n\t\t} else {\n\t\t\tlet byteMultipler = getByteMultipler(getArrayTypeCode(config?.type ?? Uint32Array));\n\t\t\tlet baseSegmentLength = config?.baseSegmentLength ?? DEFAULT_BASE_SEGMENT_LENGTH;\n\t\t\tlet maxSegmentLength = this.resolveMaxSegmentLength(baseSegmentLength, config?.maxSegmentLength, byteMultipler);\n\t\t\tlet maxSegments = this.resolveMaxSegments(config?.maxLength, baseSegmentLength, maxSegmentLength);\n\n\t\t\tthis.firstBlock = memory.allocUI32(SharedStack.BASE_ALLOCATE_COUNT + maxSegments);\n\t\t\tthis.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + TYPE_INDEX * Uint32Array.BYTES_PER_ELEMENT, 2);\n\t\t\tthis.ownsFirstBlock = true;\n\n\t\t\tthis.initialize(config);\n\t\t}\n\n\t\tthis.growLock = new Int32Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + GROW_LOCK_INDEX * Uint32Array.BYTES_PER_ELEMENT, 1);\n\t\tthis.cachedByteMultipler = getByteMultipler(this.type);\n\t\tthis.cachedBaseSegmentLength = this.baseSegmentLength;\n\t\tthis.cachedMaxSegmentLength = this.maxSegmentLength;\n\t\t// Segments 0..(K-1) double geometrically up to the cap, where K = log2(cap / base) + 1\n\t\tthis.cachedCapSegmentCount = 32 - Math.clz32(((this.cachedMaxSegmentLength / this.cachedBaseSegmentLength) | 0));\n\t\tthis.cachedGeometricCapacity = this.cachedBaseSegmentLength * ((1 << this.cachedCapSegmentCount) - 1);\n\t\tthis.maxLength = SharedStack.getCapacity(this.maxSegments, this.cachedBaseSegmentLength, this.cachedMaxSegmentLength);\n\t\tif(this.maxLength > MAX_COUNT) {\n\t\t\t// The length word reserves LEN_BITS for the count and the rest for the ABA version tag\n\t\t\tthrow new Error(`SharedStack maxLength ${this.maxLength} exceeds the ${MAX_COUNT} the versioned length counter can address`);\n\t\t}\n\t}\n\n\t// Cap on segment length: the largest a single buffer can hold, optionally lowered to a caller-requested value (floored\n\t// to a power-of-two multiple of the base). Beyond this segments stop doubling and stay flat so each still fits a buffer.\n\tprivate resolveMaxSegmentLength(baseSegmentLength: number, requested?: number, byteMultipler = 1): number {\n\t\tlet cap = SharedStack.getMaxSegmentLength(this.memory.maxAllocationLength, baseSegmentLength, byteMultipler);\n\t\tif(requested) {\n\t\t\tlet floored = baseSegmentLength;\n\t\t\twhile(floored * 2 <= requested) {\n\t\t\t\tfloored *= 2;\n\t\t\t}\n\t\t\tcap = Math.min(cap, floored);\n\t\t}\n\t\treturn cap;\n\t}\n\n\t// Segments needed to cover maxLength (or the default budget when none is given), but never more than the segment-pointer\n\t// spine can hold in a single buffer. Once capped, segments no longer double, so a huge maxLength on a small buffer is\n\t// honestly bounded here instead of blowing up later when the spine (or a segment) can't be allocated.\n\tprivate resolveMaxSegments(maxLength: number | undefined, baseSegmentLength: number, maxSegmentLength: number): number {\n\t\tlet needed = maxLength === undefined\n\t\t\t? DEFAULT_MAX_SEGMENTS\n\t\t\t: SharedStack.getMaxSegments(maxLength, baseSegmentLength, maxSegmentLength);\n\t\tlet spineLimit = this.memory.maxAllocationLength - SharedStack.BASE_ALLOCATE_COUNT;\n\t\treturn Math.min(needed, spineLimit);\n\t}\n\n\tprivate initialize(config?: SharedStackConfig<T>) {\n\t\tlet typeCode = getArrayTypeCode(config?.type ?? Uint32Array);\n\t\tlet byteMultipler = getByteMultipler(typeCode);\n\t\tlet baseSegmentLength = config?.baseSegmentLength ?? DEFAULT_BASE_SEGMENT_LENGTH;\n\t\tlet maxSegmentLength = this.resolveMaxSegmentLength(baseSegmentLength, config?.maxSegmentLength, byteMultipler);\n\t\t// Each segment block holds the data slots (byteMultipler u32s each) followed by one publish sequence u32 per slot\n\t\tlet firstSegmentBlock = this.memory.allocUI32(baseSegmentLength * (byteMultipler + 1));\n\t\tstorePointer(this.firstBlock.data, SEGMENT_START_INDEX, firstSegmentBlock.bufferPosition, firstSegmentBlock.bufferByteOffset, this.memory.positionBits);\n\t\tthis.baseSegmentLength = baseSegmentLength;\n\t\tthis.maxSegmentLength = maxSegmentLength;\n\t\tthis.maxSegments = this.resolveMaxSegments(config?.maxLength, baseSegmentLength, maxSegmentLength);\n\t\tthis.segmentCount = 1;\n\t\tthis.type = typeCode;\n\t}\n\n\tat(index: number): T[number] {\n\t\tlet length = this.length;\n\t\tif(index >= length || index < 0) {\n\t\t\tthrow new Error(`${index} is out of bounds ${length}`);\n\t\t}\n\n\t\tlet geometricCapacity = this.cachedGeometricCapacity;\n\t\tlet segmentIndex: number;\n\t\tlet localIndex: number;\n\t\tif(index < geometricCapacity) {\n\t\t\tlet baseSegmentLength = this.cachedBaseSegmentLength;\n\t\t\tsegmentIndex = 31 - Math.clz32(((index / baseSegmentLength) | 0) + 1);\n\t\t\tlocalIndex = index - baseSegmentLength * ((1 << segmentIndex) - 1);\n\t\t} else {\n\t\t\tlet maxSegmentLength = this.cachedMaxSegmentLength;\n\t\t\tlet offset = index - geometricCapacity;\n\t\t\tlet extra = (offset / maxSegmentLength) | 0;\n\t\t\tsegmentIndex = this.cachedCapSegmentCount + extra;\n\t\t\tlocalIndex = offset - extra * maxSegmentLength;\n\t\t}\n\t\tlet segment = this.cachedSegments[segmentIndex] ?? this.getSegment(segmentIndex);\n\t\t// push() bumps the length before it stores the value and publishes the slot, so an index already counted in length\n\t\t// can still be mid-publish. Wait for the slot's sequence to go odd (published) before reading, otherwise we hand\n\t\t// back a slot whose value hasn't landed yet - for the pool's chunk pointers that means decoding a zeroed pointer.\n\t\tlet sequences = this.cachedSequences[segmentIndex];\n\t\twhile((Atomics.load(sequences, localIndex) & 1) === 0) { /* spin: value is being published on another thread */ }\n\t\treturn (segment as NumericArrayIO)[localIndex];\n\t}\n\n\tpush(value: T[number]): number {\n\t\tlet data = this.firstBlock.data;\n\t\tlet geometricCapacity = this.cachedGeometricCapacity;\n\t\t// eslint-disable-next-line no-constant-condition\n\t\twhile(true) {\n\t\t\t// Plain (relaxed) read is enough: it only seeds the versioned CAS below, which rejects any stale value\n\t\t\tlet packed = data[LENGTH_INDEX];\n\t\t\tlet newIndex = packed & LEN_MASK;\n\t\t\tif(newIndex >= this.maxLength) {\n\t\t\t\tthrow new Error(`${newIndex + 1} is out of bounds ${this.maxLength}`);\n\t\t\t}\n\n\t\t\tlet segmentIndex: number;\n\t\t\tlet localIndex: number;\n\t\t\tif(newIndex < geometricCapacity) {\n\t\t\t\tlet baseSegmentLength = this.cachedBaseSegmentLength;\n\t\t\t\tsegmentIndex = 31 - Math.clz32(((newIndex / baseSegmentLength) | 0) + 1);\n\t\t\t\tlocalIndex = newIndex - baseSegmentLength * ((1 << segmentIndex) - 1);\n\t\t\t} else {\n\t\t\t\tlet maxSegmentLength = this.cachedMaxSegmentLength;\n\t\t\t\tlet offset = newIndex - geometricCapacity;\n\t\t\t\tlet extra = (offset / maxSegmentLength) | 0;\n\t\t\t\tsegmentIndex = this.cachedCapSegmentCount + extra;\n\t\t\t\tlocalIndex = offset - extra * maxSegmentLength;\n\t\t\t}\n\t\t\tlet segment = this.cachedSegments[segmentIndex] ?? this.getSegment(segmentIndex);\n\t\t\tlet sequences = this.cachedSequences[segmentIndex];\n\n\t\t\t// The slot must be empty (even sequence) to fill. Odd means a popper hasn't finished emptying it yet - retry\n\t\t\tlet sequence = Atomics.load(sequences, localIndex);\n\t\t\tif((sequence & 1) !== 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// Take the slot only if the whole versioned length word is unchanged. The version bump makes this fail on any\n\t\t\t// intervening op, so a win guarantees nothing touched the top - and our sequence read above is still valid\n\t\t\tif(Atomics.compareExchange(data, LENGTH_INDEX, packed, packed + VERSION_STEP + 1) !== packed) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t(segment as NumericArrayIO)[localIndex] = value;\n\t\t\t// Publish: bump to odd (full). Safe as a plain store - no other op can claim this slot until the length comes\n\t\t\t// back down to it, which can't happen (its popper is gated on an odd sequence) until this store lands\n\t\t\tAtomics.store(sequences, localIndex, sequence + 1);\n\t\t\treturn newIndex;\n\t\t}\n\t}\n\n\tpop(): T[number] | undefined {\n\t\tlet data = this.firstBlock.data;\n\t\tlet geometricCapacity = this.cachedGeometricCapacity;\n\t\t// eslint-disable-next-line no-constant-condition\n\t\twhile(true) {\n\t\t\t// Plain (relaxed) read is enough: it only seeds the versioned CAS below, which rejects any stale value\n\t\t\tlet packed = data[LENGTH_INDEX];\n\t\t\tlet length = packed & LEN_MASK;\n\t\t\tif(length <= 0) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tlet oldIndex = length - 1;\n\n\t\t\tlet segmentIndex: number;\n\t\t\tlet localIndex: number;\n\t\t\tif(oldIndex < geometricCapacity) {\n\t\t\t\tlet baseSegmentLength = this.cachedBaseSegmentLength;\n\t\t\t\tsegmentIndex = 31 - Math.clz32(((oldIndex / baseSegmentLength) | 0) + 1);\n\t\t\t\tlocalIndex = oldIndex - baseSegmentLength * ((1 << segmentIndex) - 1);\n\t\t\t} else {\n\t\t\t\tlet maxSegmentLength = this.cachedMaxSegmentLength;\n\t\t\t\tlet offset = oldIndex - geometricCapacity;\n\t\t\t\tlet extra = (offset / maxSegmentLength) | 0;\n\t\t\t\tsegmentIndex = this.cachedCapSegmentCount + extra;\n\t\t\t\tlocalIndex = offset - extra * maxSegmentLength;\n\t\t\t}\n\t\t\tlet segment = this.cachedSegments[segmentIndex] ?? this.getSegment(segmentIndex);\n\t\t\tlet sequences = this.cachedSequences[segmentIndex];\n\n\t\t\t// The slot must be full (odd sequence) to read. Even means a pusher hasn't finished publishing it yet - retry\n\t\t\tlet sequence = Atomics.load(sequences, localIndex);\n\t\t\tif((sequence & 1) === 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif(Atomics.compareExchange(data, LENGTH_INDEX, packed, packed + VERSION_STEP - 1) !== packed) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet value = (segment as NumericArrayIO)[localIndex] as T[number];\n\t\t\t// Release the slot: bump to even (empty) so a later push may reuse it\n\t\t\tAtomics.store(sequences, localIndex, sequence + 1);\n\t\t\treturn value;\n\t\t}\n\t}\n\n\tclear() {\n\t\t// Mark every occupied slot empty (bump its odd sequence to the next even) before dropping the length, otherwise a\n\t\t// later push reusing the slot would spin forever on a stale full sequence. Not safe to call concurrently with push/pop.\n\t\tlet baseSegmentLength = this.cachedBaseSegmentLength;\n\t\tlet geometricCapacity = this.cachedGeometricCapacity;\n\t\tlet maxSegmentLength = this.cachedMaxSegmentLength;\n\t\tlet oldLength = Atomics.load(this.firstBlock.data, LENGTH_INDEX) & LEN_MASK;\n\t\tfor(let i = 0; i < oldLength; i++) {\n\t\t\tlet segmentIndex: number;\n\t\t\tlet localIndex: number;\n\t\t\tif(i < geometricCapacity) {\n\t\t\t\tsegmentIndex = 31 - Math.clz32(((i / baseSegmentLength) | 0) + 1);\n\t\t\t\tlocalIndex = i - baseSegmentLength * ((1 << segmentIndex) - 1);\n\t\t\t} else {\n\t\t\t\tlet offset = i - geometricCapacity;\n\t\t\t\tlet extra = (offset / maxSegmentLength) | 0;\n\t\t\t\tsegmentIndex = this.cachedCapSegmentCount + extra;\n\t\t\t\tlocalIndex = offset - extra * maxSegmentLength;\n\t\t\t}\n\t\t\tlet sequences = this.cachedSequences[segmentIndex] ?? (this.getSegment(segmentIndex), this.cachedSequences[segmentIndex]);\n\t\t\tlet sequence = Atomics.load(sequences, localIndex);\n\t\t\tif((sequence & 1) !== 0) {\n\t\t\t\tAtomics.store(sequences, localIndex, sequence + 1);\n\t\t\t}\n\t\t}\n\t\tAtomics.store(this.firstBlock.data, LENGTH_INDEX, 0);\n\t}\n\n\t*[Symbol.iterator]() {\n\t\tlet totalDataEntries = this.length;\n\n\t\tlet maxSegmentLength = this.cachedMaxSegmentLength;\n\t\tlet segmentLength = this.baseSegmentLength;\n\t\tlet segmentIndex = 0;\n\t\tlet segment = this.getSegment(segmentIndex);\n\t\tlet segmentDataIndex = 0;\n\t\tfor(let i = 0; i < totalDataEntries; i++) {\n\t\t\tyield (segment as NumericArrayIO)[segmentDataIndex] as T[number];\n\t\t\tsegmentDataIndex++;\n\t\t\tif(segmentDataIndex >= segmentLength) {\n\t\t\t\tsegmentIndex++;\n\t\t\t\tsegment = this.getSegment(segmentIndex);\n\t\t\t\tsegmentDataIndex = 0;\n\t\t\t\tsegmentLength = Math.min(segmentLength * 2, maxSegmentLength);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate getSegment(segmentIndex: number): T {\n\t\tif(this.cachedSegments[segmentIndex]) {\n\t\t\treturn this.cachedSegments[segmentIndex];\n\t\t}\n\n\t\t// Geometric while below the cap, then flat so the block always fits a single buffer\n\t\tlet segmentLength = segmentIndex < this.cachedCapSegmentCount\n\t\t\t? this.cachedBaseSegmentLength << segmentIndex\n\t\t\t: this.cachedMaxSegmentLength;\n\t\tlet segmentDataBlock: AllocatedMemory | undefined;\n\t\tif(segmentIndex >= this.segmentCount) {\n\t\t\t// Lock when growing so another thread doesn't also setup the same segment\n\t\t\tlock(this.growLock);\n\t\t\ttry {\n\t\t\t\t// Re-check under the lock: another thread may have already appended the segment while we were waiting\n\t\t\t\tif(segmentIndex >= this.segmentCount) {\n\t\t\t\t\t// Data slots (byteMultipler u32s each) followed by one publish sequence u32 per slot (calloc-zeroed = 0 = empty)\n\t\t\t\t\tsegmentDataBlock = this.memory.allocUI32(segmentLength * (this.cachedByteMultipler + 1));\n\t\t\t\t\tstorePointer(this.firstBlock.data, SEGMENT_START_INDEX + segmentIndex, segmentDataBlock.bufferPosition, segmentDataBlock.bufferByteOffset, this.memory.positionBits);\n\t\t\t\t\tAtomics.add(this.firstBlock.data, SEGMENT_COUNT_INDEX, 1);\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tunlock(this.growLock);\n\t\t\t}\n\t\t}\n\n\t\tif(!segmentDataBlock) {\n\t\t\tlet pointerNumber = Atomics.load(this.firstBlock.data, SEGMENT_START_INDEX + segmentIndex);\n\t\t\tlet pointer = getPointer(pointerNumber, this.memory.positionBits);\n\t\t\tsegmentDataBlock = new AllocatedMemory(this.memory, pointer);\n\t\t}\n\n\t\tlet data = makeArrayView(this.type, segmentDataBlock.data.buffer, segmentDataBlock.bufferByteOffset, segmentLength) as T;\n\t\t// Publish sequences live after the data region (segmentLength * byteMultipler u32s), always as Int32 regardless of type\n\t\tlet sequenceByteOffset = segmentDataBlock.bufferByteOffset + segmentLength * this.cachedByteMultipler * Uint32Array.BYTES_PER_ELEMENT;\n\t\tthis.cachedSequences[segmentIndex] = new Int32Array(segmentDataBlock.data.buffer, sequenceByteOffset, segmentLength);\n\t\tthis.cachedSegments[segmentIndex] = data;\n\n\t\treturn data;\n\t}\n\n\tfree() {\n\t\tfor(let i = 0; i < this.segmentCount; i++) {\n\t\t\tlet pointerNumber = Atomics.load(this.firstBlock.data, SEGMENT_START_INDEX + i);\n\t\t\tlet pointer = getPointer(pointerNumber, this.memory.positionBits);\n\t\t\tlet rawData = new AllocatedMemory(this.memory, pointer);\n\t\t\trawData.free();\n\t\t}\n\t\tif(this.ownsFirstBlock) {\n\t\t\tthis.firstBlock.free();\n\t\t}\n\t}\n\n\tgetSharedMemory(): SharedStackMemory {\n\t\treturn {\n\t\t\tfirstBlock: this.firstBlock.getSharedMemory(),\n\t\t};\n\t}\n}\n\ninterface SharedStackConfig<T extends NumericArray> {\n\ttype?: TypedArrayConstructor<T>\n\n\tbaseSegmentLength?: number\n\tmaxLength?: number\n\t// Cap on how large a single segment may grow. Defaults to the largest a buffer can hold; a smaller value is floored to\n\t// a power-of-two multiple of baseSegmentLength. Beyond the cap segments stay flat instead of doubling.\n\tmaxSegmentLength?: number\n}\ninterface SharedStackMemory {\n\tfirstBlock: SharedAllocatedMemory | AllocatedMemory\n}\n\nexport type { SharedStackConfig, SharedStackMemory };"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"shared-string.js","names":[],"sources":["../src/shared-string.ts"],"sourcesContent":["import AllocatedMemory from './allocated-memory';\nimport type { SharedAllocatedMemory } from './allocated-memory';\nimport { lock, unlock } from './lock/simple-lock';\nimport type MemoryHeap from './memory-heap';\nimport { getPointer, loadPointer, loadRawPointer, storeRawPointer } from './utils/pointer';\n\nenum CHAR_TYPE {\n\tUNDEFINED,\n\tASCII,\n\tUTF16,\n}\nconst TYPED_ARRAY_MAP = {\n\t[CHAR_TYPE.ASCII]: Uint8Array,\n\t[CHAR_TYPE.UTF16]: Uint16Array,\n};\n\nconst POINTER_INDEX = 0;\nconst LENGTH_INDEX = 1;\nconst TYPE_INDEX = 2;\nconst LOCK_INDEX = 3;\nexport default class SharedString {\n\tstatic readonly ALLOCATE_COUNT = 4;\n\n\tprivate memory: MemoryHeap;\n\tprivate allocatedMemory: AllocatedMemory;\n\tprivate lock: Int32Array;\n\n\tprivate cachedPointer?: number;\n\tprivate cachedString?: string;\n\t\n\tconstructor(memory: MemoryHeap, value: string | SharedStringConfig | SharedStringMemory) {\n\t\tthis.memory = memory;\n\n\t\tif(typeof value === 'string') {\n\t\t\tthis.allocatedMemory = this.memory.allocUI32(SharedString.ALLOCATE_COUNT);\n\t\t\tthis.lock = new Int32Array(this.allocatedMemory.data.buffer, this.allocatedMemory.bufferByteOffset + LOCK_INDEX * this.allocatedMemory.data.BYTES_PER_ELEMENT);\n\t\t\tthis.updateString(value);\n\t\t} else {\n\t\t\tthis.allocatedMemory = new AllocatedMemory(memory, value);\n\t\t\tthis.lock = new Int32Array(this.allocatedMemory.data.buffer, this.allocatedMemory.bufferByteOffset + LOCK_INDEX * this.allocatedMemory.data.BYTES_PER_ELEMENT);\n\n\t\t\t// We only allocated memory but didn't initialize the string yet\n\t\t\tif('value' in value) {\n\t\t\t\tthis.updateString(value.value);\n\t\t\t}\n\t\t}\n\t}\n\t\n\tprivate updateString(value: string) {\n\t\tlet stringMemory = this.createString(value);\n\n\t\tlock(this.lock);\n\t\tstoreRawPointer(this.allocatedMemory.data, POINTER_INDEX, stringMemory.pointer);\n\t\tAtomics.store(this.allocatedMemory.data, LENGTH_INDEX, value.length);\n\t\tAtomics.store(this.allocatedMemory.data, TYPE_INDEX, stringMemory.charType);\n\t\tunlock(this.lock);\n\n\t\tthis.cachedPointer = stringMemory.pointer;\n\t\tthis.cachedString = value;\n\t}\n\tprivate createString(value: string) {\n\t\tif(value === '') {\n\t\t\treturn {\n\t\t\t\tpointer: 0,\n\t\t\t\tcharType: CHAR_TYPE.ASCII,\n\t\t\t};\n\t\t}\n\n\t\t// NOTE: Scan with a loop instead of Math.max(...charCodes) which blows the stack for large strings\n\t\tlet maxCharCode = 0;\n\t\tfor(let i = 0; i < value.length; i++) {\n\t\t\tlet charCode = value.charCodeAt(i);\n\t\t\tif(charCode > maxCharCode) {\n\t\t\t\tmaxCharCode = charCode;\n\t\t\t}\n\t\t}\n\t\tlet charType = maxCharCode > 255 ? CHAR_TYPE.UTF16 : CHAR_TYPE.ASCII;\n\n\t\tlet typedArray = TYPED_ARRAY_MAP[charType];\n\t\tlet allocatedMemory = this.memory.allocUI32(Math.ceil(value.length / (4 / typedArray.BYTES_PER_ELEMENT)));\n\t\tlet data = new typedArray(allocatedMemory.data.buffer as ArrayBuffer, allocatedMemory.data.byteOffset, value.length);\n\t\tfor(let i = 0; i < value.length; i++) {\n\t\t\tdata[i] = value.charCodeAt(i);\n\t\t}\n\n\t\treturn {\n\t\t\tpointer: allocatedMemory.pointer,\n\t\t\tcharType,\n\t\t};\n\t}\n\n\tget value(): string {\n\t\tlet pointer = loadRawPointer(this.allocatedMemory.data, POINTER_INDEX);\n\t\tif(this.cachedPointer === pointer && this.cachedString !== undefined) {\n\t\t\treturn this.cachedString;\n\t\t} else if(pointer === 0) {\n\t\t\treturn '';\n\t\t}\n\t\tlet { bufferPosition, bufferByteOffset } = getPointer(pointer, this.memory.positionBits);\n\n\t\tlock(this.lock);\n\t\tlet charType = Atomics.load(this.allocatedMemory.data, TYPE_INDEX);\n\t\t// @ts-expect-error\n\t\tlet typedArray = TYPED_ARRAY_MAP[charType];\n\t\tlet bufferLength = Atomics.load(this.allocatedMemory.data, LENGTH_INDEX);\n\n\t\tlet data = new typedArray(this.memory.buffers[bufferPosition].buf, bufferByteOffset, bufferLength);\n\t\tlet string = fromCharCodes(data);\n\t\t// NOTE: Do not unlock until after transforming the data since the second this is done it can free that memory block\n\t\tunlock(this.lock);\n\n\t\tthis.cachedPointer = pointer;\n\t\tthis.cachedString = string;\n\n\t\treturn string;\n\t}\n\tset value(value: string) {\n\t\tlet { bufferPosition: oldBufferPosition, bufferByteOffset: oldBufferByteOffset } = loadPointer(this.allocatedMemory.data, POINTER_INDEX, this.memory.positionBits);\n\t\tthis.updateString(value);\n\n\t\tif(oldBufferByteOffset) {\n\t\t\tthis.memory.buffers[oldBufferPosition].free(oldBufferByteOffset);\n\t\t}\n\t}\n\n\tgetSharedMemory(): SharedStringMemory {\n\t\treturn this.allocatedMemory.getSharedMemory();\n\t}\n\n\tget pointer() {\n\t\treturn this.allocatedMemory.pointer;\n\t}\n\n\tfree() {\n\t\tlet { bufferPosition, bufferByteOffset } = loadPointer(this.allocatedMemory.data, POINTER_INDEX, this.memory.positionBits);\n\t\tif(bufferByteOffset) {\n\t\t\tthis.memory.buffers[bufferPosition].free(bufferByteOffset);\n\t\t}\n\t\tthis.allocatedMemory.free();\n\t}\n}\n\n// String.fromCharCode.apply throws a RangeError (Maximum call stack size exceeded) when handed too many\n// arguments at once, so decode large buffers in chunks to support arbitrarily long strings\nfunction fromCharCodes(data: Uint8Array | Uint16Array): string {\n\tconst CHUNK_SIZE = 8192;\n\tif(data.length <= CHUNK_SIZE) {\n\t\treturn String.fromCharCode.apply(null, data as unknown as Array<number>);\n\t}\n\n\tlet string = '';\n\tfor(let i = 0; i < data.length; i += CHUNK_SIZE) {\n\t\tstring += String.fromCharCode.apply(null, data.subarray(i, i + CHUNK_SIZE) as unknown as Array<number>);\n\t}\n\n\treturn string;\n}\n\ninterface SharedStringConfig extends SharedStringMemory {\n\tvalue: string\n}\ntype SharedStringMemory = SharedAllocatedMemory;"],"mappings":"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"}
1
+ {"version":3,"file":"shared-string.js","names":["AllocatedMemory","TYPED_ARRAY_MAP","POINTER_INDEX","LENGTH_INDEX","TYPE_INDEX","LOCK_INDEX","SharedString","memory","value","stringMemory","lock","storeRawPointer","unlock","maxCharCode","i","charCode","charType","typedArray","allocatedMemory","data","pointer","loadRawPointer","getPointer","bufferLength","string","fromCharCodes","bufferPosition","bufferByteOffset","oldBufferPosition","oldBufferByteOffset","loadPointer","CHUNK_SIZE"],"sources":["../src/shared-string.ts"],"sourcesContent":["import AllocatedMemory from './allocated-memory';\nimport type { SharedAllocatedMemory } from './allocated-memory';\nimport { lock, unlock } from './lock/simple-lock';\nimport type MemoryHeap from './memory-heap';\nimport { getPointer, loadPointer, loadRawPointer, storeRawPointer } from './utils/pointer';\n\nenum CHAR_TYPE {\n\tUNDEFINED,\n\tASCII,\n\tUTF16,\n}\nconst TYPED_ARRAY_MAP = {\n\t[CHAR_TYPE.ASCII]: Uint8Array,\n\t[CHAR_TYPE.UTF16]: Uint16Array,\n};\n\nconst POINTER_INDEX = 0;\nconst LENGTH_INDEX = 1;\nconst TYPE_INDEX = 2;\nconst LOCK_INDEX = 3;\nexport default class SharedString {\n\tstatic readonly ALLOCATE_COUNT = 4;\n\n\tprivate memory: MemoryHeap;\n\tprivate allocatedMemory: AllocatedMemory;\n\tprivate lock: Int32Array;\n\n\tprivate cachedPointer?: number;\n\tprivate cachedString?: string;\n\t\n\tconstructor(memory: MemoryHeap, value: string | SharedStringConfig | SharedStringMemory) {\n\t\tthis.memory = memory;\n\n\t\tif(typeof value === 'string') {\n\t\t\tthis.allocatedMemory = this.memory.allocUI32(SharedString.ALLOCATE_COUNT);\n\t\t\tthis.lock = new Int32Array(this.allocatedMemory.data.buffer, this.allocatedMemory.bufferByteOffset + LOCK_INDEX * this.allocatedMemory.data.BYTES_PER_ELEMENT);\n\t\t\tthis.updateString(value);\n\t\t} else {\n\t\t\tthis.allocatedMemory = new AllocatedMemory(memory, value);\n\t\t\tthis.lock = new Int32Array(this.allocatedMemory.data.buffer, this.allocatedMemory.bufferByteOffset + LOCK_INDEX * this.allocatedMemory.data.BYTES_PER_ELEMENT);\n\n\t\t\t// We only allocated memory but didn't initialize the string yet\n\t\t\tif('value' in value) {\n\t\t\t\tthis.updateString(value.value);\n\t\t\t}\n\t\t}\n\t}\n\t\n\tprivate updateString(value: string) {\n\t\tlet stringMemory = this.createString(value);\n\n\t\tlock(this.lock);\n\t\tstoreRawPointer(this.allocatedMemory.data, POINTER_INDEX, stringMemory.pointer);\n\t\tAtomics.store(this.allocatedMemory.data, LENGTH_INDEX, value.length);\n\t\tAtomics.store(this.allocatedMemory.data, TYPE_INDEX, stringMemory.charType);\n\t\tunlock(this.lock);\n\n\t\tthis.cachedPointer = stringMemory.pointer;\n\t\tthis.cachedString = value;\n\t}\n\tprivate createString(value: string) {\n\t\tif(value === '') {\n\t\t\treturn {\n\t\t\t\tpointer: 0,\n\t\t\t\tcharType: CHAR_TYPE.ASCII,\n\t\t\t};\n\t\t}\n\n\t\t// NOTE: Scan with a loop instead of Math.max(...charCodes) which blows the stack for large strings\n\t\tlet maxCharCode = 0;\n\t\tfor(let i = 0; i < value.length; i++) {\n\t\t\tlet charCode = value.charCodeAt(i);\n\t\t\tif(charCode > maxCharCode) {\n\t\t\t\tmaxCharCode = charCode;\n\t\t\t}\n\t\t}\n\t\tlet charType = maxCharCode > 255 ? CHAR_TYPE.UTF16 : CHAR_TYPE.ASCII;\n\n\t\tlet typedArray = TYPED_ARRAY_MAP[charType];\n\t\tlet allocatedMemory = this.memory.allocUI32(Math.ceil(value.length / (4 / typedArray.BYTES_PER_ELEMENT)));\n\t\tlet data = new typedArray(allocatedMemory.data.buffer as ArrayBuffer, allocatedMemory.data.byteOffset, value.length);\n\t\tfor(let i = 0; i < value.length; i++) {\n\t\t\tdata[i] = value.charCodeAt(i);\n\t\t}\n\n\t\treturn {\n\t\t\tpointer: allocatedMemory.pointer,\n\t\t\tcharType,\n\t\t};\n\t}\n\n\tget value(): string {\n\t\tlet pointer = loadRawPointer(this.allocatedMemory.data, POINTER_INDEX);\n\t\tif(this.cachedPointer === pointer && this.cachedString !== undefined) {\n\t\t\treturn this.cachedString;\n\t\t} else if(pointer === 0) {\n\t\t\treturn '';\n\t\t}\n\t\tlet { bufferPosition, bufferByteOffset } = getPointer(pointer, this.memory.positionBits);\n\n\t\tlock(this.lock);\n\t\tlet charType = Atomics.load(this.allocatedMemory.data, TYPE_INDEX);\n\t\t// @ts-expect-error\n\t\tlet typedArray = TYPED_ARRAY_MAP[charType];\n\t\tlet bufferLength = Atomics.load(this.allocatedMemory.data, LENGTH_INDEX);\n\n\t\tlet data = new typedArray(this.memory.buffers[bufferPosition].buf, bufferByteOffset, bufferLength);\n\t\tlet string = fromCharCodes(data);\n\t\t// NOTE: Do not unlock until after transforming the data since the second this is done it can free that memory block\n\t\tunlock(this.lock);\n\n\t\tthis.cachedPointer = pointer;\n\t\tthis.cachedString = string;\n\n\t\treturn string;\n\t}\n\tset value(value: string) {\n\t\tlet { bufferPosition: oldBufferPosition, bufferByteOffset: oldBufferByteOffset } = loadPointer(this.allocatedMemory.data, POINTER_INDEX, this.memory.positionBits);\n\t\tthis.updateString(value);\n\n\t\tif(oldBufferByteOffset) {\n\t\t\tthis.memory.buffers[oldBufferPosition].free(oldBufferByteOffset);\n\t\t}\n\t}\n\n\tgetSharedMemory(): SharedStringMemory {\n\t\treturn this.allocatedMemory.getSharedMemory();\n\t}\n\n\tget pointer() {\n\t\treturn this.allocatedMemory.pointer;\n\t}\n\n\tfree() {\n\t\tlet { bufferPosition, bufferByteOffset } = loadPointer(this.allocatedMemory.data, POINTER_INDEX, this.memory.positionBits);\n\t\tif(bufferByteOffset) {\n\t\t\tthis.memory.buffers[bufferPosition].free(bufferByteOffset);\n\t\t}\n\t\tthis.allocatedMemory.free();\n\t}\n}\n\n// String.fromCharCode.apply throws a RangeError (Maximum call stack size exceeded) when handed too many\n// arguments at once, so decode large buffers in chunks to support arbitrarily long strings\nfunction fromCharCodes(data: Uint8Array | Uint16Array): string {\n\tconst CHUNK_SIZE = 8192;\n\tif(data.length <= CHUNK_SIZE) {\n\t\treturn String.fromCharCode.apply(null, data as unknown as Array<number>);\n\t}\n\n\tlet string = '';\n\tfor(let i = 0; i < data.length; i += CHUNK_SIZE) {\n\t\tstring += String.fromCharCode.apply(null, data.subarray(i, i + CHUNK_SIZE) as unknown as Array<number>);\n\t}\n\n\treturn string;\n}\n\ninterface SharedStringConfig extends SharedStringMemory {\n\tvalue: string\n}\ntype SharedStringMemory = SharedAllocatedMemory;"],"mappings":";;;;AAWA,IAAMC,IAAkB;CACJ,GAAA;CACA,GAAA;AACpB,GAEMC,IAAgB,GAChBC,IAAe,GACfC,IAAa,GACbC,IAAa,GACEC,IAArB,MAAqBA,EAAa;CACjC,OAAgB,iBAAiB;CAEjC;CACA;CACA;CAEA;CACA;CAEA,YAAY,GAAoB,GAAyD;EAGxF,AAFA,KAAK,SAASC,GAEX,OAAOC,KAAU,YACnB,KAAK,kBAAkB,KAAK,OAAO,UAAUF,EAAa,cAAc,GACxE,KAAK,OAAO,IAAI,WAAW,KAAK,gBAAgB,KAAK,QAAQ,KAAK,gBAAgB,mBAAmBD,IAAa,KAAK,gBAAgB,KAAK,iBAAiB,GAC7J,KAAK,aAAaG,CAAK,MAEvB,KAAK,kBAAkB,IAAIR,EAAgBO,GAAQC,CAAK,GACxD,KAAK,OAAO,IAAI,WAAW,KAAK,gBAAgB,KAAK,QAAQ,KAAK,gBAAgB,mBAAmBH,IAAa,KAAK,gBAAgB,KAAK,iBAAiB,GAG1J,WAAWG,KACb,KAAK,aAAaA,EAAM,KAAK;CAGhC;CAEA,aAAqB,GAAe;EACnC,IAAIC,IAAe,KAAK,aAAaD,CAAK;EAS1C,AAPAE,EAAK,KAAK,IAAI,GACdC,EAAgB,KAAK,gBAAgB,MAAMT,GAAeO,EAAa,OAAO,GAC9E,QAAQ,MAAM,KAAK,gBAAgB,MAAMN,GAAcK,EAAM,MAAM,GACnE,QAAQ,MAAM,KAAK,gBAAgB,MAAMJ,GAAYK,EAAa,QAAQ,GAC1EG,EAAO,KAAK,IAAI,GAEhB,KAAK,gBAAgBH,EAAa,SAClC,KAAK,eAAeD;CACrB;CACA,aAAqB,GAAe;EACnC,IAAGA,MAAU,IACZ,OAAO;GACN,SAAS;GACT,UAAA;EACD;EAID,IAAIK,IAAc;EAClB,KAAI,IAAIC,IAAI,GAAGA,IAAIN,EAAM,QAAQ,KAAK;GACrC,IAAIO,IAAWP,EAAM,WAAWM,CAAC;GACjC,AAAGC,IAAWF,MACb,IAAcE;EAEhB;EACA,IAAIC,IAAWH,IAAc,MAAA,IAAA,GAEzBI,IAAahB,EAAgBe,IAC7BE,IAAkB,KAAK,OAAO,UAAU,KAAK,KAAKV,EAAM,UAAU,IAAIS,EAAW,kBAAkB,CAAC,GACpGE,IAAO,IAAIF,EAAWC,EAAgB,KAAK,QAAuBA,EAAgB,KAAK,YAAYV,EAAM,MAAM;EACnH,KAAI,IAAIM,IAAI,GAAGA,IAAIN,EAAM,QAAQ,KAChC,EAAKM,KAAKN,EAAM,WAAWM,CAAC;EAG7B,OAAO;GACN,SAASI,EAAgB;GACzB;EACD;CACD;CAEA,IAAI,QAAgB;EACnB,IAAIE,IAAUC,EAAe,KAAK,gBAAgB,MAAMnB,CAAa;EACrE,IAAG,KAAK,kBAAkBkB,KAAW,KAAK,iBAAiB,KAAA,GAC1D,OAAO,KAAK;EACN,IAAGA,MAAY,GACrB,OAAO;EAER,IAAI,EAAE,mBAAgB,wBAAqBE,EAAWF,GAAS,KAAK,OAAO,YAAY;EAEvF,EAAK,KAAK,IAAI;EAGd,IAAIH,IAAahB,EAFF,QAAQ,KAAK,KAAK,gBAAgB,MAAMG,CAEtB,IAC7BmB,IAAe,QAAQ,KAAK,KAAK,gBAAgB,MAAMpB,CAAY,GAGnEqB,IAASC,EAAc,IADZR,EAAW,KAAK,OAAO,QAAQS,EAAe,CAAC,KAAKC,GAAkBJ,CAC1D,CAAI;EAO/B,OALAX,EAAO,KAAK,IAAI,GAEhB,KAAK,gBAAgBQ,GACrB,KAAK,eAAeI,GAEbA;CACR;CACA,IAAI,MAAM,GAAe;EACxB,IAAI,EAAE,gBAAgBI,GAAmB,kBAAkBC,MAAwBC,EAAY,KAAK,gBAAgB,MAAM5B,GAAe,KAAK,OAAO,YAAY;EAGjK,AAFA,KAAK,aAAaM,CAAK,GAEpBqB,KACF,KAAK,OAAO,QAAQD,EAAkB,CAAC,KAAKC,CAAmB;CAEjE;CAEA,kBAAsC;EACrC,OAAO,KAAK,gBAAgB,gBAAgB;CAC7C;CAEA,IAAI,UAAU;EACb,OAAO,KAAK,gBAAgB;CAC7B;CAEA,OAAO;EACN,IAAI,EAAE,mBAAgB,wBAAqBC,EAAY,KAAK,gBAAgB,MAAM5B,GAAe,KAAK,OAAO,YAAY;EAIzH,AAHGyB,KACF,KAAK,OAAO,QAAQD,EAAe,CAAC,KAAKC,CAAgB,GAE1D,KAAK,gBAAgB,KAAK;CAC3B;AACD;AAIA,SAASF,EAAc,GAAwC;CAC9D,IAAMM,IAAa;CACnB,IAAGZ,EAAK,UAAUY,GACjB,OAAO,OAAO,aAAa,MAAM,MAAMZ,CAAgC;CAGxE,IAAIK,IAAS;CACb,KAAI,IAAIV,IAAI,GAAGA,IAAIK,EAAK,QAAQ,KAAKY,GACpC,KAAU,OAAO,aAAa,MAAM,MAAMZ,EAAK,SAASL,GAAGA,IAAIiB,CAAU,CAA6B;CAGvG,OAAOP;AACR"}
@@ -1 +1 @@
1
- {"version":3,"file":"shared-vector.js","names":[],"sources":["../src/shared-vector.ts"],"sourcesContent":["import type { SharedAllocatedMemory } from './allocated-memory';\nimport AllocatedMemory from './allocated-memory';\nimport type MemoryHeap from './memory-heap';\nimport { getPointer, loadPointer, storePointer, storeRawPointer } from './utils/pointer';\nimport { getArrayTypeCode, getByteMultipler, makeArrayView, type NumericArray, type NumericArrayIO, type TypedArrayConstructor } from './utils/array-type';\n\nconst VECTOR_INDEX = 0;\nconst LENGTH_INDEX = 1;\nconst BUFFER_LENGTH_INDEX = 2;\nconst TYPE_INDEX = 3;\nconst DEFAULT_SIZE = 4;\nexport default class SharedVector<T extends NumericArray = Uint32Array> implements Iterable<T> {\n\tstatic readonly ALLOCATE_COUNT = 4;\n\tprivate memory: MemoryHeap;\n\n\t// Pointer, List Length, Buffer Length, Type/DataLength\n\tprivate firstBlock: AllocatedMemory;\n\tprivate uint16Array: Uint16Array;\n\t// u32 units per element: 1 for 32-bit types, 2 for 64-bit (float64/int64/uint64). Every data-block allocation scales\n\t// by this; the typed view length stays in elements.\n\tprivate cachedByteMultipler = 1;\n\n\tget length(): number {\n\t\treturn Atomics.load(this.firstBlock.data, LENGTH_INDEX);\n\t}\n\t\n\tget type(): number {\n\t\treturn this.uint16Array[0];\n\t}\n\tprivate set type(value: number) {\n\t\tAtomics.store(this.uint16Array, 0, value);\n\t}\n\tget byteMultipler(): number {\n\t\treturn this.cachedByteMultipler;\n\t}\n\tget dataLength(): number {\n\t\t// Can technically be initialized by passing memory without actually every being called - need to make sure dataLength is always at least one\n\t\treturn this.uint16Array[1] || 1;\n\t}\n\tprivate set dataLength(value: number) {\n\t\tAtomics.store(this.uint16Array, 1, value);\n\t}\n\n\tget bufferLength(): number {\n\t\treturn Atomics.load(this.firstBlock.data, BUFFER_LENGTH_INDEX);\n\t}\n\tprivate set bufferLength(value: number) {\n\t\tAtomics.store(this.firstBlock.data, BUFFER_LENGTH_INDEX, value);\n\t}\n\n\tget pointer() {\n\t\treturn this.firstBlock.pointer;\n\t}\n\n\tget usedMemory(): number {\n\t\tlet dataMemory = new AllocatedMemory(this.memory, loadPointer(this.firstBlock.data, VECTOR_INDEX, this.memory.positionBits));\n\t\treturn this.firstBlock.usedMemory + dataMemory.usedMemory;\n\t}\n\n\tprivate cachedFullDataBlock?: T;\n\tprivate cachedPointer: number;\n\n\tconstructor(memory: MemoryHeap, config?: SharedVectorConfig<T> | SharedVectorMemory | SharedVectorMemory & SharedVectorConfig<T>) {\n\t\tthis.memory = memory;\n\n\t\tif(config && 'firstBlock' in config) {\n\t\t\tthis.firstBlock = new AllocatedMemory(memory, {\n\t\t\t\tlength: SharedVector.ALLOCATE_COUNT,\n\t\t\t\t...config.firstBlock,\n\t\t\t});\n\t\t\tthis.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + TYPE_INDEX * Uint32Array.BYTES_PER_ELEMENT, 2);\n\n\t\t\t// Pre-allocating memory and setting up in specific memory location\n\t\t\tif('type' in config || 'dataLength' in config) {\n\t\t\t\tconst typeCode = getArrayTypeCode(config.type ?? Uint32Array);\n\t\t\t\tconst byteMultipler = getByteMultipler(typeCode);\n\t\t\t\tconst bufferLength = config.bufferLength ?? DEFAULT_SIZE;\n\t\t\t\tlet dataBlock = memory.allocUI32(bufferLength * (config.dataLength ?? 1) * byteMultipler);\n\t\t\t\tstorePointer(this.firstBlock.data, VECTOR_INDEX, dataBlock.bufferPosition, dataBlock.bufferByteOffset, memory.positionBits);\n\t\t\t\tthis.bufferLength = bufferLength;\n\t\t\t\tthis.dataLength = (config.dataLength ?? 1);\n\t\t\t\tthis.type = typeCode;\n\t\t\t}\n\t\t} else {\n\t\t\tthis.firstBlock = memory.allocUI32(SharedVector.ALLOCATE_COUNT);\n\t\t\tthis.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + TYPE_INDEX * Uint32Array.BYTES_PER_ELEMENT, 2);\n\n\t\t\tlet dataLength = config?.dataLength ?? 1;\n\t\t\tlet bufferLength = config?.bufferLength ?? DEFAULT_SIZE;\n\t\t\tlet typeCode = getArrayTypeCode(config?.type ?? Uint32Array);\n\t\t\tlet dataBlock = memory.allocUI32(bufferLength * dataLength * getByteMultipler(typeCode));\n\t\t\tstorePointer(this.firstBlock.data, VECTOR_INDEX, dataBlock.bufferPosition, dataBlock.bufferByteOffset, memory.positionBits);\n\t\t\tthis.bufferLength = bufferLength;\n\t\t\tthis.type = typeCode;\n\t\t\tthis.dataLength = dataLength;\n\t\t}\n\n\t\tthis.cachedByteMultipler = getByteMultipler(this.type);\n\t\tthis.cachedPointer = this.firstBlock.data[0];\n\t\tthis.cachedFullDataBlock = this.getFullDataBlock();\n\t}\n\n\tat(index: number): T {\n\t\tlet length = this.length;\n\t\tif(index >= length || index < 0) {\n\t\t\tthrow new Error(`${index} is out of bounds ${length}`);\n\t\t}\n\n\t\tlet dataBlock = this.getFullDataBlock();\n\t\treturn this.getDataBlock(dataBlock, index);\n\t}\n\tget(index: number, dataIndex = 0): T[number] {\n\t\tif(dataIndex >= this.dataLength) {\n\t\t\tthrow new Error(`${dataIndex} is out of dataLength bounds ${this.dataLength}`);\n\t\t} else if(index >= this.length || index < 0) {\n\t\t\tthrow new Error(`${index} is out of bounds ${this.length}`);\n\t\t}\n\n\t\tlet dataBlock = this.getFullDataBlock() as NumericArrayIO;\n\t\treturn dataBlock[index * this.dataLength + dataIndex];\n\t}\n\n\tpush(values: T[number] | Array<T[number]>): number {\n\t\tlet dataLength = this.dataLength;\n\t\tconst isSingleValue = typeof values !== 'object';\n\t\tif(!isSingleValue && values.length > dataLength) {\n\t\t\tthrow new Error(`Can't insert ${values.length} array into shared list of ${dataLength} dataLength`);\n\t\t}\n\n\t\tlet dataBlock = this.getFullDataBlock() as NumericArrayIO;\n\t\tlet currentLength = this.length;\n\t\tlet startIndex = dataLength * currentLength;\n\t\tif(isSingleValue) {\n\t\t\tdataBlock[startIndex] = values;\n\t\t} else {\n\t\t\tfor(let i = 0; i < values.length; i++) {\n\t\t\t\tdataBlock[startIndex + i] = values[i];\n\t\t\t}\n\t\t}\n\n\t\tlet newLength = Atomics.add(this.firstBlock.data, LENGTH_INDEX, 1) + 1;\n\t\tif(newLength >= this.bufferLength) {\n\t\t\tthis.growBuffer();\n\t\t}\n\n\t\treturn currentLength;\n\t}\n\n\tpop(): T {\n\t\tlet oldLength = Atomics.sub(this.firstBlock.data, LENGTH_INDEX, 1);\n\n\t\tlet dataBlock = this.getFullDataBlock();\n\t\treturn this.getDataBlock(dataBlock, oldLength - 1);\n\t}\n\n\t// Returns the first number irregardless of dataLength - faster way to pop if you don't care about the rest of the data block\n\tpopNumber(): T[number] {\n\t\tconst oldLength = Atomics.sub(this.firstBlock.data, LENGTH_INDEX, 1);\n\t\tconst dataBlock = this.getFullDataBlock() as NumericArrayIO;\n\t\treturn dataBlock[(oldLength - 1) * this.dataLength];\n\t}\n\n\tdeleteIndex(index: number) {\n\t\tlet length = this.length;\n\t\tif(index >= length || index < 0) {\n\t\t\tthrow new Error(`${index} is out of bounds ${length}`);\n\t\t}\n\n\t\tlet dataLength = this.dataLength;\n\t\tlet dataBlock = this.getFullDataBlock() as NumericArrayIO;\n\t\tfor(let i = index; i < length; i++) {\n\t\t\tfor(let j = 0; j < dataLength; j++) {\n\t\t\t\tdataBlock[i * dataLength + j] = dataBlock[(i + 1) * dataLength + j];\n\t\t\t}\n\t\t}\n\n\t\tAtomics.sub(this.firstBlock.data, LENGTH_INDEX, 1);\n\t}\n\n\tclear() {\n\t\tthis.firstBlock.data[LENGTH_INDEX] = 0;\n\t}\n\n\t*[Symbol.iterator]() {\n\t\tlet dataBlock = this.getFullDataBlock();\n\t\tlet dataLength = this.dataLength;\n\t\tlet totalDataEntries = this.length * dataLength;\n\n\t\tfor(let i = 0; i < totalDataEntries; i += dataLength) {\n\t\t\tyield dataBlock.subarray(i, i + dataLength) as T;\n\t\t}\n\t}\n\n\t// Significantly faster but won't be thread safe if anything is removing/adding to it during iteration\n\tgetCurrentArray(): T {\n\t\tlet dataBlock = this.getFullDataBlock();\n\t\treturn dataBlock.subarray(0, this.length * this.dataLength) as T;\n\t}\n\n\tprivate getFullDataBlock(): T {\n\t\tlet pointerNumber = Atomics.load(this.firstBlock.data, VECTOR_INDEX);\n\t\tif(this.cachedPointer === pointerNumber && this.cachedFullDataBlock) {\n\t\t\treturn this.cachedFullDataBlock;\n\t\t}\n\n\t\tlet pointer = getPointer(pointerNumber, this.memory.positionBits);\n\t\tlet rawData = new AllocatedMemory(this.memory, pointer);\n\n\t\tlet data = makeArrayView(this.type, rawData.data.buffer, rawData.bufferByteOffset, this.dataLength * this.bufferLength) as T;\n\n\t\tthis.cachedPointer = pointerNumber;\n\t\tthis.cachedFullDataBlock = data;\n\n\t\treturn data;\n\t}\n\tprivate getDataBlock(rawData: T, index: number): T {\n\t\tconst start = index * this.dataLength;\n\t\treturn rawData.subarray(start, start + this.dataLength) as T;\n\t}\n\n\tprivate growBuffer() {\n\t\tlet oldBufferLength = this.bufferLength;\n\t\tlet newBufferLength = oldBufferLength * 2;\n\t\tlet dataLength = this.dataLength;\n\n\t\tlet oldPointer = loadPointer(this.firstBlock.data, VECTOR_INDEX, this.memory.positionBits);\n\t\tlet oldDataMemory = new AllocatedMemory(this.memory, oldPointer);\n\t\tlet oldDataBlock = this.getFullDataBlock();\n\t\tlet newDataBlock = this.memory.allocUI32(newBufferLength * dataLength * this.cachedByteMultipler);\n\n\t\tlet newData = makeArrayView(this.type, newDataBlock.data.buffer, newDataBlock.bufferByteOffset, dataLength * newBufferLength) as T;\n\n\t\t// newData and oldDataBlock are the same concrete view type, so a direct copy is valid; the cast only bridges the\n\t\t// union's mutually-incompatible set() overloads (number vs bigint element types)\n\t\t(newData as Float64Array).set(oldDataBlock as unknown as Float64Array);\n\n\t\tconst newPointer = newDataBlock.pointer;\n\t\tstoreRawPointer(this.firstBlock.data, VECTOR_INDEX, newPointer);\n\t\tthis.bufferLength = newBufferLength;\n\t\toldDataMemory.free();\n\n\t\tthis.cachedPointer = newPointer;\n\t\tthis.cachedFullDataBlock = newData;\n\t}\n\n\tfree() {\n\t\tlet pointer = loadPointer(this.firstBlock.data, VECTOR_INDEX, this.memory.positionBits);\n\t\tlet dataMemory = new AllocatedMemory(this.memory, pointer);\n\n\t\tdataMemory.free();\n\t\tthis.firstBlock.free();\n\t}\n\n\tgetSharedMemory(): SharedVectorMemory {\n\t\treturn {\n\t\t\tfirstBlock: this.firstBlock.getSharedMemory(),\n\t\t};\n\t}\n}\n\ninterface SharedVectorConfig<T extends NumericArray> {\n\ttype?: TypedArrayConstructor<T>\n\tdataLength?: number\n\n\tbufferLength?: number\n}\ninterface SharedVectorMemory {\n\tfirstBlock: SharedAllocatedMemory\n}\n\nexport type { SharedVectorConfig, SharedVectorMemory };"],"mappings":"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"}
1
+ {"version":3,"file":"shared-vector.js","names":["AllocatedMemory","VECTOR_INDEX","LENGTH_INDEX","BUFFER_LENGTH_INDEX","TYPE_INDEX","DEFAULT_SIZE","SharedVector","value","dataMemory","loadPointer","memory","config","typeCode","getArrayTypeCode","byteMultipler","getByteMultipler","bufferLength","dataBlock","storePointer","dataLength","length","index","dataIndex","isSingleValue","values","currentLength","startIndex","i","oldLength","j","totalDataEntries","pointerNumber","pointer","getPointer","rawData","data","makeArrayView","start","newBufferLength","oldPointer","oldDataMemory","oldDataBlock","newDataBlock","newData","newPointer","storeRawPointer"],"sources":["../src/shared-vector.ts"],"sourcesContent":["import type { SharedAllocatedMemory } from './allocated-memory';\nimport AllocatedMemory from './allocated-memory';\nimport type MemoryHeap from './memory-heap';\nimport { getPointer, loadPointer, storePointer, storeRawPointer } from './utils/pointer';\nimport { getArrayTypeCode, getByteMultipler, makeArrayView, type NumericArray, type NumericArrayIO, type TypedArrayConstructor } from './utils/array-type';\n\nconst VECTOR_INDEX = 0;\nconst LENGTH_INDEX = 1;\nconst BUFFER_LENGTH_INDEX = 2;\nconst TYPE_INDEX = 3;\nconst DEFAULT_SIZE = 4;\nexport default class SharedVector<T extends NumericArray = Uint32Array> implements Iterable<T> {\n\tstatic readonly ALLOCATE_COUNT = 4;\n\tprivate memory: MemoryHeap;\n\n\t// Pointer, List Length, Buffer Length, Type/DataLength\n\tprivate firstBlock: AllocatedMemory;\n\tprivate uint16Array: Uint16Array;\n\t// u32 units per element: 1 for 32-bit types, 2 for 64-bit (float64/int64/uint64). Every data-block allocation scales\n\t// by this; the typed view length stays in elements.\n\tprivate cachedByteMultipler = 1;\n\n\tget length(): number {\n\t\treturn Atomics.load(this.firstBlock.data, LENGTH_INDEX);\n\t}\n\t\n\tget type(): number {\n\t\treturn this.uint16Array[0];\n\t}\n\tprivate set type(value: number) {\n\t\tAtomics.store(this.uint16Array, 0, value);\n\t}\n\tget byteMultipler(): number {\n\t\treturn this.cachedByteMultipler;\n\t}\n\tget dataLength(): number {\n\t\t// Can technically be initialized by passing memory without actually every being called - need to make sure dataLength is always at least one\n\t\treturn this.uint16Array[1] || 1;\n\t}\n\tprivate set dataLength(value: number) {\n\t\tAtomics.store(this.uint16Array, 1, value);\n\t}\n\n\tget bufferLength(): number {\n\t\treturn Atomics.load(this.firstBlock.data, BUFFER_LENGTH_INDEX);\n\t}\n\tprivate set bufferLength(value: number) {\n\t\tAtomics.store(this.firstBlock.data, BUFFER_LENGTH_INDEX, value);\n\t}\n\n\tget pointer() {\n\t\treturn this.firstBlock.pointer;\n\t}\n\n\tget usedMemory(): number {\n\t\tlet dataMemory = new AllocatedMemory(this.memory, loadPointer(this.firstBlock.data, VECTOR_INDEX, this.memory.positionBits));\n\t\treturn this.firstBlock.usedMemory + dataMemory.usedMemory;\n\t}\n\n\tprivate cachedFullDataBlock?: T;\n\tprivate cachedPointer: number;\n\n\tconstructor(memory: MemoryHeap, config?: SharedVectorConfig<T> | SharedVectorMemory | SharedVectorMemory & SharedVectorConfig<T>) {\n\t\tthis.memory = memory;\n\n\t\tif(config && 'firstBlock' in config) {\n\t\t\tthis.firstBlock = new AllocatedMemory(memory, {\n\t\t\t\tlength: SharedVector.ALLOCATE_COUNT,\n\t\t\t\t...config.firstBlock,\n\t\t\t});\n\t\t\tthis.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + TYPE_INDEX * Uint32Array.BYTES_PER_ELEMENT, 2);\n\n\t\t\t// Pre-allocating memory and setting up in specific memory location\n\t\t\tif('type' in config || 'dataLength' in config) {\n\t\t\t\tconst typeCode = getArrayTypeCode(config.type ?? Uint32Array);\n\t\t\t\tconst byteMultipler = getByteMultipler(typeCode);\n\t\t\t\tconst bufferLength = config.bufferLength ?? DEFAULT_SIZE;\n\t\t\t\tlet dataBlock = memory.allocUI32(bufferLength * (config.dataLength ?? 1) * byteMultipler);\n\t\t\t\tstorePointer(this.firstBlock.data, VECTOR_INDEX, dataBlock.bufferPosition, dataBlock.bufferByteOffset, memory.positionBits);\n\t\t\t\tthis.bufferLength = bufferLength;\n\t\t\t\tthis.dataLength = (config.dataLength ?? 1);\n\t\t\t\tthis.type = typeCode;\n\t\t\t}\n\t\t} else {\n\t\t\tthis.firstBlock = memory.allocUI32(SharedVector.ALLOCATE_COUNT);\n\t\t\tthis.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + TYPE_INDEX * Uint32Array.BYTES_PER_ELEMENT, 2);\n\n\t\t\tlet dataLength = config?.dataLength ?? 1;\n\t\t\tlet bufferLength = config?.bufferLength ?? DEFAULT_SIZE;\n\t\t\tlet typeCode = getArrayTypeCode(config?.type ?? Uint32Array);\n\t\t\tlet dataBlock = memory.allocUI32(bufferLength * dataLength * getByteMultipler(typeCode));\n\t\t\tstorePointer(this.firstBlock.data, VECTOR_INDEX, dataBlock.bufferPosition, dataBlock.bufferByteOffset, memory.positionBits);\n\t\t\tthis.bufferLength = bufferLength;\n\t\t\tthis.type = typeCode;\n\t\t\tthis.dataLength = dataLength;\n\t\t}\n\n\t\tthis.cachedByteMultipler = getByteMultipler(this.type);\n\t\tthis.cachedPointer = this.firstBlock.data[0];\n\t\tthis.cachedFullDataBlock = this.getFullDataBlock();\n\t}\n\n\tat(index: number): T {\n\t\tlet length = this.length;\n\t\tif(index >= length || index < 0) {\n\t\t\tthrow new Error(`${index} is out of bounds ${length}`);\n\t\t}\n\n\t\tlet dataBlock = this.getFullDataBlock();\n\t\treturn this.getDataBlock(dataBlock, index);\n\t}\n\tget(index: number, dataIndex = 0): T[number] {\n\t\tif(dataIndex >= this.dataLength) {\n\t\t\tthrow new Error(`${dataIndex} is out of dataLength bounds ${this.dataLength}`);\n\t\t} else if(index >= this.length || index < 0) {\n\t\t\tthrow new Error(`${index} is out of bounds ${this.length}`);\n\t\t}\n\n\t\tlet dataBlock = this.getFullDataBlock() as NumericArrayIO;\n\t\treturn dataBlock[index * this.dataLength + dataIndex];\n\t}\n\n\tpush(values: T[number] | Array<T[number]>): number {\n\t\tlet dataLength = this.dataLength;\n\t\tconst isSingleValue = typeof values !== 'object';\n\t\tif(!isSingleValue && values.length > dataLength) {\n\t\t\tthrow new Error(`Can't insert ${values.length} array into shared list of ${dataLength} dataLength`);\n\t\t}\n\n\t\tlet dataBlock = this.getFullDataBlock() as NumericArrayIO;\n\t\tlet currentLength = this.length;\n\t\tlet startIndex = dataLength * currentLength;\n\t\tif(isSingleValue) {\n\t\t\tdataBlock[startIndex] = values;\n\t\t} else {\n\t\t\tfor(let i = 0; i < values.length; i++) {\n\t\t\t\tdataBlock[startIndex + i] = values[i];\n\t\t\t}\n\t\t}\n\n\t\tlet newLength = Atomics.add(this.firstBlock.data, LENGTH_INDEX, 1) + 1;\n\t\tif(newLength >= this.bufferLength) {\n\t\t\tthis.growBuffer();\n\t\t}\n\n\t\treturn currentLength;\n\t}\n\n\tpop(): T {\n\t\tlet oldLength = Atomics.sub(this.firstBlock.data, LENGTH_INDEX, 1);\n\n\t\tlet dataBlock = this.getFullDataBlock();\n\t\treturn this.getDataBlock(dataBlock, oldLength - 1);\n\t}\n\n\t// Returns the first number irregardless of dataLength - faster way to pop if you don't care about the rest of the data block\n\tpopNumber(): T[number] {\n\t\tconst oldLength = Atomics.sub(this.firstBlock.data, LENGTH_INDEX, 1);\n\t\tconst dataBlock = this.getFullDataBlock() as NumericArrayIO;\n\t\treturn dataBlock[(oldLength - 1) * this.dataLength];\n\t}\n\n\tdeleteIndex(index: number) {\n\t\tlet length = this.length;\n\t\tif(index >= length || index < 0) {\n\t\t\tthrow new Error(`${index} is out of bounds ${length}`);\n\t\t}\n\n\t\tlet dataLength = this.dataLength;\n\t\tlet dataBlock = this.getFullDataBlock() as NumericArrayIO;\n\t\tfor(let i = index; i < length; i++) {\n\t\t\tfor(let j = 0; j < dataLength; j++) {\n\t\t\t\tdataBlock[i * dataLength + j] = dataBlock[(i + 1) * dataLength + j];\n\t\t\t}\n\t\t}\n\n\t\tAtomics.sub(this.firstBlock.data, LENGTH_INDEX, 1);\n\t}\n\n\tclear() {\n\t\tthis.firstBlock.data[LENGTH_INDEX] = 0;\n\t}\n\n\t*[Symbol.iterator]() {\n\t\tlet dataBlock = this.getFullDataBlock();\n\t\tlet dataLength = this.dataLength;\n\t\tlet totalDataEntries = this.length * dataLength;\n\n\t\tfor(let i = 0; i < totalDataEntries; i += dataLength) {\n\t\t\tyield dataBlock.subarray(i, i + dataLength) as T;\n\t\t}\n\t}\n\n\t// Significantly faster but won't be thread safe if anything is removing/adding to it during iteration\n\tgetCurrentArray(): T {\n\t\tlet dataBlock = this.getFullDataBlock();\n\t\treturn dataBlock.subarray(0, this.length * this.dataLength) as T;\n\t}\n\n\tprivate getFullDataBlock(): T {\n\t\tlet pointerNumber = Atomics.load(this.firstBlock.data, VECTOR_INDEX);\n\t\tif(this.cachedPointer === pointerNumber && this.cachedFullDataBlock) {\n\t\t\treturn this.cachedFullDataBlock;\n\t\t}\n\n\t\tlet pointer = getPointer(pointerNumber, this.memory.positionBits);\n\t\tlet rawData = new AllocatedMemory(this.memory, pointer);\n\n\t\tlet data = makeArrayView(this.type, rawData.data.buffer, rawData.bufferByteOffset, this.dataLength * this.bufferLength) as T;\n\n\t\tthis.cachedPointer = pointerNumber;\n\t\tthis.cachedFullDataBlock = data;\n\n\t\treturn data;\n\t}\n\tprivate getDataBlock(rawData: T, index: number): T {\n\t\tconst start = index * this.dataLength;\n\t\treturn rawData.subarray(start, start + this.dataLength) as T;\n\t}\n\n\tprivate growBuffer() {\n\t\tlet oldBufferLength = this.bufferLength;\n\t\tlet newBufferLength = oldBufferLength * 2;\n\t\tlet dataLength = this.dataLength;\n\n\t\tlet oldPointer = loadPointer(this.firstBlock.data, VECTOR_INDEX, this.memory.positionBits);\n\t\tlet oldDataMemory = new AllocatedMemory(this.memory, oldPointer);\n\t\tlet oldDataBlock = this.getFullDataBlock();\n\t\tlet newDataBlock = this.memory.allocUI32(newBufferLength * dataLength * this.cachedByteMultipler);\n\n\t\tlet newData = makeArrayView(this.type, newDataBlock.data.buffer, newDataBlock.bufferByteOffset, dataLength * newBufferLength) as T;\n\n\t\t// newData and oldDataBlock are the same concrete view type, so a direct copy is valid; the cast only bridges the\n\t\t// union's mutually-incompatible set() overloads (number vs bigint element types)\n\t\t(newData as Float64Array).set(oldDataBlock as unknown as Float64Array);\n\n\t\tconst newPointer = newDataBlock.pointer;\n\t\tstoreRawPointer(this.firstBlock.data, VECTOR_INDEX, newPointer);\n\t\tthis.bufferLength = newBufferLength;\n\t\toldDataMemory.free();\n\n\t\tthis.cachedPointer = newPointer;\n\t\tthis.cachedFullDataBlock = newData;\n\t}\n\n\tfree() {\n\t\tlet pointer = loadPointer(this.firstBlock.data, VECTOR_INDEX, this.memory.positionBits);\n\t\tlet dataMemory = new AllocatedMemory(this.memory, pointer);\n\n\t\tdataMemory.free();\n\t\tthis.firstBlock.free();\n\t}\n\n\tgetSharedMemory(): SharedVectorMemory {\n\t\treturn {\n\t\t\tfirstBlock: this.firstBlock.getSharedMemory(),\n\t\t};\n\t}\n}\n\ninterface SharedVectorConfig<T extends NumericArray> {\n\ttype?: TypedArrayConstructor<T>\n\tdataLength?: number\n\n\tbufferLength?: number\n}\ninterface SharedVectorMemory {\n\tfirstBlock: SharedAllocatedMemory\n}\n\nexport type { SharedVectorConfig, SharedVectorMemory };"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"shared-quadtree.js","names":[],"sources":["../../src/spatial/shared-quadtree.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 { lock, unlock } from '../lock/simple-lock';\nimport { getPointer, loadRawPointer, storeRawPointer } from '../utils/pointer';\nimport { ARRAY_TYPE, getArrayTypeCode, getByteMultipler, makeArrayView } from '../utils/array-type';\nimport {\n\tMinPriorityQueue, addCandidate, candidateLimit, distanceSquaredToRect,\n\ttype NeighborCandidate, type NearestNeighbor,\n} from './spatial-neighbors';\nimport { getSpatialMapCapacity, getSpatialPoolChunkSize, type SpatialEntity } from './spatial-entity';\n\n// Fixed-depth region quadtree (\"implicit\" / hierarchical grid) built for concurrent updates from many threads. Dynamic\n// subdivision is deliberately avoided: creating/redistributing/freeing nodes at runtime races with readers mid-traversal\n// (the same hazard MemoryBuffer disables compaction for). Here the tree of nodes is allocated once and never changes\n// shape, so concurrency reduces to per-cell content updates - different cells never contend.\n//\n// Node layout is implicit: node 0 is the root, the four children of node i are 4i+1..4i+4 (NW, NE, SW, SE). Bounds are\n// derived from the root bounds during descent, never stored. Each node owns an intrusive singly-linked bucket: the node\n// record holds a lock and the head item index, and each item record carries the next index. Items live in a SharedPool\n// (stable indices, recycling) and an id->itemIndex SharedMap makes update/remove O(depth) by entity id.\n//\n// Each entity is stored in the single deepest node that fully contains its rect (an item too large or straddling a\n// midline stays at an ancestor). search walks every node whose region intersects the query and returns a broad-phase\n// superset of candidate ids - the same guarantee quadtree-js's retrieve gives, without duplicating straddlers.\n//\n// The whole item record (id, coords, node, bucket link) lives in one pool at the configured precision. Float32 (the\n// default, matching a physics engine that keeps positions in Float32) keeps everything exact as long as entity ids and\n// the pool's index space both stay under 2^24 (~16.7M) - every id, pool index, node index and the NULL sentinel is a\n// Float32-exact integer there. Need more than that many entities (or ids >= 2^24)? Pass type: Float64Array.\n\n// Header (firstBlock) u32 layout\nconst NODE_POINTER_INDEX = 0;\nconst POOL_POINTER_INDEX = 1;\nconst MAP_POINTER_INDEX = 2;\nconst MAX_LEVELS_INDEX = 3;\nconst NODE_COUNT_INDEX = 4;\nconst TYPE_INDEX = 5;\n// bounds view starts at slot 6 (offset 24 bytes is 8-byte aligned, so a Float64 bounds view is valid)\nconst BOUNDS_INDEX = 6;\n\n// Per-node record: [lock, head]\nconst NODE_LOCK_OFFSET = 0;\nconst NODE_HEAD_OFFSET = 1;\nconst NODE_SIZE = 2;\n\n// Item record fields\nconst ITEM_ID = 0;\nconst ITEM_X = 1;\nconst ITEM_Y = 2;\nconst ITEM_W = 3;\nconst ITEM_H = 4;\nconst ITEM_NODE = 5;\nconst ITEM_NEXT = 6;\nconst ITEM_FIELDS = 7;\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\nconst DEFAULT_MAX_LEVELS = 4;\nconst DEFAULT_MAX_ENTITIES = 1_000_000;\nconst DEFAULT_COORD_TYPE = Float32Array;\n\ntype CoordArray = Float32Array | Float64Array;\ntype CoordArrayConstructor = Float32ArrayConstructor | Float64ArrayConstructor;\n\nexport default class SharedQuadtree {\n\tstatic readonly ALLOCATE_COUNT = 14;\n\n\tprivate memory: MemoryHeap;\n\tprivate firstBlock: AllocatedMemory;\n\tprivate pool: SharedPool<CoordArray>;\n\tprivate idMap: SharedMap<number>;\n\n\t// Reused to hand a full item record to pool.push without allocating a fresh array per insert (single writer per id)\n\tprivate insertScratch: Array<number> = [0, 0, 0, 0, 0, 0, 0];\n\n\t// Node block views over one contiguous region: Int32 for the per-node locks, Uint32 for the head indexes.\n\tprivate nodeInts: Int32Array;\n\tprivate nodeHeads: Uint32Array;\n\n\tprivate readonly maxLevels: number;\n\tprivate readonly nodeCount: number;\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// Root bounds, read once (stored at the configured precision, math done in float64)\n\tprivate readonly rootX: number;\n\tprivate readonly rootY: number;\n\tprivate readonly rootWidth: number;\n\tprivate readonly rootHeight: number;\n\n\t// Number of entities currently tracked\n\tget size(): number {\n\t\treturn this.idMap.length;\n\t}\n\n\tget bounds(): QuadtreeBounds {\n\t\treturn { x: this.rootX, y: this.rootY, width: this.rootWidth, height: this.rootHeight };\n\t}\n\n\t// Element type the record is 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?: SharedQuadtreeConfig | SharedQuadtreeMemory) {\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.pool = new SharedPool<CoordArray>(memory, {\n\t\t\t\tfirstBlock: getPointer(loadRawPointer(this.firstBlock.data, 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.maxLevels = this.firstBlock.data[MAX_LEVELS_INDEX];\n\t\t\tthis.nodeCount = this.firstBlock.data[NODE_COUNT_INDEX];\n\t\t\tthis.nullIndex = nullIndexFor(this.coordType);\n\t\t} else {\n\t\t\tlet bounds = config?.bounds ?? { x: 0, y: 0, width: 0, height: 0 };\n\t\t\tlet maxEntities = config?.maxEntities ?? DEFAULT_MAX_ENTITIES;\n\t\t\tthis.maxLevels = config?.maxLevels ?? DEFAULT_MAX_LEVELS;\n\t\t\tthis.nodeCount = SharedQuadtree.getNodeCount(this.maxLevels);\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) {\n\t\t\t\tthrow new Error(`Float32 SharedQuadtree supports at most ${FLOAT32_LIMIT} entities; pass type: Float64Array for more`);\n\t\t\t}\n\n\t\t\t// Inline the pool and id-map firstBlocks right after the header so the whole spine is one contiguous allocation\n\t\t\t// (the pool's own firstBlock already inlines its two stacks). The node block stays separate: it is large and\n\t\t\t// sized independently of these headers.\n\t\t\tlet poolConfig: SharedPoolConfig<CoordArray> = {\n\t\t\t\ttype: getCoordTypeConstructor(this.coordType),\n\t\t\t\tdataLength: ITEM_FIELDS,\n\t\t\t\tmaxLength: maxEntities,\n\t\t\t\tmaxChunkSize: getSpatialPoolChunkSize(maxEntities, ITEM_FIELDS, getByteMultipler(this.coordType), memory.maxAllocationLength),\n\t\t\t};\n\t\t\tlet poolAllocateCount = SharedPool.getAllocateCount(memory, poolConfig);\n\t\t\tlet mapAllocateCount = SharedMap.ALLOCATE_COUNT;\n\t\t\tthis.firstBlock = memory.allocUI32(SharedQuadtree.ALLOCATE_COUNT + poolAllocateCount + mapAllocateCount);\n\t\t\tthis.firstBlock.data[MAX_LEVELS_INDEX] = this.maxLevels;\n\t\t\tthis.firstBlock.data[NODE_COUNT_INDEX] = this.nodeCount;\n\t\t\tthis.firstBlock.data[TYPE_INDEX] = this.coordType;\n\n\t\t\tlet boundsView = this.getBoundsView();\n\t\t\tboundsView[0] = bounds.x;\n\t\t\tboundsView[1] = bounds.y;\n\t\t\tboundsView[2] = bounds.width;\n\t\t\tboundsView[3] = bounds.height;\n\n\t\t\t// Node block must fit one allocation - it is never resized. Every head starts as NULL (calloc leaves them 0,\n\t\t\t// a valid item index, so they must be initialized explicitly).\n\t\t\tlet nodeBlock = memory.allocUI32(this.nodeCount * NODE_SIZE);\n\t\t\tlet heads = new Uint32Array(nodeBlock.data.buffer, nodeBlock.bufferByteOffset, this.nodeCount * NODE_SIZE);\n\t\t\tfor(let i = 0; i < this.nodeCount; i++) {\n\t\t\t\theads[i * NODE_SIZE + NODE_HEAD_OFFSET] = this.nullIndex;\n\t\t\t}\n\t\t\tstoreRawPointer(this.firstBlock.data, NODE_POINTER_INDEX, nodeBlock.pointer);\n\n\t\t\tlet poolBlock = this.firstBlock.getSubAllocation(SharedQuadtree.ALLOCATE_COUNT, poolAllocateCount);\n\t\t\tthis.pool = new SharedPool<CoordArray>(memory, { firstBlock: poolBlock, ...poolConfig });\n\t\t\tstoreRawPointer(this.firstBlock.data, POOL_POINTER_INDEX, poolBlock.pointer);\n\n\t\t\tlet mapBlock = this.firstBlock.getSubAllocation(SharedQuadtree.ALLOCATE_COUNT + poolAllocateCount, 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\tlet boundsView = this.getBoundsView();\n\t\tthis.rootX = boundsView[0];\n\t\tthis.rootY = boundsView[1];\n\t\tthis.rootWidth = boundsView[2];\n\t\tthis.rootHeight = boundsView[3];\n\n\t\tlet nodeBlock = new AllocatedMemory(memory, getPointer(loadRawPointer(this.firstBlock.data, NODE_POINTER_INDEX), memory.positionBits));\n\t\tthis.nodeInts = new Int32Array(nodeBlock.data.buffer, nodeBlock.bufferByteOffset, this.nodeCount * NODE_SIZE);\n\t\tthis.nodeHeads = new Uint32Array(nodeBlock.data.buffer, nodeBlock.bufferByteOffset, this.nodeCount * NODE_SIZE);\n\t}\n\n\t// Total nodes in a complete quadtree of the given depth: sum of 4^level for level 0..maxLevels.\n\tstatic getNodeCount(maxLevels: number): number {\n\t\treturn (Math.pow(4, maxLevels + 1) - 1) / 3;\n\t}\n\n\tget pointer(): number {\n\t\treturn this.firstBlock.pointer;\n\t}\n\n\tget usedMemory(): number {\n\t\tlet nodeBlock = new AllocatedMemory(this.memory, getPointer(loadRawPointer(this.firstBlock.data, NODE_POINTER_INDEX), this.memory.positionBits));\n\t\treturn this.firstBlock.usedMemory + nodeBlock.usedMemory + this.pool.usedMemory + this.idMap.usedMemory;\n\t}\n\n\tinsert(id: number, x: number, y: number, width = 0, height = 0) {\n\t\tif(this.idMap.get(id) !== undefined) {\n\t\t\tthis.update(id, x, y, width, height);\n\t\t\treturn;\n\t\t}\n\n\t\tlet node = this.locate(x, y, width, height);\n\t\tlet record = this.insertScratch;\n\t\trecord[ITEM_ID] = id;\n\t\trecord[ITEM_X] = x;\n\t\trecord[ITEM_Y] = y;\n\t\trecord[ITEM_W] = width;\n\t\trecord[ITEM_H] = height;\n\t\trecord[ITEM_NODE] = node;\n\t\trecord[ITEM_NEXT] = this.nullIndex;\n\t\tlet index = this.pool.push(record);\n\t\tthis.linkIntoNode(node, index);\n\t\tthis.idMap.set(id, index);\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 newCount = existing.reduce<number>((count, index) => count + (index === undefined ? 1 : 0), 0);\n\t\tlet nextIndex = this.pool.reserveContiguous(newCount);\n\t\tfor(let i = 0; i < unique.length; i++) {\n\t\t\tlet entity = unique[i];\n\t\t\tlet width = entity.width ?? 0;\n\t\t\tlet height = entity.height ?? 0;\n\t\t\tif(existing[i] !== undefined) {\n\t\t\t\tthis.update(entity.id, entity.x, entity.y, width, height);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet node = this.locate(entity.x, entity.y, width, height);\n\t\t\tlet index = nextIndex++;\n\t\t\tlet block = this.pool.blockFor(index);\n\t\t\tlet offset = this.pool.blockOffset(index);\n\t\t\tblock[offset + ITEM_ID] = entity.id;\n\t\t\tblock[offset + ITEM_X] = entity.x;\n\t\t\tblock[offset + ITEM_Y] = entity.y;\n\t\t\tblock[offset + ITEM_W] = width;\n\t\t\tblock[offset + ITEM_H] = height;\n\t\t\tblock[offset + ITEM_NODE] = node;\n\t\t\tblock[offset + ITEM_NEXT] = this.nullIndex;\n\t\t\tthis.linkIntoNode(node, index);\n\t\t\tmappings.push([entity.id, index]);\n\t\t}\n\t\tthis.idMap.setAll(mappings);\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 item fields are stable until we lock its node(s).\n\tupdate(id: number, x: number, y: number, width = 0, height = 0) {\n\t\tlet index = this.idMap.get(id);\n\t\tif(index === undefined) {\n\t\t\tthis.insert(id, x, y, width, height);\n\t\t\treturn;\n\t\t}\n\n\t\tlet block = this.pool.blockFor(index);\n\t\tlet off = this.pool.blockOffset(index);\n\t\tlet oldNode = block[off + ITEM_NODE];\n\t\tlet newNode = this.locate(x, y, width, height);\n\n\t\tif(oldNode === newNode) {\n\t\t\t// Same cell: only the coordinates change, no relinking. Lock so a concurrent reader sees a consistent record.\n\t\t\tlock(this.nodeInts, oldNode * NODE_SIZE + NODE_LOCK_OFFSET);\n\t\t\tblock[off + ITEM_X] = x;\n\t\t\tblock[off + ITEM_Y] = y;\n\t\t\tblock[off + ITEM_W] = width;\n\t\t\tblock[off + ITEM_H] = height;\n\t\t\tunlock(this.nodeInts, oldNode * NODE_SIZE + NODE_LOCK_OFFSET);\n\t\t\treturn;\n\t\t}\n\n\t\t// Cross-cell move: hold both node locks, ordered by index to avoid deadlock with another thread moving the reverse way.\n\t\tlet first = Math.min(oldNode, newNode);\n\t\tlet second = Math.max(oldNode, newNode);\n\t\tlock(this.nodeInts, first * NODE_SIZE + NODE_LOCK_OFFSET);\n\t\tlock(this.nodeInts, second * NODE_SIZE + NODE_LOCK_OFFSET);\n\n\t\tthis.unlinkNoLock(oldNode, index);\n\t\tblock[off + ITEM_X] = x;\n\t\tblock[off + ITEM_Y] = y;\n\t\tblock[off + ITEM_W] = width;\n\t\tblock[off + ITEM_H] = height;\n\t\tblock[off + ITEM_NODE] = newNode;\n\t\tthis.linkNoLock(newNode, index);\n\n\t\tunlock(this.nodeInts, second * NODE_SIZE + NODE_LOCK_OFFSET);\n\t\tunlock(this.nodeInts, first * NODE_SIZE + NODE_LOCK_OFFSET);\n\t}\n\n\tremove(id: number): boolean {\n\t\tlet index = this.idMap.get(id);\n\t\tif(index === undefined) {\n\t\t\treturn false;\n\t\t}\n\n\t\tlet node = this.pool.get(index, ITEM_NODE);\n\t\tthis.unlinkFromNode(node, index);\n\t\tthis.pool.deleteIndex(index);\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 in a node whose region intersects the given rect. Result is a superset of true\n\t// overlaps (the caller does the narrow-phase check), matching quadtree-js retrieve.\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\tlet start = out.length;\n\t\tthis.collect(out, 0, this.rootX, this.rootY, this.rootWidth, this.rootHeight, 0, x, y, width, height);\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\tif(maxResults <= 0 || maxDistance < 0 || Number.isNaN(maxResults) || Number.isNaN(maxDistance)) {\n\t\t\treturn [];\n\t\t}\n\n\t\tinterface Node {\n\t\t\tindex: number\n\t\t\tx: number\n\t\t\ty: number\n\t\t\twidth: number\n\t\t\theight: number\n\t\t\tlevel: number\n\t\t}\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\tif(maxResults === 1) {\n\t\t\treturn this.nearestNeighbor(x, y, maxDistanceSquared, filter);\n\t\t}\n\t\tlet candidates: Array<NeighborCandidate> = [];\n\t\tlet nodeCandidates: Array<NeighborCandidate> = [];\n\t\tlet queue = new MinPriorityQueue<Node>();\n\t\tlet root = { index: 0, x: this.rootX, y: this.rootY, width: this.rootWidth, height: this.rootHeight, level: 0 };\n\t\tqueue.push(root, distanceSquaredToRect(x, y, this.rootX, this.rootY, this.rootX + this.rootWidth, this.rootY + this.rootHeight));\n\n\t\twhile(queue.length > 0 && queue.priority <= candidateLimit(candidates, maxResults, maxDistanceSquared)) {\n\t\t\tlet node = queue.pop();\n\t\t\tif(node === undefined) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tnodeCandidates.length = 0;\n\t\t\tthis.collectNeighborNode(nodeCandidates, node.index, x, y, maxDistanceSquared);\n\t\t\tfor(let candidate of nodeCandidates) {\n\t\t\t\tlet duplicate = false;\n\t\t\t\tfor(let existing of candidates) {\n\t\t\t\t\tif(existing.id === candidate.id) {\n\t\t\t\t\t\tduplicate = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif(duplicate) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif(filter === undefined || filter(candidate.id)) {\n\t\t\t\t\taddCandidate(candidates, candidate.id, candidate.distance, maxResults);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(node.level >= this.maxLevels) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet halfWidth = node.width / 2;\n\t\t\tlet halfHeight = node.height / 2;\n\t\t\tlet childLevel = node.level + 1;\n\t\t\tlet children: Array<Node> = [\n\t\t\t\t{ index: node.index * 4 + 1, x: node.x, y: node.y, width: halfWidth, height: halfHeight, level: childLevel },\n\t\t\t\t{ index: node.index * 4 + 2, x: node.x + halfWidth, y: node.y, width: halfWidth, height: halfHeight, level: childLevel },\n\t\t\t\t{ index: node.index * 4 + 3, x: node.x, y: node.y + halfHeight, width: halfWidth, height: halfHeight, level: childLevel },\n\t\t\t\t{ index: node.index * 4 + 4, x: node.x + halfWidth, y: node.y + halfHeight, width: halfWidth, height: halfHeight, level: childLevel },\n\t\t\t];\n\t\t\tfor(let child of children) {\n\t\t\t\tlet distance = distanceSquaredToRect(x, y, child.x, child.y, child.x + child.width, child.y + child.height);\n\t\t\t\tif(distance <= candidateLimit(candidates, maxResults, maxDistanceSquared)) {\n\t\t\t\t\tqueue.push(child, distance);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn candidates.map(candidate => candidate.id);\n\t}\n\n\tprivate nearestNeighbor(\n\t\tx: number, y: number, maxDistanceSquared: number, filter: ((id: number) => boolean) | undefined,\n\t): Array<number> {\n\t\tlet nearest: NearestNeighbor = { id: undefined, distance: maxDistanceSquared };\n\t\tlet nodeCandidates: Array<NeighborCandidate> = [];\n\t\tthis.findNearest(\n\t\t\t0, this.rootX, this.rootY, this.rootWidth, this.rootHeight, 0,\n\t\t\tx, y, nearest, nodeCandidates, filter,\n\t\t);\n\t\treturn nearest.id === undefined ? [] : [nearest.id];\n\t}\n\n\tprivate findNearest(\n\t\tnode: number, nodeX: number, nodeY: number, width: number, height: number, level: number,\n\t\tx: number, y: number, nearest: NearestNeighbor,\n\t\tnodeCandidates: Array<NeighborCandidate>, filter: ((id: number) => boolean) | undefined,\n\t) {\n\t\tif(distanceSquaredToRect(x, y, nodeX, nodeY, nodeX + width, nodeY + height) > nearest.distance) {\n\t\t\treturn;\n\t\t}\n\t\tif(filter === undefined) {\n\t\t\tthis.collectNearestNode(nearest, node, x, y);\n\t\t} else {\n\t\t\tnodeCandidates.length = 0;\n\t\t\tthis.collectNeighborNode(nodeCandidates, node, x, y, nearest.distance);\n\t\t\tfor(let candidate of nodeCandidates) {\n\t\t\t\tif(filter(candidate.id)\n\t\t\t\t\t&& (candidate.distance < nearest.distance\n\t\t\t\t\t\t|| (candidate.distance === nearest.distance && (nearest.id === undefined || candidate.id < nearest.id)))) {\n\t\t\t\t\tnearest.id = candidate.id;\n\t\t\t\t\tnearest.distance = candidate.distance;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif(level >= this.maxLevels) {\n\t\t\treturn;\n\t\t}\n\n\t\tlet halfWidth = width / 2;\n\t\tlet halfHeight = height / 2;\n\t\tlet midX = nodeX + halfWidth;\n\t\tlet midY = nodeY + halfHeight;\n\t\tlet base = node * 4;\n\t\tlet nextLevel = level + 1;\n\t\tlet east = x >= midX;\n\t\tlet south = y >= midY;\n\t\tlet visit = (index: number, childX: number, childY: number) => {\n\t\t\tthis.findNearest(\n\t\t\t\tindex, childX, childY, halfWidth, halfHeight, nextLevel,\n\t\t\t\tx, y, nearest, nodeCandidates, filter,\n\t\t\t);\n\t\t};\n\t\tif(south) {\n\t\t\tif(east) {\n\t\t\t\tvisit(base + 4, midX, midY);\n\t\t\t\tvisit(base + 3, nodeX, midY);\n\t\t\t\tvisit(base + 2, midX, nodeY);\n\t\t\t\tvisit(base + 1, nodeX, nodeY);\n\t\t\t} else {\n\t\t\t\tvisit(base + 3, nodeX, midY);\n\t\t\t\tvisit(base + 4, midX, midY);\n\t\t\t\tvisit(base + 1, nodeX, nodeY);\n\t\t\t\tvisit(base + 2, midX, nodeY);\n\t\t\t}\n\t\t} else if(east) {\n\t\t\tvisit(base + 2, midX, nodeY);\n\t\t\tvisit(base + 1, nodeX, nodeY);\n\t\t\tvisit(base + 4, midX, midY);\n\t\t\tvisit(base + 3, nodeX, midY);\n\t\t} else {\n\t\t\tvisit(base + 1, nodeX, nodeY);\n\t\t\tvisit(base + 2, midX, nodeY);\n\t\t\tvisit(base + 3, nodeX, midY);\n\t\t\tvisit(base + 4, midX, midY);\n\t\t}\n\t}\n\n\tclear() {\n\t\tfor(let i = 0; i < this.nodeCount; i++) {\n\t\t\tthis.nodeHeads[i * NODE_SIZE + NODE_HEAD_OFFSET] = this.nullIndex;\n\t\t}\n\t\tthis.pool.clear();\n\t\t// SharedMap has no clear(); drop every key. Not safe to run concurrently with writers (documented on clear()).\n\t\tlet keys: Array<number> = [];\n\t\tfor(let [key] of this.idMap) {\n\t\t\tkeys.push(key);\n\t\t}\n\t\tfor(let key of keys) {\n\t\t\tthis.idMap.delete(key);\n\t\t}\n\t}\n\n\tfree() {\n\t\tlet nodeBlock = new AllocatedMemory(this.memory, getPointer(loadRawPointer(this.firstBlock.data, NODE_POINTER_INDEX), this.memory.positionBits));\n\t\tnodeBlock.free();\n\t\tthis.pool.free();\n\t\tthis.idMap.free();\n\t\tthis.firstBlock.free();\n\t}\n\n\tgetSharedMemory(): SharedQuadtreeMemory {\n\t\treturn {\n\t\t\tfirstBlock: this.firstBlock.getSharedMemory(),\n\t\t};\n\t}\n\n\tprivate getBoundsView(): CoordArray {\n\t\treturn makeArrayView(\n\t\t\tthis.coordType,\n\t\t\tthis.firstBlock.data.buffer,\n\t\t\tthis.firstBlock.bufferByteOffset + BOUNDS_INDEX * Uint32Array.BYTES_PER_ELEMENT,\n\t\t\t4,\n\t\t) as CoordArray;\n\t}\n\n\t// Deepest node index that fully contains the rect. Descends into the one child that still fully contains it; stops at a\n\t// straddle or at max depth.\n\tprivate locate(x: number, y: number, width: number, height: number): number {\n\t\tlet node = 0;\n\t\tlet nx = this.rootX;\n\t\tlet ny = this.rootY;\n\t\tlet nw = this.rootWidth;\n\t\tlet nh = this.rootHeight;\n\n\t\tfor(let level = 0; level < this.maxLevels; level++) {\n\t\t\tlet midX = nx + nw / 2;\n\t\t\tlet midY = ny + nh / 2;\n\t\t\tlet west = x >= nx && x + width <= midX && (width > 0 || x < midX);\n\t\t\tlet east = x >= midX && x + width <= nx + nw;\n\t\t\tlet north = y >= ny && y + height <= midY && (height > 0 || y < midY);\n\t\t\tlet south = y >= midY && y + height <= ny + nh;\n\n\t\t\tif(north && west) {\n\t\t\t\tnode = node * 4 + 1;\n\t\t\t\tnw /= 2;\n\t\t\t\tnh /= 2;\n\t\t\t} else if(north && east) {\n\t\t\t\tnode = node * 4 + 2;\n\t\t\t\tnx = midX;\n\t\t\t\tnw /= 2;\n\t\t\t\tnh /= 2;\n\t\t\t} else if(south && west) {\n\t\t\t\tnode = node * 4 + 3;\n\t\t\t\tny = midY;\n\t\t\t\tnw /= 2;\n\t\t\t\tnh /= 2;\n\t\t\t} else if(south && east) {\n\t\t\t\tnode = node * 4 + 4;\n\t\t\t\tnx = midX;\n\t\t\t\tny = midY;\n\t\t\t\tnw /= 2;\n\t\t\t\tnh /= 2;\n\t\t\t} else {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn node;\n\t}\n\n\tprivate collect(out: Array<number>, node: number, nx: number, ny: number, nw: number, nh: number, level: number, qx: number, qy: number, qw: number, qh: number) {\n\t\t// Peek the head unlocked: an empty bucket has nothing to read, so the lock (and its atomics) is pure overhead. Most\n\t\t// nodes in a fixed-depth tree are empty, so skipping them dominates query cost. Taking the lock only to walk a\n\t\t// non-empty bucket preserves the snapshot guarantee - a concurrent insert we race is simply not in this snapshot.\n\t\tlet headIndex = node * NODE_SIZE + NODE_HEAD_OFFSET;\n\t\tif(this.nodeHeads[headIndex] !== this.nullIndex) {\n\t\t\tlet lockIndex = node * NODE_SIZE + NODE_LOCK_OFFSET;\n\t\t\tlock(this.nodeInts, lockIndex);\n\t\t\tlet index = this.nodeHeads[headIndex];\n\t\t\tlet pool = this.pool;\n\t\t\twhile(index !== this.nullIndex) {\n\t\t\t\t// One chunk resolve per item covers both fields it reads\n\t\t\t\tlet block = pool.blockFor(index);\n\t\t\t\tlet off = pool.blockOffset(index);\n\t\t\t\tout.push(block[off + ITEM_ID]);\n\t\t\t\tindex = block[off + ITEM_NEXT];\n\t\t\t}\n\t\t\tunlock(this.nodeInts, lockIndex);\n\t\t}\n\n\t\tif(level >= this.maxLevels) {\n\t\t\treturn;\n\t\t}\n\n\t\tlet midX = nx + nw / 2;\n\t\tlet midY = ny + nh / 2;\n\t\tlet hw = nw / 2;\n\t\tlet hh = nh / 2;\n\t\tlet overlapsWest = qx < midX;\n\t\tlet overlapsEast = qx + qw > midX;\n\t\tlet overlapsNorth = qy < midY;\n\t\tlet overlapsSouth = qy + qh > midY;\n\n\t\tif(overlapsNorth && overlapsWest) {\n\t\t\tthis.collect(out, node * 4 + 1, nx, ny, hw, hh, level + 1, qx, qy, qw, qh);\n\t\t}\n\t\tif(overlapsNorth && overlapsEast) {\n\t\t\tthis.collect(out, node * 4 + 2, midX, ny, hw, hh, level + 1, qx, qy, qw, qh);\n\t\t}\n\t\tif(overlapsSouth && overlapsWest) {\n\t\t\tthis.collect(out, node * 4 + 3, nx, midY, hw, hh, level + 1, qx, qy, qw, qh);\n\t\t}\n\t\tif(overlapsSouth && overlapsEast) {\n\t\t\tthis.collect(out, node * 4 + 4, midX, midY, hw, hh, level + 1, qx, qy, qw, qh);\n\t\t}\n\t}\n\n\tprivate collectNeighborNode(\n\t\tout: Array<NeighborCandidate>, node: number, x: number, y: number, maxDistanceSquared: number,\n\t) {\n\t\tlet headIndex = node * NODE_SIZE + NODE_HEAD_OFFSET;\n\t\tif(this.nodeHeads[headIndex] === this.nullIndex) {\n\t\t\treturn;\n\t\t}\n\t\tlet lockIndex = node * NODE_SIZE + NODE_LOCK_OFFSET;\n\t\tlock(this.nodeInts, lockIndex);\n\t\tlet index = this.nodeHeads[headIndex];\n\t\twhile(index !== this.nullIndex) {\n\t\t\tlet block = this.pool.blockFor(index);\n\t\t\tlet off = this.pool.blockOffset(index);\n\t\t\tlet id = block[off + ITEM_ID];\n\t\t\tlet distance = distanceSquaredToRect(x, y, block[off + ITEM_X], block[off + ITEM_Y], block[off + ITEM_X] + block[off + ITEM_W], block[off + ITEM_Y] + block[off + ITEM_H]);\n\t\t\tif(distance <= maxDistanceSquared) {\n\t\t\t\tout.push({ id, distance });\n\t\t\t}\n\t\t\tindex = block[off + ITEM_NEXT];\n\t\t}\n\t\tunlock(this.nodeInts, lockIndex);\n\t}\n\n\tprivate collectNearestNode(nearest: NearestNeighbor, node: number, x: number, y: number) {\n\t\tlet headIndex = node * NODE_SIZE + NODE_HEAD_OFFSET;\n\t\tif(this.nodeHeads[headIndex] === this.nullIndex) {\n\t\t\treturn;\n\t\t}\n\t\tlet lockIndex = node * NODE_SIZE + NODE_LOCK_OFFSET;\n\t\tlock(this.nodeInts, lockIndex);\n\t\tlet index = this.nodeHeads[headIndex];\n\t\twhile(index !== this.nullIndex) {\n\t\t\tlet block = this.pool.blockFor(index);\n\t\t\tlet off = this.pool.blockOffset(index);\n\t\t\tlet id = block[off + ITEM_ID];\n\t\t\tlet distance = distanceSquaredToRect(\n\t\t\t\tx, y, block[off + ITEM_X], block[off + ITEM_Y],\n\t\t\t\tblock[off + ITEM_X] + block[off + ITEM_W], block[off + ITEM_Y] + block[off + ITEM_H],\n\t\t\t);\n\t\t\tif(distance < nearest.distance\n\t\t\t\t|| (distance === nearest.distance && (nearest.id === undefined || id < nearest.id))) {\n\t\t\t\tnearest.id = id;\n\t\t\t\tnearest.distance = distance;\n\t\t\t}\n\t\t\tindex = block[off + ITEM_NEXT];\n\t\t}\n\t\tunlock(this.nodeInts, lockIndex);\n\t}\n\n\tprivate linkIntoNode(node: number, index: number) {\n\t\tlet lockIndex = node * NODE_SIZE + NODE_LOCK_OFFSET;\n\t\tlock(this.nodeInts, lockIndex);\n\t\tthis.linkNoLock(node, index);\n\t\tunlock(this.nodeInts, lockIndex);\n\t}\n\t// Push the item onto the node's bucket head. Caller holds the node lock.\n\tprivate linkNoLock(node: number, index: number) {\n\t\tlet headIndex = node * NODE_SIZE + NODE_HEAD_OFFSET;\n\t\tthis.pool.set(index, ITEM_NEXT, this.nodeHeads[headIndex]);\n\t\tthis.nodeHeads[headIndex] = index;\n\t}\n\n\tprivate unlinkFromNode(node: number, index: number) {\n\t\tlet lockIndex = node * NODE_SIZE + NODE_LOCK_OFFSET;\n\t\tlock(this.nodeInts, lockIndex);\n\t\tthis.unlinkNoLock(node, index);\n\t\tunlock(this.nodeInts, lockIndex);\n\t}\n\t// Splice the item out of the node's bucket. Caller holds the node lock.\n\tprivate unlinkNoLock(node: number, index: number) {\n\t\tlet headIndex = node * NODE_SIZE + NODE_HEAD_OFFSET;\n\t\tlet head = this.nodeHeads[headIndex];\n\t\tlet next = this.pool.get(index, ITEM_NEXT);\n\t\tif(head === index) {\n\t\t\tthis.nodeHeads[headIndex] = next;\n\t\t\treturn;\n\t\t}\n\n\t\tlet prev = head;\n\t\twhile(prev !== this.nullIndex) {\n\t\t\tlet prevNext = this.pool.get(prev, ITEM_NEXT);\n\t\t\tif(prevNext === index) {\n\t\t\t\tthis.pool.set(prev, ITEM_NEXT, next);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tprev = prevNext;\n\t\t}\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('SharedQuadtree 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}\n\ninterface QuadtreeBounds {\n\tx: number\n\ty: number\n\twidth: number\n\theight: number\n}\ninterface SharedQuadtreeConfig {\n\tbounds: QuadtreeBounds\n\tmaxLevels?: number\n\tmaxEntities?: number\n\ttype?: CoordArrayConstructor\n}\ninterface SharedQuadtreeMemory {\n\tfirstBlock: SharedAllocatedMemory\n}\n\nexport type { SharedQuadtreeConfig, SharedQuadtreeMemory, QuadtreeBounds, SpatialEntity };\n"],"mappings":"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"}
1
+ {"version":3,"file":"shared-quadtree.js","names":["AllocatedMemory","SharedMap","SharedPool","NODE_POINTER_INDEX","POOL_POINTER_INDEX","MAP_POINTER_INDEX","MAX_LEVELS_INDEX","NODE_COUNT_INDEX","TYPE_INDEX","BOUNDS_INDEX","NODE_LOCK_OFFSET","NODE_HEAD_OFFSET","NODE_SIZE","ITEM_ID","ITEM_X","ITEM_Y","ITEM_W","ITEM_H","ITEM_NODE","ITEM_NEXT","ITEM_FIELDS","FLOAT32_LIMIT","FLOAT32_NULL_INDEX","FLOAT64_NULL_INDEX","DEFAULT_MAX_LEVELS","DEFAULT_MAX_ENTITIES","DEFAULT_COORD_TYPE","SharedQuadtree","memory","config","getPointer","loadRawPointer","nullIndexFor","bounds","maxEntities","getCoordTypeCode","ARRAY_TYPE","poolConfig","getCoordTypeConstructor","getSpatialPoolChunkSize","getByteMultipler","poolAllocateCount","mapAllocateCount","boundsView","nodeBlock","heads","i","poolBlock","storeRawPointer","mapBlock","getSpatialMapCapacity","maxLevels","id","x","y","width","height","node","record","index","byId","entity","entities","unique","existing","mappings","newCount","count","nextIndex","block","offset","off","oldNode","newNode","lock","unlock","first","second","out","filter","start","write","read","maxResults","maxDistance","maxDistanceSquared","candidates","nodeCandidates","queue","MinPriorityQueue","root","distanceSquaredToRect","candidateLimit","candidate","duplicate","addCandidate","halfWidth","halfHeight","childLevel","children","child","distance","nearest","nodeX","nodeY","level","midX","midY","base","nextLevel","east","south","visit","childX","childY","keys","key","makeArrayView","nx","ny","nw","nh","west","north","headIndex","lockIndex","pool","hw","hh","overlapsWest","qx","overlapsEast","qw","overlapsNorth","qy","overlapsSouth","qh","head","next","prev","prevNext","code","getArrayTypeCode","type","typeCode"],"sources":["../../src/spatial/shared-quadtree.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 { lock, unlock } from '../lock/simple-lock';\nimport { getPointer, loadRawPointer, storeRawPointer } from '../utils/pointer';\nimport { ARRAY_TYPE, getArrayTypeCode, getByteMultipler, makeArrayView } from '../utils/array-type';\nimport {\n\tMinPriorityQueue, addCandidate, candidateLimit, distanceSquaredToRect,\n\ttype NeighborCandidate, type NearestNeighbor,\n} from './spatial-neighbors';\nimport { getSpatialMapCapacity, getSpatialPoolChunkSize, type SpatialEntity } from './spatial-entity';\n\n// Fixed-depth region quadtree (\"implicit\" / hierarchical grid) built for concurrent updates from many threads. Dynamic\n// subdivision is deliberately avoided: creating/redistributing/freeing nodes at runtime races with readers mid-traversal\n// (the same hazard MemoryBuffer disables compaction for). Here the tree of nodes is allocated once and never changes\n// shape, so concurrency reduces to per-cell content updates - different cells never contend.\n//\n// Node layout is implicit: node 0 is the root, the four children of node i are 4i+1..4i+4 (NW, NE, SW, SE). Bounds are\n// derived from the root bounds during descent, never stored. Each node owns an intrusive singly-linked bucket: the node\n// record holds a lock and the head item index, and each item record carries the next index. Items live in a SharedPool\n// (stable indices, recycling) and an id->itemIndex SharedMap makes update/remove O(depth) by entity id.\n//\n// Each entity is stored in the single deepest node that fully contains its rect (an item too large or straddling a\n// midline stays at an ancestor). search walks every node whose region intersects the query and returns a broad-phase\n// superset of candidate ids - the same guarantee quadtree-js's retrieve gives, without duplicating straddlers.\n//\n// The whole item record (id, coords, node, bucket link) lives in one pool at the configured precision. Float32 (the\n// default, matching a physics engine that keeps positions in Float32) keeps everything exact as long as entity ids and\n// the pool's index space both stay under 2^24 (~16.7M) - every id, pool index, node index and the NULL sentinel is a\n// Float32-exact integer there. Need more than that many entities (or ids >= 2^24)? Pass type: Float64Array.\n\n// Header (firstBlock) u32 layout\nconst NODE_POINTER_INDEX = 0;\nconst POOL_POINTER_INDEX = 1;\nconst MAP_POINTER_INDEX = 2;\nconst MAX_LEVELS_INDEX = 3;\nconst NODE_COUNT_INDEX = 4;\nconst TYPE_INDEX = 5;\n// bounds view starts at slot 6 (offset 24 bytes is 8-byte aligned, so a Float64 bounds view is valid)\nconst BOUNDS_INDEX = 6;\n\n// Per-node record: [lock, head]\nconst NODE_LOCK_OFFSET = 0;\nconst NODE_HEAD_OFFSET = 1;\nconst NODE_SIZE = 2;\n\n// Item record fields\nconst ITEM_ID = 0;\nconst ITEM_X = 1;\nconst ITEM_Y = 2;\nconst ITEM_W = 3;\nconst ITEM_H = 4;\nconst ITEM_NODE = 5;\nconst ITEM_NEXT = 6;\nconst ITEM_FIELDS = 7;\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\nconst DEFAULT_MAX_LEVELS = 4;\nconst DEFAULT_MAX_ENTITIES = 1_000_000;\nconst DEFAULT_COORD_TYPE = Float32Array;\n\ntype CoordArray = Float32Array | Float64Array;\ntype CoordArrayConstructor = Float32ArrayConstructor | Float64ArrayConstructor;\n\nexport default class SharedQuadtree {\n\tstatic readonly ALLOCATE_COUNT = 14;\n\n\tprivate memory: MemoryHeap;\n\tprivate firstBlock: AllocatedMemory;\n\tprivate pool: SharedPool<CoordArray>;\n\tprivate idMap: SharedMap<number>;\n\n\t// Reused to hand a full item record to pool.push without allocating a fresh array per insert (single writer per id)\n\tprivate insertScratch: Array<number> = [0, 0, 0, 0, 0, 0, 0];\n\n\t// Node block views over one contiguous region: Int32 for the per-node locks, Uint32 for the head indexes.\n\tprivate nodeInts: Int32Array;\n\tprivate nodeHeads: Uint32Array;\n\n\tprivate readonly maxLevels: number;\n\tprivate readonly nodeCount: number;\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// Root bounds, read once (stored at the configured precision, math done in float64)\n\tprivate readonly rootX: number;\n\tprivate readonly rootY: number;\n\tprivate readonly rootWidth: number;\n\tprivate readonly rootHeight: number;\n\n\t// Number of entities currently tracked\n\tget size(): number {\n\t\treturn this.idMap.length;\n\t}\n\n\tget bounds(): QuadtreeBounds {\n\t\treturn { x: this.rootX, y: this.rootY, width: this.rootWidth, height: this.rootHeight };\n\t}\n\n\t// Element type the record is 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?: SharedQuadtreeConfig | SharedQuadtreeMemory) {\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.pool = new SharedPool<CoordArray>(memory, {\n\t\t\t\tfirstBlock: getPointer(loadRawPointer(this.firstBlock.data, 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.maxLevels = this.firstBlock.data[MAX_LEVELS_INDEX];\n\t\t\tthis.nodeCount = this.firstBlock.data[NODE_COUNT_INDEX];\n\t\t\tthis.nullIndex = nullIndexFor(this.coordType);\n\t\t} else {\n\t\t\tlet bounds = config?.bounds ?? { x: 0, y: 0, width: 0, height: 0 };\n\t\t\tlet maxEntities = config?.maxEntities ?? DEFAULT_MAX_ENTITIES;\n\t\t\tthis.maxLevels = config?.maxLevels ?? DEFAULT_MAX_LEVELS;\n\t\t\tthis.nodeCount = SharedQuadtree.getNodeCount(this.maxLevels);\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) {\n\t\t\t\tthrow new Error(`Float32 SharedQuadtree supports at most ${FLOAT32_LIMIT} entities; pass type: Float64Array for more`);\n\t\t\t}\n\n\t\t\t// Inline the pool and id-map firstBlocks right after the header so the whole spine is one contiguous allocation\n\t\t\t// (the pool's own firstBlock already inlines its two stacks). The node block stays separate: it is large and\n\t\t\t// sized independently of these headers.\n\t\t\tlet poolConfig: SharedPoolConfig<CoordArray> = {\n\t\t\t\ttype: getCoordTypeConstructor(this.coordType),\n\t\t\t\tdataLength: ITEM_FIELDS,\n\t\t\t\tmaxLength: maxEntities,\n\t\t\t\tmaxChunkSize: getSpatialPoolChunkSize(maxEntities, ITEM_FIELDS, getByteMultipler(this.coordType), memory.maxAllocationLength),\n\t\t\t};\n\t\t\tlet poolAllocateCount = SharedPool.getAllocateCount(memory, poolConfig);\n\t\t\tlet mapAllocateCount = SharedMap.ALLOCATE_COUNT;\n\t\t\tthis.firstBlock = memory.allocUI32(SharedQuadtree.ALLOCATE_COUNT + poolAllocateCount + mapAllocateCount);\n\t\t\tthis.firstBlock.data[MAX_LEVELS_INDEX] = this.maxLevels;\n\t\t\tthis.firstBlock.data[NODE_COUNT_INDEX] = this.nodeCount;\n\t\t\tthis.firstBlock.data[TYPE_INDEX] = this.coordType;\n\n\t\t\tlet boundsView = this.getBoundsView();\n\t\t\tboundsView[0] = bounds.x;\n\t\t\tboundsView[1] = bounds.y;\n\t\t\tboundsView[2] = bounds.width;\n\t\t\tboundsView[3] = bounds.height;\n\n\t\t\t// Node block must fit one allocation - it is never resized. Every head starts as NULL (calloc leaves them 0,\n\t\t\t// a valid item index, so they must be initialized explicitly).\n\t\t\tlet nodeBlock = memory.allocUI32(this.nodeCount * NODE_SIZE);\n\t\t\tlet heads = new Uint32Array(nodeBlock.data.buffer, nodeBlock.bufferByteOffset, this.nodeCount * NODE_SIZE);\n\t\t\tfor(let i = 0; i < this.nodeCount; i++) {\n\t\t\t\theads[i * NODE_SIZE + NODE_HEAD_OFFSET] = this.nullIndex;\n\t\t\t}\n\t\t\tstoreRawPointer(this.firstBlock.data, NODE_POINTER_INDEX, nodeBlock.pointer);\n\n\t\t\tlet poolBlock = this.firstBlock.getSubAllocation(SharedQuadtree.ALLOCATE_COUNT, poolAllocateCount);\n\t\t\tthis.pool = new SharedPool<CoordArray>(memory, { firstBlock: poolBlock, ...poolConfig });\n\t\t\tstoreRawPointer(this.firstBlock.data, POOL_POINTER_INDEX, poolBlock.pointer);\n\n\t\t\tlet mapBlock = this.firstBlock.getSubAllocation(SharedQuadtree.ALLOCATE_COUNT + poolAllocateCount, 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\tlet boundsView = this.getBoundsView();\n\t\tthis.rootX = boundsView[0];\n\t\tthis.rootY = boundsView[1];\n\t\tthis.rootWidth = boundsView[2];\n\t\tthis.rootHeight = boundsView[3];\n\n\t\tlet nodeBlock = new AllocatedMemory(memory, getPointer(loadRawPointer(this.firstBlock.data, NODE_POINTER_INDEX), memory.positionBits));\n\t\tthis.nodeInts = new Int32Array(nodeBlock.data.buffer, nodeBlock.bufferByteOffset, this.nodeCount * NODE_SIZE);\n\t\tthis.nodeHeads = new Uint32Array(nodeBlock.data.buffer, nodeBlock.bufferByteOffset, this.nodeCount * NODE_SIZE);\n\t}\n\n\t// Total nodes in a complete quadtree of the given depth: sum of 4^level for level 0..maxLevels.\n\tstatic getNodeCount(maxLevels: number): number {\n\t\treturn (Math.pow(4, maxLevels + 1) - 1) / 3;\n\t}\n\n\tget pointer(): number {\n\t\treturn this.firstBlock.pointer;\n\t}\n\n\tget usedMemory(): number {\n\t\tlet nodeBlock = new AllocatedMemory(this.memory, getPointer(loadRawPointer(this.firstBlock.data, NODE_POINTER_INDEX), this.memory.positionBits));\n\t\treturn this.firstBlock.usedMemory + nodeBlock.usedMemory + this.pool.usedMemory + this.idMap.usedMemory;\n\t}\n\n\tinsert(id: number, x: number, y: number, width = 0, height = 0) {\n\t\tif(this.idMap.get(id) !== undefined) {\n\t\t\tthis.update(id, x, y, width, height);\n\t\t\treturn;\n\t\t}\n\n\t\tlet node = this.locate(x, y, width, height);\n\t\tlet record = this.insertScratch;\n\t\trecord[ITEM_ID] = id;\n\t\trecord[ITEM_X] = x;\n\t\trecord[ITEM_Y] = y;\n\t\trecord[ITEM_W] = width;\n\t\trecord[ITEM_H] = height;\n\t\trecord[ITEM_NODE] = node;\n\t\trecord[ITEM_NEXT] = this.nullIndex;\n\t\tlet index = this.pool.push(record);\n\t\tthis.linkIntoNode(node, index);\n\t\tthis.idMap.set(id, index);\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 newCount = existing.reduce<number>((count, index) => count + (index === undefined ? 1 : 0), 0);\n\t\tlet nextIndex = this.pool.reserveContiguous(newCount);\n\t\tfor(let i = 0; i < unique.length; i++) {\n\t\t\tlet entity = unique[i];\n\t\t\tlet width = entity.width ?? 0;\n\t\t\tlet height = entity.height ?? 0;\n\t\t\tif(existing[i] !== undefined) {\n\t\t\t\tthis.update(entity.id, entity.x, entity.y, width, height);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet node = this.locate(entity.x, entity.y, width, height);\n\t\t\tlet index = nextIndex++;\n\t\t\tlet block = this.pool.blockFor(index);\n\t\t\tlet offset = this.pool.blockOffset(index);\n\t\t\tblock[offset + ITEM_ID] = entity.id;\n\t\t\tblock[offset + ITEM_X] = entity.x;\n\t\t\tblock[offset + ITEM_Y] = entity.y;\n\t\t\tblock[offset + ITEM_W] = width;\n\t\t\tblock[offset + ITEM_H] = height;\n\t\t\tblock[offset + ITEM_NODE] = node;\n\t\t\tblock[offset + ITEM_NEXT] = this.nullIndex;\n\t\t\tthis.linkIntoNode(node, index);\n\t\t\tmappings.push([entity.id, index]);\n\t\t}\n\t\tthis.idMap.setAll(mappings);\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 item fields are stable until we lock its node(s).\n\tupdate(id: number, x: number, y: number, width = 0, height = 0) {\n\t\tlet index = this.idMap.get(id);\n\t\tif(index === undefined) {\n\t\t\tthis.insert(id, x, y, width, height);\n\t\t\treturn;\n\t\t}\n\n\t\tlet block = this.pool.blockFor(index);\n\t\tlet off = this.pool.blockOffset(index);\n\t\tlet oldNode = block[off + ITEM_NODE];\n\t\tlet newNode = this.locate(x, y, width, height);\n\n\t\tif(oldNode === newNode) {\n\t\t\t// Same cell: only the coordinates change, no relinking. Lock so a concurrent reader sees a consistent record.\n\t\t\tlock(this.nodeInts, oldNode * NODE_SIZE + NODE_LOCK_OFFSET);\n\t\t\tblock[off + ITEM_X] = x;\n\t\t\tblock[off + ITEM_Y] = y;\n\t\t\tblock[off + ITEM_W] = width;\n\t\t\tblock[off + ITEM_H] = height;\n\t\t\tunlock(this.nodeInts, oldNode * NODE_SIZE + NODE_LOCK_OFFSET);\n\t\t\treturn;\n\t\t}\n\n\t\t// Cross-cell move: hold both node locks, ordered by index to avoid deadlock with another thread moving the reverse way.\n\t\tlet first = Math.min(oldNode, newNode);\n\t\tlet second = Math.max(oldNode, newNode);\n\t\tlock(this.nodeInts, first * NODE_SIZE + NODE_LOCK_OFFSET);\n\t\tlock(this.nodeInts, second * NODE_SIZE + NODE_LOCK_OFFSET);\n\n\t\tthis.unlinkNoLock(oldNode, index);\n\t\tblock[off + ITEM_X] = x;\n\t\tblock[off + ITEM_Y] = y;\n\t\tblock[off + ITEM_W] = width;\n\t\tblock[off + ITEM_H] = height;\n\t\tblock[off + ITEM_NODE] = newNode;\n\t\tthis.linkNoLock(newNode, index);\n\n\t\tunlock(this.nodeInts, second * NODE_SIZE + NODE_LOCK_OFFSET);\n\t\tunlock(this.nodeInts, first * NODE_SIZE + NODE_LOCK_OFFSET);\n\t}\n\n\tremove(id: number): boolean {\n\t\tlet index = this.idMap.get(id);\n\t\tif(index === undefined) {\n\t\t\treturn false;\n\t\t}\n\n\t\tlet node = this.pool.get(index, ITEM_NODE);\n\t\tthis.unlinkFromNode(node, index);\n\t\tthis.pool.deleteIndex(index);\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 in a node whose region intersects the given rect. Result is a superset of true\n\t// overlaps (the caller does the narrow-phase check), matching quadtree-js retrieve.\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\tlet start = out.length;\n\t\tthis.collect(out, 0, this.rootX, this.rootY, this.rootWidth, this.rootHeight, 0, x, y, width, height);\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\tif(maxResults <= 0 || maxDistance < 0 || Number.isNaN(maxResults) || Number.isNaN(maxDistance)) {\n\t\t\treturn [];\n\t\t}\n\n\t\tinterface Node {\n\t\t\tindex: number\n\t\t\tx: number\n\t\t\ty: number\n\t\t\twidth: number\n\t\t\theight: number\n\t\t\tlevel: number\n\t\t}\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\tif(maxResults === 1) {\n\t\t\treturn this.nearestNeighbor(x, y, maxDistanceSquared, filter);\n\t\t}\n\t\tlet candidates: Array<NeighborCandidate> = [];\n\t\tlet nodeCandidates: Array<NeighborCandidate> = [];\n\t\tlet queue = new MinPriorityQueue<Node>();\n\t\tlet root = { index: 0, x: this.rootX, y: this.rootY, width: this.rootWidth, height: this.rootHeight, level: 0 };\n\t\tqueue.push(root, distanceSquaredToRect(x, y, this.rootX, this.rootY, this.rootX + this.rootWidth, this.rootY + this.rootHeight));\n\n\t\twhile(queue.length > 0 && queue.priority <= candidateLimit(candidates, maxResults, maxDistanceSquared)) {\n\t\t\tlet node = queue.pop();\n\t\t\tif(node === undefined) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tnodeCandidates.length = 0;\n\t\t\tthis.collectNeighborNode(nodeCandidates, node.index, x, y, maxDistanceSquared);\n\t\t\tfor(let candidate of nodeCandidates) {\n\t\t\t\tlet duplicate = false;\n\t\t\t\tfor(let existing of candidates) {\n\t\t\t\t\tif(existing.id === candidate.id) {\n\t\t\t\t\t\tduplicate = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif(duplicate) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif(filter === undefined || filter(candidate.id)) {\n\t\t\t\t\taddCandidate(candidates, candidate.id, candidate.distance, maxResults);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(node.level >= this.maxLevels) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet halfWidth = node.width / 2;\n\t\t\tlet halfHeight = node.height / 2;\n\t\t\tlet childLevel = node.level + 1;\n\t\t\tlet children: Array<Node> = [\n\t\t\t\t{ index: node.index * 4 + 1, x: node.x, y: node.y, width: halfWidth, height: halfHeight, level: childLevel },\n\t\t\t\t{ index: node.index * 4 + 2, x: node.x + halfWidth, y: node.y, width: halfWidth, height: halfHeight, level: childLevel },\n\t\t\t\t{ index: node.index * 4 + 3, x: node.x, y: node.y + halfHeight, width: halfWidth, height: halfHeight, level: childLevel },\n\t\t\t\t{ index: node.index * 4 + 4, x: node.x + halfWidth, y: node.y + halfHeight, width: halfWidth, height: halfHeight, level: childLevel },\n\t\t\t];\n\t\t\tfor(let child of children) {\n\t\t\t\tlet distance = distanceSquaredToRect(x, y, child.x, child.y, child.x + child.width, child.y + child.height);\n\t\t\t\tif(distance <= candidateLimit(candidates, maxResults, maxDistanceSquared)) {\n\t\t\t\t\tqueue.push(child, distance);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn candidates.map(candidate => candidate.id);\n\t}\n\n\tprivate nearestNeighbor(\n\t\tx: number, y: number, maxDistanceSquared: number, filter: ((id: number) => boolean) | undefined,\n\t): Array<number> {\n\t\tlet nearest: NearestNeighbor = { id: undefined, distance: maxDistanceSquared };\n\t\tlet nodeCandidates: Array<NeighborCandidate> = [];\n\t\tthis.findNearest(\n\t\t\t0, this.rootX, this.rootY, this.rootWidth, this.rootHeight, 0,\n\t\t\tx, y, nearest, nodeCandidates, filter,\n\t\t);\n\t\treturn nearest.id === undefined ? [] : [nearest.id];\n\t}\n\n\tprivate findNearest(\n\t\tnode: number, nodeX: number, nodeY: number, width: number, height: number, level: number,\n\t\tx: number, y: number, nearest: NearestNeighbor,\n\t\tnodeCandidates: Array<NeighborCandidate>, filter: ((id: number) => boolean) | undefined,\n\t) {\n\t\tif(distanceSquaredToRect(x, y, nodeX, nodeY, nodeX + width, nodeY + height) > nearest.distance) {\n\t\t\treturn;\n\t\t}\n\t\tif(filter === undefined) {\n\t\t\tthis.collectNearestNode(nearest, node, x, y);\n\t\t} else {\n\t\t\tnodeCandidates.length = 0;\n\t\t\tthis.collectNeighborNode(nodeCandidates, node, x, y, nearest.distance);\n\t\t\tfor(let candidate of nodeCandidates) {\n\t\t\t\tif(filter(candidate.id)\n\t\t\t\t\t&& (candidate.distance < nearest.distance\n\t\t\t\t\t\t|| (candidate.distance === nearest.distance && (nearest.id === undefined || candidate.id < nearest.id)))) {\n\t\t\t\t\tnearest.id = candidate.id;\n\t\t\t\t\tnearest.distance = candidate.distance;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif(level >= this.maxLevels) {\n\t\t\treturn;\n\t\t}\n\n\t\tlet halfWidth = width / 2;\n\t\tlet halfHeight = height / 2;\n\t\tlet midX = nodeX + halfWidth;\n\t\tlet midY = nodeY + halfHeight;\n\t\tlet base = node * 4;\n\t\tlet nextLevel = level + 1;\n\t\tlet east = x >= midX;\n\t\tlet south = y >= midY;\n\t\tlet visit = (index: number, childX: number, childY: number) => {\n\t\t\tthis.findNearest(\n\t\t\t\tindex, childX, childY, halfWidth, halfHeight, nextLevel,\n\t\t\t\tx, y, nearest, nodeCandidates, filter,\n\t\t\t);\n\t\t};\n\t\tif(south) {\n\t\t\tif(east) {\n\t\t\t\tvisit(base + 4, midX, midY);\n\t\t\t\tvisit(base + 3, nodeX, midY);\n\t\t\t\tvisit(base + 2, midX, nodeY);\n\t\t\t\tvisit(base + 1, nodeX, nodeY);\n\t\t\t} else {\n\t\t\t\tvisit(base + 3, nodeX, midY);\n\t\t\t\tvisit(base + 4, midX, midY);\n\t\t\t\tvisit(base + 1, nodeX, nodeY);\n\t\t\t\tvisit(base + 2, midX, nodeY);\n\t\t\t}\n\t\t} else if(east) {\n\t\t\tvisit(base + 2, midX, nodeY);\n\t\t\tvisit(base + 1, nodeX, nodeY);\n\t\t\tvisit(base + 4, midX, midY);\n\t\t\tvisit(base + 3, nodeX, midY);\n\t\t} else {\n\t\t\tvisit(base + 1, nodeX, nodeY);\n\t\t\tvisit(base + 2, midX, nodeY);\n\t\t\tvisit(base + 3, nodeX, midY);\n\t\t\tvisit(base + 4, midX, midY);\n\t\t}\n\t}\n\n\tclear() {\n\t\tfor(let i = 0; i < this.nodeCount; i++) {\n\t\t\tthis.nodeHeads[i * NODE_SIZE + NODE_HEAD_OFFSET] = this.nullIndex;\n\t\t}\n\t\tthis.pool.clear();\n\t\t// SharedMap has no clear(); drop every key. Not safe to run concurrently with writers (documented on clear()).\n\t\tlet keys: Array<number> = [];\n\t\tfor(let [key] of this.idMap) {\n\t\t\tkeys.push(key);\n\t\t}\n\t\tfor(let key of keys) {\n\t\t\tthis.idMap.delete(key);\n\t\t}\n\t}\n\n\tfree() {\n\t\tlet nodeBlock = new AllocatedMemory(this.memory, getPointer(loadRawPointer(this.firstBlock.data, NODE_POINTER_INDEX), this.memory.positionBits));\n\t\tnodeBlock.free();\n\t\tthis.pool.free();\n\t\tthis.idMap.free();\n\t\tthis.firstBlock.free();\n\t}\n\n\tgetSharedMemory(): SharedQuadtreeMemory {\n\t\treturn {\n\t\t\tfirstBlock: this.firstBlock.getSharedMemory(),\n\t\t};\n\t}\n\n\tprivate getBoundsView(): CoordArray {\n\t\treturn makeArrayView(\n\t\t\tthis.coordType,\n\t\t\tthis.firstBlock.data.buffer,\n\t\t\tthis.firstBlock.bufferByteOffset + BOUNDS_INDEX * Uint32Array.BYTES_PER_ELEMENT,\n\t\t\t4,\n\t\t) as CoordArray;\n\t}\n\n\t// Deepest node index that fully contains the rect. Descends into the one child that still fully contains it; stops at a\n\t// straddle or at max depth.\n\tprivate locate(x: number, y: number, width: number, height: number): number {\n\t\tlet node = 0;\n\t\tlet nx = this.rootX;\n\t\tlet ny = this.rootY;\n\t\tlet nw = this.rootWidth;\n\t\tlet nh = this.rootHeight;\n\n\t\tfor(let level = 0; level < this.maxLevels; level++) {\n\t\t\tlet midX = nx + nw / 2;\n\t\t\tlet midY = ny + nh / 2;\n\t\t\tlet west = x >= nx && x + width <= midX && (width > 0 || x < midX);\n\t\t\tlet east = x >= midX && x + width <= nx + nw;\n\t\t\tlet north = y >= ny && y + height <= midY && (height > 0 || y < midY);\n\t\t\tlet south = y >= midY && y + height <= ny + nh;\n\n\t\t\tif(north && west) {\n\t\t\t\tnode = node * 4 + 1;\n\t\t\t\tnw /= 2;\n\t\t\t\tnh /= 2;\n\t\t\t} else if(north && east) {\n\t\t\t\tnode = node * 4 + 2;\n\t\t\t\tnx = midX;\n\t\t\t\tnw /= 2;\n\t\t\t\tnh /= 2;\n\t\t\t} else if(south && west) {\n\t\t\t\tnode = node * 4 + 3;\n\t\t\t\tny = midY;\n\t\t\t\tnw /= 2;\n\t\t\t\tnh /= 2;\n\t\t\t} else if(south && east) {\n\t\t\t\tnode = node * 4 + 4;\n\t\t\t\tnx = midX;\n\t\t\t\tny = midY;\n\t\t\t\tnw /= 2;\n\t\t\t\tnh /= 2;\n\t\t\t} else {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn node;\n\t}\n\n\tprivate collect(out: Array<number>, node: number, nx: number, ny: number, nw: number, nh: number, level: number, qx: number, qy: number, qw: number, qh: number) {\n\t\t// Peek the head unlocked: an empty bucket has nothing to read, so the lock (and its atomics) is pure overhead. Most\n\t\t// nodes in a fixed-depth tree are empty, so skipping them dominates query cost. Taking the lock only to walk a\n\t\t// non-empty bucket preserves the snapshot guarantee - a concurrent insert we race is simply not in this snapshot.\n\t\tlet headIndex = node * NODE_SIZE + NODE_HEAD_OFFSET;\n\t\tif(this.nodeHeads[headIndex] !== this.nullIndex) {\n\t\t\tlet lockIndex = node * NODE_SIZE + NODE_LOCK_OFFSET;\n\t\t\tlock(this.nodeInts, lockIndex);\n\t\t\tlet index = this.nodeHeads[headIndex];\n\t\t\tlet pool = this.pool;\n\t\t\twhile(index !== this.nullIndex) {\n\t\t\t\t// One chunk resolve per item covers both fields it reads\n\t\t\t\tlet block = pool.blockFor(index);\n\t\t\t\tlet off = pool.blockOffset(index);\n\t\t\t\tout.push(block[off + ITEM_ID]);\n\t\t\t\tindex = block[off + ITEM_NEXT];\n\t\t\t}\n\t\t\tunlock(this.nodeInts, lockIndex);\n\t\t}\n\n\t\tif(level >= this.maxLevels) {\n\t\t\treturn;\n\t\t}\n\n\t\tlet midX = nx + nw / 2;\n\t\tlet midY = ny + nh / 2;\n\t\tlet hw = nw / 2;\n\t\tlet hh = nh / 2;\n\t\tlet overlapsWest = qx < midX;\n\t\tlet overlapsEast = qx + qw > midX;\n\t\tlet overlapsNorth = qy < midY;\n\t\tlet overlapsSouth = qy + qh > midY;\n\n\t\tif(overlapsNorth && overlapsWest) {\n\t\t\tthis.collect(out, node * 4 + 1, nx, ny, hw, hh, level + 1, qx, qy, qw, qh);\n\t\t}\n\t\tif(overlapsNorth && overlapsEast) {\n\t\t\tthis.collect(out, node * 4 + 2, midX, ny, hw, hh, level + 1, qx, qy, qw, qh);\n\t\t}\n\t\tif(overlapsSouth && overlapsWest) {\n\t\t\tthis.collect(out, node * 4 + 3, nx, midY, hw, hh, level + 1, qx, qy, qw, qh);\n\t\t}\n\t\tif(overlapsSouth && overlapsEast) {\n\t\t\tthis.collect(out, node * 4 + 4, midX, midY, hw, hh, level + 1, qx, qy, qw, qh);\n\t\t}\n\t}\n\n\tprivate collectNeighborNode(\n\t\tout: Array<NeighborCandidate>, node: number, x: number, y: number, maxDistanceSquared: number,\n\t) {\n\t\tlet headIndex = node * NODE_SIZE + NODE_HEAD_OFFSET;\n\t\tif(this.nodeHeads[headIndex] === this.nullIndex) {\n\t\t\treturn;\n\t\t}\n\t\tlet lockIndex = node * NODE_SIZE + NODE_LOCK_OFFSET;\n\t\tlock(this.nodeInts, lockIndex);\n\t\tlet index = this.nodeHeads[headIndex];\n\t\twhile(index !== this.nullIndex) {\n\t\t\tlet block = this.pool.blockFor(index);\n\t\t\tlet off = this.pool.blockOffset(index);\n\t\t\tlet id = block[off + ITEM_ID];\n\t\t\tlet distance = distanceSquaredToRect(x, y, block[off + ITEM_X], block[off + ITEM_Y], block[off + ITEM_X] + block[off + ITEM_W], block[off + ITEM_Y] + block[off + ITEM_H]);\n\t\t\tif(distance <= maxDistanceSquared) {\n\t\t\t\tout.push({ id, distance });\n\t\t\t}\n\t\t\tindex = block[off + ITEM_NEXT];\n\t\t}\n\t\tunlock(this.nodeInts, lockIndex);\n\t}\n\n\tprivate collectNearestNode(nearest: NearestNeighbor, node: number, x: number, y: number) {\n\t\tlet headIndex = node * NODE_SIZE + NODE_HEAD_OFFSET;\n\t\tif(this.nodeHeads[headIndex] === this.nullIndex) {\n\t\t\treturn;\n\t\t}\n\t\tlet lockIndex = node * NODE_SIZE + NODE_LOCK_OFFSET;\n\t\tlock(this.nodeInts, lockIndex);\n\t\tlet index = this.nodeHeads[headIndex];\n\t\twhile(index !== this.nullIndex) {\n\t\t\tlet block = this.pool.blockFor(index);\n\t\t\tlet off = this.pool.blockOffset(index);\n\t\t\tlet id = block[off + ITEM_ID];\n\t\t\tlet distance = distanceSquaredToRect(\n\t\t\t\tx, y, block[off + ITEM_X], block[off + ITEM_Y],\n\t\t\t\tblock[off + ITEM_X] + block[off + ITEM_W], block[off + ITEM_Y] + block[off + ITEM_H],\n\t\t\t);\n\t\t\tif(distance < nearest.distance\n\t\t\t\t|| (distance === nearest.distance && (nearest.id === undefined || id < nearest.id))) {\n\t\t\t\tnearest.id = id;\n\t\t\t\tnearest.distance = distance;\n\t\t\t}\n\t\t\tindex = block[off + ITEM_NEXT];\n\t\t}\n\t\tunlock(this.nodeInts, lockIndex);\n\t}\n\n\tprivate linkIntoNode(node: number, index: number) {\n\t\tlet lockIndex = node * NODE_SIZE + NODE_LOCK_OFFSET;\n\t\tlock(this.nodeInts, lockIndex);\n\t\tthis.linkNoLock(node, index);\n\t\tunlock(this.nodeInts, lockIndex);\n\t}\n\t// Push the item onto the node's bucket head. Caller holds the node lock.\n\tprivate linkNoLock(node: number, index: number) {\n\t\tlet headIndex = node * NODE_SIZE + NODE_HEAD_OFFSET;\n\t\tthis.pool.set(index, ITEM_NEXT, this.nodeHeads[headIndex]);\n\t\tthis.nodeHeads[headIndex] = index;\n\t}\n\n\tprivate unlinkFromNode(node: number, index: number) {\n\t\tlet lockIndex = node * NODE_SIZE + NODE_LOCK_OFFSET;\n\t\tlock(this.nodeInts, lockIndex);\n\t\tthis.unlinkNoLock(node, index);\n\t\tunlock(this.nodeInts, lockIndex);\n\t}\n\t// Splice the item out of the node's bucket. Caller holds the node lock.\n\tprivate unlinkNoLock(node: number, index: number) {\n\t\tlet headIndex = node * NODE_SIZE + NODE_HEAD_OFFSET;\n\t\tlet head = this.nodeHeads[headIndex];\n\t\tlet next = this.pool.get(index, ITEM_NEXT);\n\t\tif(head === index) {\n\t\t\tthis.nodeHeads[headIndex] = next;\n\t\t\treturn;\n\t\t}\n\n\t\tlet prev = head;\n\t\twhile(prev !== this.nullIndex) {\n\t\t\tlet prevNext = this.pool.get(prev, ITEM_NEXT);\n\t\t\tif(prevNext === index) {\n\t\t\t\tthis.pool.set(prev, ITEM_NEXT, next);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tprev = prevNext;\n\t\t}\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('SharedQuadtree 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}\n\ninterface QuadtreeBounds {\n\tx: number\n\ty: number\n\twidth: number\n\theight: number\n}\ninterface SharedQuadtreeConfig {\n\tbounds: QuadtreeBounds\n\tmaxLevels?: number\n\tmaxEntities?: number\n\ttype?: CoordArrayConstructor\n}\ninterface SharedQuadtreeMemory {\n\tfirstBlock: SharedAllocatedMemory\n}\n\nexport type { SharedQuadtreeConfig, SharedQuadtreeMemory, QuadtreeBounds, SpatialEntity };\n"],"mappings":"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"}