@daneren2005/shared-memory-objects 1.4.0 → 1.5.1
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 +137 -8
- package/dist/allocated-memory.d.ts +7 -2
- package/dist/allocated-memory.js +11 -4
- package/dist/allocated-memory.js.map +1 -1
- package/dist/constant-string.js +2 -2
- package/dist/constant-string.js.map +1 -1
- package/dist/local-pool.js +2 -2
- package/dist/local-pool.js.map +1 -1
- package/dist/lock/read-write-lock.js +25 -11
- package/dist/lock/read-write-lock.js.map +1 -1
- package/dist/memory-buffer.d.ts +11 -5
- package/dist/memory-buffer.js +70 -74
- package/dist/memory-buffer.js.map +1 -1
- package/dist/memory-heap.d.ts +3 -0
- package/dist/memory-heap.js +37 -29
- package/dist/memory-heap.js.map +1 -1
- package/dist/serialize-object.js +1 -1
- package/dist/serialize-object.js.map +1 -1
- package/dist/shared-list.d.ts +2 -0
- package/dist/shared-list.js +32 -29
- package/dist/shared-list.js.map +1 -1
- package/dist/shared-map.d.ts +11 -3
- package/dist/shared-map.js +93 -47
- package/dist/shared-map.js.map +1 -1
- package/dist/shared-pointer-list.js +3 -3
- package/dist/shared-pointer-list.js.map +1 -1
- package/dist/shared-pool.d.ts +11 -2
- package/dist/shared-pool.js +58 -26
- package/dist/shared-pool.js.map +1 -1
- package/dist/shared-stack.js +19 -8
- package/dist/shared-stack.js.map +1 -1
- package/dist/shared-string.js +3 -3
- package/dist/shared-string.js.map +1 -1
- package/dist/shared-vector.js +10 -7
- package/dist/shared-vector.js.map +1 -1
- package/dist/spatial/shared-quadtree.d.ts +67 -0
- package/dist/spatial/shared-quadtree.js +361 -0
- package/dist/spatial/shared-quadtree.js.map +1 -0
- package/dist/spatial/shared-spatial-grid.d.ts +78 -0
- package/dist/spatial/shared-spatial-grid.js +426 -0
- package/dist/spatial/shared-spatial-grid.js.map +1 -0
- package/dist/spatial/shared-spatial-map.d.ts +87 -0
- package/dist/spatial/shared-spatial-map.js +555 -0
- package/dist/spatial/shared-spatial-map.js.map +1 -0
- package/dist/spatial/spatial-entity.d.ts +11 -0
- package/dist/spatial/spatial-entity.js +19 -0
- package/dist/spatial/spatial-entity.js.map +1 -0
- package/dist/spatial/spatial-neighbors.d.ts +21 -0
- package/dist/spatial/spatial-neighbors.js +55 -0
- package/dist/spatial/spatial-neighbors.js.map +1 -0
- package/dist/utils/pointer.d.ts +8 -6
- package/dist/utils/pointer.js +22 -17
- package/dist/utils/pointer.js.map +1 -1
- package/package.json +6 -1
package/dist/memory-buffer.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { lock as e, unlock as t } from "./lock/simple-lock.js";
|
|
2
2
|
import { typedArray as n } from "./utils/typedarray.js";
|
|
3
3
|
//#region src/memory-buffer.ts
|
|
4
|
-
var r = 0, i = 1, a = 2, o = 3, s = 4, c = 5, l = 6, u =
|
|
4
|
+
var r = 0, i = 1, a = 2, o = 3, s = 4, c = 5, l = 6, u = 7, d = 1, f = 2, p = {
|
|
5
5
|
u8: 1,
|
|
6
6
|
u8c: 1,
|
|
7
7
|
i8: 1,
|
|
@@ -13,20 +13,21 @@ var r = 0, i = 1, a = 2, o = 3, s = 4, c = 5, l = 6, u = 1, d = 2, f = {
|
|
|
13
13
|
u64: 8,
|
|
14
14
|
f32: 4,
|
|
15
15
|
f64: 8
|
|
16
|
-
},
|
|
16
|
+
}, m = 36, h = 0, g = 1, _ = 2, v = 3, y = 16, b = 0, x = 1, S = class {
|
|
17
17
|
buf;
|
|
18
18
|
start;
|
|
19
19
|
u8;
|
|
20
20
|
u32;
|
|
21
21
|
state;
|
|
22
22
|
lock;
|
|
23
|
+
lastMallocFresh = !1;
|
|
23
24
|
constructor(e = {}) {
|
|
24
|
-
if (this.buf = e.buf ? e.buf : new ArrayBuffer(e.size || 4096), this.start = e.start == null ? 0 :
|
|
25
|
+
if (this.buf = e.buf ? e.buf : new ArrayBuffer(e.size || 4096), this.start = e.start == null ? 0 : T(Math.max(e.start, 0), 4), this.u8 = new Uint8Array(this.buf), this.u32 = new Uint32Array(this.buf), this.state = new Uint32Array(this.buf, this.start, 9), this.lock = new Int32Array(this.buf, this.start + this.state.byteLength - 4, 1), !e.skipInitialization) {
|
|
25
26
|
let t = e.align || 8;
|
|
26
27
|
if (t < 8) throw Error(`invalid alignment: ${t}, must be a pow2 and >= 8`);
|
|
27
28
|
let n = this.initialTop(t), r = e.end == null ? this.buf.byteLength : Math.min(e.end, this.buf.byteLength);
|
|
28
29
|
if (n >= r) throw Error(`insufficient address range (0x${this.start.toString(16)} - 0x${r.toString(16)})`);
|
|
29
|
-
this.align = t, this.doCompact = e.compact !== !1, this.doSplit = e.split !== !1, this.minSplit = e.minSplit ||
|
|
30
|
+
this.align = t, this.doCompact = e.compact !== !1, this.doSplit = e.split !== !1, this.minSplit = e.minSplit || 32, this.end = r, this.top = n, this.highWater = n, this._free = 0, this._used = 0;
|
|
30
31
|
}
|
|
31
32
|
}
|
|
32
33
|
stats() {
|
|
@@ -51,71 +52,43 @@ var r = 0, i = 1, a = 2, o = 3, s = 4, c = 5, l = 6, u = 1, d = 2, f = {
|
|
|
51
52
|
}
|
|
52
53
|
callocAs(e, t, n = 0) {
|
|
53
54
|
let r = this.mallocAs(e, t);
|
|
54
|
-
return r && r.fill(n), r;
|
|
55
|
+
return r && (n !== 0 || !this.lastMallocFresh) && r.fill(n), r;
|
|
55
56
|
}
|
|
56
57
|
mallocAs(e, t) {
|
|
57
|
-
let r = this.malloc(t *
|
|
58
|
+
let r = this.malloc(t * p[e]);
|
|
58
59
|
return r ? n(e, this.buf, r, t) : void 0;
|
|
59
60
|
}
|
|
60
61
|
calloc(e, t = 0) {
|
|
61
62
|
let n = this.malloc(e);
|
|
62
|
-
return n && this.u8.fill(t, n, n + e), n;
|
|
63
|
+
return n && (t !== 0 || !this.lastMallocFresh) && this.u8.fill(t, n, n + e), n;
|
|
63
64
|
}
|
|
64
65
|
malloc(n) {
|
|
65
66
|
if (n <= 0) return 0;
|
|
66
67
|
e(this.lock);
|
|
67
|
-
let r =
|
|
68
|
-
for (;
|
|
69
|
-
let e = this.blockSize(
|
|
68
|
+
let r = T(n + 16, this.align), i = this.end, a = this.doCompact, o = a ? this.highWater : 0, s = this.top, c = this._free, l = 0;
|
|
69
|
+
for (; c;) {
|
|
70
|
+
let e = this.blockSize(c), n = c + e >= s;
|
|
70
71
|
if (n || e >= r) {
|
|
71
|
-
let i = this.mallocTop(
|
|
72
|
-
return t(this.lock), i;
|
|
72
|
+
let i = this.mallocTop(c, l, e, r, n);
|
|
73
|
+
return this.lastMallocFresh = i !== 0 && a && c >= o, a && this.top > o && (this.highWater = this.top), t(this.lock), i;
|
|
73
74
|
}
|
|
74
|
-
|
|
75
|
+
l = c, c = this.blockNext(c);
|
|
75
76
|
}
|
|
76
|
-
if (
|
|
77
|
-
this.
|
|
78
|
-
let e =
|
|
77
|
+
if (c = s, s = c + r, s <= i) {
|
|
78
|
+
this.setBlockSize(c, r), this.pushUsed(c), this.top = s, this.lastMallocFresh = !a || c >= o, a && s > o && (this.highWater = s);
|
|
79
|
+
let e = C(c);
|
|
79
80
|
return t(this.lock), e;
|
|
80
81
|
}
|
|
81
|
-
return t(this.lock), 0;
|
|
82
|
+
return this.lastMallocFresh = !1, t(this.lock), 0;
|
|
82
83
|
}
|
|
83
84
|
mallocTop(e, t, n, r, i) {
|
|
84
85
|
if (i && e + r > this.end) return 0;
|
|
85
|
-
if (t ? this.unlinkBlock(t, e) : this._free = this.blockNext(e), this.
|
|
86
|
+
if (t ? this.unlinkBlock(t, e) : this._free = this.blockNext(e), this.pushUsed(e), i) (this.doSplit || r >= n) && (this.top = e + this.setBlockSize(e, r));
|
|
86
87
|
else if (this.doSplit) {
|
|
87
88
|
let t = n - r;
|
|
88
89
|
t >= this.minSplit && this.splitBlock(e, r, t);
|
|
89
90
|
}
|
|
90
|
-
return
|
|
91
|
-
}
|
|
92
|
-
realloc(e, t) {
|
|
93
|
-
if (t <= 0) return 0;
|
|
94
|
-
let n = y(e), r = 0, i = this._used, a = 0;
|
|
95
|
-
for (; i;) {
|
|
96
|
-
if (i === n) {
|
|
97
|
-
[r, a] = this.reallocBlock(i, t);
|
|
98
|
-
break;
|
|
99
|
-
}
|
|
100
|
-
i = this.blockNext(i);
|
|
101
|
-
}
|
|
102
|
-
return r && r !== n && this.u8.copyWithin(v(r), v(n), a), v(r);
|
|
103
|
-
}
|
|
104
|
-
reallocBlock(e, t) {
|
|
105
|
-
let n = this.blockSize(e), r = e + n, i = r >= this.top, a = b(t + 8, this.align);
|
|
106
|
-
if (a <= n) {
|
|
107
|
-
if (this.doSplit) {
|
|
108
|
-
let t = n - a;
|
|
109
|
-
t >= this.minSplit ? this.splitBlock(e, a, t) : i && (this.top = e + a);
|
|
110
|
-
} else i && (this.top = e + a);
|
|
111
|
-
return [e, r];
|
|
112
|
-
}
|
|
113
|
-
return i && e + a < this.end ? (this.top = e + this.setBlockSize(e, a), [e, r]) : (this.free(e), [y(this.malloc(t)), r]);
|
|
114
|
-
}
|
|
115
|
-
reallocArray(e, t) {
|
|
116
|
-
if (e.buffer !== this.buf) return;
|
|
117
|
-
let n = this.realloc(e.byteOffset, t * e.BYTES_PER_ELEMENT);
|
|
118
|
-
return n ? new e.constructor(this.buf, n, t) : void 0;
|
|
91
|
+
return C(e);
|
|
119
92
|
}
|
|
120
93
|
bytesFor(e) {
|
|
121
94
|
let t;
|
|
@@ -123,10 +96,10 @@ var r = 0, i = 1, a = 2, o = 3, s = 4, c = 5, l = 6, u = 1, d = 2, f = {
|
|
|
123
96
|
if (e.buffer !== this.buf) return;
|
|
124
97
|
t = e.byteOffset;
|
|
125
98
|
} else t = e;
|
|
126
|
-
t =
|
|
99
|
+
t = w(t);
|
|
127
100
|
let n = this._used;
|
|
128
101
|
for (; n;) {
|
|
129
|
-
if (n === t) return this.blockSize(t) -
|
|
102
|
+
if (n === t) return this.blockSize(t) - 16;
|
|
130
103
|
n = this.blockNext(n);
|
|
131
104
|
}
|
|
132
105
|
}
|
|
@@ -134,22 +107,18 @@ var r = 0, i = 1, a = 2, o = 3, s = 4, c = 5, l = 6, u = 1, d = 2, f = {
|
|
|
134
107
|
let t = this.bytesFor(e);
|
|
135
108
|
if (t) return t / this.u32.BYTES_PER_ELEMENT;
|
|
136
109
|
}
|
|
110
|
+
allocationLengthAt(e) {
|
|
111
|
+
return (this.blockSize(w(e)) - 16) / this.u32.BYTES_PER_ELEMENT;
|
|
112
|
+
}
|
|
137
113
|
free(n) {
|
|
138
114
|
let r;
|
|
139
115
|
if (typeof n != "number") {
|
|
140
116
|
if (n.buffer !== this.buf) return !1;
|
|
141
117
|
r = n.byteOffset;
|
|
142
118
|
} else r = n;
|
|
143
|
-
e(this.lock), r =
|
|
144
|
-
let i = this.
|
|
145
|
-
|
|
146
|
-
if (i === r) return a ? this.unlinkBlock(a, i) : this._used = this.blockNext(i), this.insert(i), this.doCompact && this.compact(), t(this.lock), !0;
|
|
147
|
-
a = i, i = this.blockNext(i);
|
|
148
|
-
}
|
|
149
|
-
return t(this.lock), !1;
|
|
150
|
-
}
|
|
151
|
-
freeAll() {
|
|
152
|
-
this._free = 0, this._used = 0, this.top = this.initialTop();
|
|
119
|
+
if (e(this.lock), r = w(r), this.blockState(r) !== x) return t(this.lock), !1;
|
|
120
|
+
let i = this.blockNext(r), a = this.blockPrev(r);
|
|
121
|
+
return a ? this.setBlockNext(a, i) : this._used = i, i && this.setBlockPrev(i, a), this.setBlockState(r, b), this.insert(r), this.doCompact && this.compact(), t(this.lock), !0;
|
|
153
122
|
}
|
|
154
123
|
release() {
|
|
155
124
|
return delete this.u8, delete this.u32, delete this.state, delete this.buf, !0;
|
|
@@ -172,6 +141,12 @@ var r = 0, i = 1, a = 2, o = 3, s = 4, c = 5, l = 6, u = 1, d = 2, f = {
|
|
|
172
141
|
set top(e) {
|
|
173
142
|
Atomics.store(this.state, a, e);
|
|
174
143
|
}
|
|
144
|
+
get highWater() {
|
|
145
|
+
return Atomics.load(this.state, u);
|
|
146
|
+
}
|
|
147
|
+
set highWater(e) {
|
|
148
|
+
Atomics.store(this.state, u, e);
|
|
149
|
+
}
|
|
175
150
|
get isEmpty() {
|
|
176
151
|
return this._used === 0;
|
|
177
152
|
}
|
|
@@ -188,13 +163,13 @@ var r = 0, i = 1, a = 2, o = 3, s = 4, c = 5, l = 6, u = 1, d = 2, f = {
|
|
|
188
163
|
Atomics.store(this.state, i, e);
|
|
189
164
|
}
|
|
190
165
|
get doCompact() {
|
|
191
|
-
return !!(this.state[c] &
|
|
166
|
+
return !!(this.state[c] & d);
|
|
192
167
|
}
|
|
193
168
|
set doCompact(e) {
|
|
194
169
|
e ? this.state[c] |= 1 : this.state[c] &= -2;
|
|
195
170
|
}
|
|
196
171
|
get doSplit() {
|
|
197
|
-
return !!(this.state[c] &
|
|
172
|
+
return !!(this.state[c] & f);
|
|
198
173
|
}
|
|
199
174
|
set doSplit(e) {
|
|
200
175
|
e ? this.state[c] |= 2 : this.state[c] &= -3;
|
|
@@ -203,24 +178,40 @@ var r = 0, i = 1, a = 2, o = 3, s = 4, c = 5, l = 6, u = 1, d = 2, f = {
|
|
|
203
178
|
return this.state[l];
|
|
204
179
|
}
|
|
205
180
|
set minSplit(e) {
|
|
206
|
-
if (e <=
|
|
181
|
+
if (e <= 16) throw Error(`illegal min split threshold: ${e}, require at least 17`);
|
|
207
182
|
this.state[l] = e;
|
|
208
183
|
}
|
|
209
184
|
blockSize(e) {
|
|
210
|
-
return Atomics.load(this.u32, (e >> 2) +
|
|
185
|
+
return Atomics.load(this.u32, (e >> 2) + h);
|
|
211
186
|
}
|
|
212
187
|
setBlockSize(e, t) {
|
|
213
|
-
return Atomics.store(this.u32, (e >> 2) +
|
|
188
|
+
return Atomics.store(this.u32, (e >> 2) + h, t), t;
|
|
214
189
|
}
|
|
215
190
|
blockNext(e) {
|
|
216
|
-
return Atomics.load(this.u32, (e >> 2) +
|
|
191
|
+
return Atomics.load(this.u32, (e >> 2) + g);
|
|
217
192
|
}
|
|
218
193
|
setBlockNext(e, t) {
|
|
219
|
-
Atomics.store(this.u32, (e >> 2) +
|
|
194
|
+
Atomics.store(this.u32, (e >> 2) + g, t);
|
|
195
|
+
}
|
|
196
|
+
blockPrev(e) {
|
|
197
|
+
return Atomics.load(this.u32, (e >> 2) + _);
|
|
198
|
+
}
|
|
199
|
+
setBlockPrev(e, t) {
|
|
200
|
+
Atomics.store(this.u32, (e >> 2) + _, t);
|
|
201
|
+
}
|
|
202
|
+
blockState(e) {
|
|
203
|
+
return Atomics.load(this.u32, (e >> 2) + v);
|
|
204
|
+
}
|
|
205
|
+
setBlockState(e, t) {
|
|
206
|
+
Atomics.store(this.u32, (e >> 2) + v, t);
|
|
207
|
+
}
|
|
208
|
+
pushUsed(e) {
|
|
209
|
+
let t = this._used;
|
|
210
|
+
this.setBlockNext(e, t), this.setBlockPrev(e, 0), this.setBlockState(e, x), t && this.setBlockPrev(t, e), this._used = e;
|
|
220
211
|
}
|
|
221
212
|
initBlock(e, t, n) {
|
|
222
213
|
let r = e >>> 2;
|
|
223
|
-
return Atomics.store(this.u32, r +
|
|
214
|
+
return Atomics.store(this.u32, r + h, t), Atomics.store(this.u32, r + g, n), Atomics.store(this.u32, r + v, b), e;
|
|
224
215
|
}
|
|
225
216
|
unlinkBlock(e, t) {
|
|
226
217
|
this.setBlockNext(e, this.blockNext(t));
|
|
@@ -229,7 +220,7 @@ var r = 0, i = 1, a = 2, o = 3, s = 4, c = 5, l = 6, u = 1, d = 2, f = {
|
|
|
229
220
|
this.insert(this.initBlock(e + this.setBlockSize(e, t), n, 0)), this.doCompact && this.compact();
|
|
230
221
|
}
|
|
231
222
|
initialTop(e = this.align) {
|
|
232
|
-
return
|
|
223
|
+
return T(this.start + 36 + 16, e) - 16;
|
|
233
224
|
}
|
|
234
225
|
compact() {
|
|
235
226
|
let e = this._free, t = 0, n = 0, r, i = !1;
|
|
@@ -250,21 +241,26 @@ var r = 0, i = 1, a = 2, o = 3, s = 4, c = 5, l = 6, u = 1, d = 2, f = {
|
|
|
250
241
|
return i;
|
|
251
242
|
}
|
|
252
243
|
insert(e) {
|
|
244
|
+
if (!this.doCompact) {
|
|
245
|
+
this.setBlockNext(e, this._free), this._free = e;
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
253
248
|
let t = this._free, n = 0;
|
|
254
249
|
for (; t && !(e <= t);) n = t, t = this.blockNext(t);
|
|
255
250
|
n ? this.setBlockNext(n, e) : this._free = e, this.setBlockNext(e, t);
|
|
256
251
|
}
|
|
257
252
|
};
|
|
258
|
-
function
|
|
259
|
-
return e > 0 ? e +
|
|
253
|
+
function C(e) {
|
|
254
|
+
return e > 0 ? e + 16 : 0;
|
|
260
255
|
}
|
|
261
|
-
function
|
|
262
|
-
return e > 0 ? e -
|
|
256
|
+
function w(e) {
|
|
257
|
+
return e > 0 ? e - 16 : 0;
|
|
263
258
|
}
|
|
264
|
-
function
|
|
259
|
+
function T(e, t) {
|
|
265
260
|
return t--, e + t & ~t;
|
|
266
261
|
}
|
|
262
|
+
var E = T(52, 8);
|
|
267
263
|
//#endregion
|
|
268
|
-
export {
|
|
264
|
+
export { E as FIRST_BLOCK_DATA_BYTE_OFFSET, y as SIZEOF_MEM_BLOCK, m as SIZEOF_STATE, S as default };
|
|
269
265
|
|
|
270
266
|
//# sourceMappingURL=memory-buffer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-buffer.js","names":[],"sources":["../src/memory-buffer.ts"],"sourcesContent":["// Just disabling this rule instead of re-writing for now to keep asa close to original in case we want to pull updates later\n// Doing `block && block.fill(fill)` instead of `if(block) { block.fill(fill); }` is valid code even if it is hard to parse\n/* eslint-disable @typescript-eslint/no-unused-expressions */\n\nimport type { Pow2 } from './interfaces/pow2';\nimport type { TypedArray } from './interfaces/typed-array';\nimport { lock, unlock } from './lock/simple-lock';\nimport { typedArray } from './utils/typedarray';\n\nconst STATE_FREE = 0;\nconst STATE_USED = 1;\nconst STATE_TOP = 2;\nconst STATE_END = 3;\nconst STATE_ALIGN = 4;\nconst STATE_FLAGS = 5;\nconst STATE_MIN_SPLIT = 6;\n\nconst MASK_COMPACT = 1;\nconst MASK_SPLIT = 2;\n\nconst SIZEOF = {\n\tu8: 1,\n\tu8c: 1,\n\ti8: 1,\n\tu16: 2,\n\ti16: 2,\n\tu32: 4,\n\ti32: 4,\n\ti64: 8,\n\tu64: 8,\n\tf32: 4,\n\tf64: 8,\n};\n\nconst SIZEOF_STATE = 8 * 4;\n\nconst MEM_BLOCK_SIZE = 0;\nconst MEM_BLOCK_NEXT = 1;\n\nconst SIZEOF_MEM_BLOCK = 2 * 4;\n\n// Copied from https://github.com/thi-ng/umbrella/blob/develop/packages/malloc/src/pool.ts\n// Changes: Atomic.load/store on state, simple lock on malloc/free\n// Added bytesFor/lengthOf to be able to grab expected length of a block\nexport default class MemoryBuffer {\n\tbuf: ArrayBufferLike;\n\n\tprotected readonly start: number;\n\tprotected u8: Uint8Array;\n\tprotected u32: Uint32Array;\n\tprotected state: Uint32Array;\n\tprotected lock: Int32Array;\n\n\tconstructor(opts: Partial<MemoryBufferConfig> = {}) {\n\t\tthis.buf = opts.buf ? opts.buf : new ArrayBuffer(opts.size || 0x1000);\n\t\tthis.start = opts.start != null ? align(Math.max(opts.start, 0), 4) : 0;\n\t\tthis.u8 = new Uint8Array(this.buf);\n\t\tthis.u32 = new Uint32Array(this.buf);\n\t\tthis.state = new Uint32Array(this.buf, this.start, SIZEOF_STATE / 4);\n\t\tthis.lock = new Int32Array(this.buf, this.start + this.state.byteLength - 4, 1);\n\n\t\tif(!opts.skipInitialization) {\n\t\t\tconst _align = opts.align || 8;\n\t\t\tif(_align < 8) {\n\t\t\t\tthrow new Error(`invalid alignment: ${_align}, must be a pow2 and >= 8`);\n\t\t\t}\n\t\t\tconst top = this.initialTop(_align);\n\t\t\tconst resolvedEnd =\n\t\t\t\topts.end != null\n\t\t\t\t\t? Math.min(opts.end, this.buf.byteLength)\n\t\t\t\t\t: this.buf.byteLength;\n\n\t\t\tif(top >= resolvedEnd) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`insufficient address range (0x${this.start.toString(\n\t\t\t\t\t\t16,\n\t\t\t\t\t)} - 0x${resolvedEnd.toString(16)})`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tthis.align = _align;\n\t\t\tthis.doCompact = opts.compact !== false;\n\t\t\tthis.doSplit = opts.split !== false;\n\t\t\tthis.minSplit = opts.minSplit || 16;\n\t\t\tthis.end = resolvedEnd;\n\t\t\tthis.top = top;\n\t\t\tthis._free = 0;\n\t\t\tthis._used = 0;\n\t\t}\n\t}\n\n\tstats(): Readonly<MemoryBufferStats> {\n\t\tconst listStats = (block: number) => {\n\t\t\tlet count = 0;\n\t\t\tlet size = 0;\n\t\t\twhile(block) {\n\t\t\t\tcount++;\n\t\t\t\tsize += this.blockSize(block);\n\t\t\t\tblock = this.blockNext(block);\n\n\t\t\t\tif(block > this.end) {\n\t\t\t\t\tconsole.error(`Trying to get stats for block past end of buffer: ${block} > ${this.end}`);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { count, size };\n\t\t};\n\t\tconst free = listStats(this._free);\n\t\treturn {\n\t\t\tfree,\n\t\t\tused: listStats(this._used),\n\t\t\ttop: this.top,\n\t\t\tavailable: this.end - this.top + free.size,\n\t\t\ttotal: this.buf.byteLength,\n\t\t};\n\t}\n\n\tcallocAs<T extends Type>(type: T, num: number, fill = 0) {\n\t\tconst block = this.mallocAs(type, num);\n\t\tblock && block.fill(fill);\n\t\treturn block;\n\t}\n\n\tmallocAs<T extends Type>(type: T, num: number) {\n\t\tconst addr = this.malloc(num * SIZEOF[type]);\n\t\treturn addr ? typedArray(type, this.buf, addr, num) : undefined;\n\t}\n\n\tcalloc(bytes: number, fill = 0) {\n\t\tconst addr = this.malloc(bytes);\n\t\taddr && this.u8.fill(fill, addr, addr + bytes);\n\t\treturn addr;\n\t}\n\n\tmalloc(bytes: number) {\n\t\tif(bytes <= 0) {\n\t\t\treturn 0;\n\t\t}\n\t\tlock(this.lock);\n\t\tconst paddedSize = align(bytes + SIZEOF_MEM_BLOCK, this.align);\n\t\tconst end = this.end;\n\t\tlet top = this.top;\n\t\tlet block = this._free;\n\t\tlet prev = 0;\n\t\twhile(block) {\n\t\t\tconst blockSize = this.blockSize(block);\n\t\t\tconst isTop = block + blockSize >= top;\n\t\t\tif(isTop || blockSize >= paddedSize) {\n\t\t\t\tlet result = this.mallocTop(\n\t\t\t\t\tblock,\n\t\t\t\t\tprev,\n\t\t\t\t\tblockSize,\n\t\t\t\t\tpaddedSize,\n\t\t\t\t\tisTop,\n\t\t\t\t);\n\n\t\t\t\tunlock(this.lock);\n\t\t\t\treturn result;\n\t\t\t}\n\t\t\tprev = block;\n\t\t\tblock = this.blockNext(block);\n\t\t}\n\t\tblock = top;\n\t\ttop = block + paddedSize;\n\t\tif(top <= end) {\n\t\t\tthis.initBlock(block, paddedSize, this._used);\n\t\t\tthis._used = block;\n\t\t\tthis.top = top;\n\t\t\tlet result = blockDataAddress(block);\n\t\t\tunlock(this.lock);\n\n\t\t\treturn result;\n\t\t}\n\t\tunlock(this.lock);\n\t\treturn 0;\n\t}\n\n\tprivate mallocTop(\n\t\tblock: number,\n\t\tprev: number,\n\t\tblockSize: number,\n\t\tpaddedSize: number,\n\t\tisTop: boolean,\n\t) {\n\t\tif(isTop && block + paddedSize > this.end) return 0;\n\t\tif(prev) {\n\t\t\tthis.unlinkBlock(prev, block);\n\t\t} else {\n\t\t\tthis._free = this.blockNext(block);\n\t\t}\n\t\tthis.setBlockNext(block, this._used);\n\t\tthis._used = block;\n\t\tif(isTop) {\n\t\t\tthis.top = block + this.setBlockSize(block, paddedSize);\n\t\t} else if(this.doSplit) {\n\t\t\tconst excess = blockSize - paddedSize;\n\t\t\texcess >= this.minSplit &&\n\t\t\tthis.splitBlock(block, paddedSize, excess);\n\t\t}\n\t\treturn blockDataAddress(block);\n\t}\n\n\trealloc(ptr: number, bytes: number) {\n\t\tif(bytes <= 0) {\n\t\t\treturn 0;\n\t\t}\n\t\tconst oldAddr = blockSelfAddress(ptr);\n\t\tlet newAddr = 0;\n\t\tlet block = this._used;\n\t\tlet blockEnd = 0;\n\t\twhile(block) {\n\t\t\tif(block === oldAddr) {\n\t\t\t\t[newAddr, blockEnd] = this.reallocBlock(block, bytes);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tblock = this.blockNext(block);\n\t\t}\n\t\t// copy old block contents to new addr\n\t\tif(newAddr && newAddr !== oldAddr) {\n\t\t\tthis.u8.copyWithin(\n\t\t\t\tblockDataAddress(newAddr),\n\t\t\t\tblockDataAddress(oldAddr),\n\t\t\t\tblockEnd,\n\t\t\t);\n\t\t}\n\t\treturn blockDataAddress(newAddr);\n\t}\n\n\tprivate reallocBlock(block: number, bytes: number) {\n\t\tconst blockSize = this.blockSize(block);\n\t\tconst blockEnd = block + blockSize;\n\t\tconst isTop = blockEnd >= this.top;\n\t\tconst paddedSize = align(bytes + SIZEOF_MEM_BLOCK, this.align);\n\t\t// shrink & possibly split existing block\n\t\tif(paddedSize <= blockSize) {\n\t\t\tif(this.doSplit) {\n\t\t\t\tconst excess = blockSize - paddedSize;\n\t\t\t\tif(excess >= this.minSplit) {\n\t\t\t\t\tthis.splitBlock(block, paddedSize, excess);\n\t\t\t\t} else if(isTop) {\n\t\t\t\t\tthis.top = block + paddedSize;\n\t\t\t\t}\n\t\t\t} else if(isTop) {\n\t\t\t\tthis.top = block + paddedSize;\n\t\t\t}\n\t\t\treturn [block, blockEnd];\n\t\t}\n\t\t// try to enlarge block if current top\n\t\tif(isTop && block + paddedSize < this.end) {\n\t\t\tthis.top = block + this.setBlockSize(block, paddedSize);\n\t\t\treturn [block, blockEnd];\n\t\t}\n\t\t// fallback to free & malloc\n\t\tthis.free(block);\n\t\treturn [blockSelfAddress(this.malloc(bytes)), blockEnd];\n\t}\n\n\treallocArray<T extends TypedArray>(array: T, num: number): T | undefined {\n\t\tif(array.buffer !== this.buf) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst addr = this.realloc(\n\t\t\tarray.byteOffset,\n\t\t\tnum * array.BYTES_PER_ELEMENT,\n\t\t);\n\t\treturn addr\n\t\t\t? new (<any>array.constructor)(this.buf, addr, num)\n\t\t\t: undefined;\n\t}\n\n\tbytesFor(ptrOrArray: number | TypedArray): number | undefined {\n\t\tlet addr: number;\n\t\tif(typeof ptrOrArray !== 'number') {\n\t\t\tif(ptrOrArray.buffer !== this.buf) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\taddr = ptrOrArray.byteOffset;\n\t\t} else {\n\t\t\taddr = ptrOrArray;\n\t\t}\n\n\t\taddr = blockSelfAddress(addr);\n\t\tlet block = this._used;\n\t\twhile(block) {\n\t\t\tif(block === addr) {\n\t\t\t\treturn this.blockSize(addr) - SIZEOF_MEM_BLOCK;\n\t\t\t}\n\t\t\tblock = this.blockNext(block);\n\t\t}\n\n\t\treturn undefined;\n\t}\n\tlengthOf(ptrOrArray: number | TypedArray): number | undefined {\n\t\tlet bytes = this.bytesFor(ptrOrArray);\n\t\tif(bytes) {\n\t\t\treturn bytes / this.u32.BYTES_PER_ELEMENT;\n\t\t} else {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tfree(ptrOrArray: number | TypedArray) {\n\t\tlet addr: number;\n\t\tif(typeof ptrOrArray !== 'number') {\n\t\t\tif(ptrOrArray.buffer !== this.buf) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\taddr = ptrOrArray.byteOffset;\n\t\t} else {\n\t\t\taddr = ptrOrArray;\n\t\t}\n\t\tlock(this.lock);\n\t\taddr = blockSelfAddress(addr);\n\t\tlet block = this._used;\n\t\tlet prev = 0;\n\t\twhile(block) {\n\t\t\tif(block === addr) {\n\t\t\t\tif(prev) {\n\t\t\t\t\tthis.unlinkBlock(prev, block);\n\t\t\t\t} else {\n\t\t\t\t\tthis._used = this.blockNext(block);\n\t\t\t\t}\n\t\t\t\tthis.insert(block);\n\t\t\t\tthis.doCompact && this.compact();\n\n\t\t\t\tunlock(this.lock);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tprev = block;\n\t\t\tblock = this.blockNext(block);\n\t\t}\n\n\t\tunlock(this.lock);\n\t\treturn false;\n\t}\n\n\tfreeAll() {\n\t\tthis._free = 0;\n\t\tthis._used = 0;\n\t\tthis.top = this.initialTop();\n\t}\n\n\trelease() {\n\t\tdelete (<any> this).u8;\n\t\tdelete (<any> this).u32;\n\t\tdelete (<any> this).state;\n\t\tdelete (<any> this).buf;\n\t\treturn true;\n\t}\n\n\tprotected get align() {\n\t\treturn <Pow2> this.state[STATE_ALIGN];\n\t}\n\n\tprotected set align(x: Pow2) {\n\t\tthis.state[STATE_ALIGN] = x;\n\t}\n\n\tget end() {\n\t\treturn this.state[STATE_END];\n\t}\n\n\tprotected set end(x: number) {\n\t\tthis.state[STATE_END] = x;\n\t}\n\n\tget top() {\n\t\treturn Atomics.load(this.state, STATE_TOP);\n\t}\n\n\tprotected set top(x: number) {\n\t\tAtomics.store(this.state, STATE_TOP, x);\n\t}\n\n\t// No live allocations. O(1) (the head of the used list, not a walk like stats()), so it is cheap to poll each\n\t// frame when checking whether a spare buffer is still available.\n\tget isEmpty(): boolean {\n\t\treturn this._used === 0;\n\t}\n\n\tprotected get _free() {\n\t\treturn Atomics.load(this.state, STATE_FREE);\n\t}\n\n\tprotected set _free(block: number) {\n\t\tAtomics.store(this.state, STATE_FREE, block);\n\t}\n\n\tprotected get _used() {\n\t\treturn Atomics.load(this.state, STATE_USED);\n\t}\n\n\tprotected set _used(block: number) {\n\t\tAtomics.store(this.state, STATE_USED, block);\n\t}\n\n\tprotected get doCompact() {\n\t\treturn !!(this.state[STATE_FLAGS] & MASK_COMPACT);\n\t}\n\n\tprotected set doCompact(flag: boolean) {\n\t\tflag\n\t\t\t? (this.state[STATE_FLAGS] |= 1 << (MASK_COMPACT - 1))\n\t\t\t: (this.state[STATE_FLAGS] &= ~MASK_COMPACT);\n\t}\n\n\tprotected get doSplit() {\n\t\treturn !!(this.state[STATE_FLAGS] & MASK_SPLIT);\n\t}\n\n\tprotected set doSplit(flag: boolean) {\n\t\tflag\n\t\t\t? (this.state[STATE_FLAGS] |= 1 << (MASK_SPLIT - 1))\n\t\t\t: (this.state[STATE_FLAGS] &= ~MASK_SPLIT);\n\t}\n\n\tprotected get minSplit() {\n\t\treturn this.state[STATE_MIN_SPLIT];\n\t}\n\n\tprotected set minSplit(x: number) {\n\t\tif(x <= SIZEOF_MEM_BLOCK) {\n\t\t\tthrow new Error(`illegal min split threshold: ${x}, require at least ${\n\t\t\t\tSIZEOF_MEM_BLOCK + 1\n\t\t\t}`);\n\t\t}\n\t\tthis.state[STATE_MIN_SPLIT] = x;\n\t}\n\n\tprotected blockSize(block: number) {\n\t\treturn Atomics.load(this.u32, (block >> 2) + MEM_BLOCK_SIZE);\n\t}\n\n\t/**\n\t * Sets & returns given block size.\n\t *\n\t * @param block -\n\t * @param size -\n\t */\n\tprotected setBlockSize(block: number, size: number) {\n\t\tAtomics.store(this.u32, (block >> 2) + MEM_BLOCK_SIZE, size);\n\t\treturn size;\n\t}\n\n\tprotected blockNext(block: number) {\n\t\treturn Atomics.load(this.u32, (block >> 2) + MEM_BLOCK_NEXT);\n\t}\n\n\t/**\n\t * Sets block next pointer to `next`. Use zero to indicate list end.\n\t *\n\t * @param block -\n\t */\n\tprotected setBlockNext(block: number, next: number) {\n\t\tAtomics.store(this.u32, (block >> 2) + MEM_BLOCK_NEXT, next);\n\t}\n\n\t/**\n\t * Initializes block header with given `size` and `next` pointer. Returns `block`.\n\t *\n\t * @param block -\n\t * @param size -\n\t * @param next -\n\t */\n\tprotected initBlock(block: number, size: number, next: number) {\n\t\tconst idx = block >>> 2;\n\t\tAtomics.store(this.u32, idx + MEM_BLOCK_SIZE, size);\n\t\tAtomics.store(this.u32, idx + MEM_BLOCK_NEXT, next);\n\t\treturn block;\n\t}\n\n\tprotected unlinkBlock(prev: number, block: number) {\n\t\tthis.setBlockNext(prev, this.blockNext(block));\n\t}\n\n\tprotected splitBlock(block: number, blockSize: number, excess: number) {\n\t\tthis.insert(\n\t\t\tthis.initBlock(\n\t\t\t\tblock + this.setBlockSize(block, blockSize),\n\t\t\t\texcess,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.doCompact && this.compact();\n\t}\n\n\tprotected initialTop(_align = this.align) {\n\t\treturn (\n\t\t\talign(this.start + SIZEOF_STATE + SIZEOF_MEM_BLOCK, _align) -\n\t\t\tSIZEOF_MEM_BLOCK\n\t\t);\n\t}\n\n\t/**\n\t * Traverses free list and attempts to recursively merge blocks\n\t * occupying consecutive memory regions. Returns true if any blocks\n\t * have been merged. Only called if `compact` option is enabled.\n\t */\n\tprotected compact() {\n\t\tlet block = this._free;\n\t\tlet prev = 0;\n\t\tlet scan = 0;\n\t\tlet scanPrev: number;\n\t\tlet res = false;\n\t\twhile(block) {\n\t\t\tscanPrev = block;\n\t\t\tscan = this.blockNext(block);\n\t\t\twhile(scan && scanPrev + this.blockSize(scanPrev) === scan) {\n\t\t\t\t// console.log(\"merge:\", scan.addr, scan.size);\n\t\t\t\tscanPrev = scan;\n\t\t\t\tscan = this.blockNext(scan);\n\t\t\t}\n\t\t\tif(scanPrev !== block) {\n\t\t\t\tconst newSize = scanPrev - block + this.blockSize(scanPrev);\n\t\t\t\t// console.log(\"merged size:\", newSize);\n\t\t\t\tthis.setBlockSize(block, newSize);\n\t\t\t\tconst next = this.blockNext(scanPrev);\n\t\t\t\tlet tmp = this.blockNext(block);\n\t\t\t\twhile(tmp && tmp !== next) {\n\t\t\t\t\t// console.log(\"release:\", tmp.addr);\n\t\t\t\t\tconst tn = this.blockNext(tmp);\n\t\t\t\t\tthis.setBlockNext(tmp, 0);\n\t\t\t\t\ttmp = tn;\n\t\t\t\t}\n\t\t\t\tthis.setBlockNext(block, next);\n\t\t\t\tres = true;\n\t\t\t}\n\t\t\t// re-adjust top if poss\n\t\t\tif(block + this.blockSize(block) >= this.top) {\n\t\t\t\tthis.top = block;\n\t\t\t\tprev\n\t\t\t\t\t? this.unlinkBlock(prev, block)\n\t\t\t\t\t: (this._free = this.blockNext(block));\n\t\t\t}\n\t\t\tprev = block;\n\t\t\tblock = this.blockNext(block);\n\t\t}\n\t\treturn res;\n\t}\n\n\t/**\n\t * Inserts given block into list of free blocks, sorted by address.\n\t *\n\t * @param block -\n\t */\n\tprotected insert(block: number) {\n\t\tlet ptr = this._free;\n\t\tlet prev = 0;\n\t\twhile(ptr) {\n\t\t\tif(block <= ptr) break;\n\t\t\tprev = ptr;\n\t\t\tptr = this.blockNext(ptr);\n\t\t}\n\t\tif(prev) {\n\t\t\tthis.setBlockNext(prev, block);\n\t\t} else {\n\t\t\tthis._free = block;\n\t\t}\n\t\tthis.setBlockNext(block, ptr);\n\t}\n}\n\n/**\n * Returns a block's data address, based on given alignment.\n *\n * @param blockAddress -\n */\nfunction blockDataAddress(blockAddress: number) {\n\treturn blockAddress > 0 ? blockAddress + SIZEOF_MEM_BLOCK : 0;\n}\n\n/**\n * Returns block start address for given data address and alignment.\n *\n * @param dataAddress -\n */\nfunction blockSelfAddress(dataAddress: number) {\n\treturn dataAddress > 0 ? dataAddress - SIZEOF_MEM_BLOCK : 0;\n}\n\nfunction align(addr: number, size: number) {\n\tsize--;\n\treturn addr + size & ~size;\n}\n\n// Bytes reserved before usable heap space (allocator state) and the per-allocation block header. Exported so callers can\n// size an allocation against what a single buffer can actually hold.\nexport { SIZEOF_STATE, SIZEOF_MEM_BLOCK };\n\ntype Type = 'u8' | 'u8c' | 'i8' | 'u16' | 'i16' | 'u32' | 'i32' | 'f32' | 'f64';\n\ninterface MemoryBufferConfig {\n\t/**\n\t * Backing ArrayBuffer (or SharedArrayBuffer). If not given, a new\n\t * one will be created with given `size`.\n\t */\n\tbuf: ArrayBufferLike\n\t/**\n\t * Byte size for newly created ArrayBuffers (if `buf` is not given).\n\t *\n\t * @defaultValue 0x1000 (4KB)\n\t */\n\tsize: number\n\t/**\n\t * Anchor index (byte address) inside the array buffer. The MemPool\n\t * stores its internal state from the given address and heap space\n\t * starts at least 32 bytes later (depending on chosen `align`\n\t * value). Unlike allocator state variables, `start`` cannot be\n\t * saved inside the array buffer itself. If the ArrayBuffer is\n\t * passed to other consumers they must use the same start value.\n\t * MUST be multiple of 4.\n\t *\n\t * @defaultValue 0\n\t */\n\tstart: number\n\t/**\n\t * Byte address (+1) of the end of the memory region managed by the\n\t * {@link MemPool}.\n\t *\n\t * @defaultValue end of the backing ArrayBuffer\n\t */\n\tend: number\n\t/**\n\t * Number of bytes to align memory blocks to. MUST be a power of 2\n\t * and >= 8. Use 16 if the pool is being used for allocating memory\n\t * used in SIMD operations.\n\t *\n\t * @defaultValue 8\n\t */\n\talign: Pow2\n\t/**\n\t * Flag to configure memory block compaction. If true,\n\t * adjoining free blocks (in terms of address space) will be merged\n\t * to minimize fragementation.\n\t *\n\t * @defaultValue true\n\t */\n\tcompact: boolean\n\t/**\n\t * Flag to configure memory block splitting. If true, and when the\n\t * allocator is re-using a previously freed block larger than the\n\t * requested size, the block will be split to minimize wasted/unused\n\t * memory. The splitting behavior can further customized via the\n\t * `minSplit` option.\n\t *\n\t * @defaultValue true\n\t */\n\tsplit: boolean\n\t/**\n\t * Only used if `split` behavior is enabled. Defines min number of\n\t * excess bytes available in a block for memory block splitting to\n\t * occur.\n\t *\n\t * @defaultValue 16, MUST be > 8\n\t */\n\tminSplit: number\n\t/**\n\t * Only needed when sharing the underlying ArrayBuffer. If true, the\n\t * {@link MemPool} constructor will NOT initialize its internal state and\n\t * assume the underlying ArrayBuffer has already been initialized by\n\t * another {@link MemPool} instance. If this option is used, `buf` MUST be\n\t * given.\n\t *\n\t * @defaultValue false\n\t */\n\tskipInitialization: boolean\n}\ninterface MemoryBufferStats {\n\t/**\n\t * Free block stats.\n\t */\n\tfree: { count: number, size: number }\n\t/**\n\t * Used block stats.\n\t */\n\tused: { count: number, size: number }\n\t/**\n\t * Current top address.\n\t */\n\ttop: number\n\t/**\n\t * Bytes available\n\t */\n\tavailable: number\n\t/**\n\t * Total pool size.\n\t */\n\ttotal: number\n}"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"memory-buffer.js","names":[],"sources":["../src/memory-buffer.ts"],"sourcesContent":["// Just disabling this rule instead of re-writing for now to keep asa close to original in case we want to pull updates later\n// Doing `block && block.fill(fill)` instead of `if(block) { block.fill(fill); }` is valid code even if it is hard to parse\n/* eslint-disable @typescript-eslint/no-unused-expressions */\n\nimport type { Pow2 } from './interfaces/pow2';\nimport type { TypedArray } from './interfaces/typed-array';\nimport { lock, unlock } from './lock/simple-lock';\nimport { typedArray } from './utils/typedarray';\n\nconst STATE_FREE = 0;\nconst STATE_USED = 1;\nconst STATE_TOP = 2;\nconst STATE_END = 3;\nconst STATE_ALIGN = 4;\nconst STATE_FLAGS = 5;\nconst STATE_MIN_SPLIT = 6;\n// Highest top ever reached. callock can skip re-zeroing it\nconst STATE_HIGH_WATER = 7;\n\nconst MASK_COMPACT = 1;\nconst MASK_SPLIT = 2;\n\nconst SIZEOF = {\n\tu8: 1,\n\tu8c: 1,\n\ti8: 1,\n\tu16: 2,\n\ti16: 2,\n\tu32: 4,\n\ti32: 4,\n\ti64: 8,\n\tu64: 8,\n\tf32: 4,\n\tf64: 8,\n};\n\nconst SIZEOF_STATE = 9 * 4;\n\nconst MEM_BLOCK_SIZE = 0;\nconst MEM_BLOCK_NEXT = 1;\n// The used list is doubly linked so free() can unlink a block in O(1) instead of walking to find its predecessor.\n// MEM_BLOCK_STATE marks live blocks so free() rejects a double-free (which would otherwise splice free-list pointers\n// into the used list) without that walk. Word 3 also pads the header to 16 bytes, keeping data addresses aligned exactly\n// as the original 8-byte header did.\nconst MEM_BLOCK_PREV = 2;\nconst MEM_BLOCK_STATE = 3;\n\nconst SIZEOF_MEM_BLOCK = 4 * 4;\n\nconst BLOCK_FREE = 0;\nconst BLOCK_USED = 1;\n\n// Copied from https://github.com/thi-ng/umbrella/blob/develop/packages/malloc/src/pool.ts\n// Changes: Atomic.load/store on state, simple lock on malloc/free\n// Added bytesFor/lengthOf to be able to grab expected length of a block\nexport default class MemoryBuffer {\n\tbuf: ArrayBufferLike;\n\n\tprotected readonly start: number;\n\tprotected u8: Uint8Array;\n\tprotected u32: Uint32Array;\n\tprotected state: Uint32Array;\n\tprotected lock: Int32Array;\n\t// Whether the most recent malloc came from never-before-allocated (already-zeroed) memory. Per-instance/per-thread and\n\t// read by calloc immediately after malloc under the same call, so no locking is needed.\n\tprivate lastMallocFresh = false;\n\n\tconstructor(opts: Partial<MemoryBufferConfig> = {}) {\n\t\tthis.buf = opts.buf ? opts.buf : new ArrayBuffer(opts.size || 0x1000);\n\t\tthis.start = opts.start != null ? align(Math.max(opts.start, 0), 4) : 0;\n\t\tthis.u8 = new Uint8Array(this.buf);\n\t\tthis.u32 = new Uint32Array(this.buf);\n\t\tthis.state = new Uint32Array(this.buf, this.start, SIZEOF_STATE / 4);\n\t\tthis.lock = new Int32Array(this.buf, this.start + this.state.byteLength - 4, 1);\n\n\t\tif(!opts.skipInitialization) {\n\t\t\tconst _align = opts.align || 8;\n\t\t\tif(_align < 8) {\n\t\t\t\tthrow new Error(`invalid alignment: ${_align}, must be a pow2 and >= 8`);\n\t\t\t}\n\t\t\tconst top = this.initialTop(_align);\n\t\t\tconst resolvedEnd =\n\t\t\t\topts.end != null\n\t\t\t\t\t? Math.min(opts.end, this.buf.byteLength)\n\t\t\t\t\t: this.buf.byteLength;\n\n\t\t\tif(top >= resolvedEnd) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`insufficient address range (0x${this.start.toString(\n\t\t\t\t\t\t16,\n\t\t\t\t\t)} - 0x${resolvedEnd.toString(16)})`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tthis.align = _align;\n\t\t\tthis.doCompact = opts.compact !== false;\n\t\t\tthis.doSplit = opts.split !== false;\n\t\t\tthis.minSplit = opts.minSplit || SIZEOF_MEM_BLOCK * 2;\n\t\t\tthis.end = resolvedEnd;\n\t\t\tthis.top = top;\n\t\t\tthis.highWater = top;\n\t\t\tthis._free = 0;\n\t\t\tthis._used = 0;\n\t\t}\n\t}\n\n\tstats(): Readonly<MemoryBufferStats> {\n\t\tconst listStats = (block: number) => {\n\t\t\tlet count = 0;\n\t\t\tlet size = 0;\n\t\t\twhile(block) {\n\t\t\t\tcount++;\n\t\t\t\tsize += this.blockSize(block);\n\t\t\t\tblock = this.blockNext(block);\n\n\t\t\t\tif(block > this.end) {\n\t\t\t\t\tconsole.error(`Trying to get stats for block past end of buffer: ${block} > ${this.end}`);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { count, size };\n\t\t};\n\t\tconst free = listStats(this._free);\n\t\treturn {\n\t\t\tfree,\n\t\t\tused: listStats(this._used),\n\t\t\ttop: this.top,\n\t\t\tavailable: this.end - this.top + free.size,\n\t\t\ttotal: this.buf.byteLength,\n\t\t};\n\t}\n\n\tcallocAs<T extends Type>(type: T, num: number, fill = 0) {\n\t\tconst block = this.mallocAs(type, num);\n\t\t// Fresh bump memory is already zero from the backing buffer; only fill for a non-zero value or reused (dirty) memory.\n\t\tblock && (fill !== 0 || !this.lastMallocFresh) && block.fill(fill);\n\t\treturn block;\n\t}\n\n\tmallocAs<T extends Type>(type: T, num: number) {\n\t\tconst addr = this.malloc(num * SIZEOF[type]);\n\t\treturn addr ? typedArray(type, this.buf, addr, num) : undefined;\n\t}\n\n\tcalloc(bytes: number, fill = 0) {\n\t\tconst addr = this.malloc(bytes);\n\t\t// Fresh bump memory is already zero from the backing buffer; only fill for a non-zero value or reused (dirty) memory.\n\t\taddr && (fill !== 0 || !this.lastMallocFresh) && this.u8.fill(fill, addr, addr + bytes);\n\t\treturn addr;\n\t}\n\n\tmalloc(bytes: number) {\n\t\tif(bytes <= 0) {\n\t\t\treturn 0;\n\t\t}\n\t\tlock(this.lock);\n\t\tconst paddedSize = align(bytes + SIZEOF_MEM_BLOCK, this.align);\n\t\tconst end = this.end;\n\t\t// Without compaction top never retreats, so bump memory is always pristine and no high-water tracking is needed. With\n\t\t// compaction top can move back below the high-water mark, so a bump there reuses dirty memory and must be zeroed.\n\t\tconst doCompact = this.doCompact;\n\t\tconst highWater = doCompact ? this.highWater : 0;\n\t\tlet top = this.top;\n\t\tlet block = this._free;\n\t\tlet prev = 0;\n\t\twhile(block) {\n\t\t\tconst blockSize = this.blockSize(block);\n\t\t\tconst isTop = block + blockSize >= top;\n\t\t\tif(isTop || blockSize >= paddedSize) {\n\t\t\t\tlet result = this.mallocTop(\n\t\t\t\t\tblock,\n\t\t\t\t\tprev,\n\t\t\t\t\tblockSize,\n\t\t\t\t\tpaddedSize,\n\t\t\t\t\tisTop,\n\t\t\t\t);\n\n\t\t\t\t// A free-list block was previously allocated (below the high-water mark), so its memory is dirty.\n\t\t\t\tthis.lastMallocFresh = result !== 0 && doCompact && block >= highWater;\n\t\t\t\tif(doCompact && this.top > highWater) {\n\t\t\t\t\tthis.highWater = this.top;\n\t\t\t\t}\n\t\t\t\tunlock(this.lock);\n\t\t\t\treturn result;\n\t\t\t}\n\t\t\tprev = block;\n\t\t\tblock = this.blockNext(block);\n\t\t}\n\t\tblock = top;\n\t\ttop = block + paddedSize;\n\t\tif(top <= end) {\n\t\t\tthis.setBlockSize(block, paddedSize);\n\t\t\tthis.pushUsed(block);\n\t\t\tthis.top = top;\n\t\t\t// Bump allocation only ever advances into untouched memory, so it is fresh whenever it is at/above the mark.\n\t\t\tthis.lastMallocFresh = !doCompact || block >= highWater;\n\t\t\tif(doCompact && top > highWater) {\n\t\t\t\tthis.highWater = top;\n\t\t\t}\n\t\t\tlet result = blockDataAddress(block);\n\t\t\tunlock(this.lock);\n\n\t\t\treturn result;\n\t\t}\n\t\tthis.lastMallocFresh = false;\n\t\tunlock(this.lock);\n\t\treturn 0;\n\t}\n\n\tprivate mallocTop(\n\t\tblock: number,\n\t\tprev: number,\n\t\tblockSize: number,\n\t\tpaddedSize: number,\n\t\tisTop: boolean,\n\t) {\n\t\tif(isTop && block + paddedSize > this.end) return 0;\n\t\tif(prev) {\n\t\t\tthis.unlinkBlock(prev, block);\n\t\t} else {\n\t\t\tthis._free = this.blockNext(block);\n\t\t}\n\t\tthis.pushUsed(block);\n\t\tif(isTop) {\n\t\t\t// Without splitting, shrinking would expose a new block header inside stale views of the old allocation.\n\t\t\tif(this.doSplit || paddedSize >= blockSize) {\n\t\t\t\tthis.top = block + this.setBlockSize(block, paddedSize);\n\t\t\t}\n\t\t} else if(this.doSplit) {\n\t\t\tconst excess = blockSize - paddedSize;\n\t\t\texcess >= this.minSplit &&\n\t\t\tthis.splitBlock(block, paddedSize, excess);\n\t\t}\n\t\treturn blockDataAddress(block);\n\t}\n\n\tbytesFor(ptrOrArray: number | TypedArray): number | undefined {\n\t\tlet addr: number;\n\t\tif(typeof ptrOrArray !== 'number') {\n\t\t\tif(ptrOrArray.buffer !== this.buf) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\taddr = ptrOrArray.byteOffset;\n\t\t} else {\n\t\t\taddr = ptrOrArray;\n\t\t}\n\n\t\taddr = blockSelfAddress(addr);\n\t\tlet block = this._used;\n\t\twhile(block) {\n\t\t\tif(block === addr) {\n\t\t\t\treturn this.blockSize(addr) - SIZEOF_MEM_BLOCK;\n\t\t\t}\n\t\t\tblock = this.blockNext(block);\n\t\t}\n\n\t\treturn undefined;\n\t}\n\tlengthOf(ptrOrArray: number | TypedArray): number | undefined {\n\t\tlet bytes = this.bytesFor(ptrOrArray);\n\t\tif(bytes) {\n\t\t\treturn bytes / this.u32.BYTES_PER_ELEMENT;\n\t\t} else {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\tallocationLengthAt(dataAddress: number): number {\n\t\treturn (this.blockSize(blockSelfAddress(dataAddress)) - SIZEOF_MEM_BLOCK) / this.u32.BYTES_PER_ELEMENT;\n\t}\n\n\tfree(ptrOrArray: number | TypedArray) {\n\t\tlet addr: number;\n\t\tif(typeof ptrOrArray !== 'number') {\n\t\t\tif(ptrOrArray.buffer !== this.buf) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\taddr = ptrOrArray.byteOffset;\n\t\t} else {\n\t\t\taddr = ptrOrArray;\n\t\t}\n\t\tlock(this.lock);\n\t\taddr = blockSelfAddress(addr);\n\t\t// Reject anything not currently live (double-free or a stray pointer) without walking the used list.\n\t\tif(this.blockState(addr) !== BLOCK_USED) {\n\t\t\tunlock(this.lock);\n\t\t\treturn false;\n\t\t}\n\t\tconst next = this.blockNext(addr);\n\t\tconst prev = this.blockPrev(addr);\n\t\tif(prev) {\n\t\t\tthis.setBlockNext(prev, next);\n\t\t} else {\n\t\t\tthis._used = next;\n\t\t}\n\t\tif(next) {\n\t\t\tthis.setBlockPrev(next, prev);\n\t\t}\n\t\tthis.setBlockState(addr, BLOCK_FREE);\n\t\tthis.insert(addr);\n\t\tthis.doCompact && this.compact();\n\n\t\tunlock(this.lock);\n\t\treturn true;\n\t}\n\n\trelease() {\n\t\tdelete (<any> this).u8;\n\t\tdelete (<any> this).u32;\n\t\tdelete (<any> this).state;\n\t\tdelete (<any> this).buf;\n\t\treturn true;\n\t}\n\n\tprotected get align() {\n\t\treturn <Pow2> this.state[STATE_ALIGN];\n\t}\n\n\tprotected set align(x: Pow2) {\n\t\tthis.state[STATE_ALIGN] = x;\n\t}\n\n\tget end() {\n\t\treturn this.state[STATE_END];\n\t}\n\n\tprotected set end(x: number) {\n\t\tthis.state[STATE_END] = x;\n\t}\n\n\tget top() {\n\t\treturn Atomics.load(this.state, STATE_TOP);\n\t}\n\n\tprotected set top(x: number) {\n\t\tAtomics.store(this.state, STATE_TOP, x);\n\t}\n\n\tprotected get highWater() {\n\t\treturn Atomics.load(this.state, STATE_HIGH_WATER);\n\t}\n\n\tprotected set highWater(x: number) {\n\t\tAtomics.store(this.state, STATE_HIGH_WATER, x);\n\t}\n\n\t// No live allocations. O(1) (the head of the used list, not a walk like stats()), so it is cheap to poll each\n\t// frame when checking whether a spare buffer is still available.\n\tget isEmpty(): boolean {\n\t\treturn this._used === 0;\n\t}\n\n\tprotected get _free() {\n\t\treturn Atomics.load(this.state, STATE_FREE);\n\t}\n\n\tprotected set _free(block: number) {\n\t\tAtomics.store(this.state, STATE_FREE, block);\n\t}\n\n\tprotected get _used() {\n\t\treturn Atomics.load(this.state, STATE_USED);\n\t}\n\n\tprotected set _used(block: number) {\n\t\tAtomics.store(this.state, STATE_USED, block);\n\t}\n\n\tprotected get doCompact() {\n\t\treturn !!(this.state[STATE_FLAGS] & MASK_COMPACT);\n\t}\n\n\tprotected set doCompact(flag: boolean) {\n\t\tflag\n\t\t\t? (this.state[STATE_FLAGS] |= 1 << (MASK_COMPACT - 1))\n\t\t\t: (this.state[STATE_FLAGS] &= ~MASK_COMPACT);\n\t}\n\n\tprotected get doSplit() {\n\t\treturn !!(this.state[STATE_FLAGS] & MASK_SPLIT);\n\t}\n\n\tprotected set doSplit(flag: boolean) {\n\t\tflag\n\t\t\t? (this.state[STATE_FLAGS] |= 1 << (MASK_SPLIT - 1))\n\t\t\t: (this.state[STATE_FLAGS] &= ~MASK_SPLIT);\n\t}\n\n\tprotected get minSplit() {\n\t\treturn this.state[STATE_MIN_SPLIT];\n\t}\n\n\tprotected set minSplit(x: number) {\n\t\tif(x <= SIZEOF_MEM_BLOCK) {\n\t\t\tthrow new Error(`illegal min split threshold: ${x}, require at least ${\n\t\t\t\tSIZEOF_MEM_BLOCK + 1\n\t\t\t}`);\n\t\t}\n\t\tthis.state[STATE_MIN_SPLIT] = x;\n\t}\n\n\tprotected blockSize(block: number) {\n\t\treturn Atomics.load(this.u32, (block >> 2) + MEM_BLOCK_SIZE);\n\t}\n\n\t/**\n\t * Sets & returns given block size.\n\t *\n\t * @param block -\n\t * @param size -\n\t */\n\tprotected setBlockSize(block: number, size: number) {\n\t\tAtomics.store(this.u32, (block >> 2) + MEM_BLOCK_SIZE, size);\n\t\treturn size;\n\t}\n\n\tprotected blockNext(block: number) {\n\t\treturn Atomics.load(this.u32, (block >> 2) + MEM_BLOCK_NEXT);\n\t}\n\n\t/**\n\t * Sets block next pointer to `next`. Use zero to indicate list end.\n\t *\n\t * @param block -\n\t */\n\tprotected setBlockNext(block: number, next: number) {\n\t\tAtomics.store(this.u32, (block >> 2) + MEM_BLOCK_NEXT, next);\n\t}\n\n\tprotected blockPrev(block: number) {\n\t\treturn Atomics.load(this.u32, (block >> 2) + MEM_BLOCK_PREV);\n\t}\n\n\tprotected setBlockPrev(block: number, prev: number) {\n\t\tAtomics.store(this.u32, (block >> 2) + MEM_BLOCK_PREV, prev);\n\t}\n\n\tprotected blockState(block: number) {\n\t\treturn Atomics.load(this.u32, (block >> 2) + MEM_BLOCK_STATE);\n\t}\n\n\tprotected setBlockState(block: number, state: number) {\n\t\tAtomics.store(this.u32, (block >> 2) + MEM_BLOCK_STATE, state);\n\t}\n\n\t// Prepend a block onto the doubly-linked used list, keeping the back-links free() relies on for O(1) removal.\n\tprotected pushUsed(block: number) {\n\t\tconst head = this._used;\n\t\tthis.setBlockNext(block, head);\n\t\tthis.setBlockPrev(block, 0);\n\t\tthis.setBlockState(block, BLOCK_USED);\n\t\tif(head) {\n\t\t\tthis.setBlockPrev(head, block);\n\t\t}\n\t\tthis._used = block;\n\t}\n\n\t/**\n\t * Initializes block header with given `size` and `next` pointer. Returns `block`.\n\t *\n\t * @param block -\n\t * @param size -\n\t * @param next -\n\t */\n\t// Only used for free-list blocks (split excess); the used list is built via pushUsed which also sets prev/state.\n\tprotected initBlock(block: number, size: number, next: number) {\n\t\tconst idx = block >>> 2;\n\t\tAtomics.store(this.u32, idx + MEM_BLOCK_SIZE, size);\n\t\tAtomics.store(this.u32, idx + MEM_BLOCK_NEXT, next);\n\t\tAtomics.store(this.u32, idx + MEM_BLOCK_STATE, BLOCK_FREE);\n\t\treturn block;\n\t}\n\n\tprotected unlinkBlock(prev: number, block: number) {\n\t\tthis.setBlockNext(prev, this.blockNext(block));\n\t}\n\n\tprotected splitBlock(block: number, blockSize: number, excess: number) {\n\t\tthis.insert(\n\t\t\tthis.initBlock(\n\t\t\t\tblock + this.setBlockSize(block, blockSize),\n\t\t\t\texcess,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.doCompact && this.compact();\n\t}\n\n\tprotected initialTop(_align = this.align) {\n\t\treturn (\n\t\t\talign(this.start + SIZEOF_STATE + SIZEOF_MEM_BLOCK, _align) -\n\t\t\tSIZEOF_MEM_BLOCK\n\t\t);\n\t}\n\n\t/**\n\t * Traverses free list and attempts to recursively merge blocks\n\t * occupying consecutive memory regions. Returns true if any blocks\n\t * have been merged. Only called if `compact` option is enabled.\n\t */\n\tprotected compact() {\n\t\tlet block = this._free;\n\t\tlet prev = 0;\n\t\tlet scan = 0;\n\t\tlet scanPrev: number;\n\t\tlet res = false;\n\t\twhile(block) {\n\t\t\tscanPrev = block;\n\t\t\tscan = this.blockNext(block);\n\t\t\twhile(scan && scanPrev + this.blockSize(scanPrev) === scan) {\n\t\t\t\t// console.log(\"merge:\", scan.addr, scan.size);\n\t\t\t\tscanPrev = scan;\n\t\t\t\tscan = this.blockNext(scan);\n\t\t\t}\n\t\t\tif(scanPrev !== block) {\n\t\t\t\tconst newSize = scanPrev - block + this.blockSize(scanPrev);\n\t\t\t\t// console.log(\"merged size:\", newSize);\n\t\t\t\tthis.setBlockSize(block, newSize);\n\t\t\t\tconst next = this.blockNext(scanPrev);\n\t\t\t\tlet tmp = this.blockNext(block);\n\t\t\t\twhile(tmp && tmp !== next) {\n\t\t\t\t\t// console.log(\"release:\", tmp.addr);\n\t\t\t\t\tconst tn = this.blockNext(tmp);\n\t\t\t\t\tthis.setBlockNext(tmp, 0);\n\t\t\t\t\ttmp = tn;\n\t\t\t\t}\n\t\t\t\tthis.setBlockNext(block, next);\n\t\t\t\tres = true;\n\t\t\t}\n\t\t\t// re-adjust top if poss\n\t\t\tif(block + this.blockSize(block) >= this.top) {\n\t\t\t\tthis.top = block;\n\t\t\t\tprev\n\t\t\t\t\t? this.unlinkBlock(prev, block)\n\t\t\t\t\t: (this._free = this.blockNext(block));\n\t\t\t}\n\t\t\tprev = block;\n\t\t\tblock = this.blockNext(block);\n\t\t}\n\t\treturn res;\n\t}\n\n\t/**\n\t * Inserts given block into list of free blocks, sorted by address.\n\t *\n\t * @param block -\n\t */\n\tprotected insert(block: number) {\n\t\t// Address ordering only exists so compact() can merge neighbours; without compaction a prepend keeps free O(1).\n\t\tif(!this.doCompact) {\n\t\t\tthis.setBlockNext(block, this._free);\n\t\t\tthis._free = block;\n\t\t\treturn;\n\t\t}\n\n\t\tlet ptr = this._free;\n\t\tlet prev = 0;\n\t\twhile(ptr) {\n\t\t\tif(block <= ptr) break;\n\t\t\tprev = ptr;\n\t\t\tptr = this.blockNext(ptr);\n\t\t}\n\t\tif(prev) {\n\t\t\tthis.setBlockNext(prev, block);\n\t\t} else {\n\t\t\tthis._free = block;\n\t\t}\n\t\tthis.setBlockNext(block, ptr);\n\t}\n}\n\n/**\n * Returns a block's data address, based on given alignment.\n *\n * @param blockAddress -\n */\nfunction blockDataAddress(blockAddress: number) {\n\treturn blockAddress > 0 ? blockAddress + SIZEOF_MEM_BLOCK : 0;\n}\n\n/**\n * Returns block start address for given data address and alignment.\n *\n * @param dataAddress -\n */\nfunction blockSelfAddress(dataAddress: number) {\n\treturn dataAddress > 0 ? dataAddress - SIZEOF_MEM_BLOCK : 0;\n}\n\nfunction align(addr: number, size: number) {\n\tsize--;\n\treturn addr + size & ~size;\n}\n\n// Bytes reserved before usable heap space (allocator state) and the per-allocation block header. Exported so callers can\n// size an allocation against what a single buffer can actually hold.\n// Byte offset of the first allocation's data in a freshly initialized buffer (default align). The heap's state block is\n// always the first allocation, so a clone can resolve it without guessing.\nconst FIRST_BLOCK_DATA_BYTE_OFFSET = align(SIZEOF_STATE + SIZEOF_MEM_BLOCK, 8);\n\nexport { SIZEOF_STATE, SIZEOF_MEM_BLOCK, FIRST_BLOCK_DATA_BYTE_OFFSET };\n\ntype Type = 'u8' | 'u8c' | 'i8' | 'u16' | 'i16' | 'u32' | 'i32' | 'f32' | 'f64';\n\ninterface MemoryBufferConfig {\n\t/**\n\t * Backing ArrayBuffer (or SharedArrayBuffer). If not given, a new\n\t * one will be created with given `size`.\n\t */\n\tbuf: ArrayBufferLike\n\t/**\n\t * Byte size for newly created ArrayBuffers (if `buf` is not given).\n\t *\n\t * @defaultValue 0x1000 (4KB)\n\t */\n\tsize: number\n\t/**\n\t * Anchor index (byte address) inside the array buffer. The MemPool\n\t * stores its internal state from the given address and heap space\n\t * starts at least 32 bytes later (depending on chosen `align`\n\t * value). Unlike allocator state variables, `start`` cannot be\n\t * saved inside the array buffer itself. If the ArrayBuffer is\n\t * passed to other consumers they must use the same start value.\n\t * MUST be multiple of 4.\n\t *\n\t * @defaultValue 0\n\t */\n\tstart: number\n\t/**\n\t * Byte address (+1) of the end of the memory region managed by the\n\t * {@link MemPool}.\n\t *\n\t * @defaultValue end of the backing ArrayBuffer\n\t */\n\tend: number\n\t/**\n\t * Number of bytes to align memory blocks to. MUST be a power of 2\n\t * and >= 8. Use 16 if the pool is being used for allocating memory\n\t * used in SIMD operations.\n\t *\n\t * @defaultValue 8\n\t */\n\talign: Pow2\n\t/**\n\t * Flag to configure memory block compaction. If true,\n\t * adjoining free blocks (in terms of address space) will be merged\n\t * to minimize fragementation.\n\t *\n\t * @defaultValue true\n\t */\n\tcompact: boolean\n\t/**\n\t * Flag to configure memory block splitting. If true, and when the\n\t * allocator is re-using a previously freed block larger than the\n\t * requested size, the block will be split to minimize wasted/unused\n\t * memory. The splitting behavior can further customized via the\n\t * `minSplit` option.\n\t *\n\t * @defaultValue true\n\t */\n\tsplit: boolean\n\t/**\n\t * Only used if `split` behavior is enabled. Defines min number of\n\t * excess bytes available in a block for memory block splitting to\n\t * occur.\n\t *\n\t * @defaultValue 16, MUST be > 8\n\t */\n\tminSplit: number\n\t/**\n\t * Only needed when sharing the underlying ArrayBuffer. If true, the\n\t * {@link MemPool} constructor will NOT initialize its internal state and\n\t * assume the underlying ArrayBuffer has already been initialized by\n\t * another {@link MemPool} instance. If this option is used, `buf` MUST be\n\t * given.\n\t *\n\t * @defaultValue false\n\t */\n\tskipInitialization: boolean\n}\ninterface MemoryBufferStats {\n\t/**\n\t * Free block stats.\n\t */\n\tfree: { count: number, size: number }\n\t/**\n\t * Used block stats.\n\t */\n\tused: { count: number, size: number }\n\t/**\n\t * Current top address.\n\t */\n\ttop: number\n\t/**\n\t * Bytes available\n\t */\n\tavailable: number\n\t/**\n\t * Total pool size.\n\t */\n\ttotal: number\n}"],"mappings":"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"}
|
package/dist/memory-heap.d.ts
CHANGED
|
@@ -5,6 +5,9 @@ export default class MemoryHeap {
|
|
|
5
5
|
private onGrowBufferHandlers;
|
|
6
6
|
isClone: boolean;
|
|
7
7
|
private memory;
|
|
8
|
+
private allocCursor;
|
|
9
|
+
positionBits: number;
|
|
10
|
+
get maxBuffers(): number;
|
|
8
11
|
get bufferSize(): number;
|
|
9
12
|
get maxAllocationLength(): number;
|
|
10
13
|
constructor(config?: MemoryHeapConfig | MemoryHeapMemory);
|
package/dist/memory-heap.js
CHANGED
|
@@ -1,40 +1,47 @@
|
|
|
1
|
-
import { MAX_BYTE_OFFSET_LENGTH as e,
|
|
1
|
+
import { MAX_BYTE_OFFSET_LENGTH as e, positionBitsForBufferSize as t } from "./utils/pointer.js";
|
|
2
2
|
import n from "./allocated-memory.js";
|
|
3
|
-
import r from "./memory-buffer.js";
|
|
3
|
+
import r, { FIRST_BLOCK_DATA_BYTE_OFFSET as i } from "./memory-buffer.js";
|
|
4
4
|
//#region src/memory-heap.ts
|
|
5
|
-
var
|
|
5
|
+
var a = e, o = 2 ** 31, s = 0, c = 1, l = 2, u = 3, d = 4, f = class {
|
|
6
6
|
buffers;
|
|
7
7
|
onGrowBufferHandlers = [];
|
|
8
8
|
isClone;
|
|
9
9
|
memory;
|
|
10
|
+
allocCursor = 0;
|
|
11
|
+
positionBits;
|
|
12
|
+
get maxBuffers() {
|
|
13
|
+
return 2 ** this.positionBits;
|
|
14
|
+
}
|
|
10
15
|
get bufferSize() {
|
|
11
|
-
return this.memory.data[
|
|
16
|
+
return this.memory.data[s];
|
|
12
17
|
}
|
|
13
18
|
get maxAllocationLength() {
|
|
14
|
-
return Math.floor((this.bufferSize -
|
|
19
|
+
return Math.floor((this.bufferSize - 36 - 16) / Uint32Array.BYTES_PER_ELEMENT);
|
|
15
20
|
}
|
|
16
|
-
constructor(
|
|
17
|
-
if (
|
|
21
|
+
constructor(e) {
|
|
22
|
+
if (e && "buffers" in e) this.buffers = e.buffers.map((e) => new r({
|
|
18
23
|
buf: e,
|
|
19
24
|
skipInitialization: !0
|
|
20
25
|
})), this.memory = new n(this, {
|
|
21
26
|
bufferPosition: 0,
|
|
22
|
-
bufferByteOffset:
|
|
23
|
-
}), this.isClone = !0;
|
|
27
|
+
bufferByteOffset: i
|
|
28
|
+
}), this.positionBits = this.memory.data[u], this.isClone = !0;
|
|
24
29
|
else {
|
|
25
30
|
"SharedArrayBuffer" in globalThis || console.warn("SharedArrayBuffer is not working: falling back to ArrayBuffer");
|
|
26
|
-
let r =
|
|
27
|
-
if (r >
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
let r = e?.bufferSize ?? a;
|
|
32
|
+
if (r > o) throw Error(`Buffer size ${r} is greater than max ${o} that we can reference with pointers`);
|
|
33
|
+
this.positionBits = t(r);
|
|
34
|
+
let i = this.createBuffer(r);
|
|
35
|
+
this.buffers = [i];
|
|
36
|
+
let f = i.callocAs("u32", d);
|
|
37
|
+
if (f) this.memory = new n(this, {
|
|
32
38
|
bufferPosition: 0,
|
|
33
|
-
bufferByteOffset:
|
|
39
|
+
bufferByteOffset: f.byteOffset
|
|
34
40
|
});
|
|
35
41
|
else throw Error("Failed to initialize first byte from buffer");
|
|
36
|
-
this.memory.data[
|
|
37
|
-
for (let
|
|
42
|
+
this.memory.data[s] = r, this.memory.data[l] = e?.autoGrowSize ?? 100, this.memory.data[u] = this.positionBits, this.isClone = !1;
|
|
43
|
+
for (let t = 1; t < (e?.initialBuffers ?? 1); t++) this.buffers.push(this.createBuffer(r));
|
|
44
|
+
this.memory.data[c] = this.buffers.length;
|
|
38
45
|
}
|
|
39
46
|
}
|
|
40
47
|
addSharedBuffer(e) {
|
|
@@ -44,7 +51,7 @@ var i = e, a = 0, o = 1, s = 2, c = class {
|
|
|
44
51
|
});
|
|
45
52
|
}
|
|
46
53
|
growBuffer() {
|
|
47
|
-
let e = this.createBuffer(), t = Atomics.add(this.memory.data,
|
|
54
|
+
let e = this.createBuffer(), t = Atomics.add(this.memory.data, c, 1);
|
|
48
55
|
return this.buffers[t] = e, this.onGrowBufferHandlers.forEach((n) => n({
|
|
49
56
|
bufferPosition: t,
|
|
50
57
|
buffer: e.buf
|
|
@@ -70,18 +77,19 @@ var i = e, a = 0, o = 1, s = 2, c = class {
|
|
|
70
77
|
}
|
|
71
78
|
allocUI32(e) {
|
|
72
79
|
e = Math.ceil(e);
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
+
let t = this.buffers.length;
|
|
81
|
+
for (let r = 0; r < t; r++) {
|
|
82
|
+
let i = (this.allocCursor + r) % t, a = this.buffers[i];
|
|
83
|
+
if (!a) continue;
|
|
84
|
+
let o = a.callocAs("u32", e);
|
|
85
|
+
if (o) return this.allocCursor = i, i === this.buffers.length - 1 && Atomics.load(this.memory.data, c) === this.buffers.length && this.memory.data[l] < 100 && this.memory.data[l] > 0 && a.top / a.end > this.memory.data[l] / 100 && this.growBuffer(), new n(this, {
|
|
86
|
+
data: o,
|
|
87
|
+
buffer: a
|
|
80
88
|
});
|
|
81
89
|
}
|
|
82
|
-
if (this.buffers.length >=
|
|
90
|
+
if (this.buffers.length >= this.maxBuffers) throw Error(`Can't initialize a new buffer since it would have a position greater than the max of ${this.maxBuffers}`);
|
|
83
91
|
let r = this.growBuffer(), i = r.callocAs("u32", e);
|
|
84
|
-
if (i) return new n(this, {
|
|
92
|
+
if (i) return this.allocCursor = this.buffers.indexOf(r), new n(this, {
|
|
85
93
|
data: i,
|
|
86
94
|
buffer: r
|
|
87
95
|
});
|
|
@@ -101,6 +109,6 @@ var i = e, a = 0, o = 1, s = 2, c = class {
|
|
|
101
109
|
}
|
|
102
110
|
};
|
|
103
111
|
//#endregion
|
|
104
|
-
export {
|
|
112
|
+
export { f as default };
|
|
105
113
|
|
|
106
114
|
//# sourceMappingURL=memory-heap.js.map
|
package/dist/memory-heap.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-heap.js","names":[],"sources":["../src/memory-heap.ts"],"sourcesContent":["import AllocatedMemory, { type SharedAllocatedMemory } from './allocated-memory';\nimport { MAX_BYTE_OFFSET_LENGTH, MAX_POSITION_LENGTH } from './utils/pointer';\nimport MemoryBuffer, { SIZEOF_MEM_BLOCK, SIZEOF_STATE } from './memory-buffer';\n\nconst DEFAULT_BUFFER_SIZE = MAX_BYTE_OFFSET_LENGTH;\nconst BUFFER_SIZE_INDEX = 0;\nconst BUFFER_COUNT_INDEX = 1;\nconst BUFFER_AUTO_GROW_INDEX = 2;\nexport default class MemoryHeap {\n\tbuffers: Array<MemoryBuffer>;\n\tprivate onGrowBufferHandlers: Array<OnGrowBuffer> = [];\n\tisClone: boolean;\n\tprivate memory: AllocatedMemory;\n\n\tget bufferSize() {\n\t\treturn this.memory.data[BUFFER_SIZE_INDEX];\n\t}\n\n\t// Largest u32 count a single fresh buffer can hand out in one allocation: the buffer minus the allocator state and the\n\t// block header. A structure that grows in segments must keep each segment within this or the allocation can never fit.\n\tget maxAllocationLength() {\n\t\treturn Math.floor((this.bufferSize - SIZEOF_STATE - SIZEOF_MEM_BLOCK) / Uint32Array.BYTES_PER_ELEMENT);\n\t}\n\n\tconstructor(config?: MemoryHeapConfig | MemoryHeapMemory) {\n\t\tif(config && 'buffers' in config) {\n\t\t\tthis.buffers = config.buffers.map(buffer => {\n\t\t\t\treturn new MemoryBuffer({\n\t\t\t\t\tbuf: buffer,\n\t\t\t\t\tskipInitialization: true,\n\t\t\t\t});\n\t\t\t});\n\n\t\t\t// TODO: This should be programic instead of hoping the first allocation is always byte 40\n\t\t\tthis.memory = new AllocatedMemory(this, {\n\t\t\t\tbufferPosition: 0,\n\t\t\t\tbufferByteOffset: 40,\n\t\t\t});\n\t\t\tthis.isClone = true;\n\t\t} else {\n\t\t\tif(!('SharedArrayBuffer' in globalThis)) {\n\t\t\t\tconsole.warn('SharedArrayBuffer is not working: falling back to ArrayBuffer');\n\t\t\t}\n\n\t\t\tconst bufferSize = config?.bufferSize ?? DEFAULT_BUFFER_SIZE;\n\t\t\tif(bufferSize > MAX_BYTE_OFFSET_LENGTH) {\n\t\t\t\tthrow new Error(`Buffer size ${bufferSize} is greater than max ${MAX_BYTE_OFFSET_LENGTH} that we can reference with pointers`);\n\t\t\t}\n\t\t\t\n\t\t\tlet startBuffer = this.createBuffer(bufferSize);\n\t\t\tthis.buffers = [\n\t\t\t\tstartBuffer,\n\t\t\t];\n\t\t\tconst data = startBuffer.callocAs('u32', 3);\n\t\t\tif(data) {\n\t\t\t\tthis.memory = new AllocatedMemory(this, {\n\t\t\t\t\tbufferPosition: 0,\n\t\t\t\t\tbufferByteOffset: data.byteOffset,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tthrow new Error('Failed to initialize first byte from buffer');\n\t\t\t}\n\t\t\tthis.memory.data[BUFFER_SIZE_INDEX] = bufferSize;\n\t\t\tthis.memory.data[BUFFER_COUNT_INDEX] = 1;\n\t\t\tthis.memory.data[BUFFER_AUTO_GROW_INDEX] = config?.autoGrowSize ?? 100;\n\t\t\tthis.isClone = false;\n\n\t\t\tfor(let i = 1; i < (config?.initialBuffers ?? 1); i++) {\n\t\t\t\tthis.buffers.push(this.createBuffer(bufferSize));\n\t\t\t}\n\t\t}\n\t}\n\n\taddSharedBuffer(data: GrowBufferData) {\n\t\tthis.buffers[data.bufferPosition] = new MemoryBuffer({\n\t\t\tbuf: data.buffer,\n\t\t\tskipInitialization: true,\n\t\t});\n\t}\n\n\tprivate growBuffer() {\n\t\tconst buffer = this.createBuffer();\n\t\tlet nextBufferPosition = Atomics.add(this.memory.data, BUFFER_COUNT_INDEX, 1);\n\t\t// Setting index set by internal Atomic count so we can create new buffers from multiple threads and keep position consistent\n\t\tthis.buffers[nextBufferPosition] = buffer;\n\t\tthis.onGrowBufferHandlers.forEach(handler => handler({\n\t\t\tbufferPosition: nextBufferPosition,\n\t\t\tbuffer: buffer.buf as SharedArrayBuffer,\n\t\t}));\n\n\t\treturn buffer;\n\t}\n\tprivate createBuffer(bufferSize?: number): MemoryBuffer {\n\t\tconst usedBufferSize = bufferSize ?? this.bufferSize;\n\t\tlet buf: ArrayBuffer | SharedArrayBuffer;\n\t\tif('SharedArrayBuffer' in globalThis) {\n\t\t\tbuf = new SharedArrayBuffer(usedBufferSize);\n\t\t} else {\n\t\t\tbuf = new ArrayBuffer(usedBufferSize);\n\t\t}\n\n\t\treturn new MemoryBuffer({\n\t\t\tbuf,\n\n\t\t\t// We can't use this unless we can 100% guarantee that every thread will stop using memory the instant it is freed\n\t\t\t// ex: Allocate 16 bytes. Thread A frees that allocation and then allocates 12 bytes and 4 bytes, but Thread B is mid-execution\n\t\t\t// on the old allocation can changes the internal state of the 4-byte allocation breaking everything\n\t\t\t// After the internal state is wrong MemoryBuffer will loose track of which blocks are where and how big they are\n\t\t\tcompact: false,\n\t\t\tsplit: false,\n\t\t});\n\t}\n\n\taddOnGrowBufferHandlers(handler: OnGrowBuffer) {\n\t\tthis.onGrowBufferHandlers.push(handler);\n\t}\n\n\t// Guarantees at least one completely-empty buffer exists and (via the grow handlers) has been fanned out to every\n\t// thread. Call this on the owning thread before dispatching work that allocates on another thread (a worker): that\n\t// allocation then lands in a buffer everyone already holds instead of forcing growBuffer on the worker, which would\n\t// create a buffer only the worker can see and leave every other thread unable to resolve pointers into it. Returns\n\t// true if it had to grow one.\n\tensureSpareBuffer(): boolean {\n\t\tfor(let i = 0; i < this.buffers.length; i++) {\n\t\t\tconst buffer = this.buffers[i];\n\t\t\tif(buffer && buffer.isEmpty) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tthis.growBuffer();\n\t\treturn true;\n\t}\n\n\tallocUI32(count: number): AllocatedMemory {\n\t\tcount = Math.ceil(count);\n\t\tfor(let i = 0; i < this.buffers.length; i++) {\n\t\t\tconst buffer = this.buffers[i];\n\t\t\t// Should just mean we haven't synced this buffer from another thread yet\n\t\t\tif(!buffer) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Should be fine to initialize all values as 0s since unsigned/signed ints and floats all store 0 as all 0s\n\t\t\tconst data = buffer.callocAs('u32', count);\n\t\t\tif(data) {\n\t\t\t\t// Auto grow when nearly full when we need buffer to already be sync'd between threads BEFORE we try to use it\n\t\t\t\tif(\n\t\t\t\t\ti === (this.buffers.length - 1)\n\t\t\t\t\t&& Atomics.load(this.memory.data, BUFFER_COUNT_INDEX) === this.buffers.length\n\t\t\t\t\t&& this.memory.data[BUFFER_AUTO_GROW_INDEX] < 100\n\t\t\t\t\t&& this.memory.data[BUFFER_AUTO_GROW_INDEX] > 0\n\t\t\t\t) {\n\t\t\t\t\tconst percentFull = buffer.top / buffer.end;\n\t\t\t\t\tif(percentFull > (this.memory.data[BUFFER_AUTO_GROW_INDEX] / 100)) {\n\t\t\t\t\t\tthis.growBuffer();\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn new AllocatedMemory(this, {\n\t\t\t\t\tdata,\n\t\t\t\t\tbuffer,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tif(this.buffers.length >= MAX_POSITION_LENGTH) {\n\t\t\tthrow new Error(`Can't initialize a new buffer since it would have a position greater than the max of ${MAX_POSITION_LENGTH}`);\n\t\t}\n\n\t\t// If we get here we need to grow another buffer to continue allocating new memory\n\t\tlet buffer = this.growBuffer();\n\t\tconst data = buffer.callocAs('u32', count);\n\t\tif(data) {\n\t\t\treturn new AllocatedMemory(this, {\n\t\t\t\tdata,\n\t\t\t\tbuffer,\n\t\t\t});\n\t\t} else {\n\t\t\tthrow new Error(`Unable to allocate ${count} numbers even after adding a new buffer`);\n\t\t}\n\t}\n\n\tgetSharedAlloc(shared: SharedAllocatedMemory): AllocatedMemory | undefined {\n\t\t// Should just mean it hasn't synced to this thread yet\n\t\tif(this.buffers[shared.bufferPosition] === undefined) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\treturn new AllocatedMemory(this, shared);\n\t}\n\n\tget currentUsed() {\n\t\treturn this.totalAllocated - this.buffers.reduce((total, memPool) => total + memPool.stats().available, 0);\n\t}\n\tget totalAllocated() {\n\t\treturn this.buffers[0].buf.byteLength * this.buffers.length;\n\t}\n\n\tgetSharedMemory(): MemoryHeapMemory {\n\t\treturn {\n\t\t\tbuffers: this.buffers.map(buffer => buffer.buf as SharedArrayBuffer),\n\t\t};\n\t}\n}\n\ntype OnGrowBuffer = (newBuffer: GrowBufferData) => void;\ninterface GrowBufferData {\n\tbufferPosition: number\n\tbuffer: SharedArrayBuffer\n}\n\ninterface MemoryHeapConfig {\n\tbufferSize?: number\n\tinitialBuffers?: number\n\tautoGrowSize?: number\n}\ninterface MemoryHeapMemory {\n\tbuffers: Array<SharedArrayBuffer>\n}\n\nexport type { MemoryHeapConfig, MemoryHeapMemory, GrowBufferData };"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"memory-heap.js","names":[],"sources":["../src/memory-heap.ts"],"sourcesContent":["import AllocatedMemory, { type AllocatedMemoryPointer, type SharedAllocatedMemory } from './allocated-memory';\nimport { MAX_BYTE_OFFSET_LENGTH, positionBitsForBufferSize } from './utils/pointer';\nimport MemoryBuffer, { FIRST_BLOCK_DATA_BYTE_OFFSET, SIZEOF_MEM_BLOCK, SIZEOF_STATE } from './memory-buffer';\n\nconst DEFAULT_BUFFER_SIZE = MAX_BYTE_OFFSET_LENGTH;\nconst MAX_BUFFER_SIZE = Math.pow(2, 31);\nconst BUFFER_SIZE_INDEX = 0;\nconst BUFFER_COUNT_INDEX = 1;\nconst BUFFER_AUTO_GROW_INDEX = 2;\nconst BUFFER_POSITION_BITS_INDEX = 3;\nconst HEAP_STATE_COUNT = 4;\nexport default class MemoryHeap {\n\tbuffers: Array<MemoryBuffer>;\n\tprivate onGrowBufferHandlers: Array<OnGrowBuffer> = [];\n\tisClone: boolean;\n\tprivate memory: AllocatedMemory;\n\n\t// Per-heap (per-thread) hint of the last buffer we allocated into\n\tprivate allocCursor = 0;\n\n\tpositionBits: number;\n\t// Max buffer position (count) and max byte offset the split can address\n\tget maxBuffers() {\n\t\treturn Math.pow(2, this.positionBits);\n\t}\n\n\tget bufferSize() {\n\t\treturn this.memory.data[BUFFER_SIZE_INDEX];\n\t}\n\n\t// Largest u32 count a single fresh buffer can hand out in one allocation: the buffer minus the allocator state and the\n\t// block header. A structure that grows in segments must keep each segment within this or the allocation can never fit.\n\tget maxAllocationLength() {\n\t\treturn Math.floor((this.bufferSize - SIZEOF_STATE - SIZEOF_MEM_BLOCK) / Uint32Array.BYTES_PER_ELEMENT);\n\t}\n\n\tconstructor(config?: MemoryHeapConfig | MemoryHeapMemory) {\n\t\tif(config && 'buffers' in config) {\n\t\t\tthis.buffers = config.buffers.map(buffer => {\n\t\t\t\treturn new MemoryBuffer({\n\t\t\t\t\tbuf: buffer,\n\t\t\t\t\tskipInitialization: true,\n\t\t\t\t});\n\t\t\t});\n\n\t\t\t// The heap state block is always the first allocation, so its data sits at this fixed, header-derived offset.\n\t\t\tthis.memory = new AllocatedMemory(this, {\n\t\t\t\tbufferPosition: 0,\n\t\t\t\tbufferByteOffset: FIRST_BLOCK_DATA_BYTE_OFFSET,\n\t\t\t} satisfies AllocatedMemoryPointer);\n\t\t\tthis.positionBits = this.memory.data[BUFFER_POSITION_BITS_INDEX];\n\t\t\tthis.isClone = true;\n\t\t} else {\n\t\t\tif(!('SharedArrayBuffer' in globalThis)) {\n\t\t\t\tconsole.warn('SharedArrayBuffer is not working: falling back to ArrayBuffer');\n\t\t\t}\n\n\t\t\tconst bufferSize = config?.bufferSize ?? DEFAULT_BUFFER_SIZE;\n\t\t\tif(bufferSize > MAX_BUFFER_SIZE) {\n\t\t\t\tthrow new Error(`Buffer size ${bufferSize} is greater than max ${MAX_BUFFER_SIZE} that we can reference with pointers`);\n\t\t\t}\n\t\t\tthis.positionBits = positionBitsForBufferSize(bufferSize);\n\n\t\t\tlet startBuffer = this.createBuffer(bufferSize);\n\t\t\tthis.buffers = [\n\t\t\t\tstartBuffer,\n\t\t\t];\n\t\t\tconst data = startBuffer.callocAs('u32', HEAP_STATE_COUNT);\n\t\t\tif(data) {\n\t\t\t\tthis.memory = new AllocatedMemory(this, {\n\t\t\t\t\tbufferPosition: 0,\n\t\t\t\t\tbufferByteOffset: data.byteOffset,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tthrow new Error('Failed to initialize first byte from buffer');\n\t\t\t}\n\t\t\tthis.memory.data[BUFFER_SIZE_INDEX] = bufferSize;\n\t\t\tthis.memory.data[BUFFER_AUTO_GROW_INDEX] = config?.autoGrowSize ?? 100;\n\t\t\tthis.memory.data[BUFFER_POSITION_BITS_INDEX] = this.positionBits;\n\t\t\tthis.isClone = false;\n\n\t\t\tfor(let i = 1; i < (config?.initialBuffers ?? 1); i++) {\n\t\t\t\tthis.buffers.push(this.createBuffer(bufferSize));\n\t\t\t}\n\t\t\tthis.memory.data[BUFFER_COUNT_INDEX] = this.buffers.length;\n\t\t}\n\t}\n\n\taddSharedBuffer(data: GrowBufferData) {\n\t\tthis.buffers[data.bufferPosition] = new MemoryBuffer({\n\t\t\tbuf: data.buffer,\n\t\t\tskipInitialization: true,\n\t\t});\n\t}\n\n\tprivate growBuffer() {\n\t\tconst buffer = this.createBuffer();\n\t\tlet nextBufferPosition = Atomics.add(this.memory.data, BUFFER_COUNT_INDEX, 1);\n\t\t// Setting index set by internal Atomic count so we can create new buffers from multiple threads and keep position consistent\n\t\tthis.buffers[nextBufferPosition] = buffer;\n\t\tthis.onGrowBufferHandlers.forEach(handler => handler({\n\t\t\tbufferPosition: nextBufferPosition,\n\t\t\tbuffer: buffer.buf as SharedArrayBuffer,\n\t\t}));\n\n\t\treturn buffer;\n\t}\n\tprivate createBuffer(bufferSize?: number): MemoryBuffer {\n\t\tconst usedBufferSize = bufferSize ?? this.bufferSize;\n\t\tlet buf: ArrayBuffer | SharedArrayBuffer;\n\t\tif('SharedArrayBuffer' in globalThis) {\n\t\t\tbuf = new SharedArrayBuffer(usedBufferSize);\n\t\t} else {\n\t\t\tbuf = new ArrayBuffer(usedBufferSize);\n\t\t}\n\n\t\treturn new MemoryBuffer({\n\t\t\tbuf,\n\n\t\t\t// We can't use this unless we can 100% guarantee that every thread will stop using memory the instant it is freed\n\t\t\t// ex: Allocate 16 bytes. Thread A frees that allocation and then allocates 12 bytes and 4 bytes, but Thread B is mid-execution\n\t\t\t// on the old allocation can changes the internal state of the 4-byte allocation breaking everything\n\t\t\t// After the internal state is wrong MemoryBuffer will loose track of which blocks are where and how big they are\n\t\t\tcompact: false,\n\t\t\tsplit: false,\n\t\t});\n\t}\n\n\taddOnGrowBufferHandlers(handler: OnGrowBuffer) {\n\t\tthis.onGrowBufferHandlers.push(handler);\n\t}\n\n\t// Guarantees at least one completely-empty buffer exists and (via the grow handlers) has been fanned out to every\n\t// thread. Call this on the owning thread before dispatching work that allocates on another thread (a worker): that\n\t// allocation then lands in a buffer everyone already holds instead of forcing growBuffer on the worker, which would\n\t// create a buffer only the worker can see and leave every other thread unable to resolve pointers into it. Returns\n\t// true if it had to grow one.\n\tensureSpareBuffer(): boolean {\n\t\tfor(let i = 0; i < this.buffers.length; i++) {\n\t\t\tconst buffer = this.buffers[i];\n\t\t\tif(buffer && buffer.isEmpty) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tthis.growBuffer();\n\t\treturn true;\n\t}\n\n\tallocUI32(count: number): AllocatedMemory {\n\t\tcount = Math.ceil(count);\n\t\tconst bufferCount = this.buffers.length;\n\t\tfor(let n = 0; n < bufferCount; n++) {\n\t\t\tconst i = (this.allocCursor + n) % bufferCount;\n\t\t\tconst buffer = this.buffers[i];\n\t\t\t// Should just mean we haven't synced this buffer from another thread yet\n\t\t\tif(!buffer) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Should be fine to initialize all values as 0s since unsigned/signed ints and floats all store 0 as all 0s\n\t\t\tconst data = buffer.callocAs('u32', count);\n\t\t\tif(data) {\n\t\t\t\tthis.allocCursor = i;\n\n\t\t\t\t// Auto grow when nearly full when we need buffer to already be sync'd between threads BEFORE we try to use it\n\t\t\t\tif(\n\t\t\t\t\ti === (this.buffers.length - 1)\n\t\t\t\t\t&& Atomics.load(this.memory.data, BUFFER_COUNT_INDEX) === this.buffers.length\n\t\t\t\t\t&& this.memory.data[BUFFER_AUTO_GROW_INDEX] < 100\n\t\t\t\t\t&& this.memory.data[BUFFER_AUTO_GROW_INDEX] > 0\n\t\t\t\t) {\n\t\t\t\t\tconst percentFull = buffer.top / buffer.end;\n\t\t\t\t\tif(percentFull > (this.memory.data[BUFFER_AUTO_GROW_INDEX] / 100)) {\n\t\t\t\t\t\tthis.growBuffer();\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn new AllocatedMemory(this, {\n\t\t\t\t\tdata,\n\t\t\t\t\tbuffer,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tif(this.buffers.length >= this.maxBuffers) {\n\t\t\tthrow new Error(`Can't initialize a new buffer since it would have a position greater than the max of ${this.maxBuffers}`);\n\t\t}\n\n\t\t// If we get here we need to grow another buffer to continue allocating new memory\n\t\tlet buffer = this.growBuffer();\n\t\tconst data = buffer.callocAs('u32', count);\n\t\tif(data) {\n\t\t\tthis.allocCursor = this.buffers.indexOf(buffer);\n\t\t\treturn new AllocatedMemory(this, {\n\t\t\t\tdata,\n\t\t\t\tbuffer,\n\t\t\t});\n\t\t} else {\n\t\t\tthrow new Error(`Unable to allocate ${count} numbers even after adding a new buffer`);\n\t\t}\n\t}\n\n\tgetSharedAlloc(shared: SharedAllocatedMemory): AllocatedMemory | undefined {\n\t\t// Should just mean it hasn't synced to this thread yet\n\t\tif(this.buffers[shared.bufferPosition] === undefined) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\treturn new AllocatedMemory(this, shared);\n\t}\n\n\tget currentUsed() {\n\t\treturn this.totalAllocated - this.buffers.reduce((total, memPool) => total + memPool.stats().available, 0);\n\t}\n\tget totalAllocated() {\n\t\treturn this.buffers[0].buf.byteLength * this.buffers.length;\n\t}\n\n\tgetSharedMemory(): MemoryHeapMemory {\n\t\treturn {\n\t\t\tbuffers: this.buffers.map(buffer => buffer.buf as SharedArrayBuffer),\n\t\t};\n\t}\n}\n\ntype OnGrowBuffer = (newBuffer: GrowBufferData) => void;\ninterface GrowBufferData {\n\tbufferPosition: number\n\tbuffer: SharedArrayBuffer\n}\n\ninterface MemoryHeapConfig {\n\tbufferSize?: number\n\tinitialBuffers?: number\n\tautoGrowSize?: number\n}\ninterface MemoryHeapMemory {\n\tbuffers: Array<SharedArrayBuffer>\n}\n\nexport type { MemoryHeapConfig, MemoryHeapMemory, GrowBufferData };\n"],"mappings":"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"}
|
package/dist/serialize-object.js
CHANGED
|
@@ -16,7 +16,7 @@ function r(e, t) {
|
|
|
16
16
|
return s.getArray(Float32Array, 0, r.length).set(r), s;
|
|
17
17
|
}
|
|
18
18
|
function i(n, r) {
|
|
19
|
-
return a(new t(n, e(r)));
|
|
19
|
+
return a(new t(n, e(r, n.positionBits)));
|
|
20
20
|
}
|
|
21
21
|
function a(e) {
|
|
22
22
|
let t = new Float32Array(e.data.buffer, e.bufferByteOffset);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serialize-object.js","names":[],"sources":["../src/serialize-object.ts"],"sourcesContent":["import AllocatedMemory from './allocated-memory';\nimport type MemoryHeap from './memory-heap';\nimport { getPointer } from './utils/pointer';\n\nenum VALUE_TYPE {\n\tUNDEFINED,\n\tNUMBER,\n\tSTRING,\n}\n\n// Be able to serialize a simple object into memory and re-create the object in another thread\n// This does NOT support updating objects across threads\nconst MAGIC_NUMBER = 52361700;\nexport function serializeObjectToMemory<T extends object>(heap: MemoryHeap, object: T): AllocatedMemory {\n\tconst data: Array<number> = [];\n\tconst keys = Object.keys(object);\n\tdata.push(MAGIC_NUMBER);\n\tdata.push(keys.length);\n\n\t// Create index first so we can quickly construct index\n\tlet keyIndexes: { [key:string]:number } = {};\n\tkeys.forEach(key => {\n\t\tdata.push(key.length);\n\t\taddCharCodes(data, key);\n\n\t\tkeyIndexes[key] = data.length;\n\t\tdata.push(0);\n\t});\n\n\tkeys.forEach(key => {\n\t\tlet keyIndex = keyIndexes[key];\n\t\t// @ts-expect-error\n\t\tlet value = object[key];\n\t\tif(Number.isFinite(value)) {\n\t\t\tdata[keyIndex] = data.length;\n\t\t\tdata.push(VALUE_TYPE.NUMBER);\n\t\t\tdata.push(value);\n\t\t} else if(typeof value === 'string') {\n\t\t\tdata[keyIndex] = data.length;\n\t\t\tdata.push(VALUE_TYPE.STRING);\n\t\t\tdata.push(value.length);\n\t\t\taddCharCodes(data, value);\n\t\t}\n\t});\n\t\n\tlet memory = heap.allocUI32(data.length);\n\tlet sharedData = memory.getArray(Float32Array, 0, data.length);\n\tsharedData.set(data);\n\n\treturn memory;\n}\n\nexport function createObjectFromPointer<T extends object>(heap: MemoryHeap, pointer: number) {\n\tlet memory = new AllocatedMemory(heap, getPointer(pointer));\n\treturn createObjectFromMemory<T>(memory);\n}\nexport function createObjectFromMemory<T extends object>(memory: AllocatedMemory): T {\n\tlet sharedData = new Float32Array(memory.data.buffer, memory.bufferByteOffset);\n\tif(sharedData[0] !== MAGIC_NUMBER) {\n\t\tthrow new Error('Trying to create object from invalid memory location');\n\t}\n\n\tconst sharedObject: { [key:string]:any } = {};\n\tconst keyCount = sharedData[1];\n\tlet keyStartIndex = 2;\n\tfor(let i = 0; i < keyCount; i++) {\n\t\tlet keyLength = sharedData[keyStartIndex];\n\t\tlet keyData = new Float32Array(memory.data.buffer, memory.bufferByteOffset + (keyStartIndex + 1) * sharedData.BYTES_PER_ELEMENT, keyLength);\n\t\tlet key = String.fromCharCode.apply(null, [...keyData]);\n\t\tlet valueIndex = sharedData[keyStartIndex + keyLength + 1];\n\t\tlet valueType = sharedData[valueIndex];\n\t\tswitch(valueType) {\n\t\t\tcase VALUE_TYPE.NUMBER: {\n\t\t\t\tsharedObject[key] = sharedData[valueIndex + 1];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase VALUE_TYPE.STRING: {\n\t\t\t\tlet length = sharedData[valueIndex + 1];\n\t\t\t\tlet valueData = new Float32Array(memory.data.buffer, memory.bufferByteOffset + (valueIndex + 2) * sharedData.BYTES_PER_ELEMENT, length);\n\t\t\t\tsharedObject[key] = String.fromCharCode.apply(null, [...valueData]);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tkeyStartIndex += 2 + keyLength;\n\t}\n\n\treturn sharedObject as T;\n}\n\nfunction addCharCodes(data: Array<number>, value: string) {\n\tfor(let i = 0; i < value.length; i++) {\n\t\tdata.push(value.charCodeAt(i));\n\t}\n}"],"mappings":";;;AAYA,IAAM,IAAe;AACrB,SAAgB,EAA0C,GAAkB,GAA4B;CACvG,IAAM,IAAsB,CAAC,GACvB,IAAO,OAAO,KAAK,CAAM;CAE/B,AADA,EAAK,KAAK,CAAY,GACtB,EAAK,KAAK,EAAK,MAAM;CAGrB,IAAI,IAAsC,CAAC;CAS3C,AARA,EAAK,SAAQ,MAAO;EAKnB,AAJA,EAAK,KAAK,EAAI,MAAM,GACpB,EAAa,GAAM,CAAG,GAEtB,EAAW,KAAO,EAAK,QACvB,EAAK,KAAK,CAAC;CACZ,CAAC,GAED,EAAK,SAAQ,MAAO;EACnB,IAAI,IAAW,EAAW,IAEtB,IAAQ,EAAO;EACnB,AAAG,OAAO,SAAS,CAAK,KACvB,EAAK,KAAY,EAAK,QACtB,EAAK,KAAA,CAAsB,GAC3B,EAAK,KAAK,CAAK,KACN,OAAO,KAAU,aAC1B,EAAK,KAAY,EAAK,QACtB,EAAK,KAAA,CAAsB,GAC3B,EAAK,KAAK,EAAM,MAAM,GACtB,EAAa,GAAM,CAAK;CAE1B,CAAC;CAED,IAAI,IAAS,EAAK,UAAU,EAAK,MAAM;CAIvC,OAFA,EADwB,SAAS,cAAc,GAAG,EAAK,MACvD,CAAA,CAAW,IAAI,CAAI,GAEZ;AACR;AAEA,SAAgB,EAA0C,GAAkB,GAAiB;CAE5F,OAAO,EAA0B,IADhB,EAAgB,GAAM,EAAW,
|
|
1
|
+
{"version":3,"file":"serialize-object.js","names":[],"sources":["../src/serialize-object.ts"],"sourcesContent":["import AllocatedMemory from './allocated-memory';\nimport type MemoryHeap from './memory-heap';\nimport { getPointer } from './utils/pointer';\n\nenum VALUE_TYPE {\n\tUNDEFINED,\n\tNUMBER,\n\tSTRING,\n}\n\n// Be able to serialize a simple object into memory and re-create the object in another thread\n// This does NOT support updating objects across threads\nconst MAGIC_NUMBER = 52361700;\nexport function serializeObjectToMemory<T extends object>(heap: MemoryHeap, object: T): AllocatedMemory {\n\tconst data: Array<number> = [];\n\tconst keys = Object.keys(object);\n\tdata.push(MAGIC_NUMBER);\n\tdata.push(keys.length);\n\n\t// Create index first so we can quickly construct index\n\tlet keyIndexes: { [key:string]:number } = {};\n\tkeys.forEach(key => {\n\t\tdata.push(key.length);\n\t\taddCharCodes(data, key);\n\n\t\tkeyIndexes[key] = data.length;\n\t\tdata.push(0);\n\t});\n\n\tkeys.forEach(key => {\n\t\tlet keyIndex = keyIndexes[key];\n\t\t// @ts-expect-error\n\t\tlet value = object[key];\n\t\tif(Number.isFinite(value)) {\n\t\t\tdata[keyIndex] = data.length;\n\t\t\tdata.push(VALUE_TYPE.NUMBER);\n\t\t\tdata.push(value);\n\t\t} else if(typeof value === 'string') {\n\t\t\tdata[keyIndex] = data.length;\n\t\t\tdata.push(VALUE_TYPE.STRING);\n\t\t\tdata.push(value.length);\n\t\t\taddCharCodes(data, value);\n\t\t}\n\t});\n\t\n\tlet memory = heap.allocUI32(data.length);\n\tlet sharedData = memory.getArray(Float32Array, 0, data.length);\n\tsharedData.set(data);\n\n\treturn memory;\n}\n\nexport function createObjectFromPointer<T extends object>(heap: MemoryHeap, pointer: number) {\n\tlet memory = new AllocatedMemory(heap, getPointer(pointer, heap.positionBits));\n\treturn createObjectFromMemory<T>(memory);\n}\nexport function createObjectFromMemory<T extends object>(memory: AllocatedMemory): T {\n\tlet sharedData = new Float32Array(memory.data.buffer, memory.bufferByteOffset);\n\tif(sharedData[0] !== MAGIC_NUMBER) {\n\t\tthrow new Error('Trying to create object from invalid memory location');\n\t}\n\n\tconst sharedObject: { [key:string]:any } = {};\n\tconst keyCount = sharedData[1];\n\tlet keyStartIndex = 2;\n\tfor(let i = 0; i < keyCount; i++) {\n\t\tlet keyLength = sharedData[keyStartIndex];\n\t\tlet keyData = new Float32Array(memory.data.buffer, memory.bufferByteOffset + (keyStartIndex + 1) * sharedData.BYTES_PER_ELEMENT, keyLength);\n\t\tlet key = String.fromCharCode.apply(null, [...keyData]);\n\t\tlet valueIndex = sharedData[keyStartIndex + keyLength + 1];\n\t\tlet valueType = sharedData[valueIndex];\n\t\tswitch(valueType) {\n\t\t\tcase VALUE_TYPE.NUMBER: {\n\t\t\t\tsharedObject[key] = sharedData[valueIndex + 1];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase VALUE_TYPE.STRING: {\n\t\t\t\tlet length = sharedData[valueIndex + 1];\n\t\t\t\tlet valueData = new Float32Array(memory.data.buffer, memory.bufferByteOffset + (valueIndex + 2) * sharedData.BYTES_PER_ELEMENT, length);\n\t\t\t\tsharedObject[key] = String.fromCharCode.apply(null, [...valueData]);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tkeyStartIndex += 2 + keyLength;\n\t}\n\n\treturn sharedObject as T;\n}\n\nfunction addCharCodes(data: Array<number>, value: string) {\n\tfor(let i = 0; i < value.length; i++) {\n\t\tdata.push(value.charCodeAt(i));\n\t}\n}"],"mappings":";;;AAYA,IAAM,IAAe;AACrB,SAAgB,EAA0C,GAAkB,GAA4B;CACvG,IAAM,IAAsB,CAAC,GACvB,IAAO,OAAO,KAAK,CAAM;CAE/B,AADA,EAAK,KAAK,CAAY,GACtB,EAAK,KAAK,EAAK,MAAM;CAGrB,IAAI,IAAsC,CAAC;CAS3C,AARA,EAAK,SAAQ,MAAO;EAKnB,AAJA,EAAK,KAAK,EAAI,MAAM,GACpB,EAAa,GAAM,CAAG,GAEtB,EAAW,KAAO,EAAK,QACvB,EAAK,KAAK,CAAC;CACZ,CAAC,GAED,EAAK,SAAQ,MAAO;EACnB,IAAI,IAAW,EAAW,IAEtB,IAAQ,EAAO;EACnB,AAAG,OAAO,SAAS,CAAK,KACvB,EAAK,KAAY,EAAK,QACtB,EAAK,KAAA,CAAsB,GAC3B,EAAK,KAAK,CAAK,KACN,OAAO,KAAU,aAC1B,EAAK,KAAY,EAAK,QACtB,EAAK,KAAA,CAAsB,GAC3B,EAAK,KAAK,EAAM,MAAM,GACtB,EAAa,GAAM,CAAK;CAE1B,CAAC;CAED,IAAI,IAAS,EAAK,UAAU,EAAK,MAAM;CAIvC,OAFA,EADwB,SAAS,cAAc,GAAG,EAAK,MACvD,CAAA,CAAW,IAAI,CAAI,GAEZ;AACR;AAEA,SAAgB,EAA0C,GAAkB,GAAiB;CAE5F,OAAO,EAA0B,IADhB,EAAgB,GAAM,EAAW,GAAS,EAAK,YAAY,CAC3C,CAAM;AACxC;AACA,SAAgB,EAAyC,GAA4B;CACpF,IAAI,IAAa,IAAI,aAAa,EAAO,KAAK,QAAQ,EAAO,gBAAgB;CAC7E,IAAG,EAAW,OAAO,GACpB,MAAU,MAAM,sDAAsD;CAGvE,IAAM,IAAqC,CAAC,GACtC,IAAW,EAAW,IACxB,IAAgB;CACpB,KAAI,IAAI,IAAI,GAAG,IAAI,GAAU,KAAK;EACjC,IAAI,IAAY,EAAW,IACvB,IAAU,IAAI,aAAa,EAAO,KAAK,QAAQ,EAAO,oBAAoB,IAAgB,KAAK,EAAW,mBAAmB,CAAS,GACtI,IAAM,OAAO,aAAa,MAAM,MAAM,CAAC,GAAG,CAAO,CAAC,GAClD,IAAa,EAAW,IAAgB,IAAY;EAExD,QADgB,EAAW,IAC3B;GACC,KAAA;IACC,EAAa,KAAO,EAAW,IAAa;IAC5C;GAED,KAAA,GAAwB;IACvB,IAAI,IAAS,EAAW,IAAa,IACjC,IAAY,IAAI,aAAa,EAAO,KAAK,QAAQ,EAAO,oBAAoB,IAAa,KAAK,EAAW,mBAAmB,CAAM;IACtI,EAAa,KAAO,OAAO,aAAa,MAAM,MAAM,CAAC,GAAG,CAAS,CAAC;IAClE;GACD;EACD;EAEA,KAAiB,IAAI;CACtB;CAEA,OAAO;AACR;AAEA,SAAS,EAAa,GAAqB,GAAe;CACzD,KAAI,IAAI,IAAI,GAAG,IAAI,EAAM,QAAQ,KAChC,EAAK,KAAK,EAAM,WAAW,CAAC,CAAC;AAE/B"}
|