@daneren2005/shared-memory-objects 1.6.1 → 1.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/allocated-memory.js +1 -2
- package/dist/cached-item-list.js +1 -2
- package/dist/constant-string.js +13 -14
- package/dist/local-pool.js +5 -6
- package/dist/lock/read-write-lock.js +0 -1
- package/dist/lock/simple-lock.js +0 -1
- package/dist/memory-buffer.js +1 -2
- package/dist/memory-heap.js +24 -25
- package/dist/serialize-object.js +4 -5
- package/dist/shared-list.js +43 -44
- package/dist/shared-map.js +74 -75
- package/dist/shared-pointer-list.js +15 -16
- package/dist/shared-pool.d.ts +1 -0
- package/dist/shared-pool.js +36 -32
- package/dist/shared-pool.js.map +1 -1
- package/dist/shared-stack.js +33 -34
- package/dist/shared-string.js +19 -20
- package/dist/shared-vector.js +23 -24
- package/dist/spatial/shared-quadtree.js +81 -82
- package/dist/spatial/shared-quadtree.js.map +1 -1
- package/dist/spatial/shared-spatial-grid.js +104 -110
- package/dist/spatial/shared-spatial-grid.js.map +1 -1
- package/dist/spatial/shared-spatial-map.d.ts +2 -0
- package/dist/spatial/shared-spatial-map.js +205 -202
- package/dist/spatial/shared-spatial-map.js.map +1 -1
- package/dist/spatial/spatial-entity.js +1 -2
- package/dist/spatial/spatial-neighbors.js +1 -2
- package/dist/utils/16-from-32-array.js +1 -2
- package/dist/utils/16-from-32-float.js +1 -2
- package/dist/utils/16-from-32-int.js +1 -2
- package/dist/utils/16-from-32-uint.js +1 -2
- package/dist/utils/16-from-64-array.js +1 -2
- package/dist/utils/16-from-64-float.js +1 -2
- package/dist/utils/16-from-64-int.js +1 -2
- package/dist/utils/16-from-64-uint.js +1 -2
- package/dist/utils/32-from-64-float.js +1 -2
- package/dist/utils/32-from-64-int.js +1 -2
- package/dist/utils/32-from-64-uint.js +1 -2
- package/dist/utils/array-type.js +1 -2
- package/dist/utils/atomic-math.js +1 -2
- package/dist/utils/float32-atomics.js +1 -2
- package/dist/utils/float64-atomics.js +1 -2
- package/dist/utils/hash.js +1 -2
- package/dist/utils/pointer.js +1 -2
- package/dist/utils/typedarray.js +1 -2
- package/package.json +11 -11
package/dist/shared-map.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
import n from "./
|
|
3
|
-
import {
|
|
4
|
-
import { lock as
|
|
5
|
-
import {
|
|
6
|
-
|
|
1
|
+
import e from "./allocated-memory.js";
|
|
2
|
+
import { getPointer as t, storeRawPointer as n } from "./utils/pointer.js";
|
|
3
|
+
import { get32BitHash as r, mix32 as i } from "./utils/hash.js";
|
|
4
|
+
import { lock as a, unlock as o } from "./lock/simple-lock.js";
|
|
5
|
+
import { getArrayTypeCode as s, getByteMultipler as c, makeArrayView as l } from "./utils/array-type.js";
|
|
6
|
+
// src/shared-map.ts
|
|
7
7
|
var u = 0, d = 1, f = 2, p = 16, m = 3, h = 4, g = 0, _ = 1, v = 2, y = 3, b = 4, x = 5, S = class S {
|
|
8
8
|
static ALLOCATE_COUNT = 6;
|
|
9
9
|
memory;
|
|
@@ -19,14 +19,14 @@ var u = 0, d = 1, f = 2, p = 16, m = 3, h = 4, g = 0, _ = 1, v = 2, y = 3, b = 4
|
|
|
19
19
|
cachedValues;
|
|
20
20
|
cachedCapacity = 0;
|
|
21
21
|
ensureTable() {
|
|
22
|
-
let
|
|
22
|
+
let n = this.pointerMemory.data, r = n[g], i = n[b];
|
|
23
23
|
if (r === this.cachedTablePointer && i === this.cachedCapacity && this.cachedSlots) return;
|
|
24
|
-
let a = new
|
|
24
|
+
let a = new e(this.memory, t(r, this.memory.positionBits));
|
|
25
25
|
this.cachedTableMemory = a, this.cachedSlots = a.data, this.cachedValues = this.makeValuesView(a, i), this.cachedCapacity = i, this.cachedTablePointer = r;
|
|
26
26
|
}
|
|
27
27
|
makeValuesView(e, t) {
|
|
28
28
|
let n = e.bufferByteOffset + t * 2 * Uint32Array.BYTES_PER_ELEMENT;
|
|
29
|
-
return
|
|
29
|
+
return l(this.cachedType, e.data.buffer, n, t);
|
|
30
30
|
}
|
|
31
31
|
tableLength(e) {
|
|
32
32
|
return e * (2 + this.cachedValueUnits);
|
|
@@ -44,39 +44,39 @@ var u = 0, d = 1, f = 2, p = 16, m = 3, h = 4, g = 0, _ = 1, v = 2, y = 3, b = 4
|
|
|
44
44
|
return this.cachedType;
|
|
45
45
|
}
|
|
46
46
|
get usedMemory() {
|
|
47
|
-
|
|
47
|
+
a(this.lock);
|
|
48
48
|
try {
|
|
49
49
|
return this.ensureTable(), (this.ownsFirstBlock ? this.pointerMemory.usedMemory : 0) + this.cachedTableMemory.usedMemory;
|
|
50
50
|
} finally {
|
|
51
|
-
|
|
51
|
+
o(this.lock);
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
|
-
constructor(
|
|
55
|
-
this.memory =
|
|
54
|
+
constructor(t, n) {
|
|
55
|
+
this.memory = t, n && "firstBlock" in n ? n.firstBlock instanceof e ? (this.pointerMemory = n.firstBlock, this.initialize(n), this.ownsFirstBlock = !1) : this.pointerMemory = new e(t, {
|
|
56
56
|
length: S.ALLOCATE_COUNT,
|
|
57
|
-
...
|
|
58
|
-
}) : (this.pointerMemory =
|
|
57
|
+
...n.firstBlock
|
|
58
|
+
}) : (this.pointerMemory = t.allocUI32(S.ALLOCATE_COUNT), this.initialize(n)), this.lock = new Int32Array(this.pointerMemory.data.buffer, this.pointerMemory.bufferByteOffset + _ * Uint32Array.BYTES_PER_ELEMENT, 1), this.cachedType = Atomics.load(this.pointerMemory.data, x), this.cachedValueUnits = c(this.cachedType), this.maxTableCapacity = 2 ** Math.floor(Math.log2(this.memory.maxAllocationLength / (2 + this.cachedValueUnits)));
|
|
59
59
|
}
|
|
60
60
|
initialize(e) {
|
|
61
|
-
let
|
|
62
|
-
this.cachedValueUnits =
|
|
63
|
-
let
|
|
64
|
-
for (;
|
|
65
|
-
if (
|
|
66
|
-
let
|
|
67
|
-
|
|
61
|
+
let t = e && "type" in e ? e.type : void 0, r = s(t ?? Uint32Array);
|
|
62
|
+
this.cachedValueUnits = c(r);
|
|
63
|
+
let i = e && "capacity" in e ? e.capacity : void 0, a = p;
|
|
64
|
+
for (; a < (i ?? p);) a *= 2;
|
|
65
|
+
if (a * (2 + this.cachedValueUnits) > this.memory.maxAllocationLength) throw Error(`SharedMap capacity ${a} exceeds the largest contiguous allocation`);
|
|
66
|
+
let o = this.memory.allocUI32(a * (2 + this.cachedValueUnits));
|
|
67
|
+
n(this.pointerMemory.data, g, o.pointer), Atomics.store(this.pointerMemory.data, b, a), Atomics.store(this.pointerMemory.data, x, r);
|
|
68
68
|
}
|
|
69
69
|
set(e, t) {
|
|
70
|
-
|
|
70
|
+
a(this.lock);
|
|
71
71
|
try {
|
|
72
72
|
this.setNoLock(e, t);
|
|
73
73
|
} finally {
|
|
74
|
-
|
|
74
|
+
o(this.lock);
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
setAll(e) {
|
|
78
78
|
let t = Array.from(e);
|
|
79
|
-
|
|
79
|
+
a(this.lock);
|
|
80
80
|
try {
|
|
81
81
|
let e = this.pointerMemory.data, n = e[v] + e[y] + t.length;
|
|
82
82
|
if (n * h > e[b] * m) {
|
|
@@ -86,18 +86,18 @@ var u = 0, d = 1, f = 2, p = 16, m = 3, h = 4, g = 0, _ = 1, v = 2, y = 3, b = 4
|
|
|
86
86
|
}
|
|
87
87
|
for (let [e, n] of t) this.setNoLock(e, n);
|
|
88
88
|
} finally {
|
|
89
|
-
|
|
89
|
+
o(this.lock);
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
setNoLock(e, t) {
|
|
93
|
-
let n =
|
|
94
|
-
(
|
|
95
|
-
let
|
|
93
|
+
let n = r(e), a = this.pointerMemory.data;
|
|
94
|
+
(a[v] + a[y] + 1) * h > a[b] * m && this.resizeForInsert(e), this.ensureTable();
|
|
95
|
+
let o = this.cachedSlots, s = this.cachedValues, c = this.cachedCapacity, l = c - 1, p = c, g = i(n) & l, _ = -1;
|
|
96
96
|
for (;;) {
|
|
97
|
-
let e =
|
|
97
|
+
let e = o[g];
|
|
98
98
|
if (e === u) {
|
|
99
99
|
let e = _ >= 0 ? _ : g;
|
|
100
|
-
|
|
100
|
+
o[e] = d, o[p + e] = n, s[e] = t, a[v]++, _ >= 0 && a[y]--;
|
|
101
101
|
return;
|
|
102
102
|
}
|
|
103
103
|
if (e === f) {
|
|
@@ -106,37 +106,37 @@ var u = 0, d = 1, f = 2, p = 16, m = 3, h = 4, g = 0, _ = 1, v = 2, y = 3, b = 4
|
|
|
106
106
|
this.reuseTombstone(g, n, t);
|
|
107
107
|
return;
|
|
108
108
|
}
|
|
109
|
-
} else if (
|
|
110
|
-
|
|
109
|
+
} else if (o[p + g] === n) {
|
|
110
|
+
s[g] = t;
|
|
111
111
|
return;
|
|
112
112
|
}
|
|
113
|
-
g = g + 1 &
|
|
113
|
+
g = g + 1 & l;
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
update(e, t) {
|
|
117
|
-
let n =
|
|
118
|
-
|
|
117
|
+
let n = r(e);
|
|
118
|
+
a(this.lock);
|
|
119
119
|
try {
|
|
120
120
|
let r = this.pointerMemory.data;
|
|
121
121
|
(r[v] + r[y] + 1) * h > r[b] * m && this.resizeForInsert(e), this.ensureTable();
|
|
122
|
-
let
|
|
122
|
+
let a = this.cachedSlots, o = this.cachedValues, s = this.cachedCapacity, c = s - 1, l = i(n) & c, p = -1;
|
|
123
123
|
for (;;) {
|
|
124
|
-
let e =
|
|
124
|
+
let e = a[l];
|
|
125
125
|
if (e === u) {
|
|
126
|
-
let e = t(void 0),
|
|
127
|
-
return i
|
|
126
|
+
let e = t(void 0), i = p >= 0 ? p : l;
|
|
127
|
+
return a[i] = d, a[s + i] = n, o[i] = e, r[v]++, p >= 0 && r[y]--, e;
|
|
128
128
|
}
|
|
129
129
|
if (e === f) {
|
|
130
|
-
if (
|
|
131
|
-
p < 0 && (p =
|
|
132
|
-
} else if (
|
|
133
|
-
let e = t(
|
|
134
|
-
return
|
|
130
|
+
if (l === p) return this.reuseTombstone(l, n, t);
|
|
131
|
+
p < 0 && (p = l);
|
|
132
|
+
} else if (a[s + l] === n) {
|
|
133
|
+
let e = t(o[l]);
|
|
134
|
+
return o[l] = e, e;
|
|
135
135
|
}
|
|
136
|
-
|
|
136
|
+
l = l + 1 & c;
|
|
137
137
|
}
|
|
138
138
|
} finally {
|
|
139
|
-
|
|
139
|
+
o(this.lock);
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
142
|
resizeForInsert(e) {
|
|
@@ -149,52 +149,52 @@ var u = 0, d = 1, f = 2, p = 16, m = 3, h = 4, g = 0, _ = 1, v = 2, y = 3, b = 4
|
|
|
149
149
|
return i[e] = d, i[this.cachedCapacity + e] = t, a[e] = r, this.pointerMemory.data[v]++, this.pointerMemory.data[y]--, r;
|
|
150
150
|
}
|
|
151
151
|
get(e) {
|
|
152
|
-
|
|
152
|
+
a(this.lock);
|
|
153
153
|
try {
|
|
154
154
|
return this.getNoLock(e);
|
|
155
155
|
} finally {
|
|
156
|
-
|
|
156
|
+
o(this.lock);
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
159
|
getAll(e) {
|
|
160
|
-
|
|
160
|
+
a(this.lock);
|
|
161
161
|
try {
|
|
162
162
|
let t = [];
|
|
163
163
|
for (let n of e) t.push(this.getNoLock(n));
|
|
164
164
|
return t;
|
|
165
165
|
} finally {
|
|
166
|
-
|
|
166
|
+
o(this.lock);
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
169
|
getNoLock(e) {
|
|
170
|
-
let t =
|
|
170
|
+
let t = r(e);
|
|
171
171
|
this.ensureTable();
|
|
172
|
-
let n = this.cachedSlots,
|
|
172
|
+
let n = this.cachedSlots, a = this.cachedValues, o = this.cachedCapacity, s = o - 1, c = o, l = i(t) & s, f = l;
|
|
173
173
|
do {
|
|
174
|
-
let e = n[
|
|
174
|
+
let e = n[l];
|
|
175
175
|
if (e === u) return;
|
|
176
|
-
if (e === d && n[
|
|
177
|
-
|
|
178
|
-
} while (
|
|
176
|
+
if (e === d && n[c + l] === t) return a[l];
|
|
177
|
+
l = l + 1 & s;
|
|
178
|
+
} while (l !== f);
|
|
179
179
|
}
|
|
180
180
|
has(e) {
|
|
181
181
|
return this.get(e) !== void 0;
|
|
182
182
|
}
|
|
183
183
|
delete(e) {
|
|
184
|
-
let t =
|
|
185
|
-
|
|
184
|
+
let t = r(e);
|
|
185
|
+
a(this.lock);
|
|
186
186
|
try {
|
|
187
187
|
this.ensureTable();
|
|
188
|
-
let e = this.pointerMemory.data, n = this.cachedSlots, r = this.cachedCapacity,
|
|
188
|
+
let e = this.pointerMemory.data, n = this.cachedSlots, r = this.cachedCapacity, a = r - 1, o = r, s = i(t) & a, c = s;
|
|
189
189
|
do {
|
|
190
|
-
let r = n[
|
|
190
|
+
let r = n[s];
|
|
191
191
|
if (r === u) return !1;
|
|
192
|
-
if (r === d && n[
|
|
193
|
-
|
|
194
|
-
} while (
|
|
192
|
+
if (r === d && n[o + s] === t) return n[s] = f, e[v]--, e[y]++, !0;
|
|
193
|
+
s = s + 1 & a;
|
|
194
|
+
} while (s !== c);
|
|
195
195
|
return !1;
|
|
196
196
|
} finally {
|
|
197
|
-
|
|
197
|
+
o(this.lock);
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
200
|
*[Symbol.iterator]() {
|
|
@@ -204,19 +204,19 @@ var u = 0, d = 1, f = 2, p = 16, m = 3, h = 4, g = 0, _ = 1, v = 2, y = 3, b = 4
|
|
|
204
204
|
}
|
|
205
205
|
resize(e) {
|
|
206
206
|
this.ensureTable();
|
|
207
|
-
let
|
|
208
|
-
if (e === void 0 && (
|
|
207
|
+
let t = this.cachedTableMemory, r = this.cachedSlots, a = this.cachedValues, o = this.cachedCapacity, s = o, c = Atomics.load(this.pointerMemory.data, v), l = e ?? o;
|
|
208
|
+
if (e === void 0 && (c + 1) * h > o * m && (l = o * 2), this.tableLength(l) > this.memory.maxAllocationLength) {
|
|
209
209
|
let e = Math.floor(this.memory.maxAllocationLength / (2 + this.cachedValueUnits) * m / h);
|
|
210
|
-
throw Error(`SharedMap can't grow to capacity ${
|
|
210
|
+
throw Error(`SharedMap can't grow to capacity ${l}: its table would exceed the largest contiguous allocation (${this.memory.maxAllocationLength} u32s, ~${e} entries). Use larger buffers or shard across multiple maps.`);
|
|
211
211
|
}
|
|
212
|
-
let f = this.memory.allocUI32(this.tableLength(
|
|
213
|
-
for (let e = 0; e <
|
|
212
|
+
let f = this.memory.allocUI32(this.tableLength(l)), p = f.data, _ = this.makeValuesView(f, l), x = l - 1, S = l;
|
|
213
|
+
for (let e = 0; e < o; e++) {
|
|
214
214
|
if (r[e] !== d) continue;
|
|
215
|
-
let t = r[
|
|
216
|
-
for (; p[
|
|
217
|
-
p[
|
|
215
|
+
let t = r[s + e], n = a[e], o = i(t) & x;
|
|
216
|
+
for (; p[o] !== u;) o = o + 1 & x;
|
|
217
|
+
p[o] = d, p[S + o] = t, _[o] = n;
|
|
218
218
|
}
|
|
219
|
-
|
|
219
|
+
n(this.pointerMemory.data, g, f.pointer), Atomics.store(this.pointerMemory.data, b, l), Atomics.store(this.pointerMemory.data, y, 0), this.cachedTablePointer = 0, this.cachedSlots = void 0, t.free();
|
|
220
220
|
}
|
|
221
221
|
free() {
|
|
222
222
|
this.ensureTable(), this.cachedTableMemory.free(), this.ownsFirstBlock && this.pointerMemory.free();
|
|
@@ -225,7 +225,6 @@ var u = 0, d = 1, f = 2, p = 16, m = 3, h = 4, g = 0, _ = 1, v = 2, y = 3, b = 4
|
|
|
225
225
|
return { firstBlock: this.pointerMemory.getSharedMemory() };
|
|
226
226
|
}
|
|
227
227
|
};
|
|
228
|
-
//#endregion
|
|
229
228
|
export { S as default };
|
|
230
229
|
|
|
231
230
|
//# sourceMappingURL=shared-map.js.map
|
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import r from "./
|
|
4
|
-
|
|
1
|
+
import e from "./allocated-memory.js";
|
|
2
|
+
import t from "./shared-list.js";
|
|
3
|
+
import { createPointer as n, loadPointer as r } from "./utils/pointer.js";
|
|
4
|
+
// src/shared-pointer-list.ts
|
|
5
5
|
var i = class {
|
|
6
6
|
memory;
|
|
7
7
|
list;
|
|
8
|
-
constructor(e,
|
|
9
|
-
this.memory = e, this.list =
|
|
8
|
+
constructor(e, n) {
|
|
9
|
+
this.memory = e, this.list = n ? new t(e, n) : new t(e);
|
|
10
10
|
}
|
|
11
11
|
get length() {
|
|
12
12
|
return this.list.length;
|
|
13
13
|
}
|
|
14
|
-
insert(
|
|
15
|
-
this.list.insert(e
|
|
14
|
+
insert(e) {
|
|
15
|
+
this.list.insert(n(e.memory.bufferPosition, e.memory.bufferByteOffset, this.memory.positionBits));
|
|
16
16
|
}
|
|
17
|
-
delete(
|
|
18
|
-
let
|
|
19
|
-
return this.list.compact(),
|
|
17
|
+
delete(e) {
|
|
18
|
+
let t = this.list.deleteValue(n(e.memory.bufferPosition, e.memory.bufferByteOffset, this.memory.positionBits));
|
|
19
|
+
return this.list.compact(), t;
|
|
20
20
|
}
|
|
21
21
|
compact() {
|
|
22
22
|
this.list.compact();
|
|
23
23
|
}
|
|
24
24
|
*[Symbol.iterator]() {
|
|
25
|
-
let
|
|
26
|
-
for (let { data:
|
|
27
|
-
let { bufferPosition:
|
|
28
|
-
bufferPosition:
|
|
25
|
+
let t = this.list[Symbol.iterator]();
|
|
26
|
+
for (let { data: n } of t) {
|
|
27
|
+
let { bufferPosition: t, bufferByteOffset: i } = r(n, 0, this.memory.positionBits), a = new e(this.memory, {
|
|
28
|
+
bufferPosition: t,
|
|
29
29
|
bufferByteOffset: i
|
|
30
30
|
});
|
|
31
31
|
yield this.createItem(a);
|
|
@@ -45,7 +45,6 @@ var i = class {
|
|
|
45
45
|
this.list.free();
|
|
46
46
|
}
|
|
47
47
|
};
|
|
48
|
-
//#endregion
|
|
49
48
|
export { i as default };
|
|
50
49
|
|
|
51
50
|
//# sourceMappingURL=shared-pointer-list.js.map
|
package/dist/shared-pool.d.ts
CHANGED
|
@@ -39,6 +39,7 @@ export default class SharedPool<T extends NumericArray = Uint32Array> implements
|
|
|
39
39
|
reserveContiguous(count: number): number;
|
|
40
40
|
deleteIndex(index: number): void;
|
|
41
41
|
clear(): void;
|
|
42
|
+
getRecycledMask(): Uint8Array;
|
|
42
43
|
[Symbol.iterator](): Generator<T, void, unknown>;
|
|
43
44
|
entries(): IterableIterator<[number, T]>;
|
|
44
45
|
private getFullDataBlock;
|
package/dist/shared-pool.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import l from "./
|
|
6
|
-
|
|
1
|
+
import e from "./allocated-memory.js";
|
|
2
|
+
import { lock as t, unlock as n } from "./lock/simple-lock.js";
|
|
3
|
+
import r from "./shared-stack.js";
|
|
4
|
+
import { getPointer as i, loadRawPointer as a, storeRawPointer as o } from "./utils/pointer.js";
|
|
5
|
+
import { getArrayTypeCode as s, getByteMultipler as c, makeArrayView as l } from "./utils/array-type.js";
|
|
6
|
+
// src/shared-pool.ts
|
|
7
7
|
var u = 0, d = 1, f = 2, p = 3, m = 4, h = 5, g = 100, _ = 1e6, v = 1e5, y = class y {
|
|
8
8
|
static ALLOCATE_COUNT = 6;
|
|
9
9
|
static getAllocateCount(e, t) {
|
|
10
|
-
let n = t?.maxChunkSize ?? g,
|
|
11
|
-
return y.ALLOCATE_COUNT +
|
|
10
|
+
let n = t?.maxChunkSize ?? g, i = t?.maxLength ?? _, a = t?.maxRecycledLength ?? v, o = r.getMaxSegmentLength(e.maxAllocationLength), s = r.getAllocateCount(i / n, void 0, o), c = r.getAllocateCount(a, void 0, o);
|
|
11
|
+
return y.ALLOCATE_COUNT + s + c;
|
|
12
12
|
}
|
|
13
13
|
memory;
|
|
14
14
|
firstBlock;
|
|
@@ -52,29 +52,29 @@ var u = 0, d = 1, f = 2, p = 3, m = 4, h = 5, g = 100, _ = 1e6, v = 1e5, y = cla
|
|
|
52
52
|
}
|
|
53
53
|
get usedMemory() {
|
|
54
54
|
let t = this.ownsFirstBlock ? this.firstBlock.usedMemory : 0;
|
|
55
|
-
for (let n of this.pointerStack) t += new
|
|
55
|
+
for (let n of this.pointerStack) t += new e(this.memory, i(n, this.memory.positionBits)).usedMemory;
|
|
56
56
|
return t + this.pointerStack.usedMemory + this.recycleStack.usedMemory;
|
|
57
57
|
}
|
|
58
|
-
constructor(
|
|
59
|
-
this.memory =
|
|
58
|
+
constructor(t, n) {
|
|
59
|
+
this.memory = t, n && "firstBlock" in n ? (this.firstBlock = n.firstBlock instanceof e ? n.firstBlock : new e(t, {
|
|
60
60
|
length: y.ALLOCATE_COUNT,
|
|
61
|
-
...
|
|
62
|
-
}), this.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + d * Uint32Array.BYTES_PER_ELEMENT, 2), "type" in
|
|
61
|
+
...n.firstBlock
|
|
62
|
+
}), this.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + d * Uint32Array.BYTES_PER_ELEMENT, 2), "type" in n || "dataLength" in n || "maxLength" in n || "maxChunkSize" in n || "maxRecycledLength" in n ? (this.initialize(n), this.ownsFirstBlock = !1) : (this.pointerStack = new r(t, { firstBlock: i(a(this.firstBlock.data, m), t.positionBits) }), this.recycleStack = new r(t, { firstBlock: i(a(this.firstBlock.data, h), t.positionBits) }), this.ownsFirstBlock = !0)) : (this.firstBlock = t.allocUI32(y.getAllocateCount(t, n)), this.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + d * Uint32Array.BYTES_PER_ELEMENT, 2), this.initialize(n), this.ownsFirstBlock = !0), this.cachedType = this.uint16Array[0], this.cachedDataLength = Math.max(1, this.uint16Array[1]), this.cachedMaxChunkSize = this.firstBlock.data[f], this.cachedByteMultipler = c(this.cachedType), this.maxLength = this.cachedMaxChunkSize * this.pointerStack.maxLength, this.growLock = new Int32Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + p * Uint32Array.BYTES_PER_ELEMENT, 1);
|
|
63
63
|
}
|
|
64
64
|
initialize(e) {
|
|
65
|
-
let t = e?.dataLength ?? 1,
|
|
66
|
-
this.pointerStack = new
|
|
65
|
+
let t = e?.dataLength ?? 1, n = e?.maxChunkSize ?? g, i = e?.maxLength ?? _, a = e?.maxRecycledLength ?? v, l = r.getMaxSegmentLength(this.memory.maxAllocationLength), u = i / n, d = r.getAllocateCount(u, void 0, l), f = r.getAllocateCount(a, void 0, l);
|
|
66
|
+
this.pointerStack = new r(this.memory, {
|
|
67
67
|
firstBlock: this.firstBlock.getSubAllocation(y.ALLOCATE_COUNT, d),
|
|
68
68
|
type: Uint32Array,
|
|
69
69
|
maxLength: u
|
|
70
|
-
}),
|
|
70
|
+
}), o(this.firstBlock.data, m, this.pointerStack.pointer), this.recycleStack = new r(this.memory, {
|
|
71
71
|
firstBlock: this.firstBlock.getSubAllocation(y.ALLOCATE_COUNT + d, f),
|
|
72
72
|
type: Uint32Array,
|
|
73
|
-
maxLength:
|
|
74
|
-
}),
|
|
75
|
-
let p =
|
|
76
|
-
this.type = p, this.dataLength = t, this.maxChunkSize =
|
|
77
|
-
let b =
|
|
73
|
+
maxLength: a
|
|
74
|
+
}), o(this.firstBlock.data, h, this.recycleStack.pointer);
|
|
75
|
+
let p = s(e?.type ?? Uint32Array);
|
|
76
|
+
this.type = p, this.dataLength = t, this.maxChunkSize = n;
|
|
77
|
+
let b = c(p), x = this.memory.allocUI32(n * t * b);
|
|
78
78
|
this.pointerStack.push(x.pointer);
|
|
79
79
|
}
|
|
80
80
|
at(e) {
|
|
@@ -118,6 +118,11 @@ var u = 0, d = 1, f = 2, p = 3, m = 4, h = 5, g = 100, _ = 1e6, v = 1e5, y = cla
|
|
|
118
118
|
clear() {
|
|
119
119
|
this.firstBlock.data[u] = 0, this.recycleStack.clear();
|
|
120
120
|
}
|
|
121
|
+
getRecycledMask() {
|
|
122
|
+
let e = Atomics.load(this.firstBlock.data, u), t = new Uint8Array(e);
|
|
123
|
+
for (let n of this.recycleStack) n < e && (t[n] = 1);
|
|
124
|
+
return t;
|
|
125
|
+
}
|
|
121
126
|
*[Symbol.iterator]() {
|
|
122
127
|
let e = {};
|
|
123
128
|
for (let t of this.recycleStack) e[t] = !0;
|
|
@@ -136,22 +141,22 @@ var u = 0, d = 1, f = 2, p = 3, m = 4, h = 5, g = 100, _ = 1e6, v = 1e5, y = cla
|
|
|
136
141
|
e !== i && (r = this.getFullDataBlock(a), i = e), yield [a, this.getDataBlock(r, a % t)];
|
|
137
142
|
}
|
|
138
143
|
}
|
|
139
|
-
getFullDataBlock(
|
|
140
|
-
let
|
|
141
|
-
if (
|
|
142
|
-
if (
|
|
143
|
-
|
|
144
|
+
getFullDataBlock(r) {
|
|
145
|
+
let a = Math.floor(r / this.cachedMaxChunkSize), o = this.cachedFullDataBlock[a];
|
|
146
|
+
if (o) return o;
|
|
147
|
+
if (a >= this.pointerStack.length) {
|
|
148
|
+
t(this.growLock);
|
|
144
149
|
try {
|
|
145
|
-
for (;
|
|
150
|
+
for (; a >= this.pointerStack.length;) {
|
|
146
151
|
let e = this.memory.allocUI32(this.cachedMaxChunkSize * this.cachedDataLength * this.cachedByteMultipler);
|
|
147
152
|
this.pointerStack.push(e.pointer);
|
|
148
153
|
}
|
|
149
154
|
} finally {
|
|
150
|
-
|
|
155
|
+
n(this.growLock);
|
|
151
156
|
}
|
|
152
157
|
}
|
|
153
|
-
let
|
|
154
|
-
return this.cachedFullDataBlock[
|
|
158
|
+
let s = new e(this.memory, i(this.pointerStack.at(a), this.memory.positionBits)), c = this.cachedDataLength * this.cachedMaxChunkSize, u = l(this.cachedType, s.data.buffer, s.bufferByteOffset, c);
|
|
159
|
+
return this.cachedFullDataBlock[a] = u, u;
|
|
155
160
|
}
|
|
156
161
|
getDataBlock(e, t) {
|
|
157
162
|
let n = this.cachedDataLength, r = t * n;
|
|
@@ -159,14 +164,13 @@ var u = 0, d = 1, f = 2, p = 3, m = 4, h = 5, g = 100, _ = 1e6, v = 1e5, y = cla
|
|
|
159
164
|
}
|
|
160
165
|
free() {
|
|
161
166
|
this.recycleStack.free();
|
|
162
|
-
for (let t of this.pointerStack) new
|
|
167
|
+
for (let t of this.pointerStack) new e(this.memory, i(t, this.memory.positionBits)).free();
|
|
163
168
|
this.pointerStack.free(), this.ownsFirstBlock && this.firstBlock.free();
|
|
164
169
|
}
|
|
165
170
|
getSharedMemory() {
|
|
166
171
|
return { firstBlock: this.firstBlock.getSharedMemory() };
|
|
167
172
|
}
|
|
168
173
|
};
|
|
169
|
-
//#endregion
|
|
170
174
|
export { y as default };
|
|
171
175
|
|
|
172
176
|
//# sourceMappingURL=shared-pool.js.map
|
package/dist/shared-pool.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared-pool.js","names":["AllocatedMemory","SharedStack","LENGTH_INDEX","TYPE_INDEX","MAX_CHUNK_SIZE_INDEX","GROW_LOCK_INDEX","POINTER_STACK_INDEX","RECYCLE_STACK_INDEX","DEFAULT_MAX_CHUNK_SIZE","DEFAULT_MAX_LENGTH","DEFAULT_MAX_RECYCLED_LENGTH","SharedPool","maxChunkSize","config","maxLength","maxRecycledLength","maxSegmentLength","memory","pointerStackAllocateCount","recycleStackAllocateCount","value","total","pointer","getPointer","loadRawPointer","getByteMultipler","dataLength","pointerStackMaxLength","storeRawPointer","typeCode","getArrayTypeCode","byteMultipler","firstArray","dataBlock","index","dataIndex","isSingleValue","values","newIndex","startIndex","i","count","recycledValues","length","dataBlockIndex","newDataBlockIndex","pointerIndex","cachedDataBlock","newArray","array","blockLength","data","makeArrayView","start","rawData"],"sources":["../src/shared-pool.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 SharedStack from './shared-stack';\nimport { getPointer, loadRawPointer, storeRawPointer } 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 MAX_CHUNK_SIZE_INDEX = 2;\n// Guards appending a chunk pointer so only one thread grows the pointer stack at a time\nconst GROW_LOCK_INDEX = 3;\n// Pointers to the two backing SharedStacks (their own firstBlocks): the chunk pointers and the recycled indexes\nconst POINTER_STACK_INDEX = 4;\nconst RECYCLE_STACK_INDEX = 5;\n\nconst DEFAULT_MAX_CHUNK_SIZE = 100;\nconst DEFAULT_MAX_LENGTH = 1_000_000;\nconst DEFAULT_MAX_RECYCLED_LENGTH = 100_000;\n\n// Array with stable indexes and maximum contiguous memory sizes (necessary to fit large data sets into max 1MB buffers).\n// The chunk pointers and the recycled indexes both live in SharedStacks, whose segments are never moved or freed once\n// published, so concurrent readers never touch memory that is being reclaimed.\n// https://plflib.org/colony.htm for future enhancements - it seems to be an optimized version of what we were aiming for with this\nexport default class SharedPool<T extends NumericArray = Uint32Array> implements Iterable<T> {\n\tstatic readonly ALLOCATE_COUNT = 6;\n\n\t// u32s a pool with this config needs for its firstBlock, including both inlined stacks. Lets an owner (e.g.\n\t// SharedQuadtree) reserve one contiguous slice and hand it back through the config's firstBlock to inline the pool.\n\tstatic getAllocateCount(memory: MemoryHeap, config?: SharedPoolConfig<NumericArray>): number {\n\t\tlet maxChunkSize = config?.maxChunkSize ?? DEFAULT_MAX_CHUNK_SIZE;\n\t\tlet maxLength = config?.maxLength ?? DEFAULT_MAX_LENGTH;\n\t\tlet maxRecycledLength = config?.maxRecycledLength ?? DEFAULT_MAX_RECYCLED_LENGTH;\n\t\tlet maxSegmentLength = SharedStack.getMaxSegmentLength(memory.maxAllocationLength);\n\t\tlet pointerStackAllocateCount = SharedStack.getAllocateCount(maxLength / maxChunkSize, undefined, maxSegmentLength);\n\t\tlet recycleStackAllocateCount = SharedStack.getAllocateCount(maxRecycledLength, undefined, maxSegmentLength);\n\t\treturn SharedPool.ALLOCATE_COUNT + pointerStackAllocateCount + recycleStackAllocateCount;\n\t}\n\n\tprivate memory: MemoryHeap;\n\n\t// Current Length, Type/DataLength, MaxChunkLength, Grow lock, Pointer stack, Recycle stack\n\tprivate firstBlock: AllocatedMemory;\n\tprivate uint16Array: Uint16Array;\n\tprivate growLock: Int32Array;\n\tprivate pointerStack!: SharedStack;\n\tprivate recycleStack!: SharedStack;\n\treadonly maxLength: number;\n\n\t// False when firstBlock is a slice of another structure's memory (e.g. inlined in SharedQuadtree); that owner frees it\n\tprivate ownsFirstBlock: boolean;\n\n\t// Chunk views cached by pointer index. An array (dense, small integer keys) resolves faster than an object on the hot\n\t// get/set path and never needs rehashing.\n\tprivate cachedFullDataBlock: Array<T | undefined> = [];\n\n\t// Config is written once at construction and never mutated, so it is cached in plain fields\n\tprivate readonly cachedMaxChunkSize: number;\n\tprivate readonly cachedDataLength: number;\n\tprivate readonly cachedType: number;\n\tprivate readonly cachedByteMultipler: number;\n\n\tget length(): number {\n\t\treturn Atomics.load(this.firstBlock.data, LENGTH_INDEX) - this.recycleStack.length;\n\t}\n\tget maxChunkSize(): number {\n\t\treturn this.cachedMaxChunkSize;\n\t}\n\tprivate set maxChunkSize(value: number) {\n\t\tAtomics.store(this.firstBlock.data, MAX_CHUNK_SIZE_INDEX, value);\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\tget bufferLength(): number {\n\t\treturn this.cachedMaxChunkSize * this.pointerStack.length;\n\t}\n\tget byteMultipler(): number {\n\t\treturn this.cachedByteMultipler;\n\t}\n\n\t// Own internal memory (which already includes both stacks' inlined firstBlocks) plus every chunk buffer and the\n\t// segments each stack has grown. The stacks' usedMemory excludes their inlined firstBlocks, so they aren't double-counted.\n\tget usedMemory(): number {\n\t\tlet total = this.ownsFirstBlock ? this.firstBlock.usedMemory : 0;\n\t\tfor(let pointer of this.pointerStack) {\n\t\t\ttotal += new AllocatedMemory(this.memory, getPointer(pointer, this.memory.positionBits)).usedMemory;\n\t\t}\n\t\treturn total + this.pointerStack.usedMemory + this.recycleStack.usedMemory;\n\t}\n\n\tconstructor(memory: MemoryHeap, config?: SharedPoolConfig<T> | SharedPoolMemory | SharedPoolMemory & SharedPoolConfig<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 SharedQuadtree); 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\tthis.firstBlock = config.firstBlock instanceof AllocatedMemory\n\t\t\t\t? config.firstBlock\n\t\t\t\t: new AllocatedMemory(memory, { length: SharedPool.ALLOCATE_COUNT, ...config.firstBlock });\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 || 'dataLength' in config || 'maxLength' in config || 'maxChunkSize' in config || 'maxRecycledLength' 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.pointerStack = new SharedStack(memory, {\n\t\t\t\t\tfirstBlock: getPointer(loadRawPointer(this.firstBlock.data, POINTER_STACK_INDEX), memory.positionBits),\n\t\t\t\t});\n\t\t\t\tthis.recycleStack = new SharedStack(memory, {\n\t\t\t\t\tfirstBlock: getPointer(loadRawPointer(this.firstBlock.data, RECYCLE_STACK_INDEX), memory.positionBits),\n\t\t\t\t});\n\t\t\t\tthis.ownsFirstBlock = true;\n\t\t\t}\n\t\t} else {\n\t\t\tthis.firstBlock = memory.allocUI32(SharedPool.getAllocateCount(memory, config));\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.initialize(config);\n\t\t\tthis.ownsFirstBlock = true;\n\t\t}\n\n\t\tthis.cachedType = this.uint16Array[0];\n\t\tthis.cachedDataLength = Math.max(1, this.uint16Array[1]);\n\t\tthis.cachedMaxChunkSize = this.firstBlock.data[MAX_CHUNK_SIZE_INDEX];\n\t\tthis.cachedByteMultipler = getByteMultipler(this.cachedType);\n\t\tthis.maxLength = this.cachedMaxChunkSize * this.pointerStack.maxLength;\n\n\t\tthis.growLock = new Int32Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + GROW_LOCK_INDEX * Uint32Array.BYTES_PER_ELEMENT, 1);\n\t}\n\n\t// Lay out the two inlined stacks (their firstBlocks sliced from ours), record their pointers, and seed the first chunk.\n\t// firstBlock and uint16Array must already be set. Used by both the fresh-alloc path and the inline-in-place path.\n\tprivate initialize(config?: SharedPoolConfig<T>) {\n\t\tlet dataLength = config?.dataLength ?? 1;\n\t\tlet maxChunkSize = config?.maxChunkSize ?? DEFAULT_MAX_CHUNK_SIZE;\n\t\tlet maxLength = config?.maxLength ?? DEFAULT_MAX_LENGTH;\n\t\tlet maxRecycledLength = config?.maxRecycledLength ?? DEFAULT_MAX_RECYCLED_LENGTH;\n\n\t\t// Each stack sizes its inlined firstBlock from its own maxLength, so compute both up front to lay them out. The\n\t\t// stacks cap their segments at what a buffer can hold, so size the inlined blocks against that same cap.\n\t\tlet maxSegmentLength = SharedStack.getMaxSegmentLength(this.memory.maxAllocationLength);\n\t\tlet pointerStackMaxLength = maxLength / maxChunkSize;\n\t\tlet pointerStackAllocateCount = SharedStack.getAllocateCount(pointerStackMaxLength, undefined, maxSegmentLength);\n\t\tlet recycleStackAllocateCount = SharedStack.getAllocateCount(maxRecycledLength, undefined, maxSegmentLength);\n\n\t\t// Inline both stacks' firstBlocks right after the pool header so the whole spine is one contiguous allocation\n\t\tthis.pointerStack = new SharedStack(this.memory, {\n\t\t\tfirstBlock: this.firstBlock.getSubAllocation(SharedPool.ALLOCATE_COUNT, pointerStackAllocateCount),\n\t\t\ttype: Uint32Array,\n\t\t\tmaxLength: pointerStackMaxLength,\n\t\t});\n\t\tstoreRawPointer(this.firstBlock.data, POINTER_STACK_INDEX, this.pointerStack.pointer);\n\t\tthis.recycleStack = new SharedStack(this.memory, {\n\t\t\tfirstBlock: this.firstBlock.getSubAllocation(SharedPool.ALLOCATE_COUNT + pointerStackAllocateCount, recycleStackAllocateCount),\n\t\t\ttype: Uint32Array,\n\t\t\tmaxLength: maxRecycledLength,\n\t\t});\n\t\tstoreRawPointer(this.firstBlock.data, RECYCLE_STACK_INDEX, this.recycleStack.pointer);\n\n\t\tlet typeCode = getArrayTypeCode(config?.type ?? Uint32Array);\n\t\tthis.type = typeCode;\n\t\tthis.dataLength = dataLength;\n\t\tthis.maxChunkSize = maxChunkSize;\n\n\t\tlet byteMultipler = getByteMultipler(typeCode);\n\t\tlet firstArray = this.memory.allocUI32(maxChunkSize * dataLength * byteMultipler);\n\t\tthis.pointerStack.push(firstArray.pointer);\n\t}\n\n\tat(index: number): T {\n\t\tlet dataBlock = this.getFullDataBlock(index);\n\t\treturn this.getDataBlock(dataBlock, index % this.cachedMaxChunkSize);\n\t}\n\tget(index: number, dataIndex = 0): T[number] {\n\t\tconst dataLength = this.cachedDataLength;\n\t\tif(dataIndex >= dataLength) {\n\t\t\tthrow new Error(`${dataIndex} is out of dataLength bounds ${dataLength}`);\n\t\t}\n\n\t\tlet dataBlock = this.getFullDataBlock(index) as NumericArrayIO;\n\t\treturn dataBlock[(index % this.cachedMaxChunkSize) * dataLength + dataIndex];\n\t}\n\tset(index: number, dataIndex: number, value: T[number]) {\n\t\tconst dataLength = this.cachedDataLength;\n\t\tif(dataIndex >= dataLength) {\n\t\t\tthrow new Error(`${dataIndex} is out of dataLength bounds ${dataLength}`);\n\t\t}\n\n\t\tlet dataBlock = this.getFullDataBlock(index) as NumericArrayIO;\n\t\tdataBlock[(index % this.cachedMaxChunkSize) * dataLength + dataIndex] = value;\n\t}\n\n\t// Resolve the chunk view holding an index once. Callers touching several fields of the same record read/write straight\n\t// into this view at blockOffset(index) + fieldIndex, avoiding a repeated chunk lookup per field.\n\tblockFor(index: number): T {\n\t\treturn this.getFullDataBlock(index);\n\t}\n\t// Base slot of a record within the view blockFor(index) returns.\n\tblockOffset(index: number): number {\n\t\treturn (index % this.cachedMaxChunkSize) * this.cachedDataLength;\n\t}\n\n\tpush(values: T[number] | Array<T[number]>): number {\n\t\tlet dataLength = this.cachedDataLength;\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\t// Reuse a recycled index if one is available. pop() can come back undefined when another thread drained the last\n\t\t// slot between our two calls, so fall through to a fresh index rather than trusting a length() read\n\t\tlet newIndex = this.recycleStack.length === 0 ? undefined : this.recycleStack.pop();\n\t\tif(newIndex === undefined) {\n\t\t\tnewIndex = Atomics.add(this.firstBlock.data, LENGTH_INDEX, 1);\n\t\t}\n\n\t\tlet dataBlock = this.getFullDataBlock(newIndex) as NumericArrayIO;\n\t\tlet startIndex = dataLength * (newIndex % this.cachedMaxChunkSize);\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\treturn newIndex;\n\t}\n\n\treserveContiguous(count: number): number {\n\t\tif(!Number.isInteger(count) || count < 0) {\n\t\t\tthrow new Error(`Can't reserve ${count} pool entries`);\n\t\t}\n\t\treturn Atomics.add(this.firstBlock.data, LENGTH_INDEX, count);\n\t}\n\n\tdeleteIndex(index: number) {\n\t\tthis.recycleStack.push(index);\n\t}\n\n\tclear() {\n\t\tthis.firstBlock.data[LENGTH_INDEX] = 0;\n\t\tthis.recycleStack.clear();\n\t}\n\n\t*[Symbol.iterator]() {\n\t\tconst recycledValues: { [key: number]: true } = {};\n\t\tfor(let value of this.recycleStack) {\n\t\t\trecycledValues[value] = true;\n\t\t}\n\n\t\tconst maxChunkSize = this.cachedMaxChunkSize;\n\t\t// Snapshot the length once - iteration over a concurrently mutated pool is a snapshot anyway, and re-loading the\n\t\t// atomic every step is pure overhead\n\t\tconst length = Atomics.load(this.firstBlock.data, LENGTH_INDEX);\n\t\tlet dataBlock = this.getFullDataBlock(0);\n\t\tlet dataBlockIndex = 0;\n\t\tfor(let i = 0; i < length; i++) {\n\t\t\tif(!recycledValues[i]) {\n\t\t\t\tlet newDataBlockIndex = Math.floor(i / maxChunkSize);\n\t\t\t\tif(newDataBlockIndex !== dataBlockIndex) {\n\t\t\t\t\tdataBlock = this.getFullDataBlock(i);\n\t\t\t\t\tdataBlockIndex = newDataBlockIndex;\n\t\t\t\t}\n\t\t\t\tyield this.getDataBlock(dataBlock, i % maxChunkSize);\n\t\t\t}\n\t\t}\n\t}\n\n\t*entries(): IterableIterator<[number, T]> {\n\t\tconst recycledValues: { [key: number]: true } = {};\n\t\tfor(let value of this.recycleStack) {\n\t\t\trecycledValues[value] = true;\n\t\t}\n\n\t\tconst maxChunkSize = this.cachedMaxChunkSize;\n\t\tconst length = Atomics.load(this.firstBlock.data, LENGTH_INDEX);\n\t\tlet dataBlock = this.getFullDataBlock(0);\n\t\tlet dataBlockIndex = 0;\n\t\tfor(let i = 0; i < length; i++) {\n\t\t\tif(!recycledValues[i]) {\n\t\t\t\tlet newDataBlockIndex = Math.floor(i / maxChunkSize);\n\t\t\t\tif(newDataBlockIndex !== dataBlockIndex) {\n\t\t\t\t\tdataBlock = this.getFullDataBlock(i);\n\t\t\t\t\tdataBlockIndex = newDataBlockIndex;\n\t\t\t\t}\n\t\t\t\tyield [i, this.getDataBlock(dataBlock, i % maxChunkSize)];\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate getFullDataBlock(index: number) {\n\t\tlet pointerIndex = Math.floor(index / this.cachedMaxChunkSize);\n\t\tlet cachedDataBlock = this.cachedFullDataBlock[pointerIndex];\n\t\tif(cachedDataBlock) {\n\t\t\treturn cachedDataBlock;\n\t\t}\n\t\tif(pointerIndex >= this.pointerStack.length) {\n\t\t\t// Only let a single thread append a new chunk at a time - otherwise two concurrent inserts landing in a new\n\t\t\t// chunk would each allocate + push a buffer, orphaning one and leaking memory\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 chunk while we were waiting\n\t\t\t\twhile(pointerIndex >= this.pointerStack.length) {\n\t\t\t\t\tlet newArray = this.memory.allocUI32(this.cachedMaxChunkSize * this.cachedDataLength * this.cachedByteMultipler);\n\t\t\t\t\tthis.pointerStack.push(newArray.pointer);\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\tlet array = new AllocatedMemory(this.memory, getPointer(this.pointerStack.at(pointerIndex), this.memory.positionBits));\n\n\t\tlet blockLength = this.cachedDataLength * this.cachedMaxChunkSize;\n\t\tlet data = makeArrayView(this.cachedType, array.data.buffer, array.bufferByteOffset, blockLength) as T;\n\n\t\tthis.cachedFullDataBlock[pointerIndex] = data;\n\t\treturn data;\n\t}\n\n\tprivate getDataBlock(rawData: T, index: number): T {\n\t\tconst dataLength = this.cachedDataLength;\n\t\tconst start = index * dataLength;\n\t\treturn rawData.subarray(start, start + dataLength) as T;\n\t}\n\n\tfree() {\n\t\tthis.recycleStack.free();\n\n\t\tfor(let pointer of this.pointerStack) {\n\t\t\tlet memory = new AllocatedMemory(this.memory, getPointer(pointer, this.memory.positionBits));\n\t\t\tmemory.free();\n\t\t}\n\t\tthis.pointerStack.free();\n\t\tif(this.ownsFirstBlock) {\n\t\t\tthis.firstBlock.free();\n\t\t}\n\t}\n\n\tgetSharedMemory(): SharedPoolMemory {\n\t\treturn {\n\t\t\tfirstBlock: this.firstBlock.getSharedMemory(),\n\t\t};\n\t}\n}\n\ninterface SharedPoolConfig<T extends NumericArray> {\n\tmaxChunkSize?: number\n\tmaxLength?: number\n\tmaxRecycledLength?: number\n\ttype?: TypedArrayConstructor<T>\n\tdataLength?: number\n}\ninterface SharedPoolMemory {\n\tfirstBlock: SharedAllocatedMemory | AllocatedMemory\n}\n\nexport type { SharedPoolConfig, SharedPoolMemory };\n"],"mappings":";;;;;;AAQA,IAAME,IAAe,GACfC,IAAa,GACbC,IAAuB,GAEvBC,IAAkB,GAElBC,IAAsB,GACtBC,IAAsB,GAEtBC,IAAyB,KACzBC,IAAqB,KACrBC,IAA8B,KAMfC,IAArB,MAAqBA,EAAwE;CAC5F,OAAgB,iBAAiB;CAIjC,OAAO,iBAAiB,GAAoB,GAAiD;EAC5F,IAAIC,IAAeC,GAAQ,gBAAgBL,GACvCM,IAAYD,GAAQ,aAAaJ,GACjCM,IAAoBF,GAAQ,qBAAqBH,GACjDM,IAAmBf,EAAY,oBAAoBgB,EAAO,mBAAmB,GAC7EC,IAA4BjB,EAAY,iBAAiBa,IAAYF,GAAc,KAAA,GAAWI,CAAgB,GAC9GG,IAA4BlB,EAAY,iBAAiBc,GAAmB,KAAA,GAAWC,CAAgB;EAC3G,OAAOL,EAAW,iBAAiBO,IAA4BC;CAChE;CAEA;CAGA;CACA;CACA;CACA;CACA;CACA;CAGA;CAIA,sBAAoD,CAAC;CAGrD;CACA;CACA;CACA;CAEA,IAAI,SAAiB;EACpB,OAAO,QAAQ,KAAK,KAAK,WAAW,MAAMjB,CAAY,IAAI,KAAK,aAAa;CAC7E;CACA,IAAI,eAAuB;EAC1B,OAAO,KAAK;CACb;CACA,IAAY,aAAa,GAAe;EACvC,QAAQ,MAAM,KAAK,WAAW,MAAME,GAAsBgB,CAAK;CAChE;CAEA,IAAI,OAAe;EAClB,OAAO,KAAK;CACb;CACA,IAAY,KAAK,GAAe;EAC/B,QAAQ,MAAM,KAAK,aAAa,GAAGA,CAAK;CACzC;CACA,IAAI,aAAqB;EACxB,OAAO,KAAK;CACb;CACA,IAAY,WAAW,GAAe;EACrC,QAAQ,MAAM,KAAK,aAAa,GAAGA,CAAK;CACzC;CAEA,IAAI,eAAuB;EAC1B,OAAO,KAAK,qBAAqB,KAAK,aAAa;CACpD;CACA,IAAI,gBAAwB;EAC3B,OAAO,KAAK;CACb;CAIA,IAAI,aAAqB;EACxB,IAAIC,IAAQ,KAAK,iBAAiB,KAAK,WAAW,aAAa;EAC/D,KAAI,IAAIC,KAAW,KAAK,cACvB,KAAS,IAAItB,EAAgB,KAAK,QAAQuB,EAAWD,GAAS,KAAK,OAAO,YAAY,CAAC,CAAC,CAAC;EAE1F,OAAOD,IAAQ,KAAK,aAAa,aAAa,KAAK,aAAa;CACjE;CAEA,YAAY,GAAoB,GAA0F;EAqCzH,AApCA,KAAK,SAASJ,GAEXJ,KAAU,gBAAgBA,KAG5B,KAAK,aAAaA,EAAO,sBAAsBb,IAC5Ca,EAAO,aACP,IAAIb,EAAgBiB,GAAQ;GAAE,QAAQN,EAAW;GAAgB,GAAGE,EAAO;EAAW,CAAC,GAC1F,KAAK,cAAc,IAAI,YAAY,KAAK,WAAW,KAAK,QAAQ,KAAK,WAAW,mBAAmBV,IAAa,YAAY,mBAAmB,CAAC,GAG7I,UAAUU,KAAU,gBAAgBA,KAAU,eAAeA,KAAU,kBAAkBA,KAAU,uBAAuBA,KAC5H,KAAK,WAAWA,CAAM,GACtB,KAAK,iBAAiB,OAEtB,KAAK,eAAe,IAAIZ,EAAYgB,GAAQ,EAC3C,YAAYM,EAAWC,EAAe,KAAK,WAAW,MAAMlB,CAAmB,GAAGW,EAAO,YAAY,EACtG,CAAC,GACD,KAAK,eAAe,IAAIhB,EAAYgB,GAAQ,EAC3C,YAAYM,EAAWC,EAAe,KAAK,WAAW,MAAMjB,CAAmB,GAAGU,EAAO,YAAY,EACtG,CAAC,GACD,KAAK,iBAAiB,QAGvB,KAAK,aAAaA,EAAO,UAAUN,EAAW,iBAAiBM,GAAQJ,CAAM,CAAC,GAC9E,KAAK,cAAc,IAAI,YAAY,KAAK,WAAW,KAAK,QAAQ,KAAK,WAAW,mBAAmBV,IAAa,YAAY,mBAAmB,CAAC,GAChJ,KAAK,WAAWU,CAAM,GACtB,KAAK,iBAAiB,KAGvB,KAAK,aAAa,KAAK,YAAY,IACnC,KAAK,mBAAmB,KAAK,IAAI,GAAG,KAAK,YAAY,EAAE,GACvD,KAAK,qBAAqB,KAAK,WAAW,KAAKT,IAC/C,KAAK,sBAAsBqB,EAAiB,KAAK,UAAU,GAC3D,KAAK,YAAY,KAAK,qBAAqB,KAAK,aAAa,WAE7D,KAAK,WAAW,IAAI,WAAW,KAAK,WAAW,KAAK,QAAQ,KAAK,WAAW,mBAAmBpB,IAAkB,YAAY,mBAAmB,CAAC;CAClJ;CAIA,WAAmB,GAA8B;EAChD,IAAIqB,IAAab,GAAQ,cAAc,GACnCD,IAAeC,GAAQ,gBAAgBL,GACvCM,IAAYD,GAAQ,aAAaJ,GACjCM,IAAoBF,GAAQ,qBAAqBH,GAIjDM,IAAmBf,EAAY,oBAAoB,KAAK,OAAO,mBAAmB,GAClF0B,IAAwBb,IAAYF,GACpCM,IAA4BjB,EAAY,iBAAiB0B,GAAuB,KAAA,GAAWX,CAAgB,GAC3GG,IAA4BlB,EAAY,iBAAiBc,GAAmB,KAAA,GAAWC,CAAgB;EAc3G,AAXA,KAAK,eAAe,IAAIf,EAAY,KAAK,QAAQ;GAChD,YAAY,KAAK,WAAW,iBAAiBU,EAAW,gBAAgBO,CAAyB;GACjG,MAAM;GACN,WAAWS;EACZ,CAAC,GACDC,EAAgB,KAAK,WAAW,MAAMtB,GAAqB,KAAK,aAAa,OAAO,GACpF,KAAK,eAAe,IAAIL,EAAY,KAAK,QAAQ;GAChD,YAAY,KAAK,WAAW,iBAAiBU,EAAW,iBAAiBO,GAA2BC,CAAyB;GAC7H,MAAM;GACN,WAAWJ;EACZ,CAAC,GACDa,EAAgB,KAAK,WAAW,MAAMrB,GAAqB,KAAK,aAAa,OAAO;EAEpF,IAAIsB,IAAWC,EAAiBjB,GAAQ,QAAQ,WAAW;EAG3D,AAFA,KAAK,OAAOgB,GACZ,KAAK,aAAaH,GAClB,KAAK,eAAed;EAEpB,IAAImB,IAAgBN,EAAiBI,CAAQ,GACzCG,IAAa,KAAK,OAAO,UAAUpB,IAAec,IAAaK,CAAa;EAChF,KAAK,aAAa,KAAKC,EAAW,OAAO;CAC1C;CAEA,GAAG,GAAkB;EACpB,IAAIC,IAAY,KAAK,iBAAiBC,CAAK;EAC3C,OAAO,KAAK,aAAaD,GAAWC,IAAQ,KAAK,kBAAkB;CACpE;CACA,IAAI,GAAe,IAAY,GAAc;EAC5C,IAAMR,IAAa,KAAK;EACxB,IAAGS,KAAaT,GACf,MAAU,MAAM,GAAGS,EAAU,+BAA+BT,GAAY;EAIzE,OADgB,KAAK,iBAAiBQ,CAC/B,CAAA,CAAWA,IAAQ,KAAK,qBAAsBR,IAAaS;CACnE;CACA,IAAI,GAAe,GAAmB,GAAkB;EACvD,IAAMT,IAAa,KAAK;EACxB,IAAGS,KAAaT,GACf,MAAU,MAAM,GAAGS,EAAU,+BAA+BT,GAAY;EAGzE,IAAIO,IAAY,KAAK,iBAAiBC,CAAK;EAC3C,EAAWA,IAAQ,KAAK,qBAAsBR,IAAaS,KAAaf;CACzE;CAIA,SAAS,GAAkB;EAC1B,OAAO,KAAK,iBAAiBc,CAAK;CACnC;CAEA,YAAY,GAAuB;EAClC,OAAQA,IAAQ,KAAK,qBAAsB,KAAK;CACjD;CAEA,KAAK,GAA8C;EAClD,IAAIR,IAAa,KAAK,kBAChBU,IAAgB,OAAOC,KAAW;EACxC,IAAG,CAACD,KAAiBC,EAAO,SAASX,GACpC,MAAU,MAAM,gBAAgBW,EAAO,OAAO,6BAA6BX,EAAW,YAAY;EAKnG,IAAIY,IAAW,KAAK,aAAa,WAAW,IAAI,KAAA,IAAY,KAAK,aAAa,IAAI;EAClF,AAAGA,MAAa,KAAA,MACf,IAAW,QAAQ,IAAI,KAAK,WAAW,MAAMpC,GAAc,CAAC;EAG7D,IAAI+B,IAAY,KAAK,iBAAiBK,CAAQ,GAC1CC,IAAab,KAAcY,IAAW,KAAK;EAC/C,IAAGF,GACF,EAAUG,KAAcF;OAExB,KAAI,IAAIG,IAAI,GAAGA,IAAIH,EAAO,QAAQ,KACjC,EAAUE,IAAaC,KAAKH,EAAOG;EAIrC,OAAOF;CACR;CAEA,kBAAkB,GAAuB;EACxC,IAAG,CAAC,OAAO,UAAUG,CAAK,KAAKA,IAAQ,GACtC,MAAU,MAAM,iBAAiBA,EAAM,cAAc;EAEtD,OAAO,QAAQ,IAAI,KAAK,WAAW,MAAMvC,GAAcuC,CAAK;CAC7D;CAEA,YAAY,GAAe;EAC1B,KAAK,aAAa,KAAKP,CAAK;CAC7B;CAEA,QAAQ;EAEP,AADA,KAAK,WAAW,KAAKhC,KAAgB,GACrC,KAAK,aAAa,MAAM;CACzB;CAEA,EAAE,OAAO,YAAY;EACpB,IAAMwC,IAA0C,CAAC;EACjD,KAAI,IAAItB,KAAS,KAAK,cACrB,EAAeA,KAAS;EAGzB,IAAMR,IAAe,KAAK,oBAGpB+B,IAAS,QAAQ,KAAK,KAAK,WAAW,MAAMzC,CAAY,GAC1D+B,IAAY,KAAK,iBAAiB,CAAC,GACnCW,IAAiB;EACrB,KAAI,IAAIJ,IAAI,GAAGA,IAAIG,GAAQ,KAC1B,IAAG,CAACD,EAAeF,IAAI;GACtB,IAAIK,IAAoB,KAAK,MAAML,IAAI5B,CAAY;GAKnD,AAJGiC,MAAsBD,MACxB,IAAY,KAAK,iBAAiBJ,CAAC,GACnC,IAAiBK,IAElB,MAAM,KAAK,aAAaZ,GAAWO,IAAI5B,CAAY;EACpD;CAEF;CAEA,CAAC,UAAyC;EACzC,IAAM8B,IAA0C,CAAC;EACjD,KAAI,IAAItB,KAAS,KAAK,cACrB,EAAeA,KAAS;EAGzB,IAAMR,IAAe,KAAK,oBACpB+B,IAAS,QAAQ,KAAK,KAAK,WAAW,MAAMzC,CAAY,GAC1D+B,IAAY,KAAK,iBAAiB,CAAC,GACnCW,IAAiB;EACrB,KAAI,IAAIJ,IAAI,GAAGA,IAAIG,GAAQ,KAC1B,IAAG,CAACD,EAAeF,IAAI;GACtB,IAAIK,IAAoB,KAAK,MAAML,IAAI5B,CAAY;GAKnD,AAJGiC,MAAsBD,MACxB,IAAY,KAAK,iBAAiBJ,CAAC,GACnC,IAAiBK,IAElB,MAAM,CAACL,GAAG,KAAK,aAAaP,GAAWO,IAAI5B,CAAY,CAAC;EACzD;CAEF;CAEA,iBAAyB,GAAe;EACvC,IAAIkC,IAAe,KAAK,MAAMZ,IAAQ,KAAK,kBAAkB,GACzDa,IAAkB,KAAK,oBAAoBD;EAC/C,IAAGC,GACF,OAAOA;EAER,IAAGD,KAAgB,KAAK,aAAa,QAAQ;GAG5C,EAAK,KAAK,QAAQ;GAClB,IAAI;IAEH,OAAMA,KAAgB,KAAK,aAAa,SAAQ;KAC/C,IAAIE,IAAW,KAAK,OAAO,UAAU,KAAK,qBAAqB,KAAK,mBAAmB,KAAK,mBAAmB;KAC/G,KAAK,aAAa,KAAKA,EAAS,OAAO;IACxC;GACD,UAAU;IACT,EAAO,KAAK,QAAQ;GACrB;EACD;EAEA,IAAIC,IAAQ,IAAIjD,EAAgB,KAAK,QAAQuB,EAAW,KAAK,aAAa,GAAGuB,CAAY,GAAG,KAAK,OAAO,YAAY,CAAC,GAEjHI,IAAc,KAAK,mBAAmB,KAAK,oBAC3CC,IAAOC,EAAc,KAAK,YAAYH,EAAM,KAAK,QAAQA,EAAM,kBAAkBC,CAAW;EAGhG,OADA,KAAK,oBAAoBJ,KAAgBK,GAClCA;CACR;CAEA,aAAqB,GAAY,GAAkB;EAClD,IAAMzB,IAAa,KAAK,kBAClB2B,IAAQnB,IAAQR;EACtB,OAAO4B,EAAQ,SAASD,GAAOA,IAAQ3B,CAAU;CAClD;CAEA,OAAO;EACN,KAAK,aAAa,KAAK;EAEvB,KAAI,IAAIJ,KAAW,KAAK,cAEvB,IADiBtB,EAAgB,KAAK,QAAQuB,EAAWD,GAAS,KAAK,OAAO,YAAY,CAC1F,CAAA,CAAO,KAAK;EAGb,AADA,KAAK,aAAa,KAAK,GACpB,KAAK,kBACP,KAAK,WAAW,KAAK;CAEvB;CAEA,kBAAoC;EACnC,OAAO,EACN,YAAY,KAAK,WAAW,gBAAgB,EAC7C;CACD;AACD"}
|
|
1
|
+
{"version":3,"file":"shared-pool.js","names":["AllocatedMemory","SharedStack","LENGTH_INDEX","TYPE_INDEX","MAX_CHUNK_SIZE_INDEX","GROW_LOCK_INDEX","POINTER_STACK_INDEX","RECYCLE_STACK_INDEX","DEFAULT_MAX_CHUNK_SIZE","DEFAULT_MAX_LENGTH","DEFAULT_MAX_RECYCLED_LENGTH","SharedPool","maxChunkSize","config","maxLength","maxRecycledLength","maxSegmentLength","memory","pointerStackAllocateCount","recycleStackAllocateCount","value","total","pointer","getPointer","loadRawPointer","getByteMultipler","dataLength","pointerStackMaxLength","storeRawPointer","typeCode","getArrayTypeCode","byteMultipler","firstArray","dataBlock","index","dataIndex","isSingleValue","values","newIndex","startIndex","i","count","length","recycled","recycledValues","dataBlockIndex","newDataBlockIndex","pointerIndex","cachedDataBlock","newArray","array","blockLength","data","makeArrayView","start","rawData"],"sources":["../src/shared-pool.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 SharedStack from './shared-stack';\nimport { getPointer, loadRawPointer, storeRawPointer } 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 MAX_CHUNK_SIZE_INDEX = 2;\n// Guards appending a chunk pointer so only one thread grows the pointer stack at a time\nconst GROW_LOCK_INDEX = 3;\n// Pointers to the two backing SharedStacks (their own firstBlocks): the chunk pointers and the recycled indexes\nconst POINTER_STACK_INDEX = 4;\nconst RECYCLE_STACK_INDEX = 5;\n\nconst DEFAULT_MAX_CHUNK_SIZE = 100;\nconst DEFAULT_MAX_LENGTH = 1_000_000;\nconst DEFAULT_MAX_RECYCLED_LENGTH = 100_000;\n\n// Array with stable indexes and maximum contiguous memory sizes (necessary to fit large data sets into max 1MB buffers).\n// The chunk pointers and the recycled indexes both live in SharedStacks, whose segments are never moved or freed once\n// published, so concurrent readers never touch memory that is being reclaimed.\n// https://plflib.org/colony.htm for future enhancements - it seems to be an optimized version of what we were aiming for with this\nexport default class SharedPool<T extends NumericArray = Uint32Array> implements Iterable<T> {\n\tstatic readonly ALLOCATE_COUNT = 6;\n\n\t// u32s a pool with this config needs for its firstBlock, including both inlined stacks. Lets an owner (e.g.\n\t// SharedQuadtree) reserve one contiguous slice and hand it back through the config's firstBlock to inline the pool.\n\tstatic getAllocateCount(memory: MemoryHeap, config?: SharedPoolConfig<NumericArray>): number {\n\t\tlet maxChunkSize = config?.maxChunkSize ?? DEFAULT_MAX_CHUNK_SIZE;\n\t\tlet maxLength = config?.maxLength ?? DEFAULT_MAX_LENGTH;\n\t\tlet maxRecycledLength = config?.maxRecycledLength ?? DEFAULT_MAX_RECYCLED_LENGTH;\n\t\tlet maxSegmentLength = SharedStack.getMaxSegmentLength(memory.maxAllocationLength);\n\t\tlet pointerStackAllocateCount = SharedStack.getAllocateCount(maxLength / maxChunkSize, undefined, maxSegmentLength);\n\t\tlet recycleStackAllocateCount = SharedStack.getAllocateCount(maxRecycledLength, undefined, maxSegmentLength);\n\t\treturn SharedPool.ALLOCATE_COUNT + pointerStackAllocateCount + recycleStackAllocateCount;\n\t}\n\n\tprivate memory: MemoryHeap;\n\n\t// Current Length, Type/DataLength, MaxChunkLength, Grow lock, Pointer stack, Recycle stack\n\tprivate firstBlock: AllocatedMemory;\n\tprivate uint16Array: Uint16Array;\n\tprivate growLock: Int32Array;\n\tprivate pointerStack!: SharedStack;\n\tprivate recycleStack!: SharedStack;\n\treadonly maxLength: number;\n\n\t// False when firstBlock is a slice of another structure's memory (e.g. inlined in SharedQuadtree); that owner frees it\n\tprivate ownsFirstBlock: boolean;\n\n\t// Chunk views cached by pointer index. An array (dense, small integer keys) resolves faster than an object on the hot\n\t// get/set path and never needs rehashing.\n\tprivate cachedFullDataBlock: Array<T | undefined> = [];\n\n\t// Config is written once at construction and never mutated, so it is cached in plain fields\n\tprivate readonly cachedMaxChunkSize: number;\n\tprivate readonly cachedDataLength: number;\n\tprivate readonly cachedType: number;\n\tprivate readonly cachedByteMultipler: number;\n\n\tget length(): number {\n\t\treturn Atomics.load(this.firstBlock.data, LENGTH_INDEX) - this.recycleStack.length;\n\t}\n\tget maxChunkSize(): number {\n\t\treturn this.cachedMaxChunkSize;\n\t}\n\tprivate set maxChunkSize(value: number) {\n\t\tAtomics.store(this.firstBlock.data, MAX_CHUNK_SIZE_INDEX, value);\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\tget bufferLength(): number {\n\t\treturn this.cachedMaxChunkSize * this.pointerStack.length;\n\t}\n\tget byteMultipler(): number {\n\t\treturn this.cachedByteMultipler;\n\t}\n\n\t// Own internal memory (which already includes both stacks' inlined firstBlocks) plus every chunk buffer and the\n\t// segments each stack has grown. The stacks' usedMemory excludes their inlined firstBlocks, so they aren't double-counted.\n\tget usedMemory(): number {\n\t\tlet total = this.ownsFirstBlock ? this.firstBlock.usedMemory : 0;\n\t\tfor(let pointer of this.pointerStack) {\n\t\t\ttotal += new AllocatedMemory(this.memory, getPointer(pointer, this.memory.positionBits)).usedMemory;\n\t\t}\n\t\treturn total + this.pointerStack.usedMemory + this.recycleStack.usedMemory;\n\t}\n\n\tconstructor(memory: MemoryHeap, config?: SharedPoolConfig<T> | SharedPoolMemory | SharedPoolMemory & SharedPoolConfig<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 SharedQuadtree); 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\tthis.firstBlock = config.firstBlock instanceof AllocatedMemory\n\t\t\t\t? config.firstBlock\n\t\t\t\t: new AllocatedMemory(memory, { length: SharedPool.ALLOCATE_COUNT, ...config.firstBlock });\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 || 'dataLength' in config || 'maxLength' in config || 'maxChunkSize' in config || 'maxRecycledLength' 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.pointerStack = new SharedStack(memory, {\n\t\t\t\t\tfirstBlock: getPointer(loadRawPointer(this.firstBlock.data, POINTER_STACK_INDEX), memory.positionBits),\n\t\t\t\t});\n\t\t\t\tthis.recycleStack = new SharedStack(memory, {\n\t\t\t\t\tfirstBlock: getPointer(loadRawPointer(this.firstBlock.data, RECYCLE_STACK_INDEX), memory.positionBits),\n\t\t\t\t});\n\t\t\t\tthis.ownsFirstBlock = true;\n\t\t\t}\n\t\t} else {\n\t\t\tthis.firstBlock = memory.allocUI32(SharedPool.getAllocateCount(memory, config));\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.initialize(config);\n\t\t\tthis.ownsFirstBlock = true;\n\t\t}\n\n\t\tthis.cachedType = this.uint16Array[0];\n\t\tthis.cachedDataLength = Math.max(1, this.uint16Array[1]);\n\t\tthis.cachedMaxChunkSize = this.firstBlock.data[MAX_CHUNK_SIZE_INDEX];\n\t\tthis.cachedByteMultipler = getByteMultipler(this.cachedType);\n\t\tthis.maxLength = this.cachedMaxChunkSize * this.pointerStack.maxLength;\n\n\t\tthis.growLock = new Int32Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + GROW_LOCK_INDEX * Uint32Array.BYTES_PER_ELEMENT, 1);\n\t}\n\n\t// Lay out the two inlined stacks (their firstBlocks sliced from ours), record their pointers, and seed the first chunk.\n\t// firstBlock and uint16Array must already be set. Used by both the fresh-alloc path and the inline-in-place path.\n\tprivate initialize(config?: SharedPoolConfig<T>) {\n\t\tlet dataLength = config?.dataLength ?? 1;\n\t\tlet maxChunkSize = config?.maxChunkSize ?? DEFAULT_MAX_CHUNK_SIZE;\n\t\tlet maxLength = config?.maxLength ?? DEFAULT_MAX_LENGTH;\n\t\tlet maxRecycledLength = config?.maxRecycledLength ?? DEFAULT_MAX_RECYCLED_LENGTH;\n\n\t\t// Each stack sizes its inlined firstBlock from its own maxLength, so compute both up front to lay them out. The\n\t\t// stacks cap their segments at what a buffer can hold, so size the inlined blocks against that same cap.\n\t\tlet maxSegmentLength = SharedStack.getMaxSegmentLength(this.memory.maxAllocationLength);\n\t\tlet pointerStackMaxLength = maxLength / maxChunkSize;\n\t\tlet pointerStackAllocateCount = SharedStack.getAllocateCount(pointerStackMaxLength, undefined, maxSegmentLength);\n\t\tlet recycleStackAllocateCount = SharedStack.getAllocateCount(maxRecycledLength, undefined, maxSegmentLength);\n\n\t\t// Inline both stacks' firstBlocks right after the pool header so the whole spine is one contiguous allocation\n\t\tthis.pointerStack = new SharedStack(this.memory, {\n\t\t\tfirstBlock: this.firstBlock.getSubAllocation(SharedPool.ALLOCATE_COUNT, pointerStackAllocateCount),\n\t\t\ttype: Uint32Array,\n\t\t\tmaxLength: pointerStackMaxLength,\n\t\t});\n\t\tstoreRawPointer(this.firstBlock.data, POINTER_STACK_INDEX, this.pointerStack.pointer);\n\t\tthis.recycleStack = new SharedStack(this.memory, {\n\t\t\tfirstBlock: this.firstBlock.getSubAllocation(SharedPool.ALLOCATE_COUNT + pointerStackAllocateCount, recycleStackAllocateCount),\n\t\t\ttype: Uint32Array,\n\t\t\tmaxLength: maxRecycledLength,\n\t\t});\n\t\tstoreRawPointer(this.firstBlock.data, RECYCLE_STACK_INDEX, this.recycleStack.pointer);\n\n\t\tlet typeCode = getArrayTypeCode(config?.type ?? Uint32Array);\n\t\tthis.type = typeCode;\n\t\tthis.dataLength = dataLength;\n\t\tthis.maxChunkSize = maxChunkSize;\n\n\t\tlet byteMultipler = getByteMultipler(typeCode);\n\t\tlet firstArray = this.memory.allocUI32(maxChunkSize * dataLength * byteMultipler);\n\t\tthis.pointerStack.push(firstArray.pointer);\n\t}\n\n\tat(index: number): T {\n\t\tlet dataBlock = this.getFullDataBlock(index);\n\t\treturn this.getDataBlock(dataBlock, index % this.cachedMaxChunkSize);\n\t}\n\tget(index: number, dataIndex = 0): T[number] {\n\t\tconst dataLength = this.cachedDataLength;\n\t\tif(dataIndex >= dataLength) {\n\t\t\tthrow new Error(`${dataIndex} is out of dataLength bounds ${dataLength}`);\n\t\t}\n\n\t\tlet dataBlock = this.getFullDataBlock(index) as NumericArrayIO;\n\t\treturn dataBlock[(index % this.cachedMaxChunkSize) * dataLength + dataIndex];\n\t}\n\tset(index: number, dataIndex: number, value: T[number]) {\n\t\tconst dataLength = this.cachedDataLength;\n\t\tif(dataIndex >= dataLength) {\n\t\t\tthrow new Error(`${dataIndex} is out of dataLength bounds ${dataLength}`);\n\t\t}\n\n\t\tlet dataBlock = this.getFullDataBlock(index) as NumericArrayIO;\n\t\tdataBlock[(index % this.cachedMaxChunkSize) * dataLength + dataIndex] = value;\n\t}\n\n\t// Resolve the chunk view holding an index once. Callers touching several fields of the same record read/write straight\n\t// into this view at blockOffset(index) + fieldIndex, avoiding a repeated chunk lookup per field.\n\tblockFor(index: number): T {\n\t\treturn this.getFullDataBlock(index);\n\t}\n\t// Base slot of a record within the view blockFor(index) returns.\n\tblockOffset(index: number): number {\n\t\treturn (index % this.cachedMaxChunkSize) * this.cachedDataLength;\n\t}\n\n\tpush(values: T[number] | Array<T[number]>): number {\n\t\tlet dataLength = this.cachedDataLength;\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\t// Reuse a recycled index if one is available. pop() can come back undefined when another thread drained the last\n\t\t// slot between our two calls, so fall through to a fresh index rather than trusting a length() read\n\t\tlet newIndex = this.recycleStack.length === 0 ? undefined : this.recycleStack.pop();\n\t\tif(newIndex === undefined) {\n\t\t\tnewIndex = Atomics.add(this.firstBlock.data, LENGTH_INDEX, 1);\n\t\t}\n\n\t\tlet dataBlock = this.getFullDataBlock(newIndex) as NumericArrayIO;\n\t\tlet startIndex = dataLength * (newIndex % this.cachedMaxChunkSize);\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\treturn newIndex;\n\t}\n\n\treserveContiguous(count: number): number {\n\t\tif(!Number.isInteger(count) || count < 0) {\n\t\t\tthrow new Error(`Can't reserve ${count} pool entries`);\n\t\t}\n\t\treturn Atomics.add(this.firstBlock.data, LENGTH_INDEX, count);\n\t}\n\n\tdeleteIndex(index: number) {\n\t\tthis.recycleStack.push(index);\n\t}\n\n\tclear() {\n\t\tthis.firstBlock.data[LENGTH_INDEX] = 0;\n\t\tthis.recycleStack.clear();\n\t}\n\n\t// Flags recycled indexes up to the allocated length (the mask's length). Lets a caller walk chunks directly via\n\t// blockFor instead of paying entries()'s per-record generator, tuple and subarray.\n\tgetRecycledMask(): Uint8Array {\n\t\tconst length = Atomics.load(this.firstBlock.data, LENGTH_INDEX);\n\t\tconst recycled = new Uint8Array(length);\n\t\tfor(let index of this.recycleStack) {\n\t\t\tif(index < length) {\n\t\t\t\trecycled[index] = 1;\n\t\t\t}\n\t\t}\n\t\treturn recycled;\n\t}\n\n\t*[Symbol.iterator]() {\n\t\tconst recycledValues: { [key: number]: true } = {};\n\t\tfor(let value of this.recycleStack) {\n\t\t\trecycledValues[value] = true;\n\t\t}\n\n\t\tconst maxChunkSize = this.cachedMaxChunkSize;\n\t\t// Snapshot the length once - iteration over a concurrently mutated pool is a snapshot anyway, and re-loading the\n\t\t// atomic every step is pure overhead\n\t\tconst length = Atomics.load(this.firstBlock.data, LENGTH_INDEX);\n\t\tlet dataBlock = this.getFullDataBlock(0);\n\t\tlet dataBlockIndex = 0;\n\t\tfor(let i = 0; i < length; i++) {\n\t\t\tif(!recycledValues[i]) {\n\t\t\t\tlet newDataBlockIndex = Math.floor(i / maxChunkSize);\n\t\t\t\tif(newDataBlockIndex !== dataBlockIndex) {\n\t\t\t\t\tdataBlock = this.getFullDataBlock(i);\n\t\t\t\t\tdataBlockIndex = newDataBlockIndex;\n\t\t\t\t}\n\t\t\t\tyield this.getDataBlock(dataBlock, i % maxChunkSize);\n\t\t\t}\n\t\t}\n\t}\n\n\t*entries(): IterableIterator<[number, T]> {\n\t\tconst recycledValues: { [key: number]: true } = {};\n\t\tfor(let value of this.recycleStack) {\n\t\t\trecycledValues[value] = true;\n\t\t}\n\n\t\tconst maxChunkSize = this.cachedMaxChunkSize;\n\t\tconst length = Atomics.load(this.firstBlock.data, LENGTH_INDEX);\n\t\tlet dataBlock = this.getFullDataBlock(0);\n\t\tlet dataBlockIndex = 0;\n\t\tfor(let i = 0; i < length; i++) {\n\t\t\tif(!recycledValues[i]) {\n\t\t\t\tlet newDataBlockIndex = Math.floor(i / maxChunkSize);\n\t\t\t\tif(newDataBlockIndex !== dataBlockIndex) {\n\t\t\t\t\tdataBlock = this.getFullDataBlock(i);\n\t\t\t\t\tdataBlockIndex = newDataBlockIndex;\n\t\t\t\t}\n\t\t\t\tyield [i, this.getDataBlock(dataBlock, i % maxChunkSize)];\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate getFullDataBlock(index: number) {\n\t\tlet pointerIndex = Math.floor(index / this.cachedMaxChunkSize);\n\t\tlet cachedDataBlock = this.cachedFullDataBlock[pointerIndex];\n\t\tif(cachedDataBlock) {\n\t\t\treturn cachedDataBlock;\n\t\t}\n\t\tif(pointerIndex >= this.pointerStack.length) {\n\t\t\t// Only let a single thread append a new chunk at a time - otherwise two concurrent inserts landing in a new\n\t\t\t// chunk would each allocate + push a buffer, orphaning one and leaking memory\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 chunk while we were waiting\n\t\t\t\twhile(pointerIndex >= this.pointerStack.length) {\n\t\t\t\t\tlet newArray = this.memory.allocUI32(this.cachedMaxChunkSize * this.cachedDataLength * this.cachedByteMultipler);\n\t\t\t\t\tthis.pointerStack.push(newArray.pointer);\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\tlet array = new AllocatedMemory(this.memory, getPointer(this.pointerStack.at(pointerIndex), this.memory.positionBits));\n\n\t\tlet blockLength = this.cachedDataLength * this.cachedMaxChunkSize;\n\t\tlet data = makeArrayView(this.cachedType, array.data.buffer, array.bufferByteOffset, blockLength) as T;\n\n\t\tthis.cachedFullDataBlock[pointerIndex] = data;\n\t\treturn data;\n\t}\n\n\tprivate getDataBlock(rawData: T, index: number): T {\n\t\tconst dataLength = this.cachedDataLength;\n\t\tconst start = index * dataLength;\n\t\treturn rawData.subarray(start, start + dataLength) as T;\n\t}\n\n\tfree() {\n\t\tthis.recycleStack.free();\n\n\t\tfor(let pointer of this.pointerStack) {\n\t\t\tlet memory = new AllocatedMemory(this.memory, getPointer(pointer, this.memory.positionBits));\n\t\t\tmemory.free();\n\t\t}\n\t\tthis.pointerStack.free();\n\t\tif(this.ownsFirstBlock) {\n\t\t\tthis.firstBlock.free();\n\t\t}\n\t}\n\n\tgetSharedMemory(): SharedPoolMemory {\n\t\treturn {\n\t\t\tfirstBlock: this.firstBlock.getSharedMemory(),\n\t\t};\n\t}\n}\n\ninterface SharedPoolConfig<T extends NumericArray> {\n\tmaxChunkSize?: number\n\tmaxLength?: number\n\tmaxRecycledLength?: number\n\ttype?: TypedArrayConstructor<T>\n\tdataLength?: number\n}\ninterface SharedPoolMemory {\n\tfirstBlock: SharedAllocatedMemory | AllocatedMemory\n}\n\nexport type { SharedPoolConfig, SharedPoolMemory };\n"],"mappings":"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"}
|