@daneren2005/shared-memory-objects 0.0.8 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@daneren2005/shared-memory-objects",
3
- "version": "0.0.8",
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
  };
@@ -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
 
@@ -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) {