@daneren2005/shared-memory-objects 0.0.0 → 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@daneren2005/shared-memory-objects",
3
- "version": "0.0.0",
3
+ "version": "0.0.1",
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",
@@ -8,7 +8,6 @@
8
8
  "sharedarraybuffer"
9
9
  ],
10
10
  "license": "MIT",
11
-
12
11
  "repository": {
13
12
  "type": "git",
14
13
  "url": "git+ssh://git@github.com/daneren2005/shared-memory-objects.git"
@@ -16,10 +15,9 @@
16
15
  "bugs": {
17
16
  "url": "https://github.com/daneren2005/shared-memory-objects/issues"
18
17
  },
19
-
20
18
  "type": "module",
21
19
  "files": [
22
- "dist"
20
+ "src/"
23
21
  ],
24
22
  "main": "./src/main.ts",
25
23
  "scripts": {
@@ -0,0 +1,92 @@
1
+ import AllocatedMemory from '../allocated-memory';
2
+ import MemoryHeap from '../memory-heap';
3
+
4
+ describe('MemoryHeap', () => {
5
+ it('auto grows memory as needed', () => {
6
+ let memory = new MemoryHeap({ bufferSize: 200 });
7
+ const startedUsed = memory.currentUsed;
8
+
9
+ let allocated = memory.allocUI32(10);
10
+ expect(allocated.data.length).toEqual(10);
11
+ memory.allocUI32(10);
12
+ expect(memory.buffers.length).toEqual(1);
13
+ // Don't try to get exact size since actual used memory depends on how much memory malloc is using for internal representation
14
+ expect(memory.currentUsed).toBeGreaterThan(startedUsed + 20 * 4);
15
+ expect(memory.totalAllocated).toEqual(200);
16
+ const midUsed = memory.currentUsed;
17
+
18
+ memory.allocUI32(20);
19
+ expect(memory.buffers.length).toEqual(2);
20
+ expect(memory.currentUsed).toBeGreaterThan(midUsed + 20 * 4);
21
+ expect(memory.totalAllocated).toEqual(400);
22
+ });
23
+ it('can re-create from raw memory and continue working', () => {
24
+ let mainMemory = new MemoryHeap({ bufferSize: 200 });
25
+ let copyMemory = new MemoryHeap({
26
+ buffers: mainMemory.buffers.map(buffer => buffer.buf as SharedArrayBuffer)
27
+ });
28
+
29
+ let mainBlock = mainMemory.allocUI32(10);
30
+ let copyBlock = new AllocatedMemory(copyMemory, mainBlock.getSharedMemory());
31
+ expect(copyBlock.data.length).toEqual(12);
32
+
33
+ mainBlock.data[1] = 10;
34
+ expect(copyBlock.data[1]).toEqual(10);
35
+ });
36
+
37
+ it('grabs free memory from any used buffer', () => {
38
+ let memory = new MemoryHeap({ bufferSize: 200 });
39
+
40
+ let block1 = memory.allocUI32(20);
41
+ let block2 = memory.allocUI32(20);
42
+ let block3 = memory.allocUI32(20);
43
+ expect(memory.buffers.length).toEqual(3);
44
+ let maxMemory = memory.currentUsed;
45
+
46
+ block1.free();
47
+ expect(memory.currentUsed).toBeLessThan(maxMemory);
48
+ memory.allocUI32(20);
49
+ expect(memory.buffers.length).toEqual(3);
50
+ expect(memory.currentUsed).toEqual(maxMemory);
51
+
52
+ block2.free();
53
+ expect(memory.currentUsed).toBeLessThan(maxMemory);
54
+ memory.allocUI32(20);
55
+ expect(memory.buffers.length).toEqual(3);
56
+ expect(memory.currentUsed).toEqual(maxMemory);
57
+
58
+ block3.free();
59
+ expect(memory.currentUsed).toBeLessThan(maxMemory);
60
+ memory.allocUI32(20);
61
+ expect(memory.buffers.length).toEqual(3);
62
+ expect(memory.currentUsed).toEqual(maxMemory);
63
+ });
64
+
65
+ it('growing memory by doubles while freeing previous', () => {
66
+ let memory = new MemoryHeap({
67
+ bufferSize: 1_024 * 16
68
+ });
69
+ let allocSize = 40;
70
+
71
+ let oldMemory = memory.allocUI32(allocSize);
72
+ for(let i = 0; i < 5; i++) {
73
+ allocSize *= 2;
74
+ let newMemory = memory.allocUI32(allocSize);
75
+ expect(newMemory.bufferByteOffset).toBeGreaterThan(oldMemory.bufferByteOffset);
76
+
77
+ oldMemory.free();
78
+ oldMemory = newMemory;
79
+ }
80
+ });
81
+
82
+ it('Block creating with bufferSize greater than 1MB', () => {
83
+ let error: Error | null = null;
84
+ try {
85
+ new MemoryHeap({ bufferSize: Math.pow(2, 30) });
86
+ } catch(e) {
87
+ error = e as Error;
88
+ }
89
+
90
+ expect(error).not.toBeNull();
91
+ });
92
+ });
@@ -0,0 +1,23 @@
1
+ import MemoryHeap from '../memory-heap';
2
+ import { serializeObjectToMemory, createObjectFromMemory } from '../serialize-object';
3
+
4
+ describe('SerializeObject', () => {
5
+ let heap: MemoryHeap;
6
+ beforeEach(() => {
7
+ heap = new MemoryHeap();
8
+ });
9
+
10
+ it('simple', () => {
11
+ const object = {
12
+ test: 12.5,
13
+ test2: 'string',
14
+ fa: 'blue'
15
+ };
16
+ let memory = serializeObjectToMemory(heap, object);
17
+
18
+ let clone = createObjectFromMemory<typeof object>(memory);
19
+ expect(clone.test).toEqual(12.5);
20
+ expect(clone.test2).toEqual('string');
21
+ expect(clone.fa).toEqual('blue');
22
+ });
23
+ });
@@ -0,0 +1,166 @@
1
+ import { bench } from 'vitest';
2
+ import SharedList from '../shared-list';
3
+ import MemoryHeap from '../memory-heap';
4
+ import SharedMap from '../shared-map';
5
+ import SharedVector from '../shared-vector';
6
+
7
+ const INSERT_COUNT = 1_000;
8
+ describe(`Shared Data Structures: ${INSERT_COUNT} inserts`, () => {
9
+ bench('shared list', () => {
10
+ let memory = new MemoryHeap();
11
+ let list = new SharedList(memory);
12
+
13
+ for(let i = 0; i < INSERT_COUNT; i++) {
14
+ list.insert(Math.random() * 1_000_000);
15
+ }
16
+ });
17
+ bench('shared map', () => {
18
+ let memory = new MemoryHeap();
19
+ let list = new SharedMap<number>(memory);
20
+
21
+ for(let i = 0; i < INSERT_COUNT; i++) {
22
+ list.set(Math.random() * 1_000_000, Math.random() * 1_000_000);
23
+ }
24
+ });
25
+ bench('shared vector', () => {
26
+ let memory = new MemoryHeap();
27
+ let list = new SharedVector(memory);
28
+
29
+ for(let i = 0; i < INSERT_COUNT; i++) {
30
+ list.push(Math.random() * 1_000_000);
31
+ }
32
+ });
33
+ bench('shared vector with correct amount initialized', () => {
34
+ let memory = new MemoryHeap();
35
+ let list = new SharedVector(memory, {
36
+ bufferLength: INSERT_COUNT + 2
37
+ });
38
+
39
+ for(let i = 0; i < INSERT_COUNT; i++) {
40
+ list.push(Math.random() * 1_000_000);
41
+ }
42
+ });
43
+ bench('native array', () => {
44
+ let list: Array<number> = [];
45
+ for(let i = 0; i < INSERT_COUNT; i++) {
46
+ list.push(Math.random() * 1_000_000);
47
+ }
48
+ });
49
+ });
50
+
51
+ const DELETE_COUNT = 1_000;
52
+ describe(`Shared Data Structures: ${DELETE_COUNT} deletes end element`, () => {
53
+ const INSERT_COUNT = 2_000;
54
+
55
+ let sharedList: SharedList;
56
+ bench('shared list', () => {
57
+ for(let i = 0; i < DELETE_COUNT; i++) {
58
+ sharedList.deleteIndex(0);
59
+ }
60
+ }, {
61
+ setup: (task) => {
62
+ task.opts.beforeEach = () => {
63
+ let memory = new MemoryHeap();
64
+ sharedList = new SharedList(memory);
65
+ for(let i = 0; i < INSERT_COUNT; i++) {
66
+ sharedList.insert(Math.random() * 1_000_000);
67
+ }
68
+ };
69
+ }
70
+ });
71
+
72
+ let sharedVector: SharedVector;
73
+ bench('shared vector', () => {
74
+ for(let i = 0; i < DELETE_COUNT; i++) {
75
+ sharedVector.pop();
76
+ }
77
+ }, {
78
+ setup: (task) => {
79
+ task.opts.beforeEach = () => {
80
+ let memory = new MemoryHeap({
81
+ bufferSize: 1024 * 16
82
+ });
83
+ sharedVector = new SharedVector(memory);
84
+ for(let i = 0; i < INSERT_COUNT; i++) {
85
+ sharedVector.push(Math.random() * 1_000_000);
86
+ }
87
+ };
88
+ }
89
+ });
90
+
91
+ let nativeList: Array<number> = [];
92
+ bench('native array', () => {
93
+ for(let i = 0; i < DELETE_COUNT; i++) {
94
+ nativeList.pop();
95
+ }
96
+ }, {
97
+ setup: (task) => {
98
+ task.opts.beforeEach = () => {
99
+ nativeList = [];
100
+ for(let i = 0; i < INSERT_COUNT; i++) {
101
+ nativeList.push(Math.random() * 1_000_000);
102
+ }
103
+ };
104
+ }
105
+ });
106
+ });
107
+
108
+ describe(`Shared Data Structures: ${DELETE_COUNT} deletes random element`, () => {
109
+ const INSERT_COUNT = 2_000;
110
+
111
+ let sharedList: SharedList;
112
+ bench('shared list', () => {
113
+ for(let i = 0; i < DELETE_COUNT; i++) {
114
+ sharedList.deleteIndex(randomIndex(sharedList));
115
+ }
116
+ }, {
117
+ setup: (task) => {
118
+ task.opts.beforeEach = () => {
119
+ let memory = new MemoryHeap();
120
+ sharedList = new SharedList(memory);
121
+ for(let i = 0; i < INSERT_COUNT; i++) {
122
+ sharedList.insert(Math.random() * 1_000_000);
123
+ }
124
+ };
125
+ }
126
+ });
127
+
128
+ let sharedVector: SharedVector;
129
+ bench('shared vector', () => {
130
+ for(let i = 0; i < DELETE_COUNT; i++) {
131
+ sharedVector.deleteIndex(randomIndex(sharedList));
132
+ }
133
+ }, {
134
+ setup: (task) => {
135
+ task.opts.beforeEach = () => {
136
+ let memory = new MemoryHeap({
137
+ bufferSize: 1024 * 16
138
+ });
139
+ sharedVector = new SharedVector(memory);
140
+ for(let i = 0; i < INSERT_COUNT; i++) {
141
+ sharedVector.push(Math.random() * 1_000_000);
142
+ }
143
+ };
144
+ }
145
+ });
146
+
147
+ let nativeList: Array<number> = [];
148
+ bench('native array', () => {
149
+ for(let i = 0; i < DELETE_COUNT; i++) {
150
+ nativeList.splice(randomIndex(nativeList), 1);
151
+ }
152
+ }, {
153
+ setup: (task) => {
154
+ task.opts.beforeEach = () => {
155
+ nativeList = [];
156
+ for(let i = 0; i < INSERT_COUNT; i++) {
157
+ nativeList.push(Math.random() * 1_000_000);
158
+ }
159
+ };
160
+ }
161
+ });
162
+ });
163
+
164
+ function randomIndex(array: { length: number }) {
165
+ return Math.floor(Math.random() * array.length);
166
+ }
@@ -0,0 +1,247 @@
1
+ import SharedList from '../shared-list';
2
+ import MemoryHeap from '../memory-heap';
3
+
4
+ describe('SharedList', () => {
5
+ let memory: MemoryHeap;
6
+ beforeEach(() => {
7
+ memory = new MemoryHeap();
8
+ });
9
+
10
+ it('can insert items', () => {
11
+ let list = new SharedList(memory);
12
+
13
+ list.insert(5);
14
+ expect(list.length).toEqual(1);
15
+
16
+ list.insert(10);
17
+ list.insert(4);
18
+ expect(list.length).toEqual(3);
19
+
20
+ expect(flat(list)).toEqual([5, 10, 4]);
21
+ });
22
+ it('can delete items by index', () => {
23
+ let list = new SharedList(memory);
24
+ let startMemory = memory.currentUsed;
25
+ list.insert(5);
26
+ list.insert(10);
27
+ list.insert(4);
28
+ list.insert(8);
29
+ list.insert(20);
30
+ let fullMemory = memory.currentUsed;
31
+
32
+ list.deleteIndex(2);
33
+ expect(flat(list)).toEqual([5, 10, 8, 20]);
34
+ expect(memory.currentUsed).toBeLessThan(fullMemory);
35
+ expect(list.length).toEqual(4);
36
+
37
+ list.deleteIndex(0);
38
+ expect(flat(list)).toEqual([10, 8, 20]);
39
+ list.deleteIndex(2);
40
+ expect(flat(list)).toEqual([10, 8]);
41
+
42
+ // Do nothing for bug deletes
43
+ list.deleteIndex(2);
44
+ expect(flat(list)).toEqual([10, 8]);
45
+
46
+ // Delete everything and should still work
47
+ list.deleteIndex(0);
48
+ list.deleteIndex(0);
49
+ expect(flat(list)).toEqual([]);
50
+ expect(list.length).toEqual(0);
51
+ expect(memory.currentUsed).toEqual(startMemory);
52
+
53
+ list.insert(80);
54
+ list.insert(52);
55
+ expect(flat(list)).toEqual([80, 52]);
56
+ });
57
+ it('can delete items by value', () => {
58
+ let list = new SharedList(memory);
59
+ let startMemory = memory.currentUsed;
60
+ list.insert(5);
61
+ list.insert(10);
62
+ list.insert(4);
63
+ list.insert(8);
64
+ list.insert(20);
65
+ let fullMemory = memory.currentUsed;
66
+
67
+ list.deleteValue(4);
68
+ expect(flat(list)).toEqual([5, 10, 8, 20]);
69
+ expect(memory.currentUsed).toBeLessThan(fullMemory);
70
+ expect(list.length).toEqual(4);
71
+
72
+ list.deleteValue(5);
73
+ expect(flat(list)).toEqual([10, 8, 20]);
74
+ list.deleteValue(20);
75
+ expect(flat(list)).toEqual([10, 8]);
76
+
77
+ // Do nothing for bug deletes
78
+ list.deleteValue(15);
79
+ expect(flat(list)).toEqual([10, 8]);
80
+
81
+ // Delete everything and should still work
82
+ list.deleteValue(10);
83
+ list.deleteValue(8);
84
+ expect(flat(list)).toEqual([]);
85
+ expect(list.length).toEqual(0);
86
+ expect(memory.currentUsed).toEqual(startMemory);
87
+
88
+ list.insert(80);
89
+ list.insert(52);
90
+ expect(flat(list)).toEqual([80, 52]);
91
+ });
92
+
93
+ it('can delete during iteration', () => {
94
+ let list = new SharedList(memory);
95
+ list.insert(5);
96
+ list.insert(10);
97
+ list.insert(4);
98
+ list.insert(8);
99
+ list.insert(20);
100
+ let fullMemory = memory.currentUsed;
101
+
102
+ for(let { data, deleteCurrent } of list) {
103
+ if(data[0] === 10 || data[0] === 20) {
104
+ deleteCurrent();
105
+ }
106
+ }
107
+
108
+ expect(flat(list)).toEqual([5, 4, 8]);
109
+ expect(list.length).toEqual(3);
110
+ expect(memory.currentUsed).toBeLessThan(fullMemory);
111
+
112
+ // Delete concurrent indexes
113
+ for(let { data, deleteCurrent } of list) {
114
+ if(data[0] === 5 || data[0] === 4) {
115
+ deleteCurrent();
116
+ }
117
+ }
118
+ expect(flat(list)).toEqual([8]);
119
+ expect(list.length).toEqual(1);
120
+ });
121
+
122
+ it('can insert and delete over and over again without leaking memory', () => {
123
+ let list = new SharedList(memory);
124
+ list.insert(5);
125
+ list.insert(10);
126
+ let startMemory = memory.currentUsed;
127
+
128
+ for(let i = 0; i < 10; i++) {
129
+ list.insert(70);
130
+ list.deleteIndex(2);
131
+ }
132
+ for(let i = 0; i < 10; i++) {
133
+ list.insert(70);
134
+ list.deleteValue(70);
135
+ }
136
+
137
+ expect(memory.currentUsed).toEqual(startMemory);
138
+ });
139
+
140
+ it('can share memory and insert/delete items from either instance', () => {
141
+ let mainList = new SharedList(memory);
142
+ let secondList = new SharedList(memory, mainList.getSharedMemory());
143
+
144
+ mainList.insert(5);
145
+ mainList.insert(60);
146
+ secondList.insert(14);
147
+ mainList.insert(8);
148
+ secondList.deleteIndex(1);
149
+
150
+ expect(flat(mainList)).toEqual([5, 14, 8]);
151
+ expect(flat(secondList)).toEqual([5, 14, 8]);
152
+ });
153
+ it.skip('can delete item mid-iteration');
154
+
155
+ it('free', () => {
156
+ let startMemory = memory.currentUsed;
157
+ let list = new SharedList(memory);
158
+ list.insert(5);
159
+ list.insert(10);
160
+ list.insert(4);
161
+
162
+ list.free();
163
+ expect(memory.currentUsed).toEqual(startMemory);
164
+ });
165
+
166
+ it('with int32', () => {
167
+ let list = new SharedList(memory, {
168
+ type: Int32Array
169
+ });
170
+
171
+ list.insert(5);
172
+ expect(list.length).toEqual(1);
173
+
174
+ list.insert(-10);
175
+ list.insert(4);
176
+ expect(list.length).toEqual(3);
177
+
178
+ expect(flat(list)).toEqual([5, -10, 4]);
179
+ });
180
+ it('with float32', () => {
181
+ let list = new SharedList(memory, {
182
+ type: Float32Array
183
+ });
184
+
185
+ list.insert(5.5);
186
+ expect(list.length).toEqual(1);
187
+
188
+ list.insert(-10);
189
+ list.insert(4);
190
+ expect(list.length).toEqual(3);
191
+
192
+ expect(flat(list)).toEqual([5.5, -10, 4]);
193
+ });
194
+
195
+ it('with dataLength = 3', () => {
196
+ let list = new SharedList(memory, {
197
+ type: Int32Array,
198
+ dataLength: 3
199
+ });
200
+
201
+ list.insert(5);
202
+ expect(list.length).toEqual(1);
203
+
204
+ list.insert([-10, 20, 1]);
205
+ list.insert([4, -40]);
206
+ expect(list.length).toEqual(3);
207
+
208
+ expect(flat(list)).toEqual([
209
+ 5, 0, 0,
210
+ -10, 20, 1,
211
+ 4, -40, 0
212
+ ]);
213
+
214
+ // Don't delete middle values
215
+ list.deleteValue(20);
216
+ expect(flat(list)).toEqual([
217
+ 5, 0, 0,
218
+ -10, 20, 1,
219
+ 4, -40, 0
220
+ ]);
221
+
222
+ // Allow deleting first value only
223
+ list.deleteValue(-10);
224
+ expect(flat(list)).toEqual([
225
+ 5, 0, 0,
226
+ 4, -40, 0
227
+ ]);
228
+ expect(list.length).toEqual(2);
229
+
230
+ // Allow deleting entire set of values
231
+ list.deleteValue([4, 10, 0]);
232
+ list.deleteValue([5, 0, 0]);
233
+ expect(flat(list)).toEqual([
234
+ 4, -40, 0
235
+ ]);
236
+ expect(list.length).toEqual(1);
237
+ });
238
+ });
239
+
240
+ function flat(list: SharedList<any>) {
241
+ return [...list].reduce((array, value) => {
242
+ // @ts-expect-error
243
+ array.push(...value.data);
244
+
245
+ return array;
246
+ }, []);
247
+ }
@@ -0,0 +1,98 @@
1
+ import MemoryHeap from '../memory-heap';
2
+ import SharedMap from '../shared-map';
3
+
4
+ describe('SharedMap', () => {
5
+ let memory: MemoryHeap;
6
+ beforeEach(() => {
7
+ memory = new MemoryHeap();
8
+ });
9
+
10
+ describe('SharedMap<string>', () => {
11
+ it('insert and remove items', () => {
12
+ let map = new SharedMap<string>(memory);
13
+
14
+ // NOTE: If values are changed make sure we have at least one hash collision
15
+ map.set('ds', 3);
16
+ map.set('asd', 72);
17
+ map.set('gfredf', 8);
18
+ map.set('z', 65);
19
+ // Map should only have one instance of each key - replace
20
+ map.set('ds', 1);
21
+ map.set('red', 10);
22
+ expect(map.length).toEqual(5);
23
+
24
+ expect(map.get('gfredf')).toEqual(8);
25
+ expect(map.get('ds')).toEqual(1);
26
+ expect(map.get('z')).toEqual(65);
27
+ expect(map.get('blue')).toBeUndefined();
28
+ expect(map.get('red')).toEqual(10);
29
+
30
+ map.delete('ds');
31
+ expect(map.length).toEqual(4);
32
+ expect(map.get('ds')).toEqual(undefined);
33
+ map.delete('ds');
34
+ expect(map.length).toEqual(4);
35
+
36
+ map.delete('dfsdfsd');
37
+ expect(map.length).toEqual(4);
38
+ map.delete('gfredf');
39
+ expect(map.length).toEqual(3);
40
+ });
41
+
42
+ it('free', () => {
43
+ let startMemory = memory.currentUsed;
44
+ let map = new SharedMap<string>(memory);
45
+ map.set('ds', 3);
46
+ map.set('asd', 72);
47
+ map.set('gfredf', 8);
48
+ map.set('z', 65);
49
+
50
+ map.free();
51
+ expect(memory.currentUsed).toEqual(startMemory);
52
+ });
53
+
54
+ it('can be created from shared memory', () => {
55
+ let startMemory = memory.currentUsed;
56
+ let mainMap = new SharedMap<string>(memory);
57
+ let cloneMap = new SharedMap<string>(memory, mainMap.getSharedMemory());
58
+
59
+ mainMap.set('ds', 3);
60
+ mainMap.set('asd', 72);
61
+ cloneMap.set('ds', 1);
62
+ cloneMap.set('red', 10);
63
+
64
+ expect(mainMap.length).toEqual(3);
65
+ expect(cloneMap.length).toEqual(3);
66
+
67
+ expect(mainMap.get('ds')).toEqual(1);
68
+ expect(cloneMap.get('ds')).toEqual(1);
69
+
70
+ cloneMap.free();
71
+ expect(memory.currentUsed).toEqual(startMemory);
72
+ });
73
+
74
+ it('inserting many items will grow hash table', () => {
75
+ let map = new SharedMap<string>(memory);
76
+ const previousMaxHash = map.maxHash;
77
+ for(let i = 0; i < 5; i++) {
78
+ insertRandom(map);
79
+ }
80
+ expect(map.length).toEqual(5);
81
+
82
+ expect(map.maxHash).toEqual(previousMaxHash);
83
+ for(let i = 0; i < 20; i++) {
84
+ insertRandom(map);
85
+ }
86
+ expect(map.length).toEqual(25);
87
+ expect(map.maxHash).toBeGreaterThan(previousMaxHash);
88
+
89
+ for(let i = 0; i < map.length; i++) {
90
+ expect(map.get(`${i + 1}`)).not.toBeUndefined();
91
+ }
92
+ });
93
+
94
+ function insertRandom(map: SharedMap<string>) {
95
+ map.set(`${map.length + 1}`, Math.random() * 1_000_000);
96
+ }
97
+ });
98
+ });