@daneren2005/shared-memory-objects 1.5.1 → 1.5.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.
@@ -5,7 +5,7 @@ export default abstract class CachedItemList<T extends Item> implements Iterable
5
5
  item: T;
6
6
  deleteCurrent: () => void;
7
7
  }> {
8
- static readonly ALLOCATE_COUNT = 4;
8
+ static readonly ALLOCATE_COUNT = 7;
9
9
  protected heap: MemoryHeap;
10
10
  protected list: SharedList;
11
11
  protected cache: Map<number, T>;
@@ -1,5 +1,6 @@
1
1
  export declare function readLock(data: Int32Array, index?: number): void;
2
2
  export declare function writeLock(data: Int32Array, index?: number): void;
3
+ export declare function tryWriteLock(data: Int32Array, index?: number): boolean;
3
4
  export declare function readUnlock(data: Int32Array, index?: number): void;
4
5
  export declare function writeUnlock(data: Int32Array, index?: number): void;
5
6
  export declare const READ_WRITE_LOCK_ALLOCATE_COUNT = 2;
@@ -21,13 +21,16 @@ function n(t, n = 0) {
21
21
  Atomics.sub(t, n + 1, 1);
22
22
  }
23
23
  function r(e, t = 0) {
24
- Atomics.sub(e, t, 1) === 1 && Atomics.notify(e, t);
24
+ return Atomics.compareExchange(e, t, 0, -1) === 0;
25
25
  }
26
26
  function i(e, t = 0) {
27
+ Atomics.sub(e, t, 1) === 1 && Atomics.notify(e, t);
28
+ }
29
+ function a(e, t = 0) {
27
30
  Atomics.compareExchange(e, t, -1, 0) !== -1 && console.warn("We are unlocking when it was not write locked!"), Atomics.notify(e, t), Atomics.notify(e, t + 1);
28
31
  }
29
- var a = 2;
32
+ var o = 2;
30
33
  //#endregion
31
- export { a as READ_WRITE_LOCK_ALLOCATE_COUNT, t as readLock, r as readUnlock, n as writeLock, i as writeUnlock };
34
+ export { o as READ_WRITE_LOCK_ALLOCATE_COUNT, t as readLock, i as readUnlock, r as tryWriteLock, n as writeLock, a as writeUnlock };
32
35
 
33
36
  //# sourceMappingURL=read-write-lock.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"read-write-lock.js","names":[],"sources":["../../src/lock/read-write-lock.ts"],"sourcesContent":["const WRITE_LOCKED = -1;\n\nfunction canWait(): boolean {\n\treturn typeof self !== 'undefined' && 'WorkerGlobalScope' in self;\n}\n\nexport function readLock(data: Int32Array, index: number = 0) {\n\t// Pending writers block new readers so a steady stream of queries cannot starve a resize.\n\twhile(true) {\n\t\tlet writers = data[index + 1];\n\t\tif(writers !== 0) {\n\t\t\tif(canWait()) {\n\t\t\t\tAtomics.wait(data, index + 1, writers);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tlet readers = data[index];\n\t\tif(readers !== WRITE_LOCKED && Atomics.compareExchange(data, index, readers, readers + 1) === readers) {\n\t\t\treturn;\n\t\t}\n\t\tif(canWait() && readers === WRITE_LOCKED) {\n\t\t\tAtomics.wait(data, index, WRITE_LOCKED);\n\t\t}\n\t}\n}\nexport function writeLock(data: Int32Array, index: number = 0) {\n\tAtomics.add(data, index + 1, 1);\n\twhile(Atomics.compareExchange(data, index, 0, WRITE_LOCKED) !== 0) {\n\t\tlet state = data[index];\n\t\tif(canWait() && state !== 0) {\n\t\t\tAtomics.wait(data, index, state);\n\t\t}\n\t}\n\tAtomics.sub(data, index + 1, 1);\n}\n\nexport function readUnlock(data: Int32Array, index: number = 0) {\n\tif(Atomics.sub(data, index, 1) === 1) {\n\t\tAtomics.notify(data, index);\n\t}\n}\nexport function writeUnlock(data: Int32Array, index: number = 0) {\n\tif(Atomics.compareExchange(data, index, WRITE_LOCKED, 0) !== WRITE_LOCKED) {\n\t\tconsole.warn('We are unlocking when it was not write locked!');\n\t}\n\tAtomics.notify(data, index);\n\tAtomics.notify(data, index + 1);\n}\n\nexport const READ_WRITE_LOCK_ALLOCATE_COUNT = 2;\n"],"mappings":"AAEA,SAAS,IAAmB;CAC3B,OAAO,OAAO,OAAS,OAAe,uBAAuB;AAC9D;AAEA,SAAgB,EAAS,GAAkB,IAAgB,GAAG;CAE7D,SAAY;EACX,IAAI,IAAU,EAAK,IAAQ;EAC3B,IAAG,MAAY,GAAG;GACjB,AAAG,EAAQ,KACV,QAAQ,KAAK,GAAM,IAAQ,GAAG,CAAO;GAEtC;EACD;EACA,IAAI,IAAU,EAAK;EACnB,IAAG,MAAY,MAAgB,QAAQ,gBAAgB,GAAM,GAAO,GAAS,IAAU,CAAC,MAAM,GAC7F;EAED,AAAG,EAAQ,KAAK,MAAY,MAC3B,QAAQ,KAAK,GAAM,GAAO,EAAY;CAExC;AACD;AACA,SAAgB,EAAU,GAAkB,IAAgB,GAAG;CAE9D,KADA,QAAQ,IAAI,GAAM,IAAQ,GAAG,CAAC,GACxB,QAAQ,gBAAgB,GAAM,GAAO,GAAG,EAAY,MAAM,IAAG;EAClE,IAAI,IAAQ,EAAK;EACjB,AAAG,EAAQ,KAAK,MAAU,KACzB,QAAQ,KAAK,GAAM,GAAO,CAAK;CAEjC;CACA,QAAQ,IAAI,GAAM,IAAQ,GAAG,CAAC;AAC/B;AAEA,SAAgB,EAAW,GAAkB,IAAgB,GAAG;CAC/D,AAAG,QAAQ,IAAI,GAAM,GAAO,CAAC,MAAM,KAClC,QAAQ,OAAO,GAAM,CAAK;AAE5B;AACA,SAAgB,EAAY,GAAkB,IAAgB,GAAG;CAKhE,AAJG,QAAQ,gBAAgB,GAAM,GAAO,IAAc,CAAC,MAAM,MAC5D,QAAQ,KAAK,gDAAgD,GAE9D,QAAQ,OAAO,GAAM,CAAK,GAC1B,QAAQ,OAAO,GAAM,IAAQ,CAAC;AAC/B;AAEA,IAAa,IAAiC"}
1
+ {"version":3,"file":"read-write-lock.js","names":[],"sources":["../../src/lock/read-write-lock.ts"],"sourcesContent":["const WRITE_LOCKED = -1;\n\nfunction canWait(): boolean {\n\treturn typeof self !== 'undefined' && 'WorkerGlobalScope' in self;\n}\n\nexport function readLock(data: Int32Array, index: number = 0) {\n\t// Pending writers block new readers so a steady stream of queries cannot starve a resize.\n\twhile(true) {\n\t\tlet writers = data[index + 1];\n\t\tif(writers !== 0) {\n\t\t\tif(canWait()) {\n\t\t\t\tAtomics.wait(data, index + 1, writers);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tlet readers = data[index];\n\t\tif(readers !== WRITE_LOCKED && Atomics.compareExchange(data, index, readers, readers + 1) === readers) {\n\t\t\treturn;\n\t\t}\n\t\tif(canWait() && readers === WRITE_LOCKED) {\n\t\t\tAtomics.wait(data, index, WRITE_LOCKED);\n\t\t}\n\t}\n}\nexport function writeLock(data: Int32Array, index: number = 0) {\n\tAtomics.add(data, index + 1, 1);\n\twhile(Atomics.compareExchange(data, index, 0, WRITE_LOCKED) !== 0) {\n\t\tlet state = data[index];\n\t\tif(canWait() && state !== 0) {\n\t\t\tAtomics.wait(data, index, state);\n\t\t}\n\t}\n\tAtomics.sub(data, index + 1, 1);\n}\n// Non-blocking write acquire: succeeds only when there is no active reader and no writer. Unlike writeLock it never\n// registers a pending writer, so a failed attempt leaves readers completely untouched - the caller just backs off.\nexport function tryWriteLock(data: Int32Array, index: number = 0): boolean {\n\treturn Atomics.compareExchange(data, index, 0, WRITE_LOCKED) === 0;\n}\n\nexport function readUnlock(data: Int32Array, index: number = 0) {\n\tif(Atomics.sub(data, index, 1) === 1) {\n\t\tAtomics.notify(data, index);\n\t}\n}\nexport function writeUnlock(data: Int32Array, index: number = 0) {\n\tif(Atomics.compareExchange(data, index, WRITE_LOCKED, 0) !== WRITE_LOCKED) {\n\t\tconsole.warn('We are unlocking when it was not write locked!');\n\t}\n\tAtomics.notify(data, index);\n\tAtomics.notify(data, index + 1);\n}\n\nexport const READ_WRITE_LOCK_ALLOCATE_COUNT = 2;\n"],"mappings":"AAEA,SAAS,IAAmB;CAC3B,OAAO,OAAO,OAAS,OAAe,uBAAuB;AAC9D;AAEA,SAAgB,EAAS,GAAkB,IAAgB,GAAG;CAE7D,SAAY;EACX,IAAI,IAAU,EAAK,IAAQ;EAC3B,IAAG,MAAY,GAAG;GACjB,AAAG,EAAQ,KACV,QAAQ,KAAK,GAAM,IAAQ,GAAG,CAAO;GAEtC;EACD;EACA,IAAI,IAAU,EAAK;EACnB,IAAG,MAAY,MAAgB,QAAQ,gBAAgB,GAAM,GAAO,GAAS,IAAU,CAAC,MAAM,GAC7F;EAED,AAAG,EAAQ,KAAK,MAAY,MAC3B,QAAQ,KAAK,GAAM,GAAO,EAAY;CAExC;AACD;AACA,SAAgB,EAAU,GAAkB,IAAgB,GAAG;CAE9D,KADA,QAAQ,IAAI,GAAM,IAAQ,GAAG,CAAC,GACxB,QAAQ,gBAAgB,GAAM,GAAO,GAAG,EAAY,MAAM,IAAG;EAClE,IAAI,IAAQ,EAAK;EACjB,AAAG,EAAQ,KAAK,MAAU,KACzB,QAAQ,KAAK,GAAM,GAAO,CAAK;CAEjC;CACA,QAAQ,IAAI,GAAM,IAAQ,GAAG,CAAC;AAC/B;AAGA,SAAgB,EAAa,GAAkB,IAAgB,GAAY;CAC1E,OAAO,QAAQ,gBAAgB,GAAM,GAAO,GAAG,EAAY,MAAM;AAClE;AAEA,SAAgB,EAAW,GAAkB,IAAgB,GAAG;CAC/D,AAAG,QAAQ,IAAI,GAAM,GAAO,CAAC,MAAM,KAClC,QAAQ,OAAO,GAAM,CAAK;AAE5B;AACA,SAAgB,EAAY,GAAkB,IAAgB,GAAG;CAKhE,AAJG,QAAQ,gBAAgB,GAAM,GAAO,IAAc,CAAC,MAAM,MAC5D,QAAQ,KAAK,gDAAgD,GAE9D,QAAQ,OAAO,GAAM,CAAK,GAC1B,QAAQ,OAAO,GAAM,IAAQ,CAAC;AAC/B;AAEA,IAAa,IAAiC"}
@@ -2,12 +2,13 @@ import type { SharedAllocatedMemory } from './allocated-memory';
2
2
  import type MemoryHeap from './memory-heap';
3
3
  import { type NumericArray, type TypedArrayConstructor } from './utils/array-type';
4
4
  export default class SharedList<T extends NumericArray = Uint32Array> implements Iterable<SharedListIterable<T>> {
5
- static readonly ALLOCATE_COUNT = 4;
5
+ static readonly ALLOCATE_COUNT = 7;
6
6
  private memory;
7
7
  private positionBits;
8
8
  private positionMask;
9
9
  private firstBlock;
10
10
  private uint16Array;
11
+ private lockArray;
11
12
  private cachedType;
12
13
  private cachedDataLength;
13
14
  private cachedByteMultipler;
@@ -27,7 +28,10 @@ export default class SharedList<T extends NumericArray = Uint32Array> implements
27
28
  deleteIndex(deleteIndex: number): boolean;
28
29
  deleteValue(deleteValues: T[number] | Array<T[number]>): boolean;
29
30
  compact(): void;
31
+ private autoReclaim;
32
+ private reclaimTombstones;
30
33
  clear(): void;
34
+ private clearLocked;
31
35
  [Symbol.iterator](): Generator<{
32
36
  data: T;
33
37
  index: number;
@@ -1,14 +1,16 @@
1
1
  import { loadPointer as e, loadRawPointer as t, replaceRawPointer as n, storeRawPointer as r } from "./utils/pointer.js";
2
2
  import i from "./allocated-memory.js";
3
- import { getArrayTypeCode as a, getByteMultipler as o, isBigIntType as s, isFloatType as c, makeArrayView as l } from "./utils/array-type.js";
3
+ import { readLock as a, readUnlock as o, tryWriteLock as s, writeLock as c, writeUnlock as l } from "./lock/read-write-lock.js";
4
+ import { getArrayTypeCode as u, getByteMultipler as d, isBigIntType as f, isFloatType as p, makeArrayView as m } from "./utils/array-type.js";
4
5
  //#region src/shared-list.ts
5
- var u = 4, d = 2, f = d * Uint32Array.BYTES_PER_ELEMENT, p = 0, m = 1, h = 2, g = 0, _ = 1, v = class {
6
- static ALLOCATE_COUNT = u;
6
+ var h = 7, g = 2, _ = g * Uint32Array.BYTES_PER_ELEMENT, v = 0, y = 1, b = 2, x = 4, S = 5, C = 0, w = 1, T = 16, E = class {
7
+ static ALLOCATE_COUNT = h;
7
8
  memory;
8
9
  positionBits;
9
10
  positionMask;
10
11
  firstBlock;
11
12
  uint16Array;
13
+ lockArray;
12
14
  cachedType;
13
15
  cachedDataLength;
14
16
  cachedByteMultipler = 1;
@@ -17,16 +19,16 @@ var u = 4, d = 2, f = d * Uint32Array.BYTES_PER_ELEMENT, p = 0, m = 1, h = 2, g
17
19
  bufferViews = [];
18
20
  onDelete;
19
21
  get length() {
20
- return Atomics.load(this.firstBlock.data, h);
22
+ return Atomics.load(this.firstBlock.data, b);
21
23
  }
22
24
  get usedMemory() {
23
- let t = this.firstBlock.usedMemory, { bufferPosition: n, bufferByteOffset: r } = e(this.firstBlock.data, p, this.positionBits);
25
+ let t = this.firstBlock.usedMemory, { bufferPosition: n, bufferByteOffset: r } = e(this.firstBlock.data, v, this.positionBits);
24
26
  for (; r;) {
25
27
  let a = new i(this.memory, {
26
28
  bufferPosition: n,
27
29
  bufferByteOffset: r
28
30
  });
29
- t += a.usedMemory, {bufferPosition: n, bufferByteOffset: r} = e(a.data, g, this.positionBits);
31
+ t += a.usedMemory, {bufferPosition: n, bufferByteOffset: r} = e(a.data, C, this.positionBits);
30
32
  }
31
33
  return t;
32
34
  }
@@ -43,38 +45,46 @@ var u = 4, d = 2, f = d * Uint32Array.BYTES_PER_ELEMENT, p = 0, m = 1, h = 2, g
43
45
  Atomics.store(this.uint16Array, 1, e);
44
46
  }
45
47
  constructor(e, t) {
46
- this.memory = e, this.positionBits = e.positionBits, this.positionMask = (1 << this.positionBits) - 1, t && "firstBlock" in t ? (this.firstBlock = new i(e, t.firstBlock), this.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + 3 * Uint32Array.BYTES_PER_ELEMENT, 2)) : (this.firstBlock = t && t.initWithBlock ? new i(e, t.initWithBlock) : e.allocUI32(u), this.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + 3 * Uint32Array.BYTES_PER_ELEMENT, 2), r(this.firstBlock.data, m, this.firstBlock.pointer), this.type = a(t?.type ?? Uint32Array), this.dataLength = t?.dataLength ?? 1), this.cachedType = Atomics.load(this.uint16Array, 0), this.cachedDataLength = Math.max(1, Atomics.load(this.uint16Array, 1)), this.cachedByteMultipler = o(this.cachedType), this.cachedIsBigInt = s(this.cachedType), this.cachedIsFloat = c(this.cachedType);
48
+ this.memory = e, this.positionBits = e.positionBits, this.positionMask = (1 << this.positionBits) - 1, t && "firstBlock" in t ? (this.firstBlock = new i(e, t.firstBlock), this.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + 3 * Uint32Array.BYTES_PER_ELEMENT, 2)) : (this.firstBlock = t && t.initWithBlock ? new i(e, t.initWithBlock) : e.allocUI32(h), this.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + 3 * Uint32Array.BYTES_PER_ELEMENT, 2), r(this.firstBlock.data, y, this.firstBlock.pointer), this.type = u(t?.type ?? Uint32Array), this.dataLength = t?.dataLength ?? 1), this.lockArray = new Int32Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + S * Uint32Array.BYTES_PER_ELEMENT, 2), this.cachedType = Atomics.load(this.uint16Array, 0), this.cachedDataLength = Math.max(1, Atomics.load(this.uint16Array, 1)), this.cachedByteMultipler = d(this.cachedType), this.cachedIsBigInt = f(this.cachedType), this.cachedIsFloat = p(this.cachedType);
47
49
  }
48
50
  insert(e) {
49
51
  Array.isArray(e) || (e = [e]);
50
52
  let r = this.cachedDataLength;
51
53
  if (e.length > r) throw Error(`Can't insert ${e.length} array into shared list of ${r} dataLength`);
52
- let i = this.memory.allocUI32(d + r * this.cachedByteMultipler), a = this.getDataBlock(i.data.buffer, i.data.byteOffset), o = i.pointer, s = a;
54
+ let i = this.memory.allocUI32(g + r * this.cachedByteMultipler), a = this.getDataBlock(i.data.buffer, i.data.byteOffset), o = i.pointer, s = a;
53
55
  for (let t = 0; t < e.length; t++) this.cachedIsFloat ? s[t] = e[t] : (this.cachedIsBigInt, Atomics.store(a, t, e[t]));
54
56
  let c = this.positionBits, l = this.positionMask;
55
57
  for (;;) {
56
- let e = t(this.firstBlock.data, m), r = this.getBufferView(e & l);
58
+ let e = t(this.firstBlock.data, y), r = this.getBufferView(e & l);
57
59
  if (!r) continue;
58
- let i = (e >>> c >>> 2) + g, a = Atomics.load(r, i);
59
- if (e === t(this.firstBlock.data, m)) {
60
+ let i = (e >>> c >>> 2) + C, a = Atomics.load(r, i);
61
+ if (e === t(this.firstBlock.data, y)) {
60
62
  if (a === 0) {
61
63
  if (Atomics.compareExchange(r, i, 0, o) === 0) {
62
- n(this.firstBlock.data, m, o, e);
64
+ n(this.firstBlock.data, y, o, e);
63
65
  break;
64
66
  }
65
- } else n(this.firstBlock.data, m, a, e);
67
+ } else n(this.firstBlock.data, y, a, e);
66
68
  }
67
69
  }
68
- Atomics.add(this.firstBlock.data, h, 1);
70
+ Atomics.add(this.firstBlock.data, b, 1);
69
71
  }
70
72
  deleteMatch(e) {
71
- for (let { data: t, index: n, deleteCurrent: r } of this) if (e(t, n) && r()) return !0;
72
- return !1;
73
+ let t = !1;
74
+ for (let { data: n, index: r, deleteCurrent: i } of this) if (e(n, r) && i()) {
75
+ t = !0;
76
+ break;
77
+ }
78
+ return this.autoReclaim(), t;
73
79
  }
74
80
  deleteIndex(e) {
75
81
  if (e >= this.length || e < 0) return !1;
76
- for (let t of this) if (t.index === e && t.deleteCurrent()) return !0;
77
- return !1;
82
+ let t = !1;
83
+ for (let n of this) if (n.index === e && n.deleteCurrent()) {
84
+ t = !0;
85
+ break;
86
+ }
87
+ return this.autoReclaim(), t;
78
88
  }
79
89
  deleteValue(e) {
80
90
  return Array.isArray(e) ? this.deleteMatch((t) => {
@@ -84,45 +94,79 @@ var u = 4, d = 2, f = d * Uint32Array.BYTES_PER_ELEMENT, p = 0, m = 1, h = 2, g
84
94
  }) : this.deleteMatch((t) => t[0] === e);
85
95
  }
86
96
  compact() {
87
- let e = this.firstBlock.pointer, n = this.firstBlock.data, i = g, a = this.positionBits, o = this.positionMask, s = t(n, i);
88
- for (; s;) {
89
- let t = s & o, c = this.memory.buffers[t];
90
- if (!c) break;
91
- let l = this.getBufferView(t), u = s >>> a, d = u >>> 2, f = Atomics.load(l, d + g);
92
- Atomics.load(l, d + _) ? (r(n, i, f), c.free(u)) : (e = s, n = l, i = d + g), s = f;
97
+ c(this.lockArray);
98
+ try {
99
+ this.reclaimTombstones(!0);
100
+ } finally {
101
+ l(this.lockArray);
93
102
  }
94
- r(this.firstBlock.data, m, e);
103
+ }
104
+ autoReclaim() {
105
+ let e = Atomics.load(this.firstBlock.data, x);
106
+ if (!(e < T || e < this.length) && s(this.lockArray)) try {
107
+ this.reclaimTombstones(!1);
108
+ } finally {
109
+ l(this.lockArray);
110
+ }
111
+ }
112
+ reclaimTombstones(e) {
113
+ let i = this.firstBlock.pointer, a = this.firstBlock.data, o = C, s = this.positionBits, c = this.positionMask, l = 0, u = t(a, o);
114
+ for (; u;) {
115
+ let t = u & c, d = this.memory.buffers[t];
116
+ if (!d) break;
117
+ let f = this.getBufferView(t), p = u >>> s, m = p >>> 2, h = Atomics.load(f, m + C);
118
+ if (Atomics.load(f, m + w)) {
119
+ if (h === 0 && !e) break;
120
+ r(a, o, h), n(this.firstBlock.data, y, i, u), d.free(p), l++;
121
+ } else i = u, a = f, o = m + C;
122
+ u = h;
123
+ }
124
+ return e && r(this.firstBlock.data, y, i), l && Atomics.sub(this.firstBlock.data, x, l), l;
95
125
  }
96
126
  clear() {
127
+ c(this.lockArray);
128
+ try {
129
+ this.clearLocked();
130
+ } finally {
131
+ l(this.lockArray);
132
+ }
133
+ }
134
+ clearLocked() {
97
135
  let e, i = !1;
98
136
  for (; !i;) {
99
- if (e = t(this.firstBlock.data, p), !e) return;
100
- i = n(this.firstBlock.data, p, 0, e);
137
+ if (e = t(this.firstBlock.data, v), !e) return;
138
+ i = n(this.firstBlock.data, v, 0, e);
101
139
  }
102
140
  if (!e) return;
103
- r(this.firstBlock.data, m, this.firstBlock.pointer);
141
+ r(this.firstBlock.data, y, this.firstBlock.pointer);
104
142
  let a = 0, o = this.positionBits, s = this.positionMask, c = e;
105
143
  for (; c;) {
106
144
  let e = c & s, t = this.memory.buffers[e];
107
145
  if (!t) break;
108
146
  let n = this.getBufferView(e), r = c >>> o, i = r >>> 2;
109
- c = Atomics.load(n, i + g), Atomics.load(n, i + _) || (a++, this.onDelete && this.onDelete(this.getDataBlock(n.buffer, r))), t.free(r);
147
+ c = Atomics.load(n, i + C), Atomics.load(n, i + w) || (a++, this.onDelete && this.onDelete(this.getDataBlock(n.buffer, r))), t.free(r);
110
148
  }
111
- Atomics.sub(this.firstBlock.data, h, a);
149
+ Atomics.sub(this.firstBlock.data, b, a), Atomics.store(this.firstBlock.data, x, 0);
112
150
  }
113
151
  *[Symbol.iterator]() {
114
- let e = 0, n = this.positionBits, r = this.positionMask, i = t(this.firstBlock.data, p);
115
- for (; i;) {
116
- let t = i & r, a = this.getBufferView(t);
117
- if (!a) return;
118
- let o = i >>> n, s = o >>> 2;
119
- if (i = Atomics.load(a, s + g), Atomics.load(a, s + _)) continue;
120
- let c = a.buffer, l = s + _;
121
- yield {
122
- data: this.getDataBlock(c, o),
123
- index: e,
124
- deleteCurrent: () => Atomics.compareExchange(a, l, 0, 1) === 0 && (Atomics.sub(this.firstBlock.data, h, 1), this.onDelete && this.onDelete(this.getDataBlock(c, o)), !0)
125
- }, e++;
152
+ let e = 0, n = this.positionBits, r = this.positionMask;
153
+ a(this.lockArray);
154
+ try {
155
+ let i = t(this.firstBlock.data, v);
156
+ for (; i;) {
157
+ let t = i & r, a = this.getBufferView(t);
158
+ if (!a) return;
159
+ let o = i >>> n, s = o >>> 2;
160
+ if (i = Atomics.load(a, s + C), Atomics.load(a, s + w)) continue;
161
+ let c = a.buffer, l = s + w;
162
+ yield {
163
+ data: this.getDataBlock(c, o),
164
+ index: e,
165
+ deleteCurrent: () => Atomics.compareExchange(a, l, 0, 1) === 0 && (Atomics.sub(this.firstBlock.data, b, 1), Atomics.add(this.firstBlock.data, x, 1), this.onDelete && this.onDelete(this.getDataBlock(c, o)), !0)
166
+ }, e++;
167
+ }
168
+ } finally {
169
+ o(this.lockArray);
126
170
  }
127
171
  }
128
172
  forEach(e) {
@@ -138,22 +182,22 @@ var u = 4, d = 2, f = d * Uint32Array.BYTES_PER_ELEMENT, p = 0, m = 1, h = 2, g
138
182
  if (n) return t = new Uint32Array(n.buf), this.bufferViews[e] = t, t;
139
183
  }
140
184
  getDataBlock(e, t) {
141
- let n = t + f;
142
- return l(this.cachedType, e, n, this.cachedDataLength);
185
+ let n = t + _;
186
+ return m(this.cachedType, e, n, this.cachedDataLength);
143
187
  }
144
188
  free() {
145
- let { bufferPosition: t, bufferByteOffset: n } = e(this.firstBlock.data, p, this.positionBits);
189
+ let { bufferPosition: t, bufferByteOffset: n } = e(this.firstBlock.data, v, this.positionBits);
146
190
  for (; n;) {
147
191
  let r = new i(this.memory, {
148
192
  bufferPosition: t,
149
193
  bufferByteOffset: n
150
194
  });
151
- ({bufferPosition: t, bufferByteOffset: n} = e(r.data, g, this.positionBits)), this.onDelete && !Atomics.load(r.data, _) && this.onDelete(this.getDataBlock(r.data.buffer, r.data.byteOffset)), r.free();
195
+ ({bufferPosition: t, bufferByteOffset: n} = e(r.data, C, this.positionBits)), this.onDelete && !Atomics.load(r.data, w) && this.onDelete(this.getDataBlock(r.data.buffer, r.data.byteOffset)), r.free();
152
196
  }
153
197
  this.firstBlock.free();
154
198
  }
155
199
  };
156
200
  //#endregion
157
- export { v as default };
201
+ export { E as default };
158
202
 
159
203
  //# sourceMappingURL=shared-list.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"shared-list.js","names":[],"sources":["../src/shared-list.ts"],"sourcesContent":["import type { SharedAllocatedMemory } from './allocated-memory';\nimport AllocatedMemory from './allocated-memory';\nimport type MemoryHeap from './memory-heap';\nimport { loadPointer, loadRawPointer, replaceRawPointer, storeRawPointer } from './utils/pointer';\nimport { getArrayTypeCode, getByteMultipler, isBigIntType, isFloatType, makeArrayView, type NumericArray, type NumericArrayIO, type TypedArrayConstructor } from './utils/array-type';\n\n// Lock-free singly-linked list. firstBlock doubles as an immortal sentinel node whose next pointer (index 0) is the head.\n// insert appends with a Michael-Scott CAS enqueue; delete only tombstones a node (never unlinks/frees). Memory is\n// reclaimed exclusively by the quiescent compact()/clear()/free() passes. That deferral is what makes the CAS loops\n// safe without hazard pointers: a live node's address is never handed back to the allocator, so no append can ever\n// observe a recycled address (ABA) while another thread is mid-traversal.\nconst FIRST_BLOCK_RECORD_KEEPING_COUNT = 4;\nconst DATA_BLOCK_RECORD_KEEPING_COUNT = 2;\nconst DATA_BLOCK_BYTE_OFFSET = DATA_BLOCK_RECORD_KEEPING_COUNT * Uint32Array.BYTES_PER_ELEMENT;\nconst HEAD_INDEX = 0;\nconst TAIL_INDEX = 1;\nconst LENGTH_INDEX = 2;\nconst NODE_NEXT_INDEX = 0;\nconst NODE_DELETED_INDEX = 1;\n// A pointer packs the buffer position in its low `positionBits` bits and the byte offset in the high bits (see\n// utils/pointer). The split is per-heap, so it is read from the heap and cached rather than a module constant.\nexport default class SharedList<T extends NumericArray = Uint32Array> implements Iterable<SharedListIterable<T>> {\n\tstatic readonly ALLOCATE_COUNT = FIRST_BLOCK_RECORD_KEEPING_COUNT;\n\n\tprivate memory: MemoryHeap;\n\t// Per-heap pointer split, cached from the heap. Hoisted into locals inside the hot loops below.\n\tprivate positionBits: number;\n\tprivate positionMask: number;\n\t/* First block (also the sentinel node - index 0 is its next pointer = list head)\n\t\t32 index 0\n\t\tuint16 0 - head buffer position | sentinel.next\n\t\tuint16 1 - head buffer index\n\t\t32 index 1\n\t\tuint16 2 - tail hint buffer position\n\t\tuint16 3 - tail hint buffer index\n\t\t32 index 2\n\t\tuint32 4 - length\n\t\t32 index 3\n\t\tuint16 6 - type\n\t\tuint16 7 - data length (defaults to 1 number per data)\n\t*/\n\t/* Other blocks\n\t\t32 index 0 - next buffer pointer\n\t\t32 index 1 - deleted flag (0 = live, 1 = tombstoned)\n\t\t32 index 2 => data\n\t*/\n\tprivate firstBlock: AllocatedMemory;\n\tprivate uint16Array: Uint16Array;\n\t// type and dataLength are written once at init and never change, so cache them instead of atomic-loading per node\n\tprivate cachedType: number;\n\tprivate cachedDataLength: number;\n\t// u32 units per element: 1 for 32-bit types, 2 for 64-bit (float64/int64/uint64). Node data blocks scale by this.\n\tprivate cachedByteMultipler = 1;\n\t// Whether the value type is a BigInt view (int64/uint64), so insert stores bigint via Atomics; cached to keep the\n\t// per-insert branch off an instanceof check\n\tprivate cachedIsBigInt = false;\n\t// Float views can't be published with Atomics, so insert falls back to a plain store for them\n\tprivate cachedIsFloat = false;\n\t// One full-buffer Uint32Array per buffer position, so traversal indexes shared memory directly instead of\n\t// allocating a fresh view (and a getPointer object) for every node it touches\n\tprivate bufferViews: Array<Uint32Array> = [];\n\tonDelete?: (data: T) => void;\n\n\tget length(): number {\n\t\treturn Atomics.load(this.firstBlock.data, LENGTH_INDEX);\n\t}\n\n\t// Own internal memory plus every allocated node still linked in the chain (tombstoned nodes included, since they stay\n\t// allocated until a compact()/clear() reclaims them).\n\tget usedMemory(): number {\n\t\tlet total = this.firstBlock.usedMemory;\n\t\tlet { bufferPosition, bufferByteOffset } = loadPointer(this.firstBlock.data, HEAD_INDEX, this.positionBits);\n\t\twhile(bufferByteOffset) {\n\t\t\tlet node = new AllocatedMemory(this.memory, { bufferPosition, bufferByteOffset });\n\t\t\ttotal += node.usedMemory;\n\t\t\t({ bufferPosition, bufferByteOffset } = loadPointer(node.data, NODE_NEXT_INDEX, this.positionBits));\n\t\t}\n\t\treturn total;\n\t}\n\n\tget type(): number {\n\t\treturn this.cachedType;\n\t}\n\tprivate set type(value: number) {\n\t\tAtomics.store(this.uint16Array, 0, value);\n\t}\n\tget dataLength(): number {\n\t\treturn this.cachedDataLength;\n\t}\n\tprivate set dataLength(value: number) {\n\t\tAtomics.store(this.uint16Array, 1, value);\n\t}\n\n\tconstructor(memory: MemoryHeap, config?: SharedListConfig<T> | SharedListMemory) {\n\t\tthis.memory = memory;\n\t\tthis.positionBits = memory.positionBits;\n\t\tthis.positionMask = (1 << this.positionBits) - 1;\n\n\t\tif(config && 'firstBlock' in config) {\n\t\t\t// TODO: How to handle referencing memory we don't have access to yet because buffer not synced from worker?\n\t\t\tthis.firstBlock = new AllocatedMemory(memory, config.firstBlock);\n\t\t\tthis.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + (LENGTH_INDEX + 1) * Uint32Array.BYTES_PER_ELEMENT, 2);\n\t\t} else {\n\t\t\tif(config && config.initWithBlock) {\n\t\t\t\tthis.firstBlock = new AllocatedMemory(memory, config.initWithBlock);\n\t\t\t} else {\n\t\t\t\tthis.firstBlock = memory.allocUI32(FIRST_BLOCK_RECORD_KEEPING_COUNT);\n\t\t\t}\n\t\t\tthis.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + (LENGTH_INDEX + 1) * Uint32Array.BYTES_PER_ELEMENT, 2);\n\n\t\t\t// Empty list: the tail hint points at the sentinel (firstBlock) so insert never special-cases the first node\n\t\t\tstoreRawPointer(this.firstBlock.data, TAIL_INDEX, this.firstBlock.pointer);\n\n\t\t\tthis.type = getArrayTypeCode(config?.type ?? Uint32Array);\n\t\t\tthis.dataLength = config?.dataLength ?? 1;\n\t\t}\n\n\t\tthis.cachedType = Atomics.load(this.uint16Array, 0);\n\t\t// dataLength defaults to at least one even when initialized from raw memory that was never explicitly set\n\t\tthis.cachedDataLength = Math.max(1, Atomics.load(this.uint16Array, 1));\n\t\tthis.cachedByteMultipler = getByteMultipler(this.cachedType);\n\t\tthis.cachedIsBigInt = isBigIntType(this.cachedType);\n\t\tthis.cachedIsFloat = isFloatType(this.cachedType);\n\t}\n\n\tinsert(values: T[number] | Array<T[number]>) {\n\t\tif(!Array.isArray(values)) {\n\t\t\tvalues = [values];\n\t\t}\n\n\t\tlet dataLength = this.cachedDataLength;\n\t\tif(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\t\tlet newBlock = this.memory.allocUI32(DATA_BLOCK_RECORD_KEEPING_COUNT + dataLength * this.cachedByteMultipler);\n\t\tlet newData = this.getDataBlock(newBlock.data.buffer, newBlock.data.byteOffset);\n\t\tlet newBlockPointer = newBlock.pointer;\n\n\t\tlet io = newData as NumericArrayIO;\n\t\tfor(let i = 0; i < values.length; i++) {\n\t\t\tif(this.cachedIsFloat) {\n\t\t\t\t// Float views don't support Atomics; the enqueue CAS below still provides the cross-thread publish\n\t\t\t\tio[i] = values[i];\n\t\t\t} else if(this.cachedIsBigInt) {\n\t\t\t\tAtomics.store(newData as BigInt64Array, i, values[i] as bigint);\n\t\t\t} else {\n\t\t\t\tAtomics.store(newData as Int32Array, i, values[i] as number);\n\t\t\t}\n\t\t}\n\n\t\t// Michael-Scott enqueue: link the node onto tail.next BEFORE swinging the tail hint, so a committed node is never\n\t\t// invisible to a concurrent traversal. The tail hint may lag by one node; enqueuers help advance it.\n\t\tlet positionBits = this.positionBits;\n\t\tlet positionMask = this.positionMask;\n\t\t// eslint-disable-next-line no-constant-condition\n\t\twhile(true) {\n\t\t\tlet tailPointer = loadRawPointer(this.firstBlock.data, TAIL_INDEX);\n\t\t\tlet view = this.getBufferView(tailPointer & positionMask);\n\t\t\t// Tail node lives in a buffer this thread hasn't synced yet - retry until it appears\n\t\t\tif(!view) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlet tailNextIndex = ((tailPointer >>> positionBits) >>> 2) + NODE_NEXT_INDEX;\n\t\t\tlet nextPointer = Atomics.load(view, tailNextIndex);\n\n\t\t\t// Re-check the hint hasn't moved out from under us before acting on what we read\n\t\t\tif(tailPointer !== loadRawPointer(this.firstBlock.data, TAIL_INDEX)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif(nextPointer === 0) {\n\t\t\t\tif(Atomics.compareExchange(view, tailNextIndex, 0, newBlockPointer) === 0) {\n\t\t\t\t\t// Linked. Try to swing the hint forward - failure is harmless, the next enqueuer will help.\n\t\t\t\t\treplaceRawPointer(this.firstBlock.data, TAIL_INDEX, newBlockPointer, tailPointer);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Tail hint is lagging behind a linked node - help it catch up, then retry\n\t\t\t\treplaceRawPointer(this.firstBlock.data, TAIL_INDEX, nextPointer, tailPointer);\n\t\t\t}\n\t\t}\n\n\t\tAtomics.add(this.firstBlock.data, LENGTH_INDEX, 1);\n\t}\n\n\tdeleteMatch(callback: (values: T, index: number) => boolean): boolean {\n\t\tfor(let { data, index, deleteCurrent } of this) {\n\t\t\tif(callback(data, index)) {\n\t\t\t\tif(deleteCurrent()) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\tdeleteIndex(deleteIndex: number): boolean {\n\t\tif(deleteIndex >= this.length || deleteIndex < 0) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Compare on index only so the payload view is never materialized for an index-based delete\n\t\tfor(let item of this) {\n\t\t\tif(item.index === deleteIndex) {\n\t\t\t\tif(item.deleteCurrent()) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\tdeleteValue(deleteValues: T[number] | Array<T[number]>) {\n\t\tif(!Array.isArray(deleteValues)) {\n\t\t\treturn this.deleteMatch(values => values[0] === deleteValues);\n\t\t} else {\n\t\t\treturn this.deleteMatch(values => {\n\t\t\t\tif(values.length !== deleteValues.length) {\n\t\t\t\t\treturn false;\n\t\t\t\t} else {\n\t\t\t\t\tfor(let i = 0; i < values.length; i++) {\n\t\t\t\t\t\tif(values[i] !== deleteValues[i]) {\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\n\t// Reclaims tombstoned nodes: physically unlinks and frees them. Not safe to run concurrently with insert/delete.\n\tcompact() {\n\t\tlet prevPointer = this.firstBlock.pointer;\n\t\tlet prevView = this.firstBlock.data;\n\t\tlet prevNextIndex = NODE_NEXT_INDEX;\n\t\tlet positionBits = this.positionBits;\n\t\tlet positionMask = this.positionMask;\n\t\tlet nextPointer = loadRawPointer(prevView, prevNextIndex);\n\t\twhile(nextPointer) {\n\t\t\tlet position = nextPointer & positionMask;\n\t\t\tlet memPool = this.memory.buffers[position];\n\t\t\tif(!memPool) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tlet view = this.getBufferView(position)!;\n\t\t\tlet byteOffset = nextPointer >>> positionBits;\n\t\t\tlet nodeIndex = byteOffset >>> 2;\n\t\t\tlet followingPointer = Atomics.load(view, nodeIndex + NODE_NEXT_INDEX);\n\n\t\t\tif(Atomics.load(view, nodeIndex + NODE_DELETED_INDEX)) {\n\t\t\t\t// Skip over the tombstone and free it. onDelete already fired when the node was logically deleted.\n\t\t\t\tstoreRawPointer(prevView, prevNextIndex, followingPointer);\n\t\t\t\tmemPool.free(byteOffset);\n\t\t\t} else {\n\t\t\t\tprevPointer = nextPointer;\n\t\t\t\tprevView = view;\n\t\t\t\tprevNextIndex = nodeIndex + NODE_NEXT_INDEX;\n\t\t\t}\n\n\t\t\tnextPointer = followingPointer;\n\t\t}\n\n\t\t// The last surviving node (or the sentinel when everything is gone) is the new tail\n\t\tstoreRawPointer(this.firstBlock.data, TAIL_INDEX, prevPointer);\n\t}\n\n\tclear() {\n\t\t// Detach the whole chain, then free it. Not safe to run concurrently with insert/delete.\n\t\tlet firstBlockPointer;\n\t\tlet updateWorked = false;\n\t\twhile(!updateWorked) {\n\t\t\tfirstBlockPointer = loadRawPointer(this.firstBlock.data, HEAD_INDEX);\n\t\t\t// Already cleared\n\t\t\tif(!firstBlockPointer) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tupdateWorked = replaceRawPointer(this.firstBlock.data, HEAD_INDEX, 0, firstBlockPointer);\n\t\t}\n\t\t// Shouldn't be possible to hit: making Typescript happy\n\t\tif(!firstBlockPointer) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Reset the tail hint back to the sentinel\n\t\tstoreRawPointer(this.firstBlock.data, TAIL_INDEX, this.firstBlock.pointer);\n\n\t\tlet liveItems = 0;\n\t\tlet positionBits = this.positionBits;\n\t\tlet positionMask = this.positionMask;\n\t\tlet nextBlockPointer = firstBlockPointer;\n\t\twhile(nextBlockPointer) {\n\t\t\tlet position = nextBlockPointer & positionMask;\n\t\t\tlet memPool = this.memory.buffers[position];\n\t\t\t// Short circuit iterations if we can't access memory\n\t\t\tif(!memPool) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tlet view = this.getBufferView(position)!;\n\t\t\tlet byteOffset = nextBlockPointer >>> positionBits;\n\t\t\tlet nodeIndex = byteOffset >>> 2;\n\t\t\tnextBlockPointer = Atomics.load(view, nodeIndex + NODE_NEXT_INDEX);\n\n\t\t\tif(!Atomics.load(view, nodeIndex + NODE_DELETED_INDEX)) {\n\t\t\t\tliveItems++;\n\t\t\t\tif(this.onDelete) {\n\t\t\t\t\tthis.onDelete(this.getDataBlock(view.buffer, byteOffset));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tmemPool.free(byteOffset);\n\t\t}\n\n\t\t// Only subtract the live nodes we detached so a concurrent insert's increment stays accurate\n\t\tAtomics.sub(this.firstBlock.data, LENGTH_INDEX, liveItems);\n\t}\n\n\t*[Symbol.iterator]() {\n\t\tlet currentIndex = 0;\n\t\tlet positionBits = this.positionBits;\n\t\tlet positionMask = this.positionMask;\n\t\tlet nextPointer = loadRawPointer(this.firstBlock.data, HEAD_INDEX);\n\t\twhile(nextPointer) {\n\t\t\tlet position = nextPointer & positionMask;\n\t\t\tlet view = this.getBufferView(position);\n\t\t\t// Short circuit iterations if we can't access memory\n\t\t\tif(!view) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlet byteOffset = nextPointer >>> positionBits;\n\t\t\tlet nodeIndex = byteOffset >>> 2;\n\t\t\tnextPointer = Atomics.load(view, nodeIndex + NODE_NEXT_INDEX);\n\n\t\t\t// Skip tombstoned nodes - they stay linked until a quiescent compact()/clear()\n\t\t\tif(Atomics.load(view, nodeIndex + NODE_DELETED_INDEX)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet buffer = view.buffer;\n\t\t\tlet deletedIndex = nodeIndex + NODE_DELETED_INDEX;\n\t\t\t// Plain object with an eager data view: V8 keeps this monomorphic and fast. A lazy `get data()` accessor is far\n\t\t\t// slower to allocate than the payload view it would save, so pay for the view up front.\n\t\t\tyield {\n\t\t\t\tdata: this.getDataBlock(buffer, byteOffset),\n\t\t\t\tindex: currentIndex,\n\t\t\t\t// Logical delete: tombstone the node and drop length. Returns false if another thread beat us to it.\n\t\t\t\tdeleteCurrent: (): boolean => {\n\t\t\t\t\tif(Atomics.compareExchange(view, deletedIndex, 0, 1) === 0) {\n\t\t\t\t\t\tAtomics.sub(this.firstBlock.data, LENGTH_INDEX, 1);\n\t\t\t\t\t\tif(this.onDelete) {\n\t\t\t\t\t\t\tthis.onDelete(this.getDataBlock(buffer, byteOffset));\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn false;\n\t\t\t\t},\n\t\t\t};\n\n\t\t\tcurrentIndex++;\n\t\t}\n\t}\n\n\tforEach(callback: (data: T) => void) {\n\t\tfor(let value of this) {\n\t\t\tcallback(value.data);\n\t\t}\n\t}\n\n\tgetSharedMemory(): SharedListMemory {\n\t\treturn {\n\t\t\tfirstBlock: this.firstBlock.getSharedMemory(),\n\t\t};\n\t}\n\n\tprivate getBufferView(position: number): Uint32Array | undefined {\n\t\tlet view = this.bufferViews[position];\n\t\tif(view) {\n\t\t\treturn view;\n\t\t}\n\n\t\tlet buffer = this.memory.buffers[position];\n\t\tif(!buffer) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tview = new Uint32Array(buffer.buf);\n\t\tthis.bufferViews[position] = view;\n\t\treturn view;\n\t}\n\n\tprivate getDataBlock(buffer: ArrayBufferLike, nodeByteOffset: number): T {\n\t\tconst startIndex = nodeByteOffset + DATA_BLOCK_BYTE_OFFSET;\n\t\treturn makeArrayView(this.cachedType, buffer, startIndex, this.cachedDataLength) as T;\n\t}\n\n\tfree() {\n\t\tlet { bufferPosition: nextBlockPosition, bufferByteOffset: nextBlockByteOffset } = loadPointer(this.firstBlock.data, HEAD_INDEX, this.positionBits);\n\t\twhile(nextBlockByteOffset) {\n\t\t\tlet allocatedMemory = new AllocatedMemory(this.memory, {\n\t\t\t\tbufferPosition: nextBlockPosition,\n\t\t\t\tbufferByteOffset: nextBlockByteOffset,\n\t\t\t});\n\n\t\t\t({ bufferPosition: nextBlockPosition, bufferByteOffset: nextBlockByteOffset } = loadPointer(allocatedMemory.data, NODE_NEXT_INDEX, this.positionBits));\n\n\t\t\tif(this.onDelete && !Atomics.load(allocatedMemory.data, NODE_DELETED_INDEX)) {\n\t\t\t\tthis.onDelete(this.getDataBlock(allocatedMemory.data.buffer, allocatedMemory.data.byteOffset));\n\t\t\t}\n\n\t\t\tallocatedMemory.free();\n\t\t}\n\n\t\tthis.firstBlock.free();\n\t}\n}\n\ninterface SharedListConfig<T extends NumericArray> {\n\tinitWithBlock?: SharedAllocatedMemory\n\ttype?: TypedArrayConstructor<T>\n\tdataLength?: number\n}\ninterface SharedListMemory {\n\tfirstBlock: SharedAllocatedMemory\n}\n\ninterface SharedListIterable<T extends NumericArray> {\n\tdata: T\n\tindex: number\n\tdeleteCurrent: () => boolean\n}\n\nexport { type SharedListMemory };\n"],"mappings":"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"}
1
+ {"version":3,"file":"shared-list.js","names":[],"sources":["../src/shared-list.ts"],"sourcesContent":["import type { SharedAllocatedMemory } from './allocated-memory';\nimport AllocatedMemory from './allocated-memory';\nimport type MemoryHeap from './memory-heap';\nimport { loadPointer, loadRawPointer, replaceRawPointer, storeRawPointer } from './utils/pointer';\nimport { readLock, readUnlock, tryWriteLock, writeLock, writeUnlock } from './lock/read-write-lock';\nimport { getArrayTypeCode, getByteMultipler, isBigIntType, isFloatType, makeArrayView, type NumericArray, type NumericArrayIO, type TypedArrayConstructor } from './utils/array-type';\n\n// Lock-free singly-linked list. firstBlock doubles as an immortal sentinel node whose next pointer (index 0) is the head.\n// insert appends with a Michael-Scott CAS enqueue and never takes a lock; delete only tombstones a node (a lock-free CAS).\n//\n// Reclamation: freeing a node hands its address back to the allocator, so it is only safe once no thread can still be\n// dereferencing it. insert is proven safe against this by the \"tail rules\" below (it only ever touches a tail node, whose\n// next is 0, and a reclaim never frees such a node), so insert stays completely lock-free. Traversal is NOT safe against a\n// concurrent free (a parked walker would read a recycled node), so iteration takes the SHARED side of a read/write lock and\n// reclaim takes the EXCLUSIVE side. reclaim uses a non-blocking acquire: it runs only when no other thread is mid-iteration\n// (exactly the quiescence we need) and is skipped otherwise, so it never stalls readers. Reclaim is throttled to when dead\n// nodes have caught up to live ones, making it amortized O(1) per delete and keeping the chain within ~2x of live size -\n// the manual compact() is no longer required to bound memory (it remains as a force-everything pass).\nconst FIRST_BLOCK_RECORD_KEEPING_COUNT = 7;\nconst DATA_BLOCK_RECORD_KEEPING_COUNT = 2;\nconst DATA_BLOCK_BYTE_OFFSET = DATA_BLOCK_RECORD_KEEPING_COUNT * Uint32Array.BYTES_PER_ELEMENT;\nconst HEAD_INDEX = 0;\nconst TAIL_INDEX = 1;\nconst LENGTH_INDEX = 2;\n// index 3 holds type (u16) + dataLength (u16)\nconst DEAD_COUNT_INDEX = 4;\n// Two consecutive u32s (readers, pending-writers) backing the reclaim read/write lock (see lock/read-write-lock).\nconst LOCK_INDEX = 5;\nconst NODE_NEXT_INDEX = 0;\nconst NODE_DELETED_INDEX = 1;\n// Only reclaim once at least this many dead nodes have piled up, so tiny lists don't pay a walk on every delete.\nconst RECLAIM_MIN = 16;\n// A pointer packs the buffer position in its low `positionBits` bits and the byte offset in the high bits (see\n// utils/pointer). The split is per-heap, so it is read from the heap and cached rather than a module constant.\nexport default class SharedList<T extends NumericArray = Uint32Array> implements Iterable<SharedListIterable<T>> {\n\tstatic readonly ALLOCATE_COUNT = FIRST_BLOCK_RECORD_KEEPING_COUNT;\n\n\tprivate memory: MemoryHeap;\n\t// Per-heap pointer split, cached from the heap. Hoisted into locals inside the hot loops below.\n\tprivate positionBits: number;\n\tprivate positionMask: number;\n\t/* First block (also the sentinel node - index 0 is its next pointer = list head)\n\t\t32 index 0\n\t\tuint16 0 - head buffer position | sentinel.next\n\t\tuint16 1 - head buffer index\n\t\t32 index 1\n\t\tuint16 2 - tail hint buffer position\n\t\tuint16 3 - tail hint buffer index\n\t\t32 index 2\n\t\tuint32 4 - length\n\t\t32 index 3\n\t\tuint16 6 - type\n\t\tuint16 7 - data length (defaults to 1 number per data)\n\t\t32 index 4 - dead (tombstoned-but-still-linked) node count, drives reclaim throttling\n\t\t32 index 5 - reclaim lock: reader count\n\t\t32 index 6 - reclaim lock: pending-writer count\n\t*/\n\t/* Other blocks\n\t\t32 index 0 - next buffer pointer\n\t\t32 index 1 - deleted flag (0 = live, 1 = tombstoned)\n\t\t32 index 2 => data\n\t*/\n\tprivate firstBlock: AllocatedMemory;\n\tprivate uint16Array: Uint16Array;\n\t// Int32 view over the two reclaim-lock words (readers, pending-writers) inside firstBlock\n\tprivate lockArray: Int32Array;\n\t// type and dataLength are written once at init and never change, so cache them instead of atomic-loading per node\n\tprivate cachedType: number;\n\tprivate cachedDataLength: number;\n\t// u32 units per element: 1 for 32-bit types, 2 for 64-bit (float64/int64/uint64). Node data blocks scale by this.\n\tprivate cachedByteMultipler = 1;\n\t// Whether the value type is a BigInt view (int64/uint64), so insert stores bigint via Atomics; cached to keep the\n\t// per-insert branch off an instanceof check\n\tprivate cachedIsBigInt = false;\n\t// Float views can't be published with Atomics, so insert falls back to a plain store for them\n\tprivate cachedIsFloat = false;\n\t// One full-buffer Uint32Array per buffer position, so traversal indexes shared memory directly instead of\n\t// allocating a fresh view (and a getPointer object) for every node it touches\n\tprivate bufferViews: Array<Uint32Array> = [];\n\tonDelete?: (data: T) => void;\n\n\tget length(): number {\n\t\treturn Atomics.load(this.firstBlock.data, LENGTH_INDEX);\n\t}\n\n\t// Own internal memory plus every allocated node still linked in the chain (tombstoned nodes included, since they stay\n\t// allocated until a reclaim pass unlinks and frees them).\n\tget usedMemory(): number {\n\t\tlet total = this.firstBlock.usedMemory;\n\t\tlet { bufferPosition, bufferByteOffset } = loadPointer(this.firstBlock.data, HEAD_INDEX, this.positionBits);\n\t\twhile(bufferByteOffset) {\n\t\t\tlet node = new AllocatedMemory(this.memory, { bufferPosition, bufferByteOffset });\n\t\t\ttotal += node.usedMemory;\n\t\t\t({ bufferPosition, bufferByteOffset } = loadPointer(node.data, NODE_NEXT_INDEX, this.positionBits));\n\t\t}\n\t\treturn total;\n\t}\n\n\tget type(): number {\n\t\treturn this.cachedType;\n\t}\n\tprivate set type(value: number) {\n\t\tAtomics.store(this.uint16Array, 0, value);\n\t}\n\tget dataLength(): number {\n\t\treturn this.cachedDataLength;\n\t}\n\tprivate set dataLength(value: number) {\n\t\tAtomics.store(this.uint16Array, 1, value);\n\t}\n\n\tconstructor(memory: MemoryHeap, config?: SharedListConfig<T> | SharedListMemory) {\n\t\tthis.memory = memory;\n\t\tthis.positionBits = memory.positionBits;\n\t\tthis.positionMask = (1 << this.positionBits) - 1;\n\n\t\tif(config && 'firstBlock' in config) {\n\t\t\t// TODO: How to handle referencing memory we don't have access to yet because buffer not synced from worker?\n\t\t\tthis.firstBlock = new AllocatedMemory(memory, config.firstBlock);\n\t\t\tthis.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + (LENGTH_INDEX + 1) * Uint32Array.BYTES_PER_ELEMENT, 2);\n\t\t} else {\n\t\t\tif(config && config.initWithBlock) {\n\t\t\t\tthis.firstBlock = new AllocatedMemory(memory, config.initWithBlock);\n\t\t\t} else {\n\t\t\t\tthis.firstBlock = memory.allocUI32(FIRST_BLOCK_RECORD_KEEPING_COUNT);\n\t\t\t}\n\t\t\tthis.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + (LENGTH_INDEX + 1) * Uint32Array.BYTES_PER_ELEMENT, 2);\n\n\t\t\t// Empty list: the tail hint points at the sentinel (firstBlock) so insert never special-cases the first node\n\t\t\tstoreRawPointer(this.firstBlock.data, TAIL_INDEX, this.firstBlock.pointer);\n\n\t\t\tthis.type = getArrayTypeCode(config?.type ?? Uint32Array);\n\t\t\tthis.dataLength = config?.dataLength ?? 1;\n\t\t}\n\n\t\tthis.lockArray = new Int32Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + LOCK_INDEX * Uint32Array.BYTES_PER_ELEMENT, 2);\n\n\t\tthis.cachedType = Atomics.load(this.uint16Array, 0);\n\t\t// dataLength defaults to at least one even when initialized from raw memory that was never explicitly set\n\t\tthis.cachedDataLength = Math.max(1, Atomics.load(this.uint16Array, 1));\n\t\tthis.cachedByteMultipler = getByteMultipler(this.cachedType);\n\t\tthis.cachedIsBigInt = isBigIntType(this.cachedType);\n\t\tthis.cachedIsFloat = isFloatType(this.cachedType);\n\t}\n\n\tinsert(values: T[number] | Array<T[number]>) {\n\t\tif(!Array.isArray(values)) {\n\t\t\tvalues = [values];\n\t\t}\n\n\t\tlet dataLength = this.cachedDataLength;\n\t\tif(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\t\tlet newBlock = this.memory.allocUI32(DATA_BLOCK_RECORD_KEEPING_COUNT + dataLength * this.cachedByteMultipler);\n\t\tlet newData = this.getDataBlock(newBlock.data.buffer, newBlock.data.byteOffset);\n\t\tlet newBlockPointer = newBlock.pointer;\n\n\t\tlet io = newData as NumericArrayIO;\n\t\tfor(let i = 0; i < values.length; i++) {\n\t\t\tif(this.cachedIsFloat) {\n\t\t\t\t// Float views don't support Atomics; the enqueue CAS below still provides the cross-thread publish\n\t\t\t\tio[i] = values[i];\n\t\t\t} else if(this.cachedIsBigInt) {\n\t\t\t\tAtomics.store(newData as BigInt64Array, i, values[i] as bigint);\n\t\t\t} else {\n\t\t\t\tAtomics.store(newData as Int32Array, i, values[i] as number);\n\t\t\t}\n\t\t}\n\n\t\t// Michael-Scott enqueue: link the node onto tail.next BEFORE swinging the tail hint, so a committed node is never\n\t\t// invisible to a concurrent traversal. The tail hint may lag by one node; enqueuers help advance it.\n\t\tlet positionBits = this.positionBits;\n\t\tlet positionMask = this.positionMask;\n\t\t// eslint-disable-next-line no-constant-condition\n\t\twhile(true) {\n\t\t\tlet tailPointer = loadRawPointer(this.firstBlock.data, TAIL_INDEX);\n\t\t\tlet view = this.getBufferView(tailPointer & positionMask);\n\t\t\t// Tail node lives in a buffer this thread hasn't synced yet - retry until it appears\n\t\t\tif(!view) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlet tailNextIndex = ((tailPointer >>> positionBits) >>> 2) + NODE_NEXT_INDEX;\n\t\t\tlet nextPointer = Atomics.load(view, tailNextIndex);\n\n\t\t\t// Re-check the hint hasn't moved out from under us before acting on what we read\n\t\t\tif(tailPointer !== loadRawPointer(this.firstBlock.data, TAIL_INDEX)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif(nextPointer === 0) {\n\t\t\t\tif(Atomics.compareExchange(view, tailNextIndex, 0, newBlockPointer) === 0) {\n\t\t\t\t\t// Linked. Try to swing the hint forward - failure is harmless, the next enqueuer will help.\n\t\t\t\t\treplaceRawPointer(this.firstBlock.data, TAIL_INDEX, newBlockPointer, tailPointer);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Tail hint is lagging behind a linked node - help it catch up, then retry\n\t\t\t\treplaceRawPointer(this.firstBlock.data, TAIL_INDEX, nextPointer, tailPointer);\n\t\t\t}\n\t\t}\n\n\t\tAtomics.add(this.firstBlock.data, LENGTH_INDEX, 1);\n\t}\n\n\tdeleteMatch(callback: (values: T, index: number) => boolean): boolean {\n\t\tlet deleted = false;\n\t\tfor(let { data, index, deleteCurrent } of this) {\n\t\t\tif(callback(data, index)) {\n\t\t\t\tif(deleteCurrent()) {\n\t\t\t\t\tdeleted = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Reclaim runs after the iterator's read lock is released, so it can win exclusivity when no one else is walking\n\t\tthis.autoReclaim();\n\t\treturn deleted;\n\t}\n\tdeleteIndex(deleteIndex: number): boolean {\n\t\tif(deleteIndex >= this.length || deleteIndex < 0) {\n\t\t\treturn false;\n\t\t}\n\n\t\tlet deleted = false;\n\t\t// Compare on index only so the payload view is never materialized for an index-based delete\n\t\tfor(let item of this) {\n\t\t\tif(item.index === deleteIndex) {\n\t\t\t\tif(item.deleteCurrent()) {\n\t\t\t\t\tdeleted = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthis.autoReclaim();\n\t\treturn deleted;\n\t}\n\tdeleteValue(deleteValues: T[number] | Array<T[number]>) {\n\t\tif(!Array.isArray(deleteValues)) {\n\t\t\treturn this.deleteMatch(values => values[0] === deleteValues);\n\t\t} else {\n\t\t\treturn this.deleteMatch(values => {\n\t\t\t\tif(values.length !== deleteValues.length) {\n\t\t\t\t\treturn false;\n\t\t\t\t} else {\n\t\t\t\t\tfor(let i = 0; i < values.length; i++) {\n\t\t\t\t\t\tif(values[i] !== deleteValues[i]) {\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\n\t// Force-reclaim every tombstoned node, including a lone tail tombstone that autoReclaim leaves behind. Takes the reclaim\n\t// lock exclusively (blocking), so it is safe against concurrent iterate/delete - but must NOT be called from inside an\n\t// iteration on this same thread, since the walker still holds the read lock and the two would deadlock.\n\tcompact() {\n\t\twriteLock(this.lockArray);\n\t\ttry {\n\t\t\tthis.reclaimTombstones(true);\n\t\t} finally {\n\t\t\twriteUnlock(this.lockArray);\n\t\t}\n\t}\n\n\t// Fires after a delete once dead nodes have caught up to live ones. Grabs the reclaim lock only if no thread is\n\t// mid-iteration (non-blocking) - \"not in the middle of an iteration by another thread\" - and otherwise skips, leaving\n\t// the garbage for the next delete to retry. Amortizes to O(1) per delete while keeping the chain within ~2x of live.\n\tprivate autoReclaim() {\n\t\tlet dead = Atomics.load(this.firstBlock.data, DEAD_COUNT_INDEX);\n\t\tif(dead < RECLAIM_MIN || dead < this.length) {\n\t\t\treturn;\n\t\t}\n\n\t\tif(!tryWriteLock(this.lockArray)) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tthis.reclaimTombstones(false);\n\t\t} finally {\n\t\t\twriteUnlock(this.lockArray);\n\t\t}\n\t}\n\n\t// Physically unlinks and frees tombstoned nodes. The caller MUST hold the reclaim lock exclusively, so no iterator is\n\t// walking and no other reclaim runs concurrently - that is what rules out freeing a node another walker is parked on and\n\t// the stale-predecessor races of arbitrary lock-free deletion. insert stays lock-free and runs concurrently here, so the\n\t// tail rules keep it safe: a node whose next is 0 might be the enqueue target, so unless freeTail it is left in place, and\n\t// every freed node has the tail hint swung off it first (any insert holding a stale hint then re-checks and discards).\n\tprivate reclaimTombstones(freeTail: boolean): number {\n\t\tlet prevPointer = this.firstBlock.pointer;\n\t\tlet prevView = this.firstBlock.data;\n\t\tlet prevNextIndex = NODE_NEXT_INDEX;\n\t\tlet positionBits = this.positionBits;\n\t\tlet positionMask = this.positionMask;\n\t\tlet freed = 0;\n\t\tlet nextPointer = loadRawPointer(prevView, prevNextIndex);\n\t\twhile(nextPointer) {\n\t\t\tlet position = nextPointer & positionMask;\n\t\t\tlet memPool = this.memory.buffers[position];\n\t\t\tif(!memPool) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tlet view = this.getBufferView(position)!;\n\t\t\tlet byteOffset = nextPointer >>> positionBits;\n\t\t\tlet nodeIndex = byteOffset >>> 2;\n\t\t\tlet followingPointer = Atomics.load(view, nodeIndex + NODE_NEXT_INDEX);\n\n\t\t\tif(Atomics.load(view, nodeIndex + NODE_DELETED_INDEX)) {\n\t\t\t\tif(followingPointer === 0 && !freeTail) {\n\t\t\t\t\t// Tail tombstone: a concurrent insert may be about to CAS its next, so leave it for compact() or the next\n\t\t\t\t\t// append (which turns it into a safe-to-free interior node)\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t// Unlink first so the following node stays reachable, then swing the tail hint off the node before freeing it,\n\t\t\t\t// then hand the memory back. onDelete already fired when the node was tombstoned.\n\t\t\t\tstoreRawPointer(prevView, prevNextIndex, followingPointer);\n\t\t\t\treplaceRawPointer(this.firstBlock.data, TAIL_INDEX, prevPointer, nextPointer);\n\t\t\t\tmemPool.free(byteOffset);\n\t\t\t\tfreed++;\n\t\t\t} else {\n\t\t\t\tprevPointer = nextPointer;\n\t\t\t\tprevView = view;\n\t\t\t\tprevNextIndex = nodeIndex + NODE_NEXT_INDEX;\n\t\t\t}\n\n\t\t\tnextPointer = followingPointer;\n\t\t}\n\n\t\tif(freeTail) {\n\t\t\t// Quiescent full pass: the last surviving node (or the sentinel) is unambiguously the tail\n\t\t\tstoreRawPointer(this.firstBlock.data, TAIL_INDEX, prevPointer);\n\t\t}\n\t\tif(freed) {\n\t\t\tAtomics.sub(this.firstBlock.data, DEAD_COUNT_INDEX, freed);\n\t\t}\n\t\treturn freed;\n\t}\n\n\tclear() {\n\t\twriteLock(this.lockArray);\n\t\ttry {\n\t\t\tthis.clearLocked();\n\t\t} finally {\n\t\t\twriteUnlock(this.lockArray);\n\t\t}\n\t}\n\tprivate clearLocked() {\n\t\t// Detach the whole chain, then free it. Reclaim lock is held, so no iterator is walking the chain we are freeing.\n\t\tlet firstBlockPointer;\n\t\tlet updateWorked = false;\n\t\twhile(!updateWorked) {\n\t\t\tfirstBlockPointer = loadRawPointer(this.firstBlock.data, HEAD_INDEX);\n\t\t\t// Already cleared\n\t\t\tif(!firstBlockPointer) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tupdateWorked = replaceRawPointer(this.firstBlock.data, HEAD_INDEX, 0, firstBlockPointer);\n\t\t}\n\t\t// Shouldn't be possible to hit: making Typescript happy\n\t\tif(!firstBlockPointer) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Reset the tail hint back to the sentinel\n\t\tstoreRawPointer(this.firstBlock.data, TAIL_INDEX, this.firstBlock.pointer);\n\n\t\tlet liveItems = 0;\n\t\tlet positionBits = this.positionBits;\n\t\tlet positionMask = this.positionMask;\n\t\tlet nextBlockPointer = firstBlockPointer;\n\t\twhile(nextBlockPointer) {\n\t\t\tlet position = nextBlockPointer & positionMask;\n\t\t\tlet memPool = this.memory.buffers[position];\n\t\t\t// Short circuit iterations if we can't access memory\n\t\t\tif(!memPool) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tlet view = this.getBufferView(position)!;\n\t\t\tlet byteOffset = nextBlockPointer >>> positionBits;\n\t\t\tlet nodeIndex = byteOffset >>> 2;\n\t\t\tnextBlockPointer = Atomics.load(view, nodeIndex + NODE_NEXT_INDEX);\n\n\t\t\tif(!Atomics.load(view, nodeIndex + NODE_DELETED_INDEX)) {\n\t\t\t\tliveItems++;\n\t\t\t\tif(this.onDelete) {\n\t\t\t\t\tthis.onDelete(this.getDataBlock(view.buffer, byteOffset));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tmemPool.free(byteOffset);\n\t\t}\n\n\t\t// Only subtract the live nodes we detached so a concurrent insert's increment stays accurate\n\t\tAtomics.sub(this.firstBlock.data, LENGTH_INDEX, liveItems);\n\t\t// The whole chain (dead nodes included) is gone; no new dead nodes can appear while we hold the reclaim lock\n\t\tAtomics.store(this.firstBlock.data, DEAD_COUNT_INDEX, 0);\n\t}\n\n\t*[Symbol.iterator]() {\n\t\tlet currentIndex = 0;\n\t\tlet positionBits = this.positionBits;\n\t\tlet positionMask = this.positionMask;\n\t\t// Shared read lock: many walkers (and lock-free inserts) run at once, but it blocks a reclaim from freeing a node out\n\t\t// from under us. Held for the whole walk and released in finally, so an early break/throw still frees it.\n\t\treadLock(this.lockArray);\n\t\ttry {\n\t\t\tlet nextPointer = loadRawPointer(this.firstBlock.data, HEAD_INDEX);\n\t\t\twhile(nextPointer) {\n\t\t\t\tlet position = nextPointer & positionMask;\n\t\t\t\tlet view = this.getBufferView(position);\n\t\t\t\t// Short circuit iterations if we can't access memory\n\t\t\t\tif(!view) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tlet byteOffset = nextPointer >>> positionBits;\n\t\t\t\tlet nodeIndex = byteOffset >>> 2;\n\t\t\t\tnextPointer = Atomics.load(view, nodeIndex + NODE_NEXT_INDEX);\n\n\t\t\t\t// Skip tombstoned nodes - they stay linked until a reclaim pass unlinks and frees them\n\t\t\t\tif(Atomics.load(view, nodeIndex + NODE_DELETED_INDEX)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tlet buffer = view.buffer;\n\t\t\t\tlet deletedIndex = nodeIndex + NODE_DELETED_INDEX;\n\t\t\t\t// Plain object with an eager data view: V8 keeps this monomorphic and fast. A lazy `get data()` accessor is far\n\t\t\t\t// slower to allocate than the payload view it would save, so pay for the view up front.\n\t\t\t\tyield {\n\t\t\t\t\tdata: this.getDataBlock(buffer, byteOffset),\n\t\t\t\t\tindex: currentIndex,\n\t\t\t\t\t// Logical delete: tombstone the node and drop length. Returns false if another thread beat us to it.\n\t\t\t\t\tdeleteCurrent: (): boolean => {\n\t\t\t\t\t\tif(Atomics.compareExchange(view, deletedIndex, 0, 1) === 0) {\n\t\t\t\t\t\t\tAtomics.sub(this.firstBlock.data, LENGTH_INDEX, 1);\n\t\t\t\t\t\t\t// Count the dead node so autoReclaim knows when garbage has caught up to live nodes\n\t\t\t\t\t\t\tAtomics.add(this.firstBlock.data, DEAD_COUNT_INDEX, 1);\n\t\t\t\t\t\t\tif(this.onDelete) {\n\t\t\t\t\t\t\t\tthis.onDelete(this.getDataBlock(buffer, byteOffset));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t},\n\t\t\t\t};\n\n\t\t\t\tcurrentIndex++;\n\t\t\t}\n\t\t} finally {\n\t\t\treadUnlock(this.lockArray);\n\t\t}\n\t}\n\n\tforEach(callback: (data: T) => void) {\n\t\tfor(let value of this) {\n\t\t\tcallback(value.data);\n\t\t}\n\t}\n\n\tgetSharedMemory(): SharedListMemory {\n\t\treturn {\n\t\t\tfirstBlock: this.firstBlock.getSharedMemory(),\n\t\t};\n\t}\n\n\tprivate getBufferView(position: number): Uint32Array | undefined {\n\t\tlet view = this.bufferViews[position];\n\t\tif(view) {\n\t\t\treturn view;\n\t\t}\n\n\t\tlet buffer = this.memory.buffers[position];\n\t\tif(!buffer) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tview = new Uint32Array(buffer.buf);\n\t\tthis.bufferViews[position] = view;\n\t\treturn view;\n\t}\n\n\tprivate getDataBlock(buffer: ArrayBufferLike, nodeByteOffset: number): T {\n\t\tconst startIndex = nodeByteOffset + DATA_BLOCK_BYTE_OFFSET;\n\t\treturn makeArrayView(this.cachedType, buffer, startIndex, this.cachedDataLength) as T;\n\t}\n\n\tfree() {\n\t\tlet { bufferPosition: nextBlockPosition, bufferByteOffset: nextBlockByteOffset } = loadPointer(this.firstBlock.data, HEAD_INDEX, this.positionBits);\n\t\twhile(nextBlockByteOffset) {\n\t\t\tlet allocatedMemory = new AllocatedMemory(this.memory, {\n\t\t\t\tbufferPosition: nextBlockPosition,\n\t\t\t\tbufferByteOffset: nextBlockByteOffset,\n\t\t\t});\n\n\t\t\t({ bufferPosition: nextBlockPosition, bufferByteOffset: nextBlockByteOffset } = loadPointer(allocatedMemory.data, NODE_NEXT_INDEX, this.positionBits));\n\n\t\t\tif(this.onDelete && !Atomics.load(allocatedMemory.data, NODE_DELETED_INDEX)) {\n\t\t\t\tthis.onDelete(this.getDataBlock(allocatedMemory.data.buffer, allocatedMemory.data.byteOffset));\n\t\t\t}\n\n\t\t\tallocatedMemory.free();\n\t\t}\n\n\t\tthis.firstBlock.free();\n\t}\n}\n\ninterface SharedListConfig<T extends NumericArray> {\n\tinitWithBlock?: SharedAllocatedMemory\n\ttype?: TypedArrayConstructor<T>\n\tdataLength?: number\n}\ninterface SharedListMemory {\n\tfirstBlock: SharedAllocatedMemory\n}\n\ninterface SharedListIterable<T extends NumericArray> {\n\tdata: T\n\tindex: number\n\tdeleteCurrent: () => boolean\n}\n\nexport { type SharedListMemory };\n"],"mappings":"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"}
@@ -1,15 +1,15 @@
1
1
  import { getPointer as e, loadRawPointer as t, storeRawPointer as n } from "../utils/pointer.js";
2
2
  import r from "../allocated-memory.js";
3
- import { ARRAY_TYPE as i, getArrayTypeCode as a, getByteMultipler as o, makeArrayView as s } from "../utils/array-type.js";
4
- import { lock as c, unlock as l } from "../lock/simple-lock.js";
5
- import u from "../shared-map.js";
6
- import d from "../shared-stack.js";
7
- import f from "../shared-pool.js";
8
- import { readLock as p, readUnlock as m, writeLock as h, writeUnlock as g } from "../lock/read-write-lock.js";
3
+ import { readLock as i, readUnlock as a, writeLock as o, writeUnlock as s } from "../lock/read-write-lock.js";
4
+ import { ARRAY_TYPE as c, getArrayTypeCode as l, getByteMultipler as u, makeArrayView as d } from "../utils/array-type.js";
5
+ import { lock as f, unlock as p } from "../lock/simple-lock.js";
6
+ import m from "../shared-map.js";
7
+ import h from "../shared-stack.js";
8
+ import g from "../shared-pool.js";
9
9
  import { addCandidate as _, candidateLimit as v, distanceSquaredToRect as y } from "./spatial-neighbors.js";
10
10
  import { getSpatialMapCapacity as ee, getSpatialPoolChunkSize as te } from "./spatial-entity.js";
11
11
  //#region src/spatial/shared-spatial-map.ts
12
- var b = 0, x = 1, S = 2, C = 3, w = 4, T = 5, E = 6, D = 8, ne = 10, O = 0, k = 1, A = 2, j = 8, M = 256, re = 255, N = 0, P = 1, F = 2, I = 3, L = 4, R = 5, z = 6, B = 7, V = 0, H = 1, U = 2, W = 3, G = 4, K = 5, q = 6, J = 7, Y = 8, X = 16777216, ie = X, ae = 4294967295, oe = 73856093, se = 19349663, ce = 50, le = 256, Z = 1, Q = 2, ue = 1e6, de = 4, fe = Float32Array, pe = class a {
12
+ var b = 0, x = 1, S = 2, C = 3, w = 4, T = 5, E = 6, D = 8, ne = 10, O = 0, k = 1, A = 2, j = 8, M = 256, re = 255, N = 0, P = 1, F = 2, I = 3, L = 4, R = 5, z = 6, B = 7, V = 0, H = 1, U = 2, W = 3, G = 4, K = 5, q = 6, J = 7, Y = 8, X = 16777216, ie = X, ae = 4294967295, oe = 73856093, se = 19349663, ce = 50, le = 256, Z = 1, Q = 2, ue = 1e6, de = 4, fe = Float32Array, pe = class l {
13
13
  static ALLOCATE_COUNT = 11;
14
14
  memory;
15
15
  firstBlock;
@@ -60,46 +60,46 @@ var b = 0, x = 1, S = 2, C = 3, w = 4, T = 5, E = 6, D = 8, ne = 10, O = 0, k =
60
60
  get type() {
61
61
  return this.coordType;
62
62
  }
63
- constructor(s, c) {
64
- if (this.memory = s, c && "firstBlock" in c) this.firstBlock = new r(s, c.firstBlock), this.coordType = this.firstBlock.data[T], this.bucketPointers = new d(s, { firstBlock: e(t(this.firstBlock.data, b), s.positionBits) }), this.entityPool = new f(s, { firstBlock: e(t(this.firstBlock.data, x), s.positionBits) }), this.slotPool = new f(s, { firstBlock: e(t(this.firstBlock.data, S), s.positionBits) }), this.idMap = new u(s, { firstBlock: e(t(this.firstBlock.data, C), s.positionBits) }), this.bucketCount = Atomics.load(this.firstBlock.data, w), this.maxBucketCount = this.firstBlock.data[ne], this.nullIndex = ge(this.coordType);
63
+ constructor(i, a) {
64
+ if (this.memory = i, a && "firstBlock" in a) this.firstBlock = new r(i, a.firstBlock), this.coordType = this.firstBlock.data[T], this.bucketPointers = new h(i, { firstBlock: e(t(this.firstBlock.data, b), i.positionBits) }), this.entityPool = new g(i, { firstBlock: e(t(this.firstBlock.data, x), i.positionBits) }), this.slotPool = new g(i, { firstBlock: e(t(this.firstBlock.data, S), i.positionBits) }), this.idMap = new m(i, { firstBlock: e(t(this.firstBlock.data, C), i.positionBits) }), this.bucketCount = Atomics.load(this.firstBlock.data, w), this.maxBucketCount = this.firstBlock.data[ne], this.nullIndex = ge(this.coordType);
65
65
  else {
66
- let e = c?.gridSize ?? ce;
67
- this.bucketCount = Math.min(1073741824, Math.max(1, Math.floor(c?.buckets ?? le)));
68
- let t = c?.maxEntities ?? ue, r = c?.maxSlots ?? t * de;
69
- if (this.maxBucketCount = Math.min(1073741824, Math.max(this.bucketCount, Math.floor(c?.maxBuckets ?? _e(r * 2)))), this.coordType = me(c?.type), this.nullIndex = ge(this.coordType), this.coordType === i.float32 && (t > X || r > X)) throw Error(`Float32 SharedSpatialMap supports at most ${X} entities and slots; pass type: Float64Array for more`);
70
- let l = {
66
+ let e = a?.gridSize ?? ce;
67
+ this.bucketCount = Math.min(1073741824, Math.max(1, Math.floor(a?.buckets ?? le)));
68
+ let t = a?.maxEntities ?? ue, r = a?.maxSlots ?? t * de;
69
+ if (this.maxBucketCount = Math.min(1073741824, Math.max(this.bucketCount, Math.floor(a?.maxBuckets ?? _e(r * 2)))), this.coordType = me(a?.type), this.nullIndex = ge(this.coordType), this.coordType === c.float32 && (t > X || r > X)) throw Error(`Float32 SharedSpatialMap supports at most ${X} entities and slots; pass type: Float64Array for more`);
70
+ let o = {
71
71
  type: he(this.coordType),
72
72
  dataLength: Y,
73
73
  maxLength: t,
74
- maxChunkSize: te(t, Y, o(this.coordType), s.maxAllocationLength)
75
- }, p = {
74
+ maxChunkSize: te(t, Y, u(this.coordType), i.maxAllocationLength)
75
+ }, s = {
76
76
  type: he(this.coordType),
77
77
  dataLength: B,
78
78
  maxLength: r,
79
- maxChunkSize: te(r, B, o(this.coordType), s.maxAllocationLength)
80
- }, m = f.getAllocateCount(s, l), h = f.getAllocateCount(s, p), g = u.ALLOCATE_COUNT, _ = a.ALLOCATE_COUNT;
81
- this.firstBlock = s.allocUI32(_ + m + h + g), this.firstBlock.data[w] = this.bucketCount, this.firstBlock.data[T] = this.coordType, this.firstBlock.data[ne] = this.maxBucketCount;
79
+ maxChunkSize: te(r, B, u(this.coordType), i.maxAllocationLength)
80
+ }, d = g.getAllocateCount(i, o), f = g.getAllocateCount(i, s), p = m.ALLOCATE_COUNT, _ = l.ALLOCATE_COUNT;
81
+ this.firstBlock = i.allocUI32(_ + d + f + p), this.firstBlock.data[w] = this.bucketCount, this.firstBlock.data[T] = this.coordType, this.firstBlock.data[ne] = this.maxBucketCount;
82
82
  let v = this.getConfigView();
83
- v[0] = e, this.bucketPointers = new d(s), n(this.firstBlock.data, b, this.bucketPointers.pointer), this.appendBucketSegments(this.bucketCount);
84
- let y = this.firstBlock.getSubAllocation(_, m);
85
- this.entityPool = new f(s, {
83
+ v[0] = e, this.bucketPointers = new h(i), n(this.firstBlock.data, b, this.bucketPointers.pointer), this.appendBucketSegments(this.bucketCount);
84
+ let y = this.firstBlock.getSubAllocation(_, d);
85
+ this.entityPool = new g(i, {
86
86
  firstBlock: y,
87
- ...l
87
+ ...o
88
88
  }), n(this.firstBlock.data, x, y.pointer);
89
- let E = this.firstBlock.getSubAllocation(_ + m, h);
90
- this.slotPool = new f(s, {
89
+ let E = this.firstBlock.getSubAllocation(_ + d, f);
90
+ this.slotPool = new g(i, {
91
91
  firstBlock: E,
92
- ...p
92
+ ...s
93
93
  }), n(this.firstBlock.data, S, E.pointer);
94
- let D = this.firstBlock.getSubAllocation(_ + m + h, g);
95
- this.idMap = new u(s, {
94
+ let D = this.firstBlock.getSubAllocation(_ + d + f, p);
95
+ this.idMap = new m(i, {
96
96
  firstBlock: D,
97
- capacity: ee(t, s.maxAllocationLength)
97
+ capacity: ee(t, i.maxAllocationLength)
98
98
  }), n(this.firstBlock.data, C, D.pointer);
99
99
  }
100
100
  this.bucketMask = $(this.bucketCount), this.tableLock = new Int32Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + D * Uint32Array.BYTES_PER_ELEMENT, 2);
101
- let l = this.getConfigView();
102
- this.cellSize = l[0];
101
+ let o = this.getConfigView();
102
+ this.cellSize = o[0];
103
103
  }
104
104
  get pointer() {
105
105
  return this.firstBlock.pointer;
@@ -109,41 +109,41 @@ var b = 0, x = 1, S = 2, C = 3, w = 4, T = 5, E = 6, D = 8, ne = 10, O = 0, k =
109
109
  for (let n of this.bucketPointers) t += new r(this.memory, e(n, this.memory.positionBits)).usedMemory;
110
110
  return t;
111
111
  }
112
- insert(e, t, n, r = 0, i = 0) {
113
- p(this.tableLock);
112
+ insert(e, t, n, r = 0, o = 0) {
113
+ i(this.tableLock);
114
114
  try {
115
- this.syncBucketCount(), this.insertLocked(e, t, n, r, i);
115
+ this.syncBucketCount(), this.insertLocked(e, t, n, r, o);
116
116
  } finally {
117
- m(this.tableLock);
117
+ a(this.tableLock);
118
118
  }
119
119
  this.growBucketsIfNeeded();
120
120
  }
121
121
  bulkInsert(e) {
122
122
  let t = /* @__PURE__ */ new Map();
123
123
  for (let n of e) t.set(n.id, n);
124
- let n = Array.from(t.values()), r = this.idMap.getAll(n.map((e) => e.id)), i = [], a = 0, o = 0;
124
+ let n = Array.from(t.values()), r = this.idMap.getAll(n.map((e) => e.id)), o = [], s = 0, c = 0;
125
125
  for (let e = 0; e < n.length; e++) if (r[e] === void 0) {
126
126
  let t = n[e];
127
- o++, a += (this.colOf(t.x + (t.width ?? 0)) - this.colOf(t.x) + 1) * (this.rowOf(t.y + (t.height ?? 0)) - this.rowOf(t.y) + 1);
127
+ c++, s += (this.colOf(t.x + (t.width ?? 0)) - this.colOf(t.x) + 1) * (this.rowOf(t.y + (t.height ?? 0)) - this.rowOf(t.y) + 1);
128
128
  }
129
- this.ensureBucketCapacity(this.slotPool.length + a);
130
- let s = this.entityPool.reserveContiguous(o), c = this.slotPool.reserveContiguous(a);
131
- p(this.tableLock);
129
+ this.ensureBucketCapacity(this.slotPool.length + s);
130
+ let l = this.entityPool.reserveContiguous(c), u = this.slotPool.reserveContiguous(s);
131
+ i(this.tableLock);
132
132
  try {
133
133
  this.syncBucketCount();
134
134
  for (let e = 0; e < n.length; e++) {
135
- let t = n[e], a = t.width ?? 0, o = t.height ?? 0;
135
+ let t = n[e], i = t.width ?? 0, a = t.height ?? 0;
136
136
  if (r[e] !== void 0) {
137
- this.updateLocked(t.id, t.x, t.y, a, o);
137
+ this.updateLocked(t.id, t.x, t.y, i, a);
138
138
  continue;
139
139
  }
140
- let l = this.colOf(t.x), u = this.rowOf(t.y), d = this.colOf(t.x + a), f = this.rowOf(t.y + o), p = s++, m = this.entityPool.blockFor(p), h = this.entityPool.blockOffset(p);
141
- m[h + V] = l, m[h + H] = u, m[h + U] = d, m[h + W] = f, m[h + G] = t.x, m[h + K] = t.y, m[h + q] = a, m[h + J] = o, i.push([t.id, p]);
142
- for (let e = u; e <= f; e++) for (let n = l; n <= d; n++) this.linkReservedBucket(n, e, c++, t.id, p, l, u);
140
+ let s = this.colOf(t.x), c = this.rowOf(t.y), d = this.colOf(t.x + i), f = this.rowOf(t.y + a), p = l++, m = this.entityPool.blockFor(p), h = this.entityPool.blockOffset(p);
141
+ m[h + V] = s, m[h + H] = c, m[h + U] = d, m[h + W] = f, m[h + G] = t.x, m[h + K] = t.y, m[h + q] = i, m[h + J] = a, o.push([t.id, p]);
142
+ for (let e = c; e <= f; e++) for (let n = s; n <= d; n++) this.linkReservedBucket(n, e, u++, t.id, p, s, c);
143
143
  }
144
- this.idMap.setAll(i);
144
+ this.idMap.setAll(o);
145
145
  } finally {
146
- m(this.tableLock);
146
+ a(this.tableLock);
147
147
  }
148
148
  this.growBucketsForBulk();
149
149
  }
@@ -157,20 +157,20 @@ var b = 0, x = 1, S = 2, C = 3, w = 4, T = 5, E = 6, D = 8, ne = 10, O = 0, k =
157
157
  let u = this.entityPool.push(l);
158
158
  this.idMap.set(e, u), this.linkRange(e, u, a, o, s, c);
159
159
  }
160
- update(e, t, n, r = 0, i = 0) {
161
- let a = this.idMap.get(e);
162
- if (a !== void 0) {
163
- let e = this.entityPool.blockFor(a), o = this.entityPool.blockOffset(a), s = this.colOf(t), c = this.rowOf(n), l = this.colOf(t + r), u = this.rowOf(n + i);
164
- if (s === e[o + V] && c === e[o + H] && l === e[o + U] && u === e[o + W]) {
165
- e[o + G] = t, e[o + K] = n, e[o + q] = r, e[o + J] = i;
160
+ update(e, t, n, r = 0, o = 0) {
161
+ let s = this.idMap.get(e);
162
+ if (s !== void 0) {
163
+ let e = this.entityPool.blockFor(s), i = this.entityPool.blockOffset(s), a = this.colOf(t), c = this.rowOf(n), l = this.colOf(t + r), u = this.rowOf(n + o);
164
+ if (a === e[i + V] && c === e[i + H] && l === e[i + U] && u === e[i + W]) {
165
+ e[i + G] = t, e[i + K] = n, e[i + q] = r, e[i + J] = o;
166
166
  return;
167
167
  }
168
168
  }
169
- p(this.tableLock);
169
+ i(this.tableLock);
170
170
  try {
171
- this.syncBucketCount(), this.updateLocked(e, t, n, r, i);
171
+ this.syncBucketCount(), this.updateLocked(e, t, n, r, o);
172
172
  } finally {
173
- m(this.tableLock);
173
+ a(this.tableLock);
174
174
  }
175
175
  this.growBucketsIfNeeded();
176
176
  }
@@ -188,11 +188,11 @@ var b = 0, x = 1, S = 2, C = 3, w = 4, T = 5, E = 6, D = 8, ne = 10, O = 0, k =
188
188
  this.relinkRange(e, a, f, p, m, h, c, l, u, d), o[s + V] = f, o[s + H] = p, o[s + U] = m, o[s + W] = h, o[s + G] = t, o[s + K] = n, o[s + q] = r, o[s + J] = i;
189
189
  }
190
190
  remove(e) {
191
- p(this.tableLock);
191
+ i(this.tableLock);
192
192
  try {
193
193
  return this.syncBucketCount(), this.removeLocked(e);
194
194
  } finally {
195
- m(this.tableLock);
195
+ a(this.tableLock);
196
196
  }
197
197
  }
198
198
  removeLocked(e) {
@@ -208,12 +208,12 @@ var b = 0, x = 1, S = 2, C = 3, w = 4, T = 5, E = 6, D = 8, ne = 10, O = 0, k =
208
208
  let a = [];
209
209
  return this.searchInto(a, e, t, n, r, i), a;
210
210
  }
211
- searchInto(e, t, n, r, i, a) {
212
- p(this.tableLock);
211
+ searchInto(e, t, n, r, o, s) {
212
+ i(this.tableLock);
213
213
  try {
214
- return this.syncBucketCount(), this.searchIntoLocked(e, t, n, r, i, a);
214
+ return this.syncBucketCount(), this.searchIntoLocked(e, t, n, r, o, s);
215
215
  } finally {
216
- m(this.tableLock);
216
+ a(this.tableLock);
217
217
  }
218
218
  }
219
219
  searchIntoLocked(e, t, n, r, i, a) {
@@ -232,12 +232,12 @@ var b = 0, x = 1, S = 2, C = 3, w = 4, T = 5, E = 6, D = 8, ne = 10, O = 0, k =
232
232
  }
233
233
  return e;
234
234
  }
235
- neighbors(e, t, n, r, i) {
236
- p(this.tableLock);
235
+ neighbors(e, t, n, r, o) {
236
+ i(this.tableLock);
237
237
  try {
238
- return this.syncBucketCount(), this.neighborsLocked(e, t, n, r, i);
238
+ return this.syncBucketCount(), this.neighborsLocked(e, t, n, r, o);
239
239
  } finally {
240
- m(this.tableLock);
240
+ a(this.tableLock);
241
241
  }
242
242
  }
243
243
  neighborsLocked(e, t, n, r, i) {
@@ -282,7 +282,7 @@ var b = 0, x = 1, S = 2, C = 3, w = 4, T = 5, E = 6, D = 8, ne = 10, O = 0, k =
282
282
  return n === 1 ? d.id === void 0 ? [] : [d.id] : o.map((e) => e.id);
283
283
  }
284
284
  clear() {
285
- h(this.tableLock);
285
+ o(this.tableLock);
286
286
  try {
287
287
  this.syncBucketCount();
288
288
  for (let e = 0; e < this.bucketPointers.length; e++) {
@@ -295,7 +295,7 @@ var b = 0, x = 1, S = 2, C = 3, w = 4, T = 5, E = 6, D = 8, ne = 10, O = 0, k =
295
295
  for (let [t] of this.idMap) e.push(t);
296
296
  for (let t of e) this.idMap.delete(t);
297
297
  } finally {
298
- g(this.tableLock);
298
+ s(this.tableLock);
299
299
  }
300
300
  }
301
301
  free() {
@@ -306,7 +306,7 @@ var b = 0, x = 1, S = 2, C = 3, w = 4, T = 5, E = 6, D = 8, ne = 10, O = 0, k =
306
306
  return { firstBlock: this.firstBlock.getSharedMemory() };
307
307
  }
308
308
  getConfigView() {
309
- return s(this.coordType, this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + E * Uint32Array.BYTES_PER_ELEMENT, 1);
309
+ return d(this.coordType, this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + E * Uint32Array.BYTES_PER_ELEMENT, 1);
310
310
  }
311
311
  colOf(e) {
312
312
  return Math.floor(e / this.cellSize);
@@ -347,25 +347,25 @@ var b = 0, x = 1, S = 2, C = 3, w = 4, T = 5, E = 6, D = 8, ne = 10, O = 0, k =
347
347
  growBucketsIfNeeded() {
348
348
  let e = Atomics.load(this.firstBlock.data, w);
349
349
  if (!(e >= this.maxBucketCount || this.slotPool.length * Q <= e * Z)) {
350
- h(this.tableLock);
350
+ o(this.tableLock);
351
351
  try {
352
352
  if (this.syncBucketCount(), this.slotPool.length * Q <= this.bucketCount * Z) return;
353
353
  let e = Math.min(this.bucketCount * 2, this.maxBucketCount);
354
354
  if (e === this.bucketCount) return;
355
355
  this.resizeBuckets(e);
356
356
  } finally {
357
- g(this.tableLock);
357
+ s(this.tableLock);
358
358
  }
359
359
  }
360
360
  }
361
361
  ensureBucketCapacity(e) {
362
362
  let t = Math.ceil(e * Q / Z), n = Math.min(_e(t), this.maxBucketCount);
363
363
  if (!(n <= Atomics.load(this.firstBlock.data, w))) {
364
- h(this.tableLock);
364
+ o(this.tableLock);
365
365
  try {
366
366
  this.syncBucketCount(), n > this.bucketCount && this.resizeBuckets(n);
367
367
  } finally {
368
- g(this.tableLock);
368
+ s(this.tableLock);
369
369
  }
370
370
  }
371
371
  }
@@ -396,18 +396,18 @@ var b = 0, x = 1, S = 2, C = 3, w = 4, T = 5, E = 6, D = 8, ne = 10, O = 0, k =
396
396
  }
397
397
  linkIntoBucket(e, t, n) {
398
398
  n[P] = e, n[F] = t;
399
- let r = this.slotPool, i = r.push(n), a = this.bucketOf(e, t), o = this.getBucketSegment(a), s = this.bucketHeadSegments[a >>> j], u = this.bucketOffset(a), d = u + O;
400
- c(o, d);
401
- let f = u + k;
402
- r.set(i, R, s[f]), s[f] = i, l(o, d);
399
+ let r = this.slotPool, i = r.push(n), a = this.bucketOf(e, t), o = this.getBucketSegment(a), s = this.bucketHeadSegments[a >>> j], c = this.bucketOffset(a), l = c + O;
400
+ f(o, l);
401
+ let u = c + k;
402
+ r.set(i, R, s[u]), s[u] = i, p(o, l);
403
403
  }
404
404
  linkReservedBucket(e, t, n, r, i, a, o) {
405
- let s = this.slotPool, u = s.blockFor(n), d = s.blockOffset(n);
406
- u[d + N] = r, u[d + P] = e, u[d + F] = t, u[d + I] = a, u[d + L] = o, u[d + z] = i;
407
- let f = this.bucketOf(e, t), p = this.getBucketSegment(f), m = this.bucketHeadSegments[f >>> j], h = this.bucketOffset(f), g = h + O;
408
- c(p, g);
405
+ let s = this.slotPool, c = s.blockFor(n), l = s.blockOffset(n);
406
+ c[l + N] = r, c[l + P] = e, c[l + F] = t, c[l + I] = a, c[l + L] = o, c[l + z] = i;
407
+ let u = this.bucketOf(e, t), d = this.getBucketSegment(u), m = this.bucketHeadSegments[u >>> j], h = this.bucketOffset(u), g = h + O;
408
+ f(d, g);
409
409
  let _ = h + k;
410
- u[d + R] = m[_], m[_] = n, l(p, g);
410
+ c[l + R] = m[_], m[_] = n, p(d, g);
411
411
  }
412
412
  relinkRange(e, t, n, r, i, a, o, s, c, l) {
413
413
  let u = n > o ? n : o, d = i < c ? i : c, f = r > s ? r : s, p = a < l ? a : l, m = u <= d && f <= p, h = this.slotScratch;
@@ -427,18 +427,18 @@ var b = 0, x = 1, S = 2, C = 3, w = 4, T = 5, E = 6, D = 8, ne = 10, O = 0, k =
427
427
  }
428
428
  }
429
429
  refreshAnchorInBucket(e, t, n, r, i, a) {
430
- let o = this.nullIndex, s = this.getBucketSegment(e), u = this.bucketOffset(e), d = u + O;
431
- c(s, d);
432
- let f = this.slotPool, p = this.bucketHeadSegments[e >>> j][u + k];
433
- for (; p !== o;) {
434
- let e = f.blockFor(p), o = f.blockOffset(p);
430
+ let o = this.nullIndex, s = this.getBucketSegment(e), c = this.bucketOffset(e), l = c + O;
431
+ f(s, l);
432
+ let u = this.slotPool, d = this.bucketHeadSegments[e >>> j][c + k];
433
+ for (; d !== o;) {
434
+ let e = u.blockFor(d), o = u.blockOffset(d);
435
435
  if (e[o + N] === t && e[o + P] === n && e[o + F] === r) {
436
436
  e[o + I] = i, e[o + L] = a;
437
437
  break;
438
438
  }
439
- p = e[o + R];
439
+ d = e[o + R];
440
440
  }
441
- l(s, d);
441
+ p(s, l);
442
442
  }
443
443
  unlinkRange(e, t, n, r, i) {
444
444
  let a = this.nullIndex, o = this.slotPool;
@@ -448,65 +448,65 @@ var b = 0, x = 1, S = 2, C = 3, w = 4, T = 5, E = 6, D = 8, ne = 10, O = 0, k =
448
448
  }
449
449
  }
450
450
  collectBucket(e, t, n, r, i, a, o, s) {
451
- let u = this.nullIndex, d = r + k, f = r + O;
452
- c(t, f);
453
- let p = n[d], m = this.slotPool;
454
- for (; p !== u;) {
455
- let t = m.blockFor(p), n = m.blockOffset(p);
451
+ let c = this.nullIndex, l = r + k, u = r + O;
452
+ f(t, u);
453
+ let d = n[l], m = this.slotPool;
454
+ for (; d !== c;) {
455
+ let t = m.blockFor(d), n = m.blockOffset(d);
456
456
  if (t[n + P] === i && t[n + F] === a) {
457
457
  let r = t[n + I], c = t[n + L];
458
458
  i === (r > o ? r : o) && a === (c > s ? c : s) && e.push(t[n + N]);
459
459
  }
460
- p = t[n + R];
460
+ d = t[n + R];
461
461
  }
462
- l(t, f);
462
+ p(t, u);
463
463
  }
464
464
  collectNeighborBucket(e, t, n) {
465
465
  let r = this.bucketOf(t, n), i = this.getBucketSegment(r), a = this.bucketHeadSegments[r >>> j], o = this.bucketOffset(r), s = o + k;
466
466
  if (a[s] === this.nullIndex) return;
467
- let u = o + O;
468
- c(i, u);
469
- let d = a[s];
470
- for (; d !== this.nullIndex;) {
471
- let r = this.slotPool.blockFor(d), i = this.slotPool.blockOffset(d);
472
- r[i + P] === t && r[i + F] === n && e.push(r[i + N]), d = r[i + R];
467
+ let c = o + O;
468
+ f(i, c);
469
+ let l = a[s];
470
+ for (; l !== this.nullIndex;) {
471
+ let r = this.slotPool.blockFor(l), i = this.slotPool.blockOffset(l);
472
+ r[i + P] === t && r[i + F] === n && e.push(r[i + N]), l = r[i + R];
473
473
  }
474
- l(i, u);
474
+ p(i, c);
475
475
  }
476
476
  collectNearestBucket(e, t, n, r, i) {
477
- let a = this.bucketOf(t, n), o = this.getBucketSegment(a), s = this.bucketHeadSegments[a >>> j], u = this.bucketOffset(a), d = u + k;
478
- if (s[d] === this.nullIndex) return;
479
- let f = u + O;
480
- c(o, f);
481
- let p = s[d];
482
- for (; p !== this.nullIndex;) {
483
- let a = this.slotPool.blockFor(p), o = this.slotPool.blockOffset(p);
477
+ let a = this.bucketOf(t, n), o = this.getBucketSegment(a), s = this.bucketHeadSegments[a >>> j], c = this.bucketOffset(a), l = c + k;
478
+ if (s[l] === this.nullIndex) return;
479
+ let u = c + O;
480
+ f(o, u);
481
+ let d = s[l];
482
+ for (; d !== this.nullIndex;) {
483
+ let a = this.slotPool.blockFor(d), o = this.slotPool.blockOffset(d);
484
484
  if (a[o + P] === t && a[o + F] === n) {
485
485
  let t = a[o + z], n = this.entityPool.blockFor(t), s = this.entityPool.blockOffset(t), c = a[o + N], l = this.entityBlockDistanceSquared(n, s, r, i);
486
486
  (l < e.distance || l === e.distance && (e.id === void 0 || c < e.id)) && (e.id = c, e.distance = l);
487
487
  }
488
- p = a[o + R];
488
+ d = a[o + R];
489
489
  }
490
- l(o, f);
490
+ p(o, u);
491
491
  }
492
- collectNeighborCandidates(e, t, n, r, i, a, o, s, u) {
493
- let d = this.bucketOf(t, n), f = this.getBucketSegment(d), p = this.bucketHeadSegments[d >>> j], m = this.bucketOffset(d), h = m + k;
494
- if (p[h] === this.nullIndex) return;
492
+ collectNeighborCandidates(e, t, n, r, i, a, o, s, c) {
493
+ let l = this.bucketOf(t, n), u = this.getBucketSegment(l), d = this.bucketHeadSegments[l >>> j], m = this.bucketOffset(l), h = m + k;
494
+ if (d[h] === this.nullIndex) return;
495
495
  let g = m + O;
496
- c(f, g);
497
- let v = p[h];
496
+ f(u, g);
497
+ let v = d[h];
498
498
  for (; v !== this.nullIndex;) {
499
- let c = this.slotPool.blockFor(v), l = this.slotPool.blockOffset(v);
500
- if (c[l + P] === t && c[l + F] === n) {
501
- let d = c[l + z], f = this.entityPool.blockFor(d), p = this.entityPool.blockOffset(d), m = f[p + V], h = f[p + U], g = f[p + H], v = f[p + W];
499
+ let l = this.slotPool.blockFor(v), u = this.slotPool.blockOffset(v);
500
+ if (l[u + P] === t && l[u + F] === n) {
501
+ let d = l[u + z], f = this.entityPool.blockFor(d), p = this.entityPool.blockOffset(d), m = f[p + V], h = f[p + U], g = f[p + H], v = f[p + W];
502
502
  if (t === (r < m ? m : r > h ? h : r) && n === (i < g ? g : i > v ? v : i)) {
503
503
  let t = this.entityBlockDistanceSquared(f, p, a, o);
504
- t <= u && _(e, c[l + N], t, s);
504
+ t <= c && _(e, l[u + N], t, s);
505
505
  }
506
506
  }
507
- v = c[l + R];
507
+ v = l[u + R];
508
508
  }
509
- l(f, g);
509
+ p(u, g);
510
510
  }
511
511
  addEntityCandidate(e, t, n, r, i, a, o) {
512
512
  let s = this.entityDistanceSquared(n, r, i);
@@ -521,27 +521,27 @@ var b = 0, x = 1, S = 2, C = 3, w = 4, T = 5, E = 6, D = 8, ne = 10, O = 0, k =
521
521
  return y(n, r, i, a, i + e[t + q], a + e[t + J]);
522
522
  }
523
523
  unlinkFromBucket(e, t, n, r) {
524
- let i = this.nullIndex, a = this.getBucketSegment(e), o = this.bucketHeadSegments[e >>> j], s = this.bucketOffset(e), u = s + O;
525
- c(a, u);
526
- let d = s + k, f = this.slotPool, p = i, m = o[d];
524
+ let i = this.nullIndex, a = this.getBucketSegment(e), o = this.bucketHeadSegments[e >>> j], s = this.bucketOffset(e), c = s + O;
525
+ f(a, c);
526
+ let l = s + k, u = this.slotPool, d = i, m = o[l];
527
527
  for (; m !== i;) {
528
- let e = f.blockFor(m), s = f.blockOffset(m), c = e[s + R];
529
- if (e[s + N] === t && e[s + P] === n && e[s + F] === r) return p === i ? o[d] = c : f.set(p, R, c), l(a, u), m;
530
- p = m, m = c;
528
+ let e = u.blockFor(m), s = u.blockOffset(m), f = e[s + R];
529
+ if (e[s + N] === t && e[s + P] === n && e[s + F] === r) return d === i ? o[l] = f : u.set(d, R, f), p(a, c), m;
530
+ d = m, m = f;
531
531
  }
532
- return l(a, u), i;
532
+ return p(a, c), i;
533
533
  }
534
534
  };
535
535
  function me(e) {
536
- let t = a(e ?? fe);
537
- if (t !== i.float32 && t !== i.float64) throw Error("SharedSpatialMap must be Float32Array or Float64Array");
536
+ let t = l(e ?? fe);
537
+ if (t !== c.float32 && t !== c.float64) throw Error("SharedSpatialMap must be Float32Array or Float64Array");
538
538
  return t;
539
539
  }
540
540
  function he(e) {
541
- return o(e) === 2 ? Float64Array : Float32Array;
541
+ return u(e) === 2 ? Float64Array : Float32Array;
542
542
  }
543
543
  function ge(e) {
544
- return e === i.float32 ? ie : ae;
544
+ return e === c.float32 ? ie : ae;
545
545
  }
546
546
  function $(e) {
547
547
  return e & e - 1 ? -1 : e - 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@daneren2005/shared-memory-objects",
3
- "version": "1.5.1",
3
+ "version": "1.5.2",
4
4
  "author": "daneren2005@gmail.com",
5
5
  "description": "Creating objects with a SharedArrayBuffer",
6
6
  "homepage": "https://github.com/daneren2005/shared-memory-objects#readme",