@daneren2005/shared-memory-objects 0.0.7 → 0.0.9
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/package.json +1 -1
- package/src/cached-item-list.ts +143 -0
- package/src/main.ts +4 -1
- package/src/shared-list.ts +58 -0
- package/src/shared-string.ts +4 -0
- package/src/utils/float32-atomics.ts +7 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@daneren2005/shared-memory-objects",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"author": "daneren2005@gmail.com",
|
|
5
5
|
"description": "Creating objects with a SharedArrayBuffer",
|
|
6
6
|
"homepage": "https://github.com/daneren2005/shared-memory-objects#readme",
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import type { SharedAllocatedMemory } from './allocated-memory';
|
|
2
|
+
import type MemoryHeap from './memory-heap';
|
|
3
|
+
import SharedList, { type SharedListMemory } from './shared-list';
|
|
4
|
+
|
|
5
|
+
export default abstract class CachedItemList<T extends Item> implements Iterable<{ item: T, deleteCurrent: () => void }> {
|
|
6
|
+
static readonly ALLOCATE_COUNT = SharedList.ALLOCATE_COUNT;
|
|
7
|
+
|
|
8
|
+
protected heap: MemoryHeap;
|
|
9
|
+
protected list: SharedList;
|
|
10
|
+
protected cache: Map<number, T> = new Map();
|
|
11
|
+
|
|
12
|
+
constructor(heap: MemoryHeap, config?: CachedListConfig | SharedListMemory) {
|
|
13
|
+
if(config) {
|
|
14
|
+
this.list = new SharedList(heap, config);
|
|
15
|
+
} else {
|
|
16
|
+
this.list = new SharedList(heap);
|
|
17
|
+
}
|
|
18
|
+
this.heap = heap;
|
|
19
|
+
|
|
20
|
+
this.list.onDelete = (pointerData: Uint32Array) => {
|
|
21
|
+
let pointer = Atomics.load(pointerData, 0);
|
|
22
|
+
if(pointer) {
|
|
23
|
+
let item = this.cache.get(pointer);
|
|
24
|
+
if(!item) {
|
|
25
|
+
item = this.initItem(pointer);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if(item) {
|
|
29
|
+
item.free();
|
|
30
|
+
this.cache.delete(pointer);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
get length() {
|
|
37
|
+
return this.list.length;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
clear() {
|
|
41
|
+
this.list.clear();
|
|
42
|
+
this.cache.clear();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
insert(item: T) {
|
|
46
|
+
this.list.insert(item.pointer);
|
|
47
|
+
this.cache.set(item.pointer, item);
|
|
48
|
+
}
|
|
49
|
+
delete(item: T) {
|
|
50
|
+
this.cache.delete(item.pointer);
|
|
51
|
+
return this.list.deleteValue(item.pointer);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
getByPointer(pointer: number): T | undefined {
|
|
55
|
+
let item = this.cache.get(pointer);
|
|
56
|
+
if(!item) {
|
|
57
|
+
item = this.initItem(pointer);
|
|
58
|
+
if(item) {
|
|
59
|
+
this.cache.set(pointer, item);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return item;
|
|
64
|
+
}
|
|
65
|
+
protected abstract initItem(pointer: number): T | undefined
|
|
66
|
+
|
|
67
|
+
*[Symbol.iterator]() {
|
|
68
|
+
let iterator = this.list[Symbol.iterator]();
|
|
69
|
+
|
|
70
|
+
for(let { data: pointerData, deleteCurrent } of iterator) {
|
|
71
|
+
let pointer = Atomics.load(pointerData, 0);
|
|
72
|
+
if(!pointer) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
let item = this.cache.get(pointer);
|
|
77
|
+
if(!item) {
|
|
78
|
+
item = this.initItem(pointer);
|
|
79
|
+
if(item) {
|
|
80
|
+
this.cache.set(pointer, item);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if(item) {
|
|
85
|
+
yield {
|
|
86
|
+
item,
|
|
87
|
+
deleteCurrent
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
forEach(callback: (item: T) => void, filter?: (item: T) => boolean) {
|
|
93
|
+
for(let { item } of this) {
|
|
94
|
+
if(!filter || filter(item)) {
|
|
95
|
+
callback(item);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
find(callback: (item: T) => boolean): T | undefined {
|
|
101
|
+
for(let { item } of this) {
|
|
102
|
+
if(callback(item)) {
|
|
103
|
+
return item;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
filter(callback: (entity: T) => boolean): Array<T> {
|
|
108
|
+
let items = [];
|
|
109
|
+
for(let { item } of this) {
|
|
110
|
+
if(callback(item)) {
|
|
111
|
+
items.push(item);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return items;
|
|
116
|
+
}
|
|
117
|
+
map<X>(callback: (item: T) => X): Array<X> {
|
|
118
|
+
const array: Array<X> = [];
|
|
119
|
+
for(let { item } of this) {
|
|
120
|
+
array.push(callback(item));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return array;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
getSharedMemory() {
|
|
127
|
+
return this.list.getSharedMemory();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
free() {
|
|
131
|
+
this.list.free();
|
|
132
|
+
this.cache.clear();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export interface CachedListConfig {
|
|
137
|
+
initWithBlock: SharedAllocatedMemory
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
interface Item {
|
|
141
|
+
pointer: number
|
|
142
|
+
free: () => void
|
|
143
|
+
}
|
package/src/main.ts
CHANGED
|
@@ -7,6 +7,7 @@ import SharedMap, { type SharedMapMemory } from './shared-map';
|
|
|
7
7
|
import SharedPointerList from './shared-pointer-list';
|
|
8
8
|
import SharedString from './shared-string';
|
|
9
9
|
import SharedVector, { type SharedVectorMemory } from './shared-vector';
|
|
10
|
+
import CachedItemList, { type CachedListConfig } from './cached-item-list';
|
|
10
11
|
|
|
11
12
|
export * from './interfaces/typed-array';
|
|
12
13
|
export * from './interfaces/typed-array-constructor';
|
|
@@ -33,5 +34,7 @@ export {
|
|
|
33
34
|
SharedPointerList,
|
|
34
35
|
SharedString,
|
|
35
36
|
SharedVector,
|
|
36
|
-
type SharedVectorMemory
|
|
37
|
+
type SharedVectorMemory,
|
|
38
|
+
CachedItemList,
|
|
39
|
+
type CachedListConfig
|
|
37
40
|
};
|
package/src/shared-list.ts
CHANGED
|
@@ -39,6 +39,7 @@ export default class SharedList<T extends Uint32Array | Int32Array | Float32Arra
|
|
|
39
39
|
*/
|
|
40
40
|
private firstBlock: AllocatedMemory;
|
|
41
41
|
private uint16Array: Uint16Array;
|
|
42
|
+
onDelete?: (data: T) => void;
|
|
42
43
|
|
|
43
44
|
get length(): number {
|
|
44
45
|
return Atomics.load(this.firstBlock.data, LENGTH_INDEX);
|
|
@@ -169,6 +170,54 @@ export default class SharedList<T extends Uint32Array | Int32Array | Float32Arra
|
|
|
169
170
|
}
|
|
170
171
|
}
|
|
171
172
|
|
|
173
|
+
clear() {
|
|
174
|
+
let firstBlockPointer, lastBlockPointer;
|
|
175
|
+
let updateWorked = false;
|
|
176
|
+
while(!updateWorked) {
|
|
177
|
+
firstBlockPointer = loadRawPointer(this.firstBlock.data, 0);
|
|
178
|
+
lastBlockPointer = loadRawPointer(this.firstBlock.data, 1);
|
|
179
|
+
// Already cleared
|
|
180
|
+
if(!lastBlockPointer) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
updateWorked = replaceRawPointer(this.firstBlock.data, 1, 0, lastBlockPointer);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Shouldn't be possible to hit: making Typescript happy
|
|
188
|
+
if(!firstBlockPointer) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// We only want to update the last block if this is ran before something new was inserted
|
|
193
|
+
replaceRawPointer(this.firstBlock.data, 0, 0, firstBlockPointer);
|
|
194
|
+
|
|
195
|
+
// Iterate through inaccessible nodes and delete them
|
|
196
|
+
let deletedItems = 0;
|
|
197
|
+
let nextBlockPointer = firstBlockPointer;
|
|
198
|
+
while(nextBlockPointer) {
|
|
199
|
+
let { bufferPosition: nextBlockPosition, bufferByteOffset: nextBlockByteOffset } = getPointer(nextBlockPointer);
|
|
200
|
+
let memPool = this.memory.buffers[nextBlockPosition];
|
|
201
|
+
// Short circuit iterations if we can't access memory
|
|
202
|
+
if(!memPool) {
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
let blockRecord = new Uint32Array(memPool.buf, nextBlockByteOffset, 2);
|
|
207
|
+
nextBlockPointer = loadRawPointer(blockRecord, 0);
|
|
208
|
+
deletedItems++;
|
|
209
|
+
|
|
210
|
+
if(this.onDelete) {
|
|
211
|
+
this.onDelete(this.getDataBlock(blockRecord));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
memPool.free(blockRecord.byteOffset);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Subtract by however many we deleted so that a insert during this operation is accurate
|
|
218
|
+
Atomics.sub(this.firstBlock.data, LENGTH_INDEX, deletedItems);
|
|
219
|
+
}
|
|
220
|
+
|
|
172
221
|
*[Symbol.iterator]() {
|
|
173
222
|
let currentIndex = 0;
|
|
174
223
|
let { bufferPosition: nextBlockPosition, bufferByteOffset: nextBlockByteOffset } = loadPointer(this.firstBlock.data, 0);
|
|
@@ -202,6 +251,10 @@ export default class SharedList<T extends Uint32Array | Int32Array | Float32Arra
|
|
|
202
251
|
storePointer(this.firstBlock.data, 1, lastBlockPosition, lastBlockByteOffset);
|
|
203
252
|
}
|
|
204
253
|
|
|
254
|
+
if(this.onDelete) {
|
|
255
|
+
this.onDelete(this.getDataBlock(blockRecord));
|
|
256
|
+
}
|
|
257
|
+
|
|
205
258
|
memPool.free(blockRecord.byteOffset);
|
|
206
259
|
Atomics.sub(this.firstBlock.data, LENGTH_INDEX, 1);
|
|
207
260
|
updateLastBlock = false;
|
|
@@ -256,6 +309,11 @@ export default class SharedList<T extends Uint32Array | Int32Array | Float32Arra
|
|
|
256
309
|
});
|
|
257
310
|
|
|
258
311
|
({ bufferPosition: nextBlockPosition, bufferByteOffset: nextBlockByteOffset } = loadPointer(allocatedMemory.data, 0));
|
|
312
|
+
|
|
313
|
+
if(this.onDelete) {
|
|
314
|
+
this.onDelete(this.getDataBlock(allocatedMemory.data));
|
|
315
|
+
}
|
|
316
|
+
|
|
259
317
|
allocatedMemory.free();
|
|
260
318
|
}
|
|
261
319
|
|
package/src/shared-string.ts
CHANGED
|
@@ -125,6 +125,10 @@ export default class SharedString {
|
|
|
125
125
|
return this.allocatedMemory.getSharedMemory();
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
+
get pointer() {
|
|
129
|
+
return this.allocatedMemory.pointer;
|
|
130
|
+
}
|
|
131
|
+
|
|
128
132
|
free() {
|
|
129
133
|
let { bufferPosition, bufferByteOffset } = loadPointer(this.allocatedMemory.data, POINTER_INDEX);
|
|
130
134
|
if(bufferByteOffset) {
|
|
@@ -3,22 +3,24 @@ const buffer = new ArrayBuffer(4);
|
|
|
3
3
|
const float32 = new Float32Array(buffer);
|
|
4
4
|
const int32 = new Int32Array(buffer);
|
|
5
5
|
|
|
6
|
-
function loadFloat32(data: Int32Array, index: number) {
|
|
6
|
+
export function loadFloat32(data: Int32Array | Uint32Array, index: number) {
|
|
7
7
|
return convertInt32ToFloat32(Atomics.load(data, index));
|
|
8
8
|
}
|
|
9
|
-
function storeFloat32(data: Int32Array, index: number, value: number) {
|
|
9
|
+
export function storeFloat32(data: Int32Array | Uint32Array, index: number, value: number) {
|
|
10
10
|
Atomics.store(data, index, convertFloat32ToInt32(value));
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
function convertInt32ToFloat32(value: number) {
|
|
13
|
+
export function convertInt32ToFloat32(value: number) {
|
|
14
14
|
int32[0] = value;
|
|
15
15
|
|
|
16
16
|
return float32[0];
|
|
17
17
|
}
|
|
18
|
-
function convertFloat32ToInt32(value: number) {
|
|
18
|
+
export function convertFloat32ToInt32(value: number) {
|
|
19
19
|
float32[0] = value;
|
|
20
20
|
|
|
21
21
|
return int32[0];
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
export
|
|
24
|
+
export function exchangeFloat32(data: Int32Array | Uint32Array, index: number, value: number) {
|
|
25
|
+
return convertInt32ToFloat32(Atomics.exchange(data, index, convertFloat32ToInt32(value)));
|
|
26
|
+
}
|