@daneren2005/shared-memory-objects 0.0.9 → 0.0.11
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/dist/shared-memory-objects.js +1325 -0
- package/dist/shared-memory-objects.umd.cjs +1 -0
- package/dist/src/allocated-memory.d.ts +26 -0
- package/dist/src/cached-item-list.d.ts +37 -0
- package/{src/interfaces/pow2.ts → dist/src/interfaces/pow2.d.ts} +2 -3
- package/dist/src/interfaces/typed-array-constructor.d.ts +5 -0
- package/{src/interfaces/typed-array.ts → dist/src/interfaces/typed-array.d.ts} +1 -1
- package/dist/src/lock/read-write-lock.d.ts +5 -0
- package/dist/src/lock/simple-lock.d.ts +3 -0
- package/dist/src/main.d.ts +18 -0
- package/dist/src/memory-buffer.d.ts +185 -0
- package/dist/src/memory-heap.d.ts +34 -0
- package/dist/src/serialize-object.d.ts +5 -0
- package/dist/src/shared-list.d.ts +44 -0
- package/dist/src/shared-map.d.ts +25 -0
- package/dist/src/shared-pointer-list.d.ts +21 -0
- package/dist/src/shared-string.d.ts +23 -0
- package/dist/src/shared-vector.d.ts +40 -0
- package/dist/src/utils/16-from-32-array.d.ts +4 -0
- package/dist/src/utils/16-from-64-array.d.ts +2 -0
- package/dist/src/utils/float32-atomics.d.ts +5 -0
- package/dist/src/utils/pointer.d.ts +19 -0
- package/package.json +17 -5
- package/src/allocated-memory.ts +0 -89
- package/src/cached-item-list.ts +0 -143
- package/src/interfaces/typed-array-constructor.ts +0 -6
- package/src/lock/read-write-lock.ts +0 -41
- package/src/lock/simple-lock.ts +0 -21
- package/src/main.ts +0 -40
- package/src/memory-buffer.ts +0 -666
- package/src/memory-heap.ts +0 -191
- package/src/serialize-object.ts +0 -95
- package/src/shared-list.ts +0 -339
- package/src/shared-map.ts +0 -252
- package/src/shared-pointer-list.ts +0 -80
- package/src/shared-string.ts +0 -144
- package/src/shared-vector.ts +0 -236
- package/src/utils/16-from-32-array.ts +0 -23
- package/src/utils/16-from-64-array.ts +0 -18
- package/src/utils/float32-atomics.ts +0 -26
- package/src/utils/pointer.ts +0 -40
- package/src/utils/typedarray.js +0 -162
- package/src/vite-env.d.ts +0 -1
- /package/{src → dist/src}/utils/typedarray.d.ts +0 -0
package/src/shared-map.ts
DELETED
|
@@ -1,252 +0,0 @@
|
|
|
1
|
-
import AllocatedMemory, { type SharedAllocatedMemory } from './allocated-memory';
|
|
2
|
-
import type MemoryHeap from './memory-heap';
|
|
3
|
-
import SharedList from './shared-list';
|
|
4
|
-
import { loadPointer, storePointer } from './utils/pointer';
|
|
5
|
-
|
|
6
|
-
// TODO: Grow hashMemory
|
|
7
|
-
// TODO: Add iterator
|
|
8
|
-
// TODO: Add read/write locks
|
|
9
|
-
const DEFAULT_HASH_SIZE = 10;
|
|
10
|
-
export default class SharedMap<K extends string | number> {
|
|
11
|
-
static readonly ALLOCATE_COUNT = 4;
|
|
12
|
-
|
|
13
|
-
private memory: MemoryHeap;
|
|
14
|
-
// Memory order: Pointer, Lock, Length, MaxHash
|
|
15
|
-
private pointerMemory: AllocatedMemory;
|
|
16
|
-
private lock: Int32Array;
|
|
17
|
-
private get hashMemory(): AllocatedMemory {
|
|
18
|
-
return new AllocatedMemory(this.memory, loadPointer(this.pointerMemory.data, 0));
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
get length(): number {
|
|
22
|
-
return Atomics.load(this.pointerMemory.data, 2);
|
|
23
|
-
}
|
|
24
|
-
get maxHash(): number {
|
|
25
|
-
return Atomics.load(this.pointerMemory.data, 3);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
constructor(memory: MemoryHeap, clone?: SharedMapMemory) {
|
|
29
|
-
this.memory = memory;
|
|
30
|
-
|
|
31
|
-
if(clone) {
|
|
32
|
-
this.pointerMemory = new AllocatedMemory(memory, clone.firstBlock);
|
|
33
|
-
} else {
|
|
34
|
-
this.pointerMemory = memory.allocUI32(SharedMap.ALLOCATE_COUNT);
|
|
35
|
-
let hashMemory = memory.allocUI32(DEFAULT_HASH_SIZE);
|
|
36
|
-
storePointer(this.pointerMemory.data, 0, hashMemory.bufferPosition, hashMemory.bufferByteOffset);
|
|
37
|
-
Atomics.store(this.pointerMemory.data, 3, DEFAULT_HASH_SIZE);
|
|
38
|
-
}
|
|
39
|
-
this.lock = new Int32Array(this.pointerMemory.data.buffer, this.pointerMemory.bufferByteOffset + Uint32Array.BYTES_PER_ELEMENT, 1);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
set(key: K, value: number) {
|
|
43
|
-
if(this.length >= this.maxHash * 2) {
|
|
44
|
-
this.growHashTable();
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
let fullHashKey = get32BitHash(key);
|
|
48
|
-
if(this.setHashKey(this.hashMemory, this.maxHash, fullHashKey, value)) {
|
|
49
|
-
Atomics.add(this.pointerMemory.data, 2, 1);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
private setHashKey(hashMemory: AllocatedMemory, maxHash: number, fullHashKey: number, value: number) {
|
|
53
|
-
let hashKey = this.hash(fullHashKey, maxHash);
|
|
54
|
-
|
|
55
|
-
let list: SharedList;
|
|
56
|
-
let pointer = loadPointer(hashMemory.data, hashKey);
|
|
57
|
-
if(pointer.bufferByteOffset === 0) {
|
|
58
|
-
// Initialize a list here
|
|
59
|
-
list = new SharedList(this.memory, {
|
|
60
|
-
dataLength: 2
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
let listMemory = list.getSharedMemory();
|
|
64
|
-
storePointer(hashMemory.data, hashKey, listMemory.firstBlock.bufferPosition, listMemory.firstBlock.bufferByteOffset);
|
|
65
|
-
} else {
|
|
66
|
-
list = new SharedList(this.memory, {
|
|
67
|
-
firstBlock: pointer
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Check if any other items in list have the same key and delete them
|
|
72
|
-
let inserted = true;
|
|
73
|
-
if(list.deleteValue(fullHashKey)) {
|
|
74
|
-
inserted = false;
|
|
75
|
-
}
|
|
76
|
-
list.insert([fullHashKey, value]);
|
|
77
|
-
|
|
78
|
-
return inserted;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
get(key: K): number | undefined {
|
|
82
|
-
let fullHashKey = get32BitHash(key);
|
|
83
|
-
let hashKey = this.hash(fullHashKey, this.maxHash);
|
|
84
|
-
|
|
85
|
-
let pointer = loadPointer(this.hashMemory.data, hashKey);
|
|
86
|
-
if(pointer.bufferByteOffset === 0) {
|
|
87
|
-
return undefined;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
let list = new SharedList(this.memory, {
|
|
91
|
-
firstBlock: pointer
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
for(let { data } of list) {
|
|
95
|
-
if(data[0] === fullHashKey) {
|
|
96
|
-
return data[1];
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return undefined;
|
|
101
|
-
}
|
|
102
|
-
has(key: K): boolean {
|
|
103
|
-
let fullHashKey = get32BitHash(key);
|
|
104
|
-
let hashKey = this.hash(fullHashKey, this.maxHash);
|
|
105
|
-
|
|
106
|
-
let pointer = loadPointer(this.hashMemory.data, hashKey);
|
|
107
|
-
if(pointer.bufferByteOffset === 0) {
|
|
108
|
-
return false;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
let list = new SharedList(this.memory, {
|
|
112
|
-
firstBlock: pointer
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
for(let { data } of list) {
|
|
116
|
-
if(data[0] === fullHashKey) {
|
|
117
|
-
return true;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return false;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
delete(key: K): boolean {
|
|
125
|
-
let fullHashKey = get32BitHash(key);
|
|
126
|
-
let hashKey = this.hash(fullHashKey, this.maxHash);
|
|
127
|
-
|
|
128
|
-
let pointer = loadPointer(this.hashMemory.data, hashKey);
|
|
129
|
-
if(pointer.bufferByteOffset === 0) {
|
|
130
|
-
return false;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
let list = new SharedList(this.memory, {
|
|
134
|
-
firstBlock: pointer
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
for(let { data, deleteCurrent } of list) {
|
|
138
|
-
if(data[0] === fullHashKey) {
|
|
139
|
-
deleteCurrent();
|
|
140
|
-
|
|
141
|
-
Atomics.sub(this.pointerMemory.data, 2, 1);
|
|
142
|
-
return true;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
return false;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
private growHashTable() {
|
|
150
|
-
let oldMaxHash = this.maxHash;
|
|
151
|
-
let newMaxHash = oldMaxHash * 2;
|
|
152
|
-
let newHashMemory = this.memory.allocUI32(newMaxHash);
|
|
153
|
-
let oldHashMemory = this.hashMemory;
|
|
154
|
-
|
|
155
|
-
// Copy each old hash value into new hash memory
|
|
156
|
-
for(let i = 0; i < oldMaxHash; i++) {
|
|
157
|
-
let pointer = loadPointer(oldHashMemory.data, i);
|
|
158
|
-
if(pointer.bufferByteOffset === 0) {
|
|
159
|
-
continue;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
let list = new SharedList(this.memory, {
|
|
163
|
-
firstBlock: pointer
|
|
164
|
-
});
|
|
165
|
-
for(let { data } of list) {
|
|
166
|
-
this.setHashKey(newHashMemory, newMaxHash, data[0], data[1]);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
storePointer(this.pointerMemory.data, 0, newHashMemory.bufferPosition, newHashMemory.bufferByteOffset);
|
|
171
|
-
Atomics.store(this.pointerMemory.data, 3, newMaxHash);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
private hash(key: number, maxHash: number) {
|
|
175
|
-
return key % maxHash;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
free() {
|
|
179
|
-
// Loop through and free lists in hash table first
|
|
180
|
-
for(let i = 0; i < this.maxHash; i++) {
|
|
181
|
-
let pointer = loadPointer(this.hashMemory.data, i);
|
|
182
|
-
if(pointer.bufferByteOffset === 0) {
|
|
183
|
-
continue;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
let list = new SharedList(this.memory, {
|
|
187
|
-
firstBlock: pointer
|
|
188
|
-
});
|
|
189
|
-
list.free();
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
this.hashMemory.free();
|
|
193
|
-
this.pointerMemory.free();
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
getSharedMemory(): SharedMapMemory {
|
|
197
|
-
return {
|
|
198
|
-
firstBlock: this.pointerMemory.getSharedMemory()
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
interface SharedMapMemory {
|
|
204
|
-
firstBlock: SharedAllocatedMemory
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
function get32BitHash<K extends string | number>(key: K): number {
|
|
208
|
-
if(typeof key === 'number') {
|
|
209
|
-
return key;
|
|
210
|
-
} else if(typeof key === 'string') {
|
|
211
|
-
return hashString(key as string);
|
|
212
|
-
} else {
|
|
213
|
-
return key;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// Copied from https://github.com/mmomtchev/SharedMap/blob/master/index.js - MurmurHash2
|
|
218
|
-
function hashString(str: string): number {
|
|
219
|
-
let
|
|
220
|
-
l = str.length,
|
|
221
|
-
h = 17 ^ l,
|
|
222
|
-
i = 0,
|
|
223
|
-
k;
|
|
224
|
-
while(l >= 4) {
|
|
225
|
-
k =
|
|
226
|
-
((str.charCodeAt(i) & 0xff)) |
|
|
227
|
-
((str.charCodeAt(++i) & 0xff) << 8) |
|
|
228
|
-
((str.charCodeAt(++i) & 0xff) << 16) |
|
|
229
|
-
((str.charCodeAt(++i) & 0xff) << 14);
|
|
230
|
-
k = (((k & 0xffff) * 0x5bd1e995) + ((((k >>> 16) * 0x5bd1e995) & 0xffff) << 16));
|
|
231
|
-
k ^= k >>> 14;
|
|
232
|
-
k = (((k & 0xffff) * 0x5bd1e995) + ((((k >>> 16) * 0x5bd1e995) & 0xffff) << 16));
|
|
233
|
-
h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16)) ^ k;
|
|
234
|
-
l -= 4;
|
|
235
|
-
++i;
|
|
236
|
-
}
|
|
237
|
-
/* eslint-disable no-fallthrough */
|
|
238
|
-
switch(l) {
|
|
239
|
-
case 3: h ^= (str.charCodeAt(i + 2) & 0xff) << 16;
|
|
240
|
-
case 2: h ^= (str.charCodeAt(i + 1) & 0xff) << 8;
|
|
241
|
-
case 1: h ^= (str.charCodeAt(i) & 0xff);
|
|
242
|
-
h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16));
|
|
243
|
-
}
|
|
244
|
-
/* eslint-enable no-fallthrough */
|
|
245
|
-
h ^= h >>> 13;
|
|
246
|
-
h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16));
|
|
247
|
-
h ^= h >>> 15;
|
|
248
|
-
h = h >>> 0;
|
|
249
|
-
return h;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
export type { SharedMapMemory };
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import AllocatedMemory from './allocated-memory';
|
|
2
|
-
import type MemoryHeap from './memory-heap';
|
|
3
|
-
import type { SharedListMemory } from './shared-list';
|
|
4
|
-
import SharedList from './shared-list';
|
|
5
|
-
import { createPointer, loadPointer } from './utils/pointer';
|
|
6
|
-
|
|
7
|
-
export default abstract class SharedPointerList<T extends PointerItem> implements Iterable<T> {
|
|
8
|
-
private memory: MemoryHeap;
|
|
9
|
-
private list: SharedList;
|
|
10
|
-
|
|
11
|
-
constructor(heap: MemoryHeap, memory?: SharedListMemory) {
|
|
12
|
-
this.memory = heap;
|
|
13
|
-
|
|
14
|
-
if(memory) {
|
|
15
|
-
this.list = new SharedList(heap, memory);
|
|
16
|
-
} else {
|
|
17
|
-
this.list = new SharedList(heap);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
get length() {
|
|
22
|
-
return this.list.length;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
insert(item: T) {
|
|
26
|
-
this.list.insert(createPointer(item.memory.bufferPosition, item.memory.bufferByteOffset));
|
|
27
|
-
}
|
|
28
|
-
delete(item: T) {
|
|
29
|
-
return this.list.deleteValue(createPointer(item.memory.bufferPosition, item.memory.bufferByteOffset));
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
*[Symbol.iterator]() {
|
|
33
|
-
let iterator = this.list[Symbol.iterator]();
|
|
34
|
-
|
|
35
|
-
for(let { data: pointerData } of iterator) {
|
|
36
|
-
let { bufferPosition, bufferByteOffset } = loadPointer(pointerData, 0);
|
|
37
|
-
let allocatedMemory = new AllocatedMemory(this.memory, {
|
|
38
|
-
bufferPosition,
|
|
39
|
-
bufferByteOffset
|
|
40
|
-
});
|
|
41
|
-
yield this.createItem(allocatedMemory);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
forEach(callback: (item: T) => void) {
|
|
45
|
-
for(let value of this) {
|
|
46
|
-
callback(value);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
find(callback: (item: T) => boolean): T | undefined {
|
|
51
|
-
for(let value of this) {
|
|
52
|
-
if(callback(value)) {
|
|
53
|
-
return value;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
getSharedMemory() {
|
|
59
|
-
return this.list.getSharedMemory();
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
protected abstract createItem(allocatedMemory: AllocatedMemory): T;
|
|
63
|
-
|
|
64
|
-
free() {
|
|
65
|
-
for(let item of this) {
|
|
66
|
-
// 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
|
|
67
|
-
if('free' in item && typeof item.free === 'function') {
|
|
68
|
-
item.free();
|
|
69
|
-
} else {
|
|
70
|
-
item.memory.free();
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
this.list.free();
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
interface PointerItem {
|
|
79
|
-
readonly memory: AllocatedMemory
|
|
80
|
-
}
|
package/src/shared-string.ts
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import AllocatedMemory from './allocated-memory';
|
|
2
|
-
import type { SharedAllocatedMemory } from './allocated-memory';
|
|
3
|
-
import { lock, unlock } from './lock/simple-lock';
|
|
4
|
-
import type MemoryHeap from './memory-heap';
|
|
5
|
-
import { getPointer, loadPointer, loadRawPointer, storeRawPointer } from './utils/pointer';
|
|
6
|
-
|
|
7
|
-
enum CHAR_TYPE {
|
|
8
|
-
UNDEFINED,
|
|
9
|
-
ASCII,
|
|
10
|
-
UTF16
|
|
11
|
-
}
|
|
12
|
-
const TYPED_ARRAY_MAP = {
|
|
13
|
-
[CHAR_TYPE.ASCII]: Uint8Array,
|
|
14
|
-
[CHAR_TYPE.UTF16]: Uint16Array
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
const POINTER_INDEX = 0;
|
|
18
|
-
const LENGTH_INDEX = 1;
|
|
19
|
-
const TYPE_INDEX = 2;
|
|
20
|
-
const LOCK_INDEX = 3;
|
|
21
|
-
export default class SharedString {
|
|
22
|
-
static readonly ALLOCATE_COUNT = 4;
|
|
23
|
-
|
|
24
|
-
private memory: MemoryHeap;
|
|
25
|
-
private allocatedMemory: AllocatedMemory;
|
|
26
|
-
private lock: Int32Array;
|
|
27
|
-
|
|
28
|
-
private cachedPointer?: number;
|
|
29
|
-
private cachedString?: string;
|
|
30
|
-
|
|
31
|
-
constructor(memory: MemoryHeap, value: string | SharedStringConfig | SharedStringMemory) {
|
|
32
|
-
this.memory = memory;
|
|
33
|
-
|
|
34
|
-
if(typeof value === 'string') {
|
|
35
|
-
this.allocatedMemory = this.memory.allocUI32(SharedString.ALLOCATE_COUNT);
|
|
36
|
-
this.lock = new Int32Array(this.allocatedMemory.data.buffer, this.allocatedMemory.bufferByteOffset + LOCK_INDEX * this.allocatedMemory.data.BYTES_PER_ELEMENT);
|
|
37
|
-
this.updateString(value);
|
|
38
|
-
} else {
|
|
39
|
-
this.allocatedMemory = new AllocatedMemory(memory, value);
|
|
40
|
-
this.lock = new Int32Array(this.allocatedMemory.data.buffer, this.allocatedMemory.bufferByteOffset + LOCK_INDEX * this.allocatedMemory.data.BYTES_PER_ELEMENT);
|
|
41
|
-
|
|
42
|
-
// We only allocated memory but didn't initialize the string yet
|
|
43
|
-
if('value' in value) {
|
|
44
|
-
this.updateString(value.value);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
private updateString(value: string) {
|
|
50
|
-
let stringMemory = this.createString(value);
|
|
51
|
-
|
|
52
|
-
lock(this.lock);
|
|
53
|
-
storeRawPointer(this.allocatedMemory.data, POINTER_INDEX, stringMemory.pointer);
|
|
54
|
-
Atomics.store(this.allocatedMemory.data, LENGTH_INDEX, value.length);
|
|
55
|
-
Atomics.store(this.allocatedMemory.data, TYPE_INDEX, stringMemory.charType);
|
|
56
|
-
unlock(this.lock);
|
|
57
|
-
|
|
58
|
-
this.cachedPointer = stringMemory.pointer;
|
|
59
|
-
this.cachedString = value;
|
|
60
|
-
}
|
|
61
|
-
private createString(value: string) {
|
|
62
|
-
if(value === '') {
|
|
63
|
-
return {
|
|
64
|
-
pointer: 0,
|
|
65
|
-
charType: CHAR_TYPE.ASCII
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
let charCodes = [];
|
|
70
|
-
for(let i = 0; i < value.length; i++) {
|
|
71
|
-
charCodes.push(value.charCodeAt(i));
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
let maxCharCode = Math.max(...charCodes);
|
|
75
|
-
let charType = maxCharCode > 255 ? CHAR_TYPE.UTF16 : CHAR_TYPE.ASCII;
|
|
76
|
-
|
|
77
|
-
let typedArray = TYPED_ARRAY_MAP[charType];
|
|
78
|
-
let allocatedMemory = this.memory.allocUI32(Math.ceil(value.length / (4 / typedArray.BYTES_PER_ELEMENT)));
|
|
79
|
-
let data = new typedArray(allocatedMemory.data.buffer, allocatedMemory.data.byteOffset, value.length);
|
|
80
|
-
for(let i = 0; i < value.length; i++) {
|
|
81
|
-
data[i] = value.charCodeAt(i);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return {
|
|
85
|
-
pointer: allocatedMemory.pointer,
|
|
86
|
-
charType
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
get value(): string {
|
|
91
|
-
let pointer = loadRawPointer(this.allocatedMemory.data, POINTER_INDEX);
|
|
92
|
-
if(this.cachedPointer === pointer && this.cachedString !== undefined) {
|
|
93
|
-
return this.cachedString;
|
|
94
|
-
} else if(pointer === 0) {
|
|
95
|
-
return '';
|
|
96
|
-
}
|
|
97
|
-
let { bufferPosition, bufferByteOffset } = getPointer(pointer);
|
|
98
|
-
|
|
99
|
-
lock(this.lock);
|
|
100
|
-
let charType = Atomics.load(this.allocatedMemory.data, TYPE_INDEX) as number;
|
|
101
|
-
// @ts-expect-error
|
|
102
|
-
let typedArray = TYPED_ARRAY_MAP[charType];
|
|
103
|
-
let bufferLength = Atomics.load(this.allocatedMemory.data, LENGTH_INDEX);
|
|
104
|
-
|
|
105
|
-
let data = new typedArray(this.memory.buffers[bufferPosition].buf, bufferByteOffset, bufferLength);
|
|
106
|
-
let string = String.fromCharCode.apply(null, data);
|
|
107
|
-
// NOTE: Do not unlock until after transforming the data since the second this is done it can free that memory block
|
|
108
|
-
unlock(this.lock);
|
|
109
|
-
|
|
110
|
-
this.cachedPointer = pointer;
|
|
111
|
-
this.cachedString = string;
|
|
112
|
-
|
|
113
|
-
return string;
|
|
114
|
-
}
|
|
115
|
-
set value(value: string) {
|
|
116
|
-
let { bufferPosition: oldBufferPosition, bufferByteOffset: oldBufferByteOffset } = loadPointer(this.allocatedMemory.data, POINTER_INDEX);
|
|
117
|
-
this.updateString(value);
|
|
118
|
-
|
|
119
|
-
if(oldBufferByteOffset) {
|
|
120
|
-
this.memory.buffers[oldBufferPosition].free(oldBufferByteOffset);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
getSharedMemory(): SharedStringMemory {
|
|
125
|
-
return this.allocatedMemory.getSharedMemory();
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
get pointer() {
|
|
129
|
-
return this.allocatedMemory.pointer;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
free() {
|
|
133
|
-
let { bufferPosition, bufferByteOffset } = loadPointer(this.allocatedMemory.data, POINTER_INDEX);
|
|
134
|
-
if(bufferByteOffset) {
|
|
135
|
-
this.memory.buffers[bufferPosition].free(bufferByteOffset);
|
|
136
|
-
}
|
|
137
|
-
this.allocatedMemory.free();
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
interface SharedStringConfig extends SharedStringMemory {
|
|
142
|
-
value: string
|
|
143
|
-
}
|
|
144
|
-
type SharedStringMemory = SharedAllocatedMemory;
|
package/src/shared-vector.ts
DELETED
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
import type { SharedAllocatedMemory } from './allocated-memory';
|
|
2
|
-
import AllocatedMemory from './allocated-memory';
|
|
3
|
-
import type { TypedArrayConstructor } from './interfaces/typed-array-constructor';
|
|
4
|
-
import type MemoryHeap from './memory-heap';
|
|
5
|
-
import { getPointer, loadPointer, storePointer } from './utils/pointer';
|
|
6
|
-
|
|
7
|
-
enum TYPE {
|
|
8
|
-
uint32,
|
|
9
|
-
int32,
|
|
10
|
-
float32
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const LIST_LENGTH_INDEX = 1;
|
|
14
|
-
const BUFFER_LENGTH_INDEX = 2;
|
|
15
|
-
const TYPE_INDEX = 3;
|
|
16
|
-
const DEFAULT_SIZE = 4;
|
|
17
|
-
export default class SharedVector<T extends Uint32Array | Int32Array | Float32Array = Uint32Array> implements Iterable<T> {
|
|
18
|
-
static readonly ALLOCATE_COUNT = 4;
|
|
19
|
-
private memory: MemoryHeap;
|
|
20
|
-
|
|
21
|
-
// Pointer, List Length, Buffer Length, Type/DataLength
|
|
22
|
-
private firstBlock: AllocatedMemory;
|
|
23
|
-
private uint16Array: Uint16Array;
|
|
24
|
-
|
|
25
|
-
get length(): number {
|
|
26
|
-
return Atomics.load(this.firstBlock.data, LIST_LENGTH_INDEX);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
get type(): number {
|
|
30
|
-
return this.uint16Array[0];
|
|
31
|
-
}
|
|
32
|
-
private set type(value: number) {
|
|
33
|
-
Atomics.store(this.uint16Array, 0, value);
|
|
34
|
-
}
|
|
35
|
-
get dataLength(): number {
|
|
36
|
-
// Can technically be initialized by passing memory without actually every being called - need to make sure dataLength is always at least one
|
|
37
|
-
return Math.max(1, this.uint16Array[1]);
|
|
38
|
-
}
|
|
39
|
-
private set dataLength(value: number) {
|
|
40
|
-
Atomics.store(this.uint16Array, 1, value);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
get bufferLength(): number {
|
|
44
|
-
return Atomics.load(this.firstBlock.data, BUFFER_LENGTH_INDEX);
|
|
45
|
-
}
|
|
46
|
-
private set bufferLength(value: number) {
|
|
47
|
-
Atomics.store(this.firstBlock.data, BUFFER_LENGTH_INDEX, value);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
private cachedFullDataBlock?: T;
|
|
51
|
-
private cachedPointer: number;
|
|
52
|
-
|
|
53
|
-
constructor(memory: MemoryHeap, config?: SharedVectorConfig<T> | SharedVectorMemory) {
|
|
54
|
-
this.memory = memory;
|
|
55
|
-
|
|
56
|
-
if(config && 'firstBlock' in config) {
|
|
57
|
-
this.firstBlock = new AllocatedMemory(memory, config.firstBlock);
|
|
58
|
-
this.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + TYPE_INDEX * Uint32Array.BYTES_PER_ELEMENT, 2);
|
|
59
|
-
} else {
|
|
60
|
-
this.firstBlock = memory.allocUI32(SharedVector.ALLOCATE_COUNT);
|
|
61
|
-
this.uint16Array = new Uint16Array(this.firstBlock.data.buffer, this.firstBlock.bufferByteOffset + TYPE_INDEX * Uint32Array.BYTES_PER_ELEMENT, 2);
|
|
62
|
-
|
|
63
|
-
let dataLength = config?.dataLength ?? 1;
|
|
64
|
-
let bufferLength = config?.bufferLength ?? DEFAULT_SIZE;
|
|
65
|
-
let dataBlock = memory.allocUI32(bufferLength * dataLength);
|
|
66
|
-
storePointer(this.firstBlock.data, 0, dataBlock.bufferPosition, dataBlock.bufferByteOffset);
|
|
67
|
-
this.bufferLength = bufferLength;
|
|
68
|
-
|
|
69
|
-
const type = config?.type ?? Uint32Array;
|
|
70
|
-
if(type === Uint32Array) {
|
|
71
|
-
this.type = TYPE.uint32;
|
|
72
|
-
}
|
|
73
|
-
// @ts-expect-error
|
|
74
|
-
else if(type === Int32Array) {
|
|
75
|
-
this.type = TYPE.int32;
|
|
76
|
-
}
|
|
77
|
-
// @ts-expect-error
|
|
78
|
-
else if(type === Float32Array) {
|
|
79
|
-
this.type = TYPE.float32;
|
|
80
|
-
}
|
|
81
|
-
this.dataLength = dataLength;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
this.cachedPointer = this.firstBlock.data[0];
|
|
85
|
-
this.cachedFullDataBlock = this.getFullDataBlock(this.dataLength);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
push(values: number | Array<number>) {
|
|
89
|
-
if(typeof values === 'number') {
|
|
90
|
-
values = [values];
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
let dataLength = this.dataLength;
|
|
94
|
-
if(values.length > dataLength) {
|
|
95
|
-
throw new Error(`Can't insert ${values.length} array into shared list of ${dataLength} dataLength`);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
let dataBlock = this.getFullDataBlock(dataLength);
|
|
99
|
-
let currentLength = this.length;
|
|
100
|
-
dataBlock.set(values, dataLength * currentLength);
|
|
101
|
-
|
|
102
|
-
let newLength = Atomics.add(this.firstBlock.data, LIST_LENGTH_INDEX, LIST_LENGTH_INDEX) + 1;
|
|
103
|
-
if(newLength >= this.bufferLength) {
|
|
104
|
-
this.growBuffer();
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
pop(): T {
|
|
109
|
-
let oldLength = Atomics.sub(this.firstBlock.data, LIST_LENGTH_INDEX, LIST_LENGTH_INDEX);
|
|
110
|
-
|
|
111
|
-
let pointer = loadPointer(this.firstBlock.data, 0);
|
|
112
|
-
let dataMemory = new AllocatedMemory(this.memory, pointer);
|
|
113
|
-
return this.getDataBlock(dataMemory.data, oldLength - 1);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
deleteIndex(index: number) {
|
|
117
|
-
let length = this.length;
|
|
118
|
-
if(index >= length || index < 0) {
|
|
119
|
-
throw new Error(`${index} is out of bounds ${length}`);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
let dataLength = this.dataLength;
|
|
123
|
-
let dataBlock = this.getFullDataBlock(dataLength);
|
|
124
|
-
for(let i = index; i < length; i++) {
|
|
125
|
-
for(let j = 0; j < dataLength; j++) {
|
|
126
|
-
dataBlock[i * dataLength + j] = dataBlock[(i + 1) * dataLength + j];
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
Atomics.sub(this.firstBlock.data, LIST_LENGTH_INDEX, LIST_LENGTH_INDEX);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
clear() {
|
|
134
|
-
this.firstBlock.data[LIST_LENGTH_INDEX] = 0;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
*[Symbol.iterator]() {
|
|
138
|
-
let pointer = loadPointer(this.firstBlock.data, 0);
|
|
139
|
-
let dataMemory = new AllocatedMemory(this.memory, pointer);
|
|
140
|
-
|
|
141
|
-
for(let i = 0; i < this.length; i++) {
|
|
142
|
-
yield this.getDataBlock(dataMemory.data, i);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
private getFullDataBlock(dataLength: number): T {
|
|
147
|
-
let pointerNumber = Atomics.load(this.firstBlock.data, 0);
|
|
148
|
-
if(this.cachedPointer === pointerNumber && this.cachedFullDataBlock) {
|
|
149
|
-
return this.cachedFullDataBlock;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
let pointer = getPointer(pointerNumber);
|
|
153
|
-
let rawData = new AllocatedMemory(this.memory, pointer);
|
|
154
|
-
|
|
155
|
-
let data: T;
|
|
156
|
-
switch(this.type) {
|
|
157
|
-
case TYPE.int32:
|
|
158
|
-
// @ts-expect-error
|
|
159
|
-
data = new Int32Array(rawData.data.buffer, rawData.bufferByteOffset, dataLength * this.bufferLength);
|
|
160
|
-
break;
|
|
161
|
-
case TYPE.uint32:
|
|
162
|
-
// @ts-expect-error
|
|
163
|
-
data = new Uint32Array(rawData.data.buffer, rawData.bufferByteOffset, dataLength * this.bufferLength);
|
|
164
|
-
break;
|
|
165
|
-
case TYPE.float32:
|
|
166
|
-
// @ts-expect-error
|
|
167
|
-
data = new Float32Array(rawData.data.buffer, smemory.bufferByteOffset, dataLength * this.bufferLength);
|
|
168
|
-
break;
|
|
169
|
-
default:
|
|
170
|
-
throw new Error(`Unknown data block type ${this.type}`);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
this.cachedPointer = pointerNumber;
|
|
174
|
-
this.cachedFullDataBlock = data;
|
|
175
|
-
|
|
176
|
-
return data;
|
|
177
|
-
}
|
|
178
|
-
private getDataBlock(rawData: Uint32Array, index: number): T {
|
|
179
|
-
switch(this.type) {
|
|
180
|
-
case TYPE.int32:
|
|
181
|
-
// @ts-expect-error
|
|
182
|
-
return new Int32Array(rawData.buffer, rawData.byteOffset + index * this.dataLength * 4, this.dataLength);
|
|
183
|
-
case TYPE.uint32:
|
|
184
|
-
// @ts-expect-error
|
|
185
|
-
return new Uint32Array(rawData.buffer, rawData.byteOffset + index * this.dataLength * 4, this.dataLength);
|
|
186
|
-
case TYPE.float32:
|
|
187
|
-
// @ts-expect-error
|
|
188
|
-
return new Float32Array(rawData.buffer, smemory.byteOffset + index * this.dataLength * 4, this.dataLength);
|
|
189
|
-
default:
|
|
190
|
-
throw new Error(`Unknown data block type ${this.type}`);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
private growBuffer() {
|
|
195
|
-
let oldBufferLength = this.bufferLength;
|
|
196
|
-
let newBufferLength = oldBufferLength * 2;
|
|
197
|
-
let dataLength = this.dataLength;
|
|
198
|
-
|
|
199
|
-
let oldPointer = loadPointer(this.firstBlock.data, 0);
|
|
200
|
-
let oldDataMemory = new AllocatedMemory(this.memory, oldPointer);
|
|
201
|
-
let oldDataBlock = this.getFullDataBlock(dataLength);
|
|
202
|
-
let newDataBlock = this.memory.allocUI32(newBufferLength * dataLength);
|
|
203
|
-
// Copy old buffer into new buffer
|
|
204
|
-
newDataBlock.data.set(oldDataBlock);
|
|
205
|
-
|
|
206
|
-
storePointer(this.firstBlock.data, 0, newDataBlock.bufferPosition, newDataBlock.bufferByteOffset);
|
|
207
|
-
this.bufferLength = newBufferLength;
|
|
208
|
-
oldDataMemory.free();
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
free() {
|
|
212
|
-
let pointer = loadPointer(this.firstBlock.data, 0);
|
|
213
|
-
let dataMemory = new AllocatedMemory(this.memory, pointer);
|
|
214
|
-
|
|
215
|
-
dataMemory.free();
|
|
216
|
-
this.firstBlock.free();
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
getSharedMemory(): SharedVectorMemory {
|
|
220
|
-
return {
|
|
221
|
-
firstBlock: this.firstBlock.getSharedMemory()
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
interface SharedVectorConfig<T extends Uint32Array | Int32Array | Float32Array> {
|
|
227
|
-
type?: TypedArrayConstructor<T>
|
|
228
|
-
dataLength?: number
|
|
229
|
-
|
|
230
|
-
bufferLength?: number
|
|
231
|
-
}
|
|
232
|
-
interface SharedVectorMemory {
|
|
233
|
-
firstBlock: SharedAllocatedMemory
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
export type { SharedVectorConfig, SharedVectorMemory };
|