@daneren2005/shared-memory-objects 1.2.0 → 1.3.0
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 +15 -3
- package/dist/allocated-memory.d.ts +1 -0
- package/dist/allocated-memory.js +3 -0
- package/dist/allocated-memory.js.map +1 -1
- package/dist/cached-item-list.d.ts +2 -1
- package/dist/cached-item-list.js +6 -1
- package/dist/cached-item-list.js.map +1 -1
- package/dist/memory-buffer.d.ts +4 -1
- package/dist/memory-buffer.js +12 -9
- package/dist/memory-buffer.js.map +1 -1
- package/dist/memory-heap.d.ts +2 -0
- package/dist/memory-heap.js +11 -1
- package/dist/memory-heap.js.map +1 -1
- package/dist/shared-list.d.ts +8 -2
- package/dist/shared-list.js +100 -62
- package/dist/shared-list.js.map +1 -1
- package/dist/shared-map.js +7 -4
- package/dist/shared-map.js.map +1 -1
- package/dist/shared-pointer-list.d.ts +1 -0
- package/dist/shared-pointer-list.js +5 -1
- package/dist/shared-pointer-list.js.map +1 -1
- package/dist/shared-pool.d.ts +4 -0
- package/dist/shared-pool.js +21 -14
- package/dist/shared-pool.js.map +1 -1
- package/dist/shared-stack.d.ts +15 -2
- package/dist/shared-stack.js +114 -37
- package/dist/shared-stack.js.map +1 -1
- package/dist/shared-vector.d.ts +1 -0
- package/dist/shared-vector.js +4 -0
- package/dist/shared-vector.js.map +1 -1
- package/package.json +2 -1
package/dist/shared-list.js
CHANGED
|
@@ -1,55 +1,79 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import { loadPointer as e, loadRawPointer as t, replaceRawPointer as n, storeRawPointer as r } from "./utils/pointer.js";
|
|
2
|
+
import i from "./allocated-memory.js";
|
|
3
3
|
//#region src/shared-list.ts
|
|
4
|
-
var
|
|
5
|
-
static ALLOCATE_COUNT =
|
|
4
|
+
var a = 4, o = 2, s = o * Uint32Array.BYTES_PER_ELEMENT, c = 0, l = 1, u = 2, d = 0, f = 1, p = 4095, m = class {
|
|
5
|
+
static ALLOCATE_COUNT = a;
|
|
6
6
|
memory;
|
|
7
7
|
firstBlock;
|
|
8
8
|
uint16Array;
|
|
9
|
+
cachedType;
|
|
10
|
+
cachedDataLength;
|
|
11
|
+
bufferViews = [];
|
|
9
12
|
onDelete;
|
|
10
13
|
get length() {
|
|
11
|
-
return Atomics.load(this.firstBlock.data,
|
|
14
|
+
return Atomics.load(this.firstBlock.data, u);
|
|
15
|
+
}
|
|
16
|
+
get usedMemory() {
|
|
17
|
+
let t = this.firstBlock.usedMemory, { bufferPosition: n, bufferByteOffset: r } = e(this.firstBlock.data, c);
|
|
18
|
+
for (; r;) {
|
|
19
|
+
let a = new i(this.memory, {
|
|
20
|
+
bufferPosition: n,
|
|
21
|
+
bufferByteOffset: r
|
|
22
|
+
});
|
|
23
|
+
t += a.usedMemory, {bufferPosition: n, bufferByteOffset: r} = e(a.data, d);
|
|
24
|
+
}
|
|
25
|
+
return t;
|
|
12
26
|
}
|
|
13
27
|
get type() {
|
|
14
|
-
return
|
|
28
|
+
return this.cachedType;
|
|
15
29
|
}
|
|
16
30
|
set type(e) {
|
|
17
31
|
Atomics.store(this.uint16Array, 0, e);
|
|
18
32
|
}
|
|
19
33
|
get dataLength() {
|
|
20
|
-
return
|
|
34
|
+
return this.cachedDataLength;
|
|
21
35
|
}
|
|
22
36
|
set dataLength(e) {
|
|
23
37
|
Atomics.store(this.uint16Array, 1, e);
|
|
24
38
|
}
|
|
25
39
|
constructor(e, t) {
|
|
26
|
-
if (this.memory = e, t && "firstBlock" in t) this.firstBlock = new
|
|
40
|
+
if (this.memory = e, t && "firstBlock" in t) this.firstBlock = new i(e, t.firstBlock), this.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + 3 * Uint32Array.BYTES_PER_ELEMENT, 2);
|
|
27
41
|
else {
|
|
28
|
-
this.firstBlock = t && t.initWithBlock ? new
|
|
42
|
+
this.firstBlock = t && t.initWithBlock ? new i(e, t.initWithBlock) : e.allocUI32(a), this.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + 3 * Uint32Array.BYTES_PER_ELEMENT, 2), r(this.firstBlock.data, l, this.firstBlock.pointer);
|
|
29
43
|
let n = t?.type ?? Uint32Array;
|
|
30
44
|
n === Uint32Array ? this.type = 0 : n === Int32Array ? this.type = 1 : n === Float32Array && (this.type = 2), this.dataLength = t?.dataLength ?? 1;
|
|
31
45
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
let
|
|
40
|
-
for (
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
a(r,
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
this.cachedType = Atomics.load(this.uint16Array, 0), this.cachedDataLength = Math.max(1, Atomics.load(this.uint16Array, 1));
|
|
47
|
+
}
|
|
48
|
+
insert(e) {
|
|
49
|
+
typeof e == "number" && (e = [e]);
|
|
50
|
+
let r = this.cachedDataLength;
|
|
51
|
+
if (e.length > r) throw Error(`Can't insert ${e.length} array into shared list of ${r} dataLength`);
|
|
52
|
+
let i = this.memory.allocUI32(o + r), a = this.getDataBlock(i.data.buffer, i.data.byteOffset), s = i.pointer;
|
|
53
|
+
for (let t = 0; t < e.length; t++) a instanceof Int32Array || a instanceof Uint32Array ? Atomics.store(a, t, e[t]) : a[t] = e[t];
|
|
54
|
+
for (;;) {
|
|
55
|
+
let e = t(this.firstBlock.data, l), r = this.getBufferView(e & p);
|
|
56
|
+
if (!r) continue;
|
|
57
|
+
let i = (e >>> 12 >>> 2) + d, a = Atomics.load(r, i);
|
|
58
|
+
if (e === t(this.firstBlock.data, l)) {
|
|
59
|
+
if (a === 0) {
|
|
60
|
+
if (Atomics.compareExchange(r, i, 0, s) === 0) {
|
|
61
|
+
n(this.firstBlock.data, l, s, e);
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
} else n(this.firstBlock.data, l, a, e);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
Atomics.add(this.firstBlock.data, u, 1);
|
|
46
68
|
}
|
|
47
69
|
deleteMatch(e) {
|
|
48
|
-
for (let { data: t, index: n, deleteCurrent: r } of this) if (e(t, n)
|
|
70
|
+
for (let { data: t, index: n, deleteCurrent: r } of this) if (e(t, n) && r()) return !0;
|
|
49
71
|
return !1;
|
|
50
72
|
}
|
|
51
73
|
deleteIndex(e) {
|
|
52
|
-
|
|
74
|
+
if (e >= this.length || e < 0) return !1;
|
|
75
|
+
for (let t of this) if (t.index === e && t.deleteCurrent()) return !0;
|
|
76
|
+
return !1;
|
|
53
77
|
}
|
|
54
78
|
deleteValue(e) {
|
|
55
79
|
return typeof e == "number" ? this.deleteMatch((t) => t[0] === e) : this.deleteMatch((t) => {
|
|
@@ -58,38 +82,46 @@ var s = 4, c = 1, l = 2, u = class {
|
|
|
58
82
|
return !0;
|
|
59
83
|
});
|
|
60
84
|
}
|
|
85
|
+
compact() {
|
|
86
|
+
let e = this.firstBlock.pointer, n = this.firstBlock.data, i = d, a = t(n, i);
|
|
87
|
+
for (; a;) {
|
|
88
|
+
let t = a & p, o = this.memory.buffers[t];
|
|
89
|
+
if (!o) break;
|
|
90
|
+
let s = this.getBufferView(t), c = a >>> 12, l = c >>> 2, u = Atomics.load(s, l + d);
|
|
91
|
+
Atomics.load(s, l + f) ? (r(n, i, u), o.free(c)) : (e = a, n = s, i = l + d), a = u;
|
|
92
|
+
}
|
|
93
|
+
r(this.firstBlock.data, l, e);
|
|
94
|
+
}
|
|
61
95
|
clear() {
|
|
62
|
-
let
|
|
63
|
-
for (; !
|
|
64
|
-
if (
|
|
65
|
-
|
|
96
|
+
let e, i = !1;
|
|
97
|
+
for (; !i;) {
|
|
98
|
+
if (e = t(this.firstBlock.data, c), !e) return;
|
|
99
|
+
i = n(this.firstBlock.data, c, 0, e);
|
|
66
100
|
}
|
|
67
|
-
if (!
|
|
68
|
-
r(this.firstBlock.data,
|
|
69
|
-
let
|
|
70
|
-
for (;
|
|
71
|
-
let
|
|
72
|
-
if (!
|
|
73
|
-
let
|
|
74
|
-
|
|
101
|
+
if (!e) return;
|
|
102
|
+
r(this.firstBlock.data, l, this.firstBlock.pointer);
|
|
103
|
+
let a = 0, o = e;
|
|
104
|
+
for (; o;) {
|
|
105
|
+
let e = o & p, t = this.memory.buffers[e];
|
|
106
|
+
if (!t) break;
|
|
107
|
+
let n = this.getBufferView(e), r = o >>> 12, i = r >>> 2;
|
|
108
|
+
o = Atomics.load(n, i + d), Atomics.load(n, i + f) || (a++, this.onDelete && this.onDelete(this.getDataBlock(n.buffer, r))), t.free(r);
|
|
75
109
|
}
|
|
76
|
-
Atomics.sub(this.firstBlock.data,
|
|
110
|
+
Atomics.sub(this.firstBlock.data, u, a);
|
|
77
111
|
}
|
|
78
112
|
*[Symbol.iterator]() {
|
|
79
|
-
let e = 0,
|
|
80
|
-
for (;
|
|
81
|
-
let
|
|
82
|
-
if (!
|
|
83
|
-
let
|
|
84
|
-
(
|
|
85
|
-
let
|
|
113
|
+
let e = 0, n = t(this.firstBlock.data, c);
|
|
114
|
+
for (; n;) {
|
|
115
|
+
let t = n & p, r = this.getBufferView(t);
|
|
116
|
+
if (!r) return;
|
|
117
|
+
let i = n >>> 12, a = i >>> 2;
|
|
118
|
+
if (n = Atomics.load(r, a + d), Atomics.load(r, a + f)) continue;
|
|
119
|
+
let o = r.buffer, s = a + f;
|
|
86
120
|
yield {
|
|
87
|
-
data:
|
|
121
|
+
data: this.getDataBlock(o, i),
|
|
88
122
|
index: e,
|
|
89
|
-
deleteCurrent: () =>
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
}, m && (a = u, o = f, s = p, e++);
|
|
123
|
+
deleteCurrent: () => Atomics.compareExchange(r, s, 0, 1) === 0 && (Atomics.sub(this.firstBlock.data, u, 1), this.onDelete && this.onDelete(this.getDataBlock(o, i)), !0)
|
|
124
|
+
}, e++;
|
|
93
125
|
}
|
|
94
126
|
}
|
|
95
127
|
forEach(e) {
|
|
@@ -98,28 +130,34 @@ var s = 4, c = 1, l = 2, u = class {
|
|
|
98
130
|
getSharedMemory() {
|
|
99
131
|
return { firstBlock: this.firstBlock.getSharedMemory() };
|
|
100
132
|
}
|
|
101
|
-
|
|
102
|
-
let t =
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
133
|
+
getBufferView(e) {
|
|
134
|
+
let t = this.bufferViews[e];
|
|
135
|
+
if (t) return t;
|
|
136
|
+
let n = this.memory.buffers[e];
|
|
137
|
+
if (n) return t = new Uint32Array(n.buf), this.bufferViews[e] = t, t;
|
|
138
|
+
}
|
|
139
|
+
getDataBlock(e, t) {
|
|
140
|
+
let n = t + s;
|
|
141
|
+
switch (this.cachedType) {
|
|
142
|
+
case 1: return new Int32Array(e, n, this.cachedDataLength);
|
|
143
|
+
case 0: return new Uint32Array(e, n, this.cachedDataLength);
|
|
144
|
+
case 2: return new Float32Array(e, n, this.cachedDataLength);
|
|
145
|
+
default: throw Error(`Unknown data block type ${this.cachedType}`);
|
|
108
146
|
}
|
|
109
147
|
}
|
|
110
148
|
free() {
|
|
111
|
-
let { bufferPosition:
|
|
149
|
+
let { bufferPosition: t, bufferByteOffset: n } = e(this.firstBlock.data, c);
|
|
112
150
|
for (; n;) {
|
|
113
|
-
let r = new
|
|
114
|
-
bufferPosition:
|
|
151
|
+
let r = new i(this.memory, {
|
|
152
|
+
bufferPosition: t,
|
|
115
153
|
bufferByteOffset: n
|
|
116
154
|
});
|
|
117
|
-
({bufferPosition:
|
|
155
|
+
({bufferPosition: t, bufferByteOffset: n} = e(r.data, d)), this.onDelete && !Atomics.load(r.data, f) && this.onDelete(this.getDataBlock(r.data.buffer, r.data.byteOffset)), r.free();
|
|
118
156
|
}
|
|
119
157
|
this.firstBlock.free();
|
|
120
158
|
}
|
|
121
159
|
};
|
|
122
160
|
//#endregion
|
|
123
|
-
export {
|
|
161
|
+
export { m as default };
|
|
124
162
|
|
|
125
163
|
//# sourceMappingURL=shared-list.js.map
|
package/dist/shared-list.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared-list.js","names":[],"sources":["../src/shared-list.ts"],"sourcesContent":["import type { SharedAllocatedMemory } from './allocated-memory';\nimport AllocatedMemory from './allocated-memory';\nimport type { TypedArrayConstructor } from './interfaces/typed-array-constructor';\nimport type MemoryHeap from './memory-heap';\nimport { getPointer, loadPointer, loadRawPointer, replaceRawPointer, storePointer, storeRawPointer } from './utils/pointer';\n\nenum TYPE {\n\tuint32,\n\tint32,\n\tfloat32,\n}\n\n// TODO: We need some sort of locking on insert/deletes!\nconst FIRST_BLOCK_RECORD_KEEPING_COUNT = 4;\nconst DATA_BLOCK_RECORD_KEEPING_COUNT = 1;\nconst LENGTH_INDEX = 2;\nexport default class SharedList<T extends Uint32Array | Int32Array | Float32Array = Uint32Array> implements Iterable<SharedListIterable<T>> {\n\tstatic readonly ALLOCATE_COUNT = FIRST_BLOCK_RECORD_KEEPING_COUNT;\n\n\tprivate memory: MemoryHeap;\n\t/* First block\n\t\t32 index 0\n\t\tuint16 0 - next buffer position\n\t\tuint16 1 - next buffer index\n\t\t32 index 1\n\t\tuint16 2 - last buffer position\n\t\tuint16 3 - last buffer index\n\t\t32 index 2\n\t\tuint32 4 - length\n\t\t32 index 3\n\t\tuint16 6 - type\n\t\tuint16 7 - data length (defaults to 1 number per data)\n\t*/\n\t/* Other blocks\n\t\t32 index 0\n\t\tuint16 0 - next buffer position\n\t\tuint16 1 - next buffer index\n\t\t32 index 1 => data\n\t*/\n\tprivate firstBlock: AllocatedMemory;\n\tprivate uint16Array: Uint16Array;\n\tonDelete?: (data: T) => void;\n\n\tget length(): number {\n\t\treturn Atomics.load(this.firstBlock.data, LENGTH_INDEX);\n\t}\n\t\n\tget type(): number {\n\t\treturn Atomics.load(this.uint16Array, 0);\n\t}\n\tprivate set type(value: number) {\n\t\tAtomics.store(this.uint16Array, 0, value);\n\t}\n\tget dataLength(): number {\n\t\t// Can technically be initialized by passing memory without actually every being called - need to make sure dataLength is always at least one\n\t\treturn Math.max(1, Atomics.load(this.uint16Array, 1));\n\t}\n\tprivate set dataLength(value: number) {\n\t\tAtomics.store(this.uint16Array, 1, value);\n\t}\n\n\tconstructor(memory: MemoryHeap, config?: SharedListConfig<T> | SharedListMemory) {\n\t\tthis.memory = memory;\n\n\t\tif(config && 'firstBlock' in config) {\n\t\t\t// TODO: How to handle referencing memory we don't have access to yet because buffer not synced from worker?\n\t\t\tthis.firstBlock = new AllocatedMemory(memory, config.firstBlock);\n\t\t\tthis.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + (LENGTH_INDEX + 1) * Uint32Array.BYTES_PER_ELEMENT, 2);\n\t\t} else {\n\t\t\tif(config && config.initWithBlock) {\n\t\t\t\tthis.firstBlock = new AllocatedMemory(memory, config.initWithBlock);\n\t\t\t} else {\n\t\t\t\tthis.firstBlock = memory.allocUI32(FIRST_BLOCK_RECORD_KEEPING_COUNT);\n\t\t\t}\n\t\t\tthis.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + (LENGTH_INDEX + 1) * Uint32Array.BYTES_PER_ELEMENT, 2);\n\n\t\t\tconst type = config?.type ?? Uint32Array;\n\t\t\tif(type === Uint32Array) {\n\t\t\t\tthis.type = TYPE.uint32;\n\t\t\t// @ts-expect-error\n\t\t\t} else if(type === Int32Array) {\n\t\t\t\tthis.type = TYPE.int32;\n\t\t\t// @ts-expect-error\n\t\t\t} else if(type === Float32Array) {\n\t\t\t\tthis.type = TYPE.float32;\n\t\t\t}\n\t\t\tthis.dataLength = config?.dataLength ?? 1;\n\t\t}\n\t}\n\n\tinsert(values: number | Array<number>) {\n\t\tif(typeof values === 'number') {\n\t\t\tvalues = [values];\n\t\t}\n\n\t\tlet dataLength = this.dataLength;\n\t\tif(values.length > dataLength) {\n\t\t\tthrow new Error(`Can't insert ${values.length} array into shared list of ${dataLength} dataLength`);\n\t\t}\n\t\tlet newBlock = this.memory.allocUI32(DATA_BLOCK_RECORD_KEEPING_COUNT + dataLength);\n\t\tlet newData = this.getDataBlock(newBlock.data);\n\t\tlet newBlockPointer = newBlock.pointer;\n\t\t\n\t\tfor(let i = 0; i < values.length; i++) {\n\t\t\tif(newData instanceof Int32Array || newData instanceof Uint32Array) {\n\t\t\t\tAtomics.store(newData, i, values[i]);\n\t\t\t} else {\n\t\t\t\t// TODO: Should we replace with pass thru float32 conversion -> store?\n\t\t\t\tnewData[i] = values[i];\n\t\t\t}\n\t\t}\n\n\t\tlet lastBlockPointer;\n\t\tlet updateWorked = false;\n\t\twhile(!updateWorked) {\n\t\t\tlastBlockPointer = loadRawPointer(this.firstBlock.data, 1);\n\t\t\tupdateWorked = replaceRawPointer(this.firstBlock.data, 1, newBlockPointer, lastBlockPointer);\n\t\t}\n\n\t\tif(lastBlockPointer) {\n\t\t\tlet { bufferPosition: lastBlockPosition, bufferByteOffset: lastBlockByteOffset } = getPointer(lastBlockPointer);\n\t\t\t// TODO: How to handle referencing memory we don't have access to yet because buffer not synced from worker?\n\t\t\tlet lastBlock = new Uint32Array(this.memory.buffers[lastBlockPosition].buf, lastBlockByteOffset, 1);\n\t\t\tstoreRawPointer(lastBlock, 0, newBlockPointer);\n\t\t} else {\n\t\t\t// First item - store on first block\n\t\t\tstoreRawPointer(this.firstBlock.data, 0, newBlockPointer);\n\t\t}\n\t\t\n\t\t// Always update new last buffer position and length\n\t\tAtomics.add(this.firstBlock.data, LENGTH_INDEX, 1);\n\t}\n\n\tdeleteMatch(callback: (values: T, index: number) => boolean): boolean {\n\t\tfor(let { data, index, deleteCurrent } of this) {\n\t\t\tif(callback(data, index)) {\n\t\t\t\tdeleteCurrent();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\tdeleteIndex(deleteIndex: number): boolean {\n\t\tif(deleteIndex >= this.length || deleteIndex < 0) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn this.deleteMatch((values, index) => index === deleteIndex);\n\t}\n\tdeleteValue(deleteValues: number | Array<number>) {\n\t\tif(typeof deleteValues === 'number') {\n\t\t\treturn this.deleteMatch(values => values[0] === deleteValues);\n\t\t} else {\n\t\t\treturn this.deleteMatch(values => {\n\t\t\t\tif(values.length !== deleteValues.length) {\n\t\t\t\t\treturn false;\n\t\t\t\t} else {\n\t\t\t\t\tfor(let i = 0; i < values.length; i++) {\n\t\t\t\t\t\tif(values[i] !== deleteValues[i]) {\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\n\tclear() {\n\t\tlet firstBlockPointer, lastBlockPointer;\n\t\tlet updateWorked = false;\n\t\twhile(!updateWorked) {\n\t\t\tfirstBlockPointer = loadRawPointer(this.firstBlock.data, 0);\n\t\t\tlastBlockPointer = loadRawPointer(this.firstBlock.data, 1);\n\t\t\t// Already cleared\n\t\t\tif(!lastBlockPointer) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tupdateWorked = replaceRawPointer(this.firstBlock.data, 1, 0, lastBlockPointer);\n\t\t}\n\t\t\n\t\t// Shouldn't be possible to hit: making Typescript happy\n\t\tif(!firstBlockPointer) {\n\t\t\treturn;\n\t\t}\n\n\t\t// We only want to update the last block if this is ran before something new was inserted\n\t\treplaceRawPointer(this.firstBlock.data, 0, 0, firstBlockPointer);\n\n\t\t// Iterate through inaccessible nodes and delete them\n\t\tlet deletedItems = 0;\n\t\tlet nextBlockPointer = firstBlockPointer;\n\t\twhile(nextBlockPointer) {\n\t\t\tlet { bufferPosition: nextBlockPosition, bufferByteOffset: nextBlockByteOffset } = getPointer(nextBlockPointer);\n\t\t\tlet memPool = this.memory.buffers[nextBlockPosition];\n\t\t\t// Short circuit iterations if we can't access memory\n\t\t\tif(!memPool) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tlet blockRecord = new Uint32Array(memPool.buf, nextBlockByteOffset, 2);\n\t\t\tnextBlockPointer = loadRawPointer(blockRecord, 0);\n\t\t\tdeletedItems++;\n\n\t\t\tif(this.onDelete) {\n\t\t\t\tthis.onDelete(this.getDataBlock(blockRecord));\n\t\t\t}\n\n\t\t\tmemPool.free(blockRecord.byteOffset);\n\t\t}\n\t\t\n\t\t// Subtract by however many we deleted so that a insert during this operation is accurate\n\t\tAtomics.sub(this.firstBlock.data, LENGTH_INDEX, deletedItems);\n\t}\n\n\t*[Symbol.iterator]() {\n\t\tlet currentIndex = 0;\n\t\tlet { bufferPosition: nextBlockPosition, bufferByteOffset: nextBlockByteOffset } = loadPointer(this.firstBlock.data, 0);\n\t\tlet lastBlockData = this.firstBlock.data;\n\t\tlet lastBlockPosition = 0;\n\t\tlet lastBlockByteOffset = 0;\n\t\twhile(nextBlockByteOffset) {\n\t\t\tlet memPool = this.memory.buffers[nextBlockPosition];\n\t\t\t// Short circuit iterations if we can't access memory\n\t\t\tif(!memPool) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet blockRecord = new Uint32Array(memPool.buf, nextBlockByteOffset, 2);\n\t\t\tlet blockData = this.getDataBlock(blockRecord);\n\n\t\t\tlet currentBlockPosition = nextBlockPosition;\n\t\t\tlet currentBlockByteOffset = nextBlockByteOffset;\n\t\t\t({ bufferPosition: nextBlockPosition, bufferByteOffset: nextBlockByteOffset } = loadPointer(blockRecord, 0));\n\n\t\t\tlet updateLastBlock = true;\n\t\t\tyield {\n\t\t\t\tdata: blockData,\n\t\t\t\tindex: currentIndex,\n\t\t\t\tdeleteCurrent: () => {\n\t\t\t\t\t// Move previous index to point to one after\n\t\t\t\t\tstorePointer(lastBlockData, 0, nextBlockPosition, nextBlockByteOffset);\n\n\t\t\t\t\t// If this is the last item, update last block to be previous location\n\t\t\t\t\tif(!nextBlockByteOffset) {\n\t\t\t\t\t\tstorePointer(this.firstBlock.data, 1, lastBlockPosition, lastBlockByteOffset);\n\t\t\t\t\t}\n\n\t\t\t\t\tif(this.onDelete) {\n\t\t\t\t\t\tthis.onDelete(this.getDataBlock(blockRecord));\n\t\t\t\t\t}\n\n\t\t\t\t\tmemPool.free(blockRecord.byteOffset);\n\t\t\t\t\tAtomics.sub(this.firstBlock.data, LENGTH_INDEX, 1);\n\t\t\t\t\tupdateLastBlock = false;\n\t\t\t\t},\n\t\t\t};\n\n\t\t\tif(updateLastBlock) {\n\t\t\t\tlastBlockData = blockRecord;\n\t\t\t\tlastBlockPosition = currentBlockPosition;\n\t\t\t\tlastBlockByteOffset = currentBlockByteOffset;\n\t\t\t\tcurrentIndex++;\n\t\t\t}\n\t\t}\n\t}\n\n\tforEach(callback: (data: T) => void) {\n\t\tfor(let value of this) {\n\t\t\tcallback(value.data);\n\t\t}\n\t}\n\n\tgetSharedMemory(): SharedListMemory {\n\t\treturn {\n\t\t\tfirstBlock: this.firstBlock.getSharedMemory(),\n\t\t};\n\t}\n\n\tprivate getDataBlock(memory: Uint32Array): T {\n\t\tconst startIndex = memory.byteOffset + DATA_BLOCK_RECORD_KEEPING_COUNT * memory.BYTES_PER_ELEMENT;\n\n\t\tswitch(this.type) {\n\t\t\tcase TYPE.int32:\n\t\t\t\treturn new Int32Array(memory.buffer, startIndex, this.dataLength) as T;\n\t\t\tcase TYPE.uint32:\n\t\t\t\treturn new Uint32Array(memory.buffer, startIndex, this.dataLength) as T;\n\t\t\tcase TYPE.float32:\n\t\t\t\treturn new Float32Array(memory.buffer, startIndex, this.dataLength) as T;\n\t\t\tdefault:\n\t\t\t\tthrow new Error(`Unknown data block type ${this.type}`);\n\t\t}\n\t}\n\n\tfree() {\n\t\tlet { bufferPosition: nextBlockPosition, bufferByteOffset: nextBlockByteOffset } = loadPointer(this.firstBlock.data, 0);\n\t\twhile(nextBlockByteOffset) {\n\t\t\tlet allocatedMemory = new AllocatedMemory(this.memory, {\n\t\t\t\tbufferPosition: nextBlockPosition,\n\t\t\t\tbufferByteOffset: nextBlockByteOffset,\n\t\t\t});\n\n\t\t\t({ bufferPosition: nextBlockPosition, bufferByteOffset: nextBlockByteOffset } = loadPointer(allocatedMemory.data, 0));\n\n\t\t\tif(this.onDelete) {\n\t\t\t\tthis.onDelete(this.getDataBlock(allocatedMemory.data));\n\t\t\t}\n\n\t\t\tallocatedMemory.free();\n\t\t}\n\n\t\tthis.firstBlock.free();\n\t}\n}\n\ninterface SharedListConfig<T extends Uint32Array | Int32Array | Float32Array> {\n\tinitWithBlock?: SharedAllocatedMemory\n\ttype?: TypedArrayConstructor<T>\n\tdataLength?: number\n}\ninterface SharedListMemory {\n\tfirstBlock: SharedAllocatedMemory\n}\n\ninterface SharedListIterable<T extends Uint32Array | Int32Array | Float32Array> {\n\tdata: T\n\tindex: number\n\tdeleteCurrent: () => void\n}\n\nexport { type SharedListMemory };"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"shared-list.js","names":[],"sources":["../src/shared-list.ts"],"sourcesContent":["import type { SharedAllocatedMemory } from './allocated-memory';\nimport AllocatedMemory from './allocated-memory';\nimport type { TypedArrayConstructor } from './interfaces/typed-array-constructor';\nimport type MemoryHeap from './memory-heap';\nimport { loadPointer, loadRawPointer, POSITION_BIT_COUNT, replaceRawPointer, storeRawPointer } from './utils/pointer';\n\nenum TYPE {\n\tuint32,\n\tint32,\n\tfloat32,\n}\n\n// Lock-free singly-linked list. firstBlock doubles as an immortal sentinel node whose next pointer (index 0) is the head.\n// insert appends with a Michael-Scott CAS enqueue; delete only tombstones a node (never unlinks/frees). Memory is\n// reclaimed exclusively by the quiescent compact()/clear()/free() passes. That deferral is what makes the CAS loops\n// safe without hazard pointers: a live node's address is never handed back to the allocator, so no append can ever\n// observe a recycled address (ABA) while another thread is mid-traversal.\nconst FIRST_BLOCK_RECORD_KEEPING_COUNT = 4;\nconst DATA_BLOCK_RECORD_KEEPING_COUNT = 2;\nconst DATA_BLOCK_BYTE_OFFSET = DATA_BLOCK_RECORD_KEEPING_COUNT * Uint32Array.BYTES_PER_ELEMENT;\nconst HEAD_INDEX = 0;\nconst TAIL_INDEX = 1;\nconst LENGTH_INDEX = 2;\nconst NODE_NEXT_INDEX = 0;\nconst NODE_DELETED_INDEX = 1;\n// A pointer packs the buffer position in its low bits and the byte offset in the high bits (see utils/pointer)\nconst POSITION_MASK = (1 << POSITION_BIT_COUNT) - 1;\nexport default class SharedList<T extends Uint32Array | Int32Array | Float32Array = Uint32Array> implements Iterable<SharedListIterable<T>> {\n\tstatic readonly ALLOCATE_COUNT = FIRST_BLOCK_RECORD_KEEPING_COUNT;\n\n\tprivate memory: MemoryHeap;\n\t/* First block (also the sentinel node - index 0 is its next pointer = list head)\n\t\t32 index 0\n\t\tuint16 0 - head buffer position | sentinel.next\n\t\tuint16 1 - head buffer index\n\t\t32 index 1\n\t\tuint16 2 - tail hint buffer position\n\t\tuint16 3 - tail hint buffer index\n\t\t32 index 2\n\t\tuint32 4 - length\n\t\t32 index 3\n\t\tuint16 6 - type\n\t\tuint16 7 - data length (defaults to 1 number per data)\n\t*/\n\t/* Other blocks\n\t\t32 index 0 - next buffer pointer\n\t\t32 index 1 - deleted flag (0 = live, 1 = tombstoned)\n\t\t32 index 2 => data\n\t*/\n\tprivate firstBlock: AllocatedMemory;\n\tprivate uint16Array: Uint16Array;\n\t// type and dataLength are written once at init and never change, so cache them instead of atomic-loading per node\n\tprivate cachedType: number;\n\tprivate cachedDataLength: number;\n\t// One full-buffer Uint32Array per buffer position, so traversal indexes shared memory directly instead of\n\t// allocating a fresh view (and a getPointer object) for every node it touches\n\tprivate bufferViews: Array<Uint32Array> = [];\n\tonDelete?: (data: T) => void;\n\n\tget length(): number {\n\t\treturn Atomics.load(this.firstBlock.data, LENGTH_INDEX);\n\t}\n\n\t// Own internal memory plus every allocated node still linked in the chain (tombstoned nodes included, since they stay\n\t// allocated until a compact()/clear() reclaims them).\n\tget usedMemory(): number {\n\t\tlet total = this.firstBlock.usedMemory;\n\t\tlet { bufferPosition, bufferByteOffset } = loadPointer(this.firstBlock.data, HEAD_INDEX);\n\t\twhile(bufferByteOffset) {\n\t\t\tlet node = new AllocatedMemory(this.memory, { bufferPosition, bufferByteOffset });\n\t\t\ttotal += node.usedMemory;\n\t\t\t({ bufferPosition, bufferByteOffset } = loadPointer(node.data, NODE_NEXT_INDEX));\n\t\t}\n\t\treturn total;\n\t}\n\n\tget type(): number {\n\t\treturn this.cachedType;\n\t}\n\tprivate set type(value: number) {\n\t\tAtomics.store(this.uint16Array, 0, value);\n\t}\n\tget dataLength(): number {\n\t\treturn this.cachedDataLength;\n\t}\n\tprivate set dataLength(value: number) {\n\t\tAtomics.store(this.uint16Array, 1, value);\n\t}\n\n\tconstructor(memory: MemoryHeap, config?: SharedListConfig<T> | SharedListMemory) {\n\t\tthis.memory = memory;\n\n\t\tif(config && 'firstBlock' in config) {\n\t\t\t// TODO: How to handle referencing memory we don't have access to yet because buffer not synced from worker?\n\t\t\tthis.firstBlock = new AllocatedMemory(memory, config.firstBlock);\n\t\t\tthis.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + (LENGTH_INDEX + 1) * Uint32Array.BYTES_PER_ELEMENT, 2);\n\t\t} else {\n\t\t\tif(config && config.initWithBlock) {\n\t\t\t\tthis.firstBlock = new AllocatedMemory(memory, config.initWithBlock);\n\t\t\t} else {\n\t\t\t\tthis.firstBlock = memory.allocUI32(FIRST_BLOCK_RECORD_KEEPING_COUNT);\n\t\t\t}\n\t\t\tthis.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + (LENGTH_INDEX + 1) * Uint32Array.BYTES_PER_ELEMENT, 2);\n\n\t\t\t// Empty list: the tail hint points at the sentinel (firstBlock) so insert never special-cases the first node\n\t\t\tstoreRawPointer(this.firstBlock.data, TAIL_INDEX, this.firstBlock.pointer);\n\n\t\t\tconst type = config?.type ?? Uint32Array;\n\t\t\tif(type === Uint32Array) {\n\t\t\t\tthis.type = TYPE.uint32;\n\t\t\t// @ts-expect-error\n\t\t\t} else if(type === Int32Array) {\n\t\t\t\tthis.type = TYPE.int32;\n\t\t\t// @ts-expect-error\n\t\t\t} else if(type === Float32Array) {\n\t\t\t\tthis.type = TYPE.float32;\n\t\t\t}\n\t\t\tthis.dataLength = config?.dataLength ?? 1;\n\t\t}\n\n\t\tthis.cachedType = Atomics.load(this.uint16Array, 0);\n\t\t// dataLength defaults to at least one even when initialized from raw memory that was never explicitly set\n\t\tthis.cachedDataLength = Math.max(1, Atomics.load(this.uint16Array, 1));\n\t}\n\n\tinsert(values: number | Array<number>) {\n\t\tif(typeof values === 'number') {\n\t\t\tvalues = [values];\n\t\t}\n\n\t\tlet dataLength = this.cachedDataLength;\n\t\tif(values.length > dataLength) {\n\t\t\tthrow new Error(`Can't insert ${values.length} array into shared list of ${dataLength} dataLength`);\n\t\t}\n\t\tlet newBlock = this.memory.allocUI32(DATA_BLOCK_RECORD_KEEPING_COUNT + dataLength);\n\t\tlet newData = this.getDataBlock(newBlock.data.buffer, newBlock.data.byteOffset);\n\t\tlet newBlockPointer = newBlock.pointer;\n\n\t\tfor(let i = 0; i < values.length; i++) {\n\t\t\tif(newData instanceof Int32Array || newData instanceof Uint32Array) {\n\t\t\t\tAtomics.store(newData, i, values[i]);\n\t\t\t} else {\n\t\t\t\t// TODO: Should we replace with pass thru float32 conversion -> store?\n\t\t\t\tnewData[i] = values[i];\n\t\t\t}\n\t\t}\n\n\t\t// Michael-Scott enqueue: link the node onto tail.next BEFORE swinging the tail hint, so a committed node is never\n\t\t// invisible to a concurrent traversal. The tail hint may lag by one node; enqueuers help advance it.\n\t\t// eslint-disable-next-line no-constant-condition\n\t\twhile(true) {\n\t\t\tlet tailPointer = loadRawPointer(this.firstBlock.data, TAIL_INDEX);\n\t\t\tlet view = this.getBufferView(tailPointer & POSITION_MASK);\n\t\t\t// Tail node lives in a buffer this thread hasn't synced yet - retry until it appears\n\t\t\tif(!view) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlet tailNextIndex = ((tailPointer >>> POSITION_BIT_COUNT) >>> 2) + NODE_NEXT_INDEX;\n\t\t\tlet nextPointer = Atomics.load(view, tailNextIndex);\n\n\t\t\t// Re-check the hint hasn't moved out from under us before acting on what we read\n\t\t\tif(tailPointer !== loadRawPointer(this.firstBlock.data, TAIL_INDEX)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif(nextPointer === 0) {\n\t\t\t\tif(Atomics.compareExchange(view, tailNextIndex, 0, newBlockPointer) === 0) {\n\t\t\t\t\t// Linked. Try to swing the hint forward - failure is harmless, the next enqueuer will help.\n\t\t\t\t\treplaceRawPointer(this.firstBlock.data, TAIL_INDEX, newBlockPointer, tailPointer);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Tail hint is lagging behind a linked node - help it catch up, then retry\n\t\t\t\treplaceRawPointer(this.firstBlock.data, TAIL_INDEX, nextPointer, tailPointer);\n\t\t\t}\n\t\t}\n\n\t\tAtomics.add(this.firstBlock.data, LENGTH_INDEX, 1);\n\t}\n\n\tdeleteMatch(callback: (values: T, index: number) => boolean): boolean {\n\t\tfor(let { data, index, deleteCurrent } of this) {\n\t\t\tif(callback(data, index)) {\n\t\t\t\tif(deleteCurrent()) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\tdeleteIndex(deleteIndex: number): boolean {\n\t\tif(deleteIndex >= this.length || deleteIndex < 0) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Compare on index only so the payload view is never materialized for an index-based delete\n\t\tfor(let item of this) {\n\t\t\tif(item.index === deleteIndex) {\n\t\t\t\tif(item.deleteCurrent()) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\tdeleteValue(deleteValues: number | Array<number>) {\n\t\tif(typeof deleteValues === 'number') {\n\t\t\treturn this.deleteMatch(values => values[0] === deleteValues);\n\t\t} else {\n\t\t\treturn this.deleteMatch(values => {\n\t\t\t\tif(values.length !== deleteValues.length) {\n\t\t\t\t\treturn false;\n\t\t\t\t} else {\n\t\t\t\t\tfor(let i = 0; i < values.length; i++) {\n\t\t\t\t\t\tif(values[i] !== deleteValues[i]) {\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\n\t// Reclaims tombstoned nodes: physically unlinks and frees them. Not safe to run concurrently with insert/delete.\n\tcompact() {\n\t\tlet prevPointer = this.firstBlock.pointer;\n\t\tlet prevView = this.firstBlock.data;\n\t\tlet prevNextIndex = NODE_NEXT_INDEX;\n\t\tlet nextPointer = loadRawPointer(prevView, prevNextIndex);\n\t\twhile(nextPointer) {\n\t\t\tlet position = nextPointer & POSITION_MASK;\n\t\t\tlet memPool = this.memory.buffers[position];\n\t\t\tif(!memPool) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tlet view = this.getBufferView(position)!;\n\t\t\tlet byteOffset = nextPointer >>> POSITION_BIT_COUNT;\n\t\t\tlet nodeIndex = byteOffset >>> 2;\n\t\t\tlet followingPointer = Atomics.load(view, nodeIndex + NODE_NEXT_INDEX);\n\n\t\t\tif(Atomics.load(view, nodeIndex + NODE_DELETED_INDEX)) {\n\t\t\t\t// Skip over the tombstone and free it. onDelete already fired when the node was logically deleted.\n\t\t\t\tstoreRawPointer(prevView, prevNextIndex, followingPointer);\n\t\t\t\tmemPool.free(byteOffset);\n\t\t\t} else {\n\t\t\t\tprevPointer = nextPointer;\n\t\t\t\tprevView = view;\n\t\t\t\tprevNextIndex = nodeIndex + NODE_NEXT_INDEX;\n\t\t\t}\n\n\t\t\tnextPointer = followingPointer;\n\t\t}\n\n\t\t// The last surviving node (or the sentinel when everything is gone) is the new tail\n\t\tstoreRawPointer(this.firstBlock.data, TAIL_INDEX, prevPointer);\n\t}\n\n\tclear() {\n\t\t// Detach the whole chain, then free it. Not safe to run concurrently with insert/delete.\n\t\tlet firstBlockPointer;\n\t\tlet updateWorked = false;\n\t\twhile(!updateWorked) {\n\t\t\tfirstBlockPointer = loadRawPointer(this.firstBlock.data, HEAD_INDEX);\n\t\t\t// Already cleared\n\t\t\tif(!firstBlockPointer) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tupdateWorked = replaceRawPointer(this.firstBlock.data, HEAD_INDEX, 0, firstBlockPointer);\n\t\t}\n\t\t// Shouldn't be possible to hit: making Typescript happy\n\t\tif(!firstBlockPointer) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Reset the tail hint back to the sentinel\n\t\tstoreRawPointer(this.firstBlock.data, TAIL_INDEX, this.firstBlock.pointer);\n\n\t\tlet liveItems = 0;\n\t\tlet nextBlockPointer = firstBlockPointer;\n\t\twhile(nextBlockPointer) {\n\t\t\tlet position = nextBlockPointer & POSITION_MASK;\n\t\t\tlet memPool = this.memory.buffers[position];\n\t\t\t// Short circuit iterations if we can't access memory\n\t\t\tif(!memPool) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tlet view = this.getBufferView(position)!;\n\t\t\tlet byteOffset = nextBlockPointer >>> POSITION_BIT_COUNT;\n\t\t\tlet nodeIndex = byteOffset >>> 2;\n\t\t\tnextBlockPointer = Atomics.load(view, nodeIndex + NODE_NEXT_INDEX);\n\n\t\t\tif(!Atomics.load(view, nodeIndex + NODE_DELETED_INDEX)) {\n\t\t\t\tliveItems++;\n\t\t\t\tif(this.onDelete) {\n\t\t\t\t\tthis.onDelete(this.getDataBlock(view.buffer, byteOffset));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tmemPool.free(byteOffset);\n\t\t}\n\n\t\t// Only subtract the live nodes we detached so a concurrent insert's increment stays accurate\n\t\tAtomics.sub(this.firstBlock.data, LENGTH_INDEX, liveItems);\n\t}\n\n\t*[Symbol.iterator]() {\n\t\tlet currentIndex = 0;\n\t\tlet nextPointer = loadRawPointer(this.firstBlock.data, HEAD_INDEX);\n\t\twhile(nextPointer) {\n\t\t\tlet position = nextPointer & POSITION_MASK;\n\t\t\tlet view = this.getBufferView(position);\n\t\t\t// Short circuit iterations if we can't access memory\n\t\t\tif(!view) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlet byteOffset = nextPointer >>> POSITION_BIT_COUNT;\n\t\t\tlet nodeIndex = byteOffset >>> 2;\n\t\t\tnextPointer = Atomics.load(view, nodeIndex + NODE_NEXT_INDEX);\n\n\t\t\t// Skip tombstoned nodes - they stay linked until a quiescent compact()/clear()\n\t\t\tif(Atomics.load(view, nodeIndex + NODE_DELETED_INDEX)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet buffer = view.buffer;\n\t\t\tlet deletedIndex = nodeIndex + NODE_DELETED_INDEX;\n\t\t\t// Plain object with an eager data view: V8 keeps this monomorphic and fast. A lazy `get data()` accessor is far\n\t\t\t// slower to allocate than the payload view it would save, so pay for the view up front.\n\t\t\tyield {\n\t\t\t\tdata: this.getDataBlock(buffer, byteOffset),\n\t\t\t\tindex: currentIndex,\n\t\t\t\t// Logical delete: tombstone the node and drop length. Returns false if another thread beat us to it.\n\t\t\t\tdeleteCurrent: (): boolean => {\n\t\t\t\t\tif(Atomics.compareExchange(view, deletedIndex, 0, 1) === 0) {\n\t\t\t\t\t\tAtomics.sub(this.firstBlock.data, LENGTH_INDEX, 1);\n\t\t\t\t\t\tif(this.onDelete) {\n\t\t\t\t\t\t\tthis.onDelete(this.getDataBlock(buffer, byteOffset));\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn false;\n\t\t\t\t},\n\t\t\t};\n\n\t\t\tcurrentIndex++;\n\t\t}\n\t}\n\n\tforEach(callback: (data: T) => void) {\n\t\tfor(let value of this) {\n\t\t\tcallback(value.data);\n\t\t}\n\t}\n\n\tgetSharedMemory(): SharedListMemory {\n\t\treturn {\n\t\t\tfirstBlock: this.firstBlock.getSharedMemory(),\n\t\t};\n\t}\n\n\tprivate getBufferView(position: number): Uint32Array | undefined {\n\t\tlet view = this.bufferViews[position];\n\t\tif(view) {\n\t\t\treturn view;\n\t\t}\n\n\t\tlet buffer = this.memory.buffers[position];\n\t\tif(!buffer) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tview = new Uint32Array(buffer.buf);\n\t\tthis.bufferViews[position] = view;\n\t\treturn view;\n\t}\n\n\tprivate getDataBlock(buffer: ArrayBufferLike, nodeByteOffset: number): T {\n\t\tconst startIndex = nodeByteOffset + DATA_BLOCK_BYTE_OFFSET;\n\n\t\tswitch(this.cachedType) {\n\t\t\tcase TYPE.int32:\n\t\t\t\treturn new Int32Array(buffer, startIndex, this.cachedDataLength) as T;\n\t\t\tcase TYPE.uint32:\n\t\t\t\treturn new Uint32Array(buffer, startIndex, this.cachedDataLength) as T;\n\t\t\tcase TYPE.float32:\n\t\t\t\treturn new Float32Array(buffer, startIndex, this.cachedDataLength) as T;\n\t\t\tdefault:\n\t\t\t\tthrow new Error(`Unknown data block type ${this.cachedType}`);\n\t\t}\n\t}\n\n\tfree() {\n\t\tlet { bufferPosition: nextBlockPosition, bufferByteOffset: nextBlockByteOffset } = loadPointer(this.firstBlock.data, HEAD_INDEX);\n\t\twhile(nextBlockByteOffset) {\n\t\t\tlet allocatedMemory = new AllocatedMemory(this.memory, {\n\t\t\t\tbufferPosition: nextBlockPosition,\n\t\t\t\tbufferByteOffset: nextBlockByteOffset,\n\t\t\t});\n\n\t\t\t({ bufferPosition: nextBlockPosition, bufferByteOffset: nextBlockByteOffset } = loadPointer(allocatedMemory.data, NODE_NEXT_INDEX));\n\n\t\t\tif(this.onDelete && !Atomics.load(allocatedMemory.data, NODE_DELETED_INDEX)) {\n\t\t\t\tthis.onDelete(this.getDataBlock(allocatedMemory.data.buffer, allocatedMemory.data.byteOffset));\n\t\t\t}\n\n\t\t\tallocatedMemory.free();\n\t\t}\n\n\t\tthis.firstBlock.free();\n\t}\n}\n\ninterface SharedListConfig<T extends Uint32Array | Int32Array | Float32Array> {\n\tinitWithBlock?: SharedAllocatedMemory\n\ttype?: TypedArrayConstructor<T>\n\tdataLength?: number\n}\ninterface SharedListMemory {\n\tfirstBlock: SharedAllocatedMemory\n}\n\ninterface SharedListIterable<T extends Uint32Array | Int32Array | Float32Array> {\n\tdata: T\n\tindex: number\n\tdeleteCurrent: () => boolean\n}\n\nexport { type SharedListMemory };\n"],"mappings":"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"}
|
package/dist/shared-map.js
CHANGED
|
@@ -38,7 +38,7 @@ var i = 10, a = class a {
|
|
|
38
38
|
t(n.data, s, e.firstBlock.bufferPosition, e.firstBlock.bufferByteOffset);
|
|
39
39
|
} else c = new r(this.memory, { firstBlock: l });
|
|
40
40
|
let u = !0;
|
|
41
|
-
return c.deleteValue(a) && (u = !1), c.insert([a, o]), u;
|
|
41
|
+
return c.deleteValue(a) && (u = !1), c.insert([a, o]), u || c.compact(), u;
|
|
42
42
|
}
|
|
43
43
|
get(t) {
|
|
44
44
|
let n = o(t), i = this.hash(n, this.maxHash), a = e(this.hashMemory.data, i);
|
|
@@ -56,9 +56,12 @@ var i = 10, a = class a {
|
|
|
56
56
|
delete(t) {
|
|
57
57
|
let n = o(t), i = this.hash(n, this.maxHash), a = e(this.hashMemory.data, i);
|
|
58
58
|
if (a.bufferByteOffset === 0) return !1;
|
|
59
|
-
let s = new r(this.memory, { firstBlock: a });
|
|
60
|
-
for (let { data: e, deleteCurrent: t } of s) if (e[0] === n)
|
|
61
|
-
|
|
59
|
+
let s = new r(this.memory, { firstBlock: a }), c = !1;
|
|
60
|
+
for (let { data: e, deleteCurrent: t } of s) if (e[0] === n) {
|
|
61
|
+
t(), Atomics.sub(this.pointerMemory.data, 2, 1), c = !0;
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
return c && s.compact(), c;
|
|
62
65
|
}
|
|
63
66
|
growHashTable() {
|
|
64
67
|
let n = this.maxHash, i = n * 2, a = this.memory.allocUI32(i), o = this.hashMemory;
|
package/dist/shared-map.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared-map.js","names":[],"sources":["../src/shared-map.ts"],"sourcesContent":["import AllocatedMemory, { type SharedAllocatedMemory } from './allocated-memory';\nimport type MemoryHeap from './memory-heap';\nimport SharedList from './shared-list';\nimport { loadPointer, storePointer } from './utils/pointer';\n\n// TODO: Grow hashMemory\n// TODO: Add iterator\n// TODO: Add read/write locks\nconst DEFAULT_HASH_SIZE = 10;\nexport default class SharedMap<K extends string | number> {\n\tstatic readonly ALLOCATE_COUNT = 4;\n\n\tprivate memory: MemoryHeap;\n\t// Memory order: Pointer, Lock, Length, MaxHash\n\tprivate pointerMemory: AllocatedMemory;\n\tprivate lock: Int32Array;\n\tprivate get hashMemory(): AllocatedMemory {\n\t\treturn new AllocatedMemory(this.memory, loadPointer(this.pointerMemory.data, 0));\n\t}\n\n\tget length(): number {\n\t\treturn Atomics.load(this.pointerMemory.data, 2);\n\t}\n\tget maxHash(): number {\n\t\treturn Atomics.load(this.pointerMemory.data, 3);\n\t}\n\n\tconstructor(memory: MemoryHeap, clone?: SharedMapMemory) {\n\t\tthis.memory = memory;\n\n\t\tif(clone) {\n\t\t\tthis.pointerMemory = new AllocatedMemory(memory, clone.firstBlock);\n\t\t} else {\n\t\t\tthis.pointerMemory = memory.allocUI32(SharedMap.ALLOCATE_COUNT);\n\t\t\tlet hashMemory = memory.allocUI32(DEFAULT_HASH_SIZE);\n\t\t\tstorePointer(this.pointerMemory.data, 0, hashMemory.bufferPosition, hashMemory.bufferByteOffset);\n\t\t\tAtomics.store(this.pointerMemory.data, 3, DEFAULT_HASH_SIZE);\n\t\t}\n\t\tthis.lock = new Int32Array(this.pointerMemory.data.buffer, this.pointerMemory.bufferByteOffset + Uint32Array.BYTES_PER_ELEMENT, 1);\n\t}\n\n\tset(key: K, value: number) {\n\t\tif(this.length >= this.maxHash * 2) {\n\t\t\tthis.growHashTable();\n\t\t}\n\n\t\tlet fullHashKey = get32BitHash(key);\n\t\tif(this.setHashKey(this.hashMemory, this.maxHash, fullHashKey, value)) {\n\t\t\tAtomics.add(this.pointerMemory.data, 2, 1);\n\t\t}\n\t}\n\tprivate setHashKey(hashMemory: AllocatedMemory, maxHash: number, fullHashKey: number, value: number) {\n\t\tlet hashKey = this.hash(fullHashKey, maxHash);\n\t\t\n\t\tlet list: SharedList;\n\t\tlet pointer = loadPointer(hashMemory.data, hashKey);\n\t\tif(pointer.bufferByteOffset === 0) {\n\t\t\t// Initialize a list here\n\t\t\tlist = new SharedList(this.memory, {\n\t\t\t\tdataLength: 2,\n\t\t\t});\n\n\t\t\tlet listMemory = list.getSharedMemory();\n\t\t\tstorePointer(hashMemory.data, hashKey, listMemory.firstBlock.bufferPosition, listMemory.firstBlock.bufferByteOffset);\n\t\t} else {\n\t\t\tlist = new SharedList(this.memory, {\n\t\t\t\tfirstBlock: pointer,\n\t\t\t});\n\t\t}\n\n\t\t// Check if any other items in list have the same key and delete them\n\t\tlet inserted = true;\n\t\tif(list.deleteValue(fullHashKey)) {\n\t\t\tinserted = false;\n\t\t}\n\t\tlist.insert([fullHashKey, value]);\n\t\t\n\t\treturn inserted;\n\t}\n\n\tget(key: K): number | undefined {\n\t\tlet fullHashKey = get32BitHash(key);\n\t\tlet hashKey = this.hash(fullHashKey, this.maxHash);\n\n\t\tlet pointer = loadPointer(this.hashMemory.data, hashKey);\n\t\tif(pointer.bufferByteOffset === 0) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tlet list = new SharedList(this.memory, {\n\t\t\tfirstBlock: pointer,\n\t\t});\n\n\t\tfor(let { data } of list) {\n\t\t\tif(data[0] === fullHashKey) {\n\t\t\t\treturn data[1];\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t}\n\thas(key: K): boolean {\n\t\tlet fullHashKey = get32BitHash(key);\n\t\tlet hashKey = this.hash(fullHashKey, this.maxHash);\n\n\t\tlet pointer = loadPointer(this.hashMemory.data, hashKey);\n\t\tif(pointer.bufferByteOffset === 0) {\n\t\t\treturn false;\n\t\t}\n\n\t\tlet list = new SharedList(this.memory, {\n\t\t\tfirstBlock: pointer,\n\t\t});\n\n\t\tfor(let { data } of list) {\n\t\t\tif(data[0] === fullHashKey) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tdelete(key: K): boolean {\n\t\tlet fullHashKey = get32BitHash(key);\n\t\tlet hashKey = this.hash(fullHashKey, this.maxHash);\n\n\t\tlet pointer = loadPointer(this.hashMemory.data, hashKey);\n\t\tif(pointer.bufferByteOffset === 0) {\n\t\t\treturn false;\n\t\t}\n\n\t\tlet list = new SharedList(this.memory, {\n\t\t\tfirstBlock: pointer,\n\t\t});\n\n\t\tfor(let { data, deleteCurrent } of list) {\n\t\t\tif(data[0] === fullHashKey) {\n\t\t\t\tdeleteCurrent();\n\n\t\t\t\tAtomics.sub(this.pointerMemory.data, 2, 1);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tprivate growHashTable() {\n\t\tlet oldMaxHash = this.maxHash;\n\t\tlet newMaxHash = oldMaxHash * 2;\n\t\tlet newHashMemory = this.memory.allocUI32(newMaxHash);\n\t\tlet oldHashMemory = this.hashMemory;\n\n\t\t// Copy each old hash value into new hash memory\n\t\tfor(let i = 0; i < oldMaxHash; i++) {\n\t\t\tlet pointer = loadPointer(oldHashMemory.data, i);\n\t\t\tif(pointer.bufferByteOffset === 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet list = new SharedList(this.memory, {\n\t\t\t\tfirstBlock: pointer,\n\t\t\t});\n\t\t\tfor(let { data } of list) {\n\t\t\t\tthis.setHashKey(newHashMemory, newMaxHash, data[0], data[1]);\n\t\t\t}\n\n\t\t\t// The entries have been copied into freshly allocated lists in the new hash table so the old list can be freed\n\t\t\tlist.free();\n\t\t}\n\n\t\tstorePointer(this.pointerMemory.data, 0, newHashMemory.bufferPosition, newHashMemory.bufferByteOffset);\n\t\tAtomics.store(this.pointerMemory.data, 3, newMaxHash);\n\n\t\t// pointerMemory now references newHashMemory so the old hash table memory can be freed\n\t\toldHashMemory.free();\n\t}\n\n\tprivate hash(key: number, maxHash: number) {\n\t\treturn key % maxHash;\n\t}\n\n\tfree() {\n\t\t// Loop through and free lists in hash table first\n\t\tfor(let i = 0; i < this.maxHash; i++) {\n\t\t\tlet pointer = loadPointer(this.hashMemory.data, i);\n\t\t\tif(pointer.bufferByteOffset === 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet list = new SharedList(this.memory, {\n\t\t\t\tfirstBlock: pointer,\n\t\t\t});\n\t\t\tlist.free();\n\t\t}\n\n\t\tthis.hashMemory.free();\n\t\tthis.pointerMemory.free();\n\t}\n\n\tgetSharedMemory(): SharedMapMemory {\n\t\treturn {\n\t\t\tfirstBlock: this.pointerMemory.getSharedMemory(),\n\t\t};\n\t}\n}\n\ninterface SharedMapMemory {\n\tfirstBlock: SharedAllocatedMemory\n}\n\nfunction get32BitHash<K extends string | number>(key: K): number {\n\tif(typeof key === 'number') {\n\t\treturn key;\n\t} else if(typeof key === 'string') {\n\t\treturn hashString(key);\n\t} else {\n\t\treturn key;\n\t}\n}\n\n// Copied from https://github.com/mmomtchev/SharedMap/blob/master/index.js - MurmurHash2\nfunction hashString(str: string): number {\n\tlet\n\t\tl = str.length,\n\t\th = 17 ^ l,\n\t\ti = 0,\n\t\tk;\n\twhile(l >= 4) {\n\t\tk =\n\t\t\t((str.charCodeAt(i) & 0xff)) |\n\t\t\t((str.charCodeAt(++i) & 0xff) << 8) |\n\t\t\t((str.charCodeAt(++i) & 0xff) << 16) |\n\t\t\t((str.charCodeAt(++i) & 0xff) << 14);\n\t\tk = (((k & 0xffff) * 0x5bd1e995) + ((((k >>> 16) * 0x5bd1e995) & 0xffff) << 16));\n\t\tk ^= k >>> 14;\n\t\tk = (((k & 0xffff) * 0x5bd1e995) + ((((k >>> 16) * 0x5bd1e995) & 0xffff) << 16));\n\t\th = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16)) ^ k;\n\t\tl -= 4;\n\t\t++i;\n\t}\n\t/* eslint-disable no-fallthrough */\n\tswitch(l) {\n\t\tcase 3: h ^= (str.charCodeAt(i + 2) & 0xff) << 16;\n\t\tcase 2: h ^= (str.charCodeAt(i + 1) & 0xff) << 8;\n\t\tcase 1: h ^= (str.charCodeAt(i) & 0xff);\n\t\t\th = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16));\n\t}\n\t/* eslint-enable no-fallthrough */\n\th ^= h >>> 13;\n\th = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16));\n\th ^= h >>> 15;\n\th = h >>> 0;\n\treturn h;\n}\n\nexport type { SharedMapMemory };"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"shared-map.js","names":[],"sources":["../src/shared-map.ts"],"sourcesContent":["import AllocatedMemory, { type SharedAllocatedMemory } from './allocated-memory';\nimport type MemoryHeap from './memory-heap';\nimport SharedList from './shared-list';\nimport { loadPointer, storePointer } from './utils/pointer';\n\n// TODO: Grow hashMemory\n// TODO: Add iterator\n// TODO: Add read/write locks\nconst DEFAULT_HASH_SIZE = 10;\nexport default class SharedMap<K extends string | number> {\n\tstatic readonly ALLOCATE_COUNT = 4;\n\n\tprivate memory: MemoryHeap;\n\t// Memory order: Pointer, Lock, Length, MaxHash\n\tprivate pointerMemory: AllocatedMemory;\n\tprivate lock: Int32Array;\n\tprivate get hashMemory(): AllocatedMemory {\n\t\treturn new AllocatedMemory(this.memory, loadPointer(this.pointerMemory.data, 0));\n\t}\n\n\tget length(): number {\n\t\treturn Atomics.load(this.pointerMemory.data, 2);\n\t}\n\tget maxHash(): number {\n\t\treturn Atomics.load(this.pointerMemory.data, 3);\n\t}\n\n\tconstructor(memory: MemoryHeap, clone?: SharedMapMemory) {\n\t\tthis.memory = memory;\n\n\t\tif(clone) {\n\t\t\tthis.pointerMemory = new AllocatedMemory(memory, clone.firstBlock);\n\t\t} else {\n\t\t\tthis.pointerMemory = memory.allocUI32(SharedMap.ALLOCATE_COUNT);\n\t\t\tlet hashMemory = memory.allocUI32(DEFAULT_HASH_SIZE);\n\t\t\tstorePointer(this.pointerMemory.data, 0, hashMemory.bufferPosition, hashMemory.bufferByteOffset);\n\t\t\tAtomics.store(this.pointerMemory.data, 3, DEFAULT_HASH_SIZE);\n\t\t}\n\t\tthis.lock = new Int32Array(this.pointerMemory.data.buffer, this.pointerMemory.bufferByteOffset + Uint32Array.BYTES_PER_ELEMENT, 1);\n\t}\n\n\tset(key: K, value: number) {\n\t\tif(this.length >= this.maxHash * 2) {\n\t\t\tthis.growHashTable();\n\t\t}\n\n\t\tlet fullHashKey = get32BitHash(key);\n\t\tif(this.setHashKey(this.hashMemory, this.maxHash, fullHashKey, value)) {\n\t\t\tAtomics.add(this.pointerMemory.data, 2, 1);\n\t\t}\n\t}\n\tprivate setHashKey(hashMemory: AllocatedMemory, maxHash: number, fullHashKey: number, value: number) {\n\t\tlet hashKey = this.hash(fullHashKey, maxHash);\n\t\t\n\t\tlet list: SharedList;\n\t\tlet pointer = loadPointer(hashMemory.data, hashKey);\n\t\tif(pointer.bufferByteOffset === 0) {\n\t\t\t// Initialize a list here\n\t\t\tlist = new SharedList(this.memory, {\n\t\t\t\tdataLength: 2,\n\t\t\t});\n\n\t\t\tlet listMemory = list.getSharedMemory();\n\t\t\tstorePointer(hashMemory.data, hashKey, listMemory.firstBlock.bufferPosition, listMemory.firstBlock.bufferByteOffset);\n\t\t} else {\n\t\t\tlist = new SharedList(this.memory, {\n\t\t\t\tfirstBlock: pointer,\n\t\t\t});\n\t\t}\n\n\t\t// Check if any other items in list have the same key and delete them\n\t\tlet inserted = true;\n\t\tif(list.deleteValue(fullHashKey)) {\n\t\t\tinserted = false;\n\t\t}\n\t\tlist.insert([fullHashKey, value]);\n\t\t// An overwrite leaves a tombstone behind - reclaim it so buckets don't grow on repeated updates\n\t\tif(!inserted) {\n\t\t\tlist.compact();\n\t\t}\n\n\t\treturn inserted;\n\t}\n\n\tget(key: K): number | undefined {\n\t\tlet fullHashKey = get32BitHash(key);\n\t\tlet hashKey = this.hash(fullHashKey, this.maxHash);\n\n\t\tlet pointer = loadPointer(this.hashMemory.data, hashKey);\n\t\tif(pointer.bufferByteOffset === 0) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tlet list = new SharedList(this.memory, {\n\t\t\tfirstBlock: pointer,\n\t\t});\n\n\t\tfor(let { data } of list) {\n\t\t\tif(data[0] === fullHashKey) {\n\t\t\t\treturn data[1];\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t}\n\thas(key: K): boolean {\n\t\tlet fullHashKey = get32BitHash(key);\n\t\tlet hashKey = this.hash(fullHashKey, this.maxHash);\n\n\t\tlet pointer = loadPointer(this.hashMemory.data, hashKey);\n\t\tif(pointer.bufferByteOffset === 0) {\n\t\t\treturn false;\n\t\t}\n\n\t\tlet list = new SharedList(this.memory, {\n\t\t\tfirstBlock: pointer,\n\t\t});\n\n\t\tfor(let { data } of list) {\n\t\t\tif(data[0] === fullHashKey) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tdelete(key: K): boolean {\n\t\tlet fullHashKey = get32BitHash(key);\n\t\tlet hashKey = this.hash(fullHashKey, this.maxHash);\n\n\t\tlet pointer = loadPointer(this.hashMemory.data, hashKey);\n\t\tif(pointer.bufferByteOffset === 0) {\n\t\t\treturn false;\n\t\t}\n\n\t\tlet list = new SharedList(this.memory, {\n\t\t\tfirstBlock: pointer,\n\t\t});\n\n\t\tlet deleted = false;\n\t\tfor(let { data, deleteCurrent } of list) {\n\t\t\tif(data[0] === fullHashKey) {\n\t\t\t\tdeleteCurrent();\n\n\t\t\t\tAtomics.sub(this.pointerMemory.data, 2, 1);\n\t\t\t\tdeleted = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// compact only after iteration finishes - unlinking/freeing mid-iteration would corrupt the walk\n\t\tif(deleted) {\n\t\t\tlist.compact();\n\t\t}\n\n\t\treturn deleted;\n\t}\n\n\tprivate growHashTable() {\n\t\tlet oldMaxHash = this.maxHash;\n\t\tlet newMaxHash = oldMaxHash * 2;\n\t\tlet newHashMemory = this.memory.allocUI32(newMaxHash);\n\t\tlet oldHashMemory = this.hashMemory;\n\n\t\t// Copy each old hash value into new hash memory\n\t\tfor(let i = 0; i < oldMaxHash; i++) {\n\t\t\tlet pointer = loadPointer(oldHashMemory.data, i);\n\t\t\tif(pointer.bufferByteOffset === 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet list = new SharedList(this.memory, {\n\t\t\t\tfirstBlock: pointer,\n\t\t\t});\n\t\t\tfor(let { data } of list) {\n\t\t\t\tthis.setHashKey(newHashMemory, newMaxHash, data[0], data[1]);\n\t\t\t}\n\n\t\t\t// The entries have been copied into freshly allocated lists in the new hash table so the old list can be freed\n\t\t\tlist.free();\n\t\t}\n\n\t\tstorePointer(this.pointerMemory.data, 0, newHashMemory.bufferPosition, newHashMemory.bufferByteOffset);\n\t\tAtomics.store(this.pointerMemory.data, 3, newMaxHash);\n\n\t\t// pointerMemory now references newHashMemory so the old hash table memory can be freed\n\t\toldHashMemory.free();\n\t}\n\n\tprivate hash(key: number, maxHash: number) {\n\t\treturn key % maxHash;\n\t}\n\n\tfree() {\n\t\t// Loop through and free lists in hash table first\n\t\tfor(let i = 0; i < this.maxHash; i++) {\n\t\t\tlet pointer = loadPointer(this.hashMemory.data, i);\n\t\t\tif(pointer.bufferByteOffset === 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet list = new SharedList(this.memory, {\n\t\t\t\tfirstBlock: pointer,\n\t\t\t});\n\t\t\tlist.free();\n\t\t}\n\n\t\tthis.hashMemory.free();\n\t\tthis.pointerMemory.free();\n\t}\n\n\tgetSharedMemory(): SharedMapMemory {\n\t\treturn {\n\t\t\tfirstBlock: this.pointerMemory.getSharedMemory(),\n\t\t};\n\t}\n}\n\ninterface SharedMapMemory {\n\tfirstBlock: SharedAllocatedMemory\n}\n\nfunction get32BitHash<K extends string | number>(key: K): number {\n\tif(typeof key === 'number') {\n\t\treturn key;\n\t} else if(typeof key === 'string') {\n\t\treturn hashString(key);\n\t} else {\n\t\treturn key;\n\t}\n}\n\n// Copied from https://github.com/mmomtchev/SharedMap/blob/master/index.js - MurmurHash2\nfunction hashString(str: string): number {\n\tlet\n\t\tl = str.length,\n\t\th = 17 ^ l,\n\t\ti = 0,\n\t\tk;\n\twhile(l >= 4) {\n\t\tk =\n\t\t\t((str.charCodeAt(i) & 0xff)) |\n\t\t\t((str.charCodeAt(++i) & 0xff) << 8) |\n\t\t\t((str.charCodeAt(++i) & 0xff) << 16) |\n\t\t\t((str.charCodeAt(++i) & 0xff) << 14);\n\t\tk = (((k & 0xffff) * 0x5bd1e995) + ((((k >>> 16) * 0x5bd1e995) & 0xffff) << 16));\n\t\tk ^= k >>> 14;\n\t\tk = (((k & 0xffff) * 0x5bd1e995) + ((((k >>> 16) * 0x5bd1e995) & 0xffff) << 16));\n\t\th = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16)) ^ k;\n\t\tl -= 4;\n\t\t++i;\n\t}\n\t/* eslint-disable no-fallthrough */\n\tswitch(l) {\n\t\tcase 3: h ^= (str.charCodeAt(i + 2) & 0xff) << 16;\n\t\tcase 2: h ^= (str.charCodeAt(i + 1) & 0xff) << 8;\n\t\tcase 1: h ^= (str.charCodeAt(i) & 0xff);\n\t\t\th = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16));\n\t}\n\t/* eslint-enable no-fallthrough */\n\th ^= h >>> 13;\n\th = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16));\n\th ^= h >>> 15;\n\th = h >>> 0;\n\treturn h;\n}\n\nexport type { SharedMapMemory };"],"mappings":";;;;AAQA,IAAM,IAAoB,IACL,IAArB,MAAqB,EAAqC;CACzD,OAAgB,iBAAiB;CAEjC;CAEA;CACA;CACA,IAAY,aAA8B;EACzC,OAAO,IAAI,EAAgB,KAAK,QAAQ,EAAY,KAAK,cAAc,MAAM,CAAC,CAAC;CAChF;CAEA,IAAI,SAAiB;EACpB,OAAO,QAAQ,KAAK,KAAK,cAAc,MAAM,CAAC;CAC/C;CACA,IAAI,UAAkB;EACrB,OAAO,QAAQ,KAAK,KAAK,cAAc,MAAM,CAAC;CAC/C;CAEA,YAAY,GAAoB,GAAyB;EAGxD,IAFA,KAAK,SAAS,GAEX,GACF,KAAK,gBAAgB,IAAI,EAAgB,GAAQ,EAAM,UAAU;OAC3D;GACN,KAAK,gBAAgB,EAAO,UAAU,EAAU,cAAc;GAC9D,IAAI,IAAa,EAAO,UAAU,CAAiB;GAEnD,AADA,EAAa,KAAK,cAAc,MAAM,GAAG,EAAW,gBAAgB,EAAW,gBAAgB,GAC/F,QAAQ,MAAM,KAAK,cAAc,MAAM,GAAG,CAAiB;EAC5D;EACA,KAAK,OAAO,IAAI,WAAW,KAAK,cAAc,KAAK,QAAQ,KAAK,cAAc,mBAAmB,YAAY,mBAAmB,CAAC;CAClI;CAEA,IAAI,GAAQ,GAAe;EAC1B,AAAG,KAAK,UAAU,KAAK,UAAU,KAChC,KAAK,cAAc;EAGpB,IAAI,IAAc,EAAa,CAAG;EAClC,AAAG,KAAK,WAAW,KAAK,YAAY,KAAK,SAAS,GAAa,CAAK,KACnE,QAAQ,IAAI,KAAK,cAAc,MAAM,GAAG,CAAC;CAE3C;CACA,WAAmB,GAA6B,GAAiB,GAAqB,GAAe;EACpG,IAAI,IAAU,KAAK,KAAK,GAAa,CAAO,GAExC,GACA,IAAU,EAAY,EAAW,MAAM,CAAO;EAClD,IAAG,EAAQ,qBAAqB,GAAG;GAElC,IAAO,IAAI,EAAW,KAAK,QAAQ,EAClC,YAAY,EACb,CAAC;GAED,IAAI,IAAa,EAAK,gBAAgB;GACtC,EAAa,EAAW,MAAM,GAAS,EAAW,WAAW,gBAAgB,EAAW,WAAW,gBAAgB;EACpH,OACC,IAAO,IAAI,EAAW,KAAK,QAAQ,EAClC,YAAY,EACb,CAAC;EAIF,IAAI,IAAW;EAUf,OATG,EAAK,YAAY,CAAW,MAC9B,IAAW,KAEZ,EAAK,OAAO,CAAC,GAAa,CAAK,CAAC,GAE5B,KACH,EAAK,QAAQ,GAGP;CACR;CAEA,IAAI,GAA4B;EAC/B,IAAI,IAAc,EAAa,CAAG,GAC9B,IAAU,KAAK,KAAK,GAAa,KAAK,OAAO,GAE7C,IAAU,EAAY,KAAK,WAAW,MAAM,CAAO;EACvD,IAAG,EAAQ,qBAAqB,GAC/B;EAGD,IAAI,IAAO,IAAI,EAAW,KAAK,QAAQ,EACtC,YAAY,EACb,CAAC;EAED,KAAI,IAAI,EAAE,aAAU,GACnB,IAAG,EAAK,OAAO,GACd,OAAO,EAAK;CAKf;CACA,IAAI,GAAiB;EACpB,IAAI,IAAc,EAAa,CAAG,GAC9B,IAAU,KAAK,KAAK,GAAa,KAAK,OAAO,GAE7C,IAAU,EAAY,KAAK,WAAW,MAAM,CAAO;EACvD,IAAG,EAAQ,qBAAqB,GAC/B,OAAO;EAGR,IAAI,IAAO,IAAI,EAAW,KAAK,QAAQ,EACtC,YAAY,EACb,CAAC;EAED,KAAI,IAAI,EAAE,aAAU,GACnB,IAAG,EAAK,OAAO,GACd,OAAO;EAIT,OAAO;CACR;CAEA,OAAO,GAAiB;EACvB,IAAI,IAAc,EAAa,CAAG,GAC9B,IAAU,KAAK,KAAK,GAAa,KAAK,OAAO,GAE7C,IAAU,EAAY,KAAK,WAAW,MAAM,CAAO;EACvD,IAAG,EAAQ,qBAAqB,GAC/B,OAAO;EAGR,IAAI,IAAO,IAAI,EAAW,KAAK,QAAQ,EACtC,YAAY,EACb,CAAC,GAEG,IAAU;EACd,KAAI,IAAI,EAAE,SAAM,sBAAmB,GAClC,IAAG,EAAK,OAAO,GAAa;GAI3B,AAHA,EAAc,GAEd,QAAQ,IAAI,KAAK,cAAc,MAAM,GAAG,CAAC,GACzC,IAAU;GACV;EACD;EAQD,OAJG,KACF,EAAK,QAAQ,GAGP;CACR;CAEA,gBAAwB;EACvB,IAAI,IAAa,KAAK,SAClB,IAAa,IAAa,GAC1B,IAAgB,KAAK,OAAO,UAAU,CAAU,GAChD,IAAgB,KAAK;EAGzB,KAAI,IAAI,IAAI,GAAG,IAAI,GAAY,KAAK;GACnC,IAAI,IAAU,EAAY,EAAc,MAAM,CAAC;GAC/C,IAAG,EAAQ,qBAAqB,GAC/B;GAGD,IAAI,IAAO,IAAI,EAAW,KAAK,QAAQ,EACtC,YAAY,EACb,CAAC;GACD,KAAI,IAAI,EAAE,aAAU,GACnB,KAAK,WAAW,GAAe,GAAY,EAAK,IAAI,EAAK,EAAE;GAI5D,EAAK,KAAK;EACX;EAMA,AAJA,EAAa,KAAK,cAAc,MAAM,GAAG,EAAc,gBAAgB,EAAc,gBAAgB,GACrG,QAAQ,MAAM,KAAK,cAAc,MAAM,GAAG,CAAU,GAGpD,EAAc,KAAK;CACpB;CAEA,KAAa,GAAa,GAAiB;EAC1C,OAAO,IAAM;CACd;CAEA,OAAO;EAEN,KAAI,IAAI,IAAI,GAAG,IAAI,KAAK,SAAS,KAAK;GACrC,IAAI,IAAU,EAAY,KAAK,WAAW,MAAM,CAAC;GAC9C,EAAQ,qBAAqB,KAOhC,IAHe,EAAW,KAAK,QAAQ,EACtC,YAAY,EACb,CACA,CAAA,CAAK,KAAK;EACX;EAGA,AADA,KAAK,WAAW,KAAK,GACrB,KAAK,cAAc,KAAK;CACzB;CAEA,kBAAmC;EAClC,OAAO,EACN,YAAY,KAAK,cAAc,gBAAgB,EAChD;CACD;AACD;AAMA,SAAS,EAAwC,GAAgB;CAM/D,OALE,OAAO,KAAQ,WACV,IACE,OAAO,KAAQ,WACjB,EAAW,CAAG,IAEd;AAET;AAGA,SAAS,EAAW,GAAqB;CACxC,IACC,IAAI,EAAI,QACR,IAAI,KAAK,GACT,IAAI,GACJ;CACD,OAAM,KAAK,IAWV,AAVA,IACG,EAAI,WAAW,CAAC,IAAI,OACpB,EAAI,WAAW,EAAE,CAAC,IAAI,QAAS,KAC/B,EAAI,WAAW,EAAE,CAAC,IAAI,QAAS,MAC/B,EAAI,WAAW,EAAE,CAAC,IAAI,QAAS,IAClC,KAAO,IAAI,SAAU,gBAAkB,MAAM,MAAM,aAAc,UAAW,KAC5E,KAAK,MAAM,IACX,KAAO,IAAI,SAAU,gBAAkB,MAAM,MAAM,aAAc,UAAW,KAC5E,KAAO,IAAI,SAAU,gBAAkB,MAAM,MAAM,aAAc,UAAW,MAAO,GACnF,KAAK,GACL,EAAE;CAGH,QAAO,GAAP;EACC,KAAK,GAAG,MAAM,EAAI,WAAW,IAAI,CAAC,IAAI,QAAS;EAC/C,KAAK,GAAG,MAAM,EAAI,WAAW,IAAI,CAAC,IAAI,QAAS;EAC/C,KAAK,GACJ,AADO,KAAM,EAAI,WAAW,CAAC,IAAI,KACjC,KAAO,IAAI,SAAU,gBAAkB,MAAM,MAAM,aAAc,UAAW;CAC9E;CAMA,OAJA,KAAK,MAAM,IACX,KAAO,IAAI,SAAU,gBAAkB,MAAM,MAAM,aAAc,UAAW,KAC5E,KAAK,MAAM,IACX,OAAU,GACH;AACR"}
|
|
@@ -8,6 +8,7 @@ export default abstract class SharedPointerList<T extends PointerItem> implement
|
|
|
8
8
|
get length(): number;
|
|
9
9
|
insert(item: T): void;
|
|
10
10
|
delete(item: T): boolean;
|
|
11
|
+
compact(): void;
|
|
11
12
|
[Symbol.iterator](): Generator<T, void, unknown>;
|
|
12
13
|
forEach(callback: (item: T) => void): void;
|
|
13
14
|
find(callback: (item: T) => boolean): T | undefined;
|
|
@@ -15,7 +15,11 @@ var i = class {
|
|
|
15
15
|
this.list.insert(e(t.memory.bufferPosition, t.memory.bufferByteOffset));
|
|
16
16
|
}
|
|
17
17
|
delete(t) {
|
|
18
|
-
|
|
18
|
+
let n = this.list.deleteValue(e(t.memory.bufferPosition, t.memory.bufferByteOffset));
|
|
19
|
+
return this.list.compact(), n;
|
|
20
|
+
}
|
|
21
|
+
compact() {
|
|
22
|
+
this.list.compact();
|
|
19
23
|
}
|
|
20
24
|
*[Symbol.iterator]() {
|
|
21
25
|
let e = this.list[Symbol.iterator]();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared-pointer-list.js","names":[],"sources":["../src/shared-pointer-list.ts"],"sourcesContent":["import AllocatedMemory from './allocated-memory';\nimport type MemoryHeap from './memory-heap';\nimport type { SharedListMemory } from './shared-list';\nimport SharedList from './shared-list';\nimport { createPointer, loadPointer } from './utils/pointer';\n\nexport default abstract class SharedPointerList<T extends PointerItem> implements Iterable<T> {\n\tprivate memory: MemoryHeap;\n\tprivate list: SharedList;\n\n\tconstructor(heap: MemoryHeap, memory?: SharedListMemory) {\n\t\tthis.memory = heap;\n\n\t\tif(memory) {\n\t\t\tthis.list = new SharedList(heap, memory);\n\t\t} else {\n\t\t\tthis.list = new SharedList(heap);\n\t\t}\n\t}\n\n\tget length() {\n\t\treturn this.list.length;\n\t}\n\n\tinsert(item: T) {\n\t\tthis.list.insert(createPointer(item.memory.bufferPosition, item.memory.bufferByteOffset));\n\t}\n\tdelete(item: T) {\n\t\
|
|
1
|
+
{"version":3,"file":"shared-pointer-list.js","names":[],"sources":["../src/shared-pointer-list.ts"],"sourcesContent":["import AllocatedMemory from './allocated-memory';\nimport type MemoryHeap from './memory-heap';\nimport type { SharedListMemory } from './shared-list';\nimport SharedList from './shared-list';\nimport { createPointer, loadPointer } from './utils/pointer';\n\nexport default abstract class SharedPointerList<T extends PointerItem> implements Iterable<T> {\n\tprivate memory: MemoryHeap;\n\tprivate list: SharedList;\n\n\tconstructor(heap: MemoryHeap, memory?: SharedListMemory) {\n\t\tthis.memory = heap;\n\n\t\tif(memory) {\n\t\t\tthis.list = new SharedList(heap, memory);\n\t\t} else {\n\t\t\tthis.list = new SharedList(heap);\n\t\t}\n\t}\n\n\tget length() {\n\t\treturn this.list.length;\n\t}\n\n\tinsert(item: T) {\n\t\tthis.list.insert(createPointer(item.memory.bufferPosition, item.memory.bufferByteOffset));\n\t}\n\tdelete(item: T) {\n\t\tlet deleted = this.list.deleteValue(createPointer(item.memory.bufferPosition, item.memory.bufferByteOffset));\n\t\t// This wrapper is single-threaded per instance, so reclaim the tombstoned node right away\n\t\tthis.list.compact();\n\t\treturn deleted;\n\t}\n\n\tcompact() {\n\t\tthis.list.compact();\n\t}\n\n\t*[Symbol.iterator]() {\n\t\tlet iterator = this.list[Symbol.iterator]();\n\n\t\tfor(let { data: pointerData } of iterator) {\n\t\t\tlet { bufferPosition, bufferByteOffset } = loadPointer(pointerData, 0);\n\t\t\tlet allocatedMemory = new AllocatedMemory(this.memory, {\n\t\t\t\tbufferPosition,\n\t\t\t\tbufferByteOffset,\n\t\t\t});\n\t\t\tyield this.createItem(allocatedMemory);\n\t\t}\n\t}\n\tforEach(callback: (item: T) => void) {\n\t\tfor(let value of this) {\n\t\t\tcallback(value);\n\t\t}\n\t}\n\n\tfind(callback: (item: T) => boolean): T | undefined {\n\t\tfor(let value of this) {\n\t\t\tif(callback(value)) {\n\t\t\t\treturn value;\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\tgetSharedMemory() {\n\t\treturn this.list.getSharedMemory();\n\t}\n\n\tprotected abstract createItem(allocatedMemory: AllocatedMemory): T;\n\n\tfree() {\n\t\tfor(let item of this) {\n\t\t\t// NOTE: Anything that allocates it's own memory (ie: type for Item) needs to call free for that class so we can clear all memory recursively\n\t\t\tif('free' in item && typeof item.free === 'function') {\n\t\t\t\titem.free();\n\t\t\t} else {\n\t\t\t\titem.memory.free();\n\t\t\t}\n\t\t}\n\n\t\tthis.list.free();\n\t}\n}\n\ninterface PointerItem {\n\treadonly memory: AllocatedMemory\n}"],"mappings":";;;;AAMA,IAA8B,IAA9B,MAA8F;CAC7F;CACA;CAEA,YAAY,GAAkB,GAA2B;EAGxD,AAFA,KAAK,SAAS,GAEd,AAGC,KAAK,OAHH,IACU,IAAI,EAAW,GAAM,CAAM,IAE3B,IAAI,EAAW,CAAI;CAEjC;CAEA,IAAI,SAAS;EACZ,OAAO,KAAK,KAAK;CAClB;CAEA,OAAO,GAAS;EACf,KAAK,KAAK,OAAO,EAAc,EAAK,OAAO,gBAAgB,EAAK,OAAO,gBAAgB,CAAC;CACzF;CACA,OAAO,GAAS;EACf,IAAI,IAAU,KAAK,KAAK,YAAY,EAAc,EAAK,OAAO,gBAAgB,EAAK,OAAO,gBAAgB,CAAC;EAG3G,OADA,KAAK,KAAK,QAAQ,GACX;CACR;CAEA,UAAU;EACT,KAAK,KAAK,QAAQ;CACnB;CAEA,EAAE,OAAO,YAAY;EACpB,IAAI,IAAW,KAAK,KAAK,OAAO,SAAS,CAAC;EAE1C,KAAI,IAAI,EAAE,MAAM,OAAiB,GAAU;GAC1C,IAAI,EAAE,mBAAgB,wBAAqB,EAAY,GAAa,CAAC,GACjE,IAAkB,IAAI,EAAgB,KAAK,QAAQ;IACtD;IACA;GACD,CAAC;GACD,MAAM,KAAK,WAAW,CAAe;EACtC;CACD;CACA,QAAQ,GAA6B;EACpC,KAAI,IAAI,KAAS,MAChB,EAAS,CAAK;CAEhB;CAEA,KAAK,GAA+C;EACnD,KAAI,IAAI,KAAS,MAChB,IAAG,EAAS,CAAK,GAChB,OAAO;CAKV;CAEA,kBAAkB;EACjB,OAAO,KAAK,KAAK,gBAAgB;CAClC;CAIA,OAAO;EACN,KAAI,IAAI,KAAQ,MAEf,AAAG,UAAU,KAAQ,OAAO,EAAK,QAAS,aACzC,EAAK,KAAK,IAEV,EAAK,OAAO,KAAK;EAInB,KAAK,KAAK,KAAK;CAChB;AACD"}
|
package/dist/shared-pool.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export default class SharedPool<T extends Uint32Array | Int32Array | Float32Arra
|
|
|
9
9
|
private growLock;
|
|
10
10
|
private pointerStack;
|
|
11
11
|
private recycleStack;
|
|
12
|
+
readonly maxLength: number;
|
|
12
13
|
private cachedFullDataBlock;
|
|
13
14
|
private readonly cachedMaxChunkSize;
|
|
14
15
|
private readonly cachedDataLength;
|
|
@@ -23,6 +24,7 @@ export default class SharedPool<T extends Uint32Array | Int32Array | Float32Arra
|
|
|
23
24
|
private set dataLength(value);
|
|
24
25
|
get bufferLength(): number;
|
|
25
26
|
get byteMultipler(): number;
|
|
27
|
+
get usedMemory(): number;
|
|
26
28
|
constructor(memory: MemoryHeap, config?: SharedPoolConfig<T> | SharedPoolMemory);
|
|
27
29
|
at(index: number): T;
|
|
28
30
|
get(index: number, dataIndex?: number): number;
|
|
@@ -37,6 +39,8 @@ export default class SharedPool<T extends Uint32Array | Int32Array | Float32Arra
|
|
|
37
39
|
}
|
|
38
40
|
interface SharedPoolConfig<T extends Uint32Array | Int32Array | Float32Array | Float64Array> {
|
|
39
41
|
maxChunkSize?: number;
|
|
42
|
+
maxLength?: number;
|
|
43
|
+
maxRecycledLength?: number;
|
|
40
44
|
type?: TypedArrayConstructor<T>;
|
|
41
45
|
dataLength?: number;
|
|
42
46
|
}
|
package/dist/shared-pool.js
CHANGED
|
@@ -3,7 +3,7 @@ import r from "./allocated-memory.js";
|
|
|
3
3
|
import { lock as i, unlock as a } from "./lock/simple-lock.js";
|
|
4
4
|
import o from "./shared-stack.js";
|
|
5
5
|
//#region src/shared-pool.ts
|
|
6
|
-
var s = 0, c = 1, l = 2, u = 3, d = 4, f = 5, p = class
|
|
6
|
+
var s = 0, c = 1, l = 2, u = 3, d = 4, f = 5, p = 100, m = 1e6, h = 1e5, g = class g {
|
|
7
7
|
static ALLOCATE_COUNT = 6;
|
|
8
8
|
memory;
|
|
9
9
|
firstBlock;
|
|
@@ -11,6 +11,7 @@ var s = 0, c = 1, l = 2, u = 3, d = 4, f = 5, p = class p {
|
|
|
11
11
|
growLock;
|
|
12
12
|
pointerStack;
|
|
13
13
|
recycleStack;
|
|
14
|
+
maxLength;
|
|
14
15
|
cachedFullDataBlock = {};
|
|
15
16
|
cachedMaxChunkSize;
|
|
16
17
|
cachedDataLength;
|
|
@@ -43,24 +44,30 @@ var s = 0, c = 1, l = 2, u = 3, d = 4, f = 5, p = class p {
|
|
|
43
44
|
get byteMultipler() {
|
|
44
45
|
return this.cachedByteMultipler;
|
|
45
46
|
}
|
|
47
|
+
get usedMemory() {
|
|
48
|
+
let t = this.firstBlock.usedMemory;
|
|
49
|
+
for (let n of this.pointerStack) t += new r(this.memory, e(n)).usedMemory;
|
|
50
|
+
return t + this.pointerStack.usedMemory + this.recycleStack.usedMemory;
|
|
51
|
+
}
|
|
46
52
|
constructor(i, a) {
|
|
47
53
|
if (this.memory = i, a && "firstBlock" in a) this.firstBlock = new r(i, a.firstBlock), this.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + c * Uint32Array.BYTES_PER_ELEMENT, 2), this.pointerStack = new o(i, { firstBlock: e(t(this.firstBlock.data, d)) }), this.recycleStack = new o(i, { firstBlock: e(t(this.firstBlock.data, f)) });
|
|
48
54
|
else {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
55
|
+
let e = a?.dataLength ?? 1, t = a?.maxChunkSize ?? p, r = a?.maxLength ?? m, s = a?.maxRecycledLength ?? h, l = o.getMaxSegmentLength(i.maxAllocationLength), u = r / t, _ = o.getAllocateCount(u, void 0, l), v = o.getAllocateCount(s, void 0, l);
|
|
56
|
+
this.firstBlock = i.allocUI32(g.ALLOCATE_COUNT + _ + v), this.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + c * Uint32Array.BYTES_PER_ELEMENT, 2), this.pointerStack = new o(i, {
|
|
57
|
+
firstBlock: this.firstBlock.getSubAllocation(g.ALLOCATE_COUNT, _),
|
|
58
|
+
type: Uint32Array,
|
|
59
|
+
maxLength: u
|
|
54
60
|
}), n(this.firstBlock.data, d, this.pointerStack.pointer), this.recycleStack = new o(i, {
|
|
55
|
-
firstBlock: this.firstBlock.getSubAllocation(
|
|
56
|
-
type: Uint32Array
|
|
61
|
+
firstBlock: this.firstBlock.getSubAllocation(g.ALLOCATE_COUNT + _, v),
|
|
62
|
+
type: Uint32Array,
|
|
63
|
+
maxLength: s
|
|
57
64
|
}), n(this.firstBlock.data, f, this.recycleStack.pointer);
|
|
58
|
-
let
|
|
59
|
-
|
|
60
|
-
let
|
|
61
|
-
this.pointerStack.push(
|
|
65
|
+
let y = a?.type ?? Uint32Array;
|
|
66
|
+
y === Uint32Array ? this.type = 0 : y === Int32Array ? this.type = 1 : y === Float32Array ? this.type = 2 : y === Float64Array && (this.type = 3), this.dataLength = e, this.maxChunkSize = t;
|
|
67
|
+
let b = y === Float64Array ? 2 : 1, x = i.allocUI32(t * e * b);
|
|
68
|
+
this.pointerStack.push(x.pointer);
|
|
62
69
|
}
|
|
63
|
-
this.cachedType = this.uint16Array[0], this.cachedDataLength = Math.max(1, this.uint16Array[1]), this.cachedMaxChunkSize = this.firstBlock.data[l], this.cachedByteMultipler = this.cachedType === 3 ? 2 : 1, this.growLock = new Int32Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + u * Uint32Array.BYTES_PER_ELEMENT, 1);
|
|
70
|
+
this.cachedType = this.uint16Array[0], this.cachedDataLength = Math.max(1, this.uint16Array[1]), this.cachedMaxChunkSize = this.firstBlock.data[l], this.cachedByteMultipler = this.cachedType === 3 ? 2 : 1, this.maxLength = this.cachedMaxChunkSize * this.pointerStack.maxLength, this.growLock = new Int32Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + u * Uint32Array.BYTES_PER_ELEMENT, 1);
|
|
64
71
|
}
|
|
65
72
|
at(e) {
|
|
66
73
|
let t = this.getFullDataBlock(e);
|
|
@@ -140,6 +147,6 @@ var s = 0, c = 1, l = 2, u = 3, d = 4, f = 5, p = class p {
|
|
|
140
147
|
}
|
|
141
148
|
};
|
|
142
149
|
//#endregion
|
|
143
|
-
export {
|
|
150
|
+
export { g as default };
|
|
144
151
|
|
|
145
152
|
//# sourceMappingURL=shared-pool.js.map
|