bumparena 0.0.5 → 0.0.6
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 +3 -2
- package/arena.ts +0 -277
- package/bench/arena.ts +0 -75
- package/bench/array.ts +0 -61
- package/bench/generateTestdata.ts +0 -3
- package/bench/init.ts +0 -38
- package/dist/arena.d.ts +0 -58
- package/dist/arena.js +0 -178
- package/test/directAccess.ts +0 -28
- package/test/ptrs.ts +0 -37
- package/test/reserve.ts +0 -28
- package/test/test.test.ts +0 -12
- package/tsconfig.json +0 -21
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bumparena",
|
|
3
|
-
"version":"0.0.
|
|
3
|
+
"version":"0.0.6",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/arena.js",
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
"license":"MIT",
|
|
10
10
|
"scripts": {
|
|
11
11
|
"build": "tsc",
|
|
12
|
-
"test": "node ./dist/
|
|
12
|
+
"test": "node ./dist/test/test.test.js",
|
|
13
|
+
"pub": "tsc; npm publish"
|
|
13
14
|
},
|
|
14
15
|
"repository": {
|
|
15
16
|
"type": "git",
|
package/arena.ts
DELETED
|
@@ -1,277 +0,0 @@
|
|
|
1
|
-
export type ArenaLocation = bigint & { readonly __data_pointer: unique symbol };
|
|
2
|
-
export interface ArenaOptions {
|
|
3
|
-
initalSize?: number
|
|
4
|
-
littleEndian?: boolean
|
|
5
|
-
allignment?: 8 | 16 | 32 | 64
|
|
6
|
-
bucketOffsets?: number[];
|
|
7
|
-
bucketCapacities?: number[];
|
|
8
|
-
}
|
|
9
|
-
export interface ArenaCustomHeaders {
|
|
10
|
-
header0: number,
|
|
11
|
-
header1: number,
|
|
12
|
-
header2: number
|
|
13
|
-
}
|
|
14
|
-
export interface ArenaHeaders {
|
|
15
|
-
totalLength: number
|
|
16
|
-
payloadlength: number
|
|
17
|
-
deleted: boolean
|
|
18
|
-
header0: number
|
|
19
|
-
header1: number
|
|
20
|
-
header2: number
|
|
21
|
-
}
|
|
22
|
-
const enum HEADERS {
|
|
23
|
-
TOTAL_LENGTH_0_32 = 0,
|
|
24
|
-
PAYLOAD_LENGTH_0_32 = 1,
|
|
25
|
-
GENERATION_BYTE_0_32 = 2,
|
|
26
|
-
DELETED_8 = 12,
|
|
27
|
-
USER_STATUS_0_8 = 13,
|
|
28
|
-
USER_STATUS_1_8 = 14,
|
|
29
|
-
USER_STATUS_2_8 = 15,
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export class Arena {
|
|
33
|
-
private HEADER_SIZE_BYTES = 16
|
|
34
|
-
private _buffer: ArrayBuffer
|
|
35
|
-
private _view8: Uint8Array
|
|
36
|
-
private _view32: Uint32Array
|
|
37
|
-
private _offset: number
|
|
38
|
-
private _emptySpots: Uint32Array
|
|
39
|
-
private _allignMask: 7 | 15 | 31 | 63
|
|
40
|
-
private _allignShift: number
|
|
41
|
-
private _bucketOffsets: number[];
|
|
42
|
-
private _bucketCapacities: number[];
|
|
43
|
-
private _bucketcount: number;
|
|
44
|
-
|
|
45
|
-
constructor(options?: ArenaOptions) {
|
|
46
|
-
this._buffer = new ArrayBuffer(options?.initalSize || 64 * 1024)
|
|
47
|
-
this._view8 = new Uint8Array(this._buffer);
|
|
48
|
-
this._view32 = new Uint32Array(this._buffer)
|
|
49
|
-
this._offset = 0;
|
|
50
|
-
//@ts-ignore
|
|
51
|
-
this._allignMask = ((options?.allignment || 8) - 1!) as number
|
|
52
|
-
this._allignShift = Math.log2(this._allignMask + 1);
|
|
53
|
-
this._bucketCapacities = options?.bucketCapacities || [1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024]
|
|
54
|
-
this._bucketOffsets = [];
|
|
55
|
-
this._bucketcount = this._bucketCapacities.length;
|
|
56
|
-
let currentOffset = 0;
|
|
57
|
-
for (const cap of this._bucketCapacities) {
|
|
58
|
-
this._bucketOffsets.push(currentOffset)
|
|
59
|
-
currentOffset += (cap + 1)
|
|
60
|
-
}
|
|
61
|
-
this._emptySpots = new Uint32Array(currentOffset)
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
private _u(n: number): number {
|
|
65
|
-
return n >>> 0;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
private _idx32(byteOffset: number): number {
|
|
69
|
-
return (byteOffset >>> 0) >> 2;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
private _makePtr(offset: number, gen: number | bigint): ArenaLocation {
|
|
73
|
-
const bOffset = BigInt(offset >>> 0);
|
|
74
|
-
const bGen = BigInt(gen ?? 0);
|
|
75
|
-
return ((bOffset << 32n) | (bGen & 0xFFFFFFFFn)) as ArenaLocation;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
private _getOffset(ptr: ArenaLocation): number {
|
|
79
|
-
return Number(BigInt(ptr) >> 32n) >>> 0;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
private _getBucketCount(bucketIdx: number): number {
|
|
83
|
-
return this._emptySpots[this._bucketOffsets[bucketIdx]!]!;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
private _setBucketCount(bucketIdx: number, count: number): void {
|
|
87
|
-
this._emptySpots[this._bucketOffsets[bucketIdx]!] = count;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
private _getBucketOffset(bucketIdx: number, slotIdx: number): number {
|
|
91
|
-
return this._emptySpots[this._bucketOffsets[bucketIdx]! + slotIdx + 1]!;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
private _setBucketOffset(bucketIdx: number, slotIdx: number, offset: number): void {
|
|
95
|
-
this._emptySpots[this._bucketOffsets[bucketIdx]! + slotIdx + 1] = offset;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
private _initBlock(start: number, dataLength: number, headers: { header0: number, header1: number, header2: number }) {
|
|
99
|
-
const idx = this._idx32(start)
|
|
100
|
-
const { header0: h0, header1: h1, header2: h2 } = headers;
|
|
101
|
-
|
|
102
|
-
this._view32[idx + HEADERS.TOTAL_LENGTH_0_32] = this._u((dataLength + this.HEADER_SIZE_BYTES + this._allignMask) & ~this._allignMask);
|
|
103
|
-
this._view32[idx + HEADERS.PAYLOAD_LENGTH_0_32] = dataLength;
|
|
104
|
-
|
|
105
|
-
this._view32[idx + (HEADERS.DELETED_8 >> 2)] = ((h2 || 0) << 24) | ((h1 || 0) << 16) | ((h0 || 0) << 8);
|
|
106
|
-
this._view8[start + HEADERS.DELETED_8] = 0;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
public alloc(data: Uint8Array, headers?: ArenaCustomHeaders): ArenaLocation {
|
|
110
|
-
headers ||= { header0: 0, header1: 0, header2: 0 };
|
|
111
|
-
|
|
112
|
-
const needed = (data.byteLength + this.HEADER_SIZE_BYTES + this._allignMask) & ~this._allignMask;
|
|
113
|
-
const bucketIdx = (needed >> this._allignShift) - 1;
|
|
114
|
-
if (bucketIdx >= 0 && bucketIdx < this._bucketcount) {
|
|
115
|
-
const count = this._getBucketCount(bucketIdx);
|
|
116
|
-
if (count > 0) {
|
|
117
|
-
const recycledOffset = this._u(this._getBucketOffset(bucketIdx, count - 1));
|
|
118
|
-
this._setBucketCount(bucketIdx, count - 1);
|
|
119
|
-
this._initBlock(recycledOffset, data.length, headers);
|
|
120
|
-
|
|
121
|
-
const gen = this._view32[this._idx32(recycledOffset) + HEADERS.GENERATION_BYTE_0_32]!;
|
|
122
|
-
return this._makePtr(recycledOffset, gen)
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
const start = this._u(this._offset)
|
|
126
|
-
if (this._checkForSpace(needed)) this._resize()
|
|
127
|
-
this._initBlock(start, data.byteLength, headers)
|
|
128
|
-
|
|
129
|
-
this._view8.set(data, start + this.HEADER_SIZE_BYTES)
|
|
130
|
-
this._offset = this._u((start + needed) & ~this._allignMask);
|
|
131
|
-
|
|
132
|
-
const gen = this._view32[this._idx32(start) + HEADERS.GENERATION_BYTE_0_32]!
|
|
133
|
-
return this._makePtr(start, gen)
|
|
134
|
-
}
|
|
135
|
-
public read(location: ArenaLocation): Uint8Array | null {
|
|
136
|
-
const { start, generation } = this.translate(location);
|
|
137
|
-
const idx = this._idx32(start)
|
|
138
|
-
const currgen = BigInt(this._view32[idx + HEADERS.GENERATION_BYTE_0_32]!)
|
|
139
|
-
|
|
140
|
-
if (generation !== currgen) return null
|
|
141
|
-
if (this._view8[start + HEADERS.DELETED_8] === 1) return null
|
|
142
|
-
|
|
143
|
-
const dataLength = this._view32[idx + HEADERS.PAYLOAD_LENGTH_0_32]!;
|
|
144
|
-
return this._view8.subarray(start + this.HEADER_SIZE_BYTES, start + this.HEADER_SIZE_BYTES + dataLength);
|
|
145
|
-
}
|
|
146
|
-
public free(location: ArenaLocation): ArenaLocation {
|
|
147
|
-
const { start, generation: _ } = this.translate(location)
|
|
148
|
-
const idx = this._idx32(start)
|
|
149
|
-
const currgen = this._view32[idx + HEADERS.GENERATION_BYTE_0_32]!
|
|
150
|
-
|
|
151
|
-
this._view32[idx + HEADERS.GENERATION_BYTE_0_32] = currgen + 1
|
|
152
|
-
this._view8[this._u(start) + HEADERS.DELETED_8] = 1
|
|
153
|
-
|
|
154
|
-
const totalBlockSize = this._view32[this._idx32(start) + HEADERS.TOTAL_LENGTH_0_32]!
|
|
155
|
-
const bucketIdx = (totalBlockSize >> this._allignShift) - 1
|
|
156
|
-
if (bucketIdx >= 0 && bucketIdx < this._bucketcount) {
|
|
157
|
-
const count = this._getBucketCount(bucketIdx)
|
|
158
|
-
const capacity = this._bucketCapacities[bucketIdx]!
|
|
159
|
-
if (count < capacity) {
|
|
160
|
-
this._setBucketOffset(bucketIdx, count, start);
|
|
161
|
-
this._setBucketCount(bucketIdx, count + 1);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
return this._makePtr(0, 0) as ArenaLocation
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
private _checkForSpace(size: number): boolean {
|
|
168
|
-
if (this._buffer.byteLength >= (this._offset + size)) return false
|
|
169
|
-
return true
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
private _resize() {
|
|
173
|
-
//@ts-ignore
|
|
174
|
-
this._buffer = this._buffer.transfer(this._buffer.byteLength * 2)
|
|
175
|
-
this._view8 = new Uint8Array(this._buffer)
|
|
176
|
-
this._view32 = new Uint32Array(this._buffer)
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
public size(): number {
|
|
180
|
-
return this._buffer.byteLength
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
public getBuffer(): Uint8Array {
|
|
184
|
-
return this._view8.subarray(0, this._u(this._offset))
|
|
185
|
-
}
|
|
186
|
-
public reserve(size: number): Uint8Array {
|
|
187
|
-
const start = this._u(this._offset)
|
|
188
|
-
this._checkForSpace(start + this.HEADER_SIZE_BYTES + size + this._allignMask & ~this._allignMask) && this._resize()
|
|
189
|
-
this._initBlock(start, size, { header0: 0, header1: 0, header2: 0 })
|
|
190
|
-
this._offset = (this._offset + size + this.HEADER_SIZE_BYTES + this._allignMask & ~this._allignMask) >>> 0
|
|
191
|
-
return this._view8.subarray(start + this.HEADER_SIZE_BYTES, start + this.HEADER_SIZE_BYTES + size)
|
|
192
|
-
}
|
|
193
|
-
public translate(ptr: ArenaLocation) {
|
|
194
|
-
const start = this._getOffset(ptr)
|
|
195
|
-
const generation = ptr & 0xFFFFFFFFn;
|
|
196
|
-
return { start, generation }
|
|
197
|
-
}
|
|
198
|
-
public readWithHeaders(ptr: ArenaLocation): Uint8Array | null {
|
|
199
|
-
const { start, generation } = this.translate(ptr)
|
|
200
|
-
let idx32 = this._idx32(start)
|
|
201
|
-
let length = this._view32[idx32 + HEADERS.PAYLOAD_LENGTH_0_32]!
|
|
202
|
-
if (BigInt(this._view32[idx32 + HEADERS.GENERATION_BYTE_0_32]!) !== generation) return null
|
|
203
|
-
return this._view8.subarray(start + 12, start + this.HEADER_SIZE_BYTES + Number(length))
|
|
204
|
-
}
|
|
205
|
-
public label(): Array<ArenaLocation> {
|
|
206
|
-
const ptrArray = new Array<ArenaLocation>()
|
|
207
|
-
const limit = this._offset;
|
|
208
|
-
let pos = 0
|
|
209
|
-
while (this._u(pos) < this._u(limit)) {
|
|
210
|
-
const totalLength = this._view32[this._idx32(pos)]!;
|
|
211
|
-
if (totalLength === 0) {
|
|
212
|
-
pos = this._idx32((pos + 16) & ~15)
|
|
213
|
-
continue;
|
|
214
|
-
}
|
|
215
|
-
const isDeleted = this._view8[this._u(pos) + HEADERS.DELETED_8] === 1
|
|
216
|
-
if (!isDeleted) {
|
|
217
|
-
ptrArray.push(
|
|
218
|
-
this._makePtr(pos, this._view32[this._idx32(pos) + HEADERS.GENERATION_BYTE_0_32]!)
|
|
219
|
-
);
|
|
220
|
-
}
|
|
221
|
-
pos += totalLength
|
|
222
|
-
if (totalLength === 0) break;
|
|
223
|
-
}
|
|
224
|
-
return ptrArray
|
|
225
|
-
}
|
|
226
|
-
public getHeaders(ptr: ArenaLocation): ArenaHeaders {
|
|
227
|
-
const { start, generation: _ } = this.translate(ptr)
|
|
228
|
-
const idx = this._u(start)
|
|
229
|
-
return {
|
|
230
|
-
totalLength: Number(this._view32[this._idx32(start) + HEADERS.TOTAL_LENGTH_0_32]!.toString()),
|
|
231
|
-
payloadlength: Number(this._view32[this._idx32(start) + HEADERS.PAYLOAD_LENGTH_0_32]!.toString()),
|
|
232
|
-
deleted: this._view8[idx + HEADERS.DELETED_8] === 1,
|
|
233
|
-
header0: Number(this._view8[idx + HEADERS.USER_STATUS_0_8]!.toString()),
|
|
234
|
-
header1: Number(this._view8[idx + HEADERS.USER_STATUS_1_8]!.toString()),
|
|
235
|
-
header2: Number(this._view8[idx + HEADERS.USER_STATUS_2_8]!.toString()),
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
public estimate(size: number, amnt: number): number {
|
|
239
|
-
return (((size + this._allignMask) & ~this._allignMask) + this.HEADER_SIZE_BYTES) * amnt
|
|
240
|
-
}
|
|
241
|
-
public directAlloc(source: Uint8Array, startn: number, endn: number) {
|
|
242
|
-
const start = this._u(this._offset)
|
|
243
|
-
const len = endn - startn;
|
|
244
|
-
const needed = (len + this.HEADER_SIZE_BYTES + this._allignMask) & ~this._allignMask;
|
|
245
|
-
const bucketIdx = (needed >> this._allignShift) - 1;
|
|
246
|
-
if (bucketIdx >= 0 && bucketIdx < this._bucketcount) {
|
|
247
|
-
const count = this._getBucketCount(bucketIdx);
|
|
248
|
-
if (count > 0) {
|
|
249
|
-
const recycledOffset = this._u(this._getBucketOffset(bucketIdx, count - 1));
|
|
250
|
-
this._setBucketCount(bucketIdx, count - 1);
|
|
251
|
-
this._initBlock(recycledOffset, needed, { header0: 0, header1: 0, header2: 0 });
|
|
252
|
-
const gen = this._view32[this._idx32(recycledOffset) + HEADERS.GENERATION_BYTE_0_32]!;
|
|
253
|
-
this._view8.set(source.subarray(startn, endn), recycledOffset + this.HEADER_SIZE_BYTES)
|
|
254
|
-
return this._makePtr(recycledOffset, gen)
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
if (this._checkForSpace(needed)) this._resize()
|
|
258
|
-
this._initBlock(start, len, { header0: 0, header1: 0, header2: 0 });
|
|
259
|
-
this._offset = this._u((start + needed) & ~this._allignMask);
|
|
260
|
-
this._view8.set(source.subarray(startn, endn), start + this.HEADER_SIZE_BYTES)
|
|
261
|
-
const gen = this._view32[this._idx32(start) + HEADERS.GENERATION_BYTE_0_32]!
|
|
262
|
-
return this._makePtr(start, gen)
|
|
263
|
-
|
|
264
|
-
// if (this._checkForSpace(needed)) this._resize()
|
|
265
|
-
// this._initBlock(start, needed, { header0: 0, header1: 0, header2: 0 })
|
|
266
|
-
// this._view8.set(source.subarray(startn, endn), start + this.HEADER_SIZE_BYTES)
|
|
267
|
-
// this._offset = this._u((start + needed) & ~this._allignMask);
|
|
268
|
-
//
|
|
269
|
-
// const gen = this._view32[this._idx32(start) + HEADERS.GENERATION_BYTE_0_32]!
|
|
270
|
-
// return this._makePtr(start, gen)
|
|
271
|
-
|
|
272
|
-
}
|
|
273
|
-
public clear() {
|
|
274
|
-
this._offset = 0
|
|
275
|
-
this._emptySpots.fill(0)
|
|
276
|
-
}
|
|
277
|
-
}
|
package/bench/arena.ts
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { pipeline } from "node:stream/promises";
|
|
2
|
-
import { Arena } from "../arena";
|
|
3
|
-
import { stream } from "./init.ts"
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
let tmp: Uint8Array | null = null
|
|
7
|
-
function getNewArenaTransform() {
|
|
8
|
-
return new WritableStream<Uint8Array>({
|
|
9
|
-
write(chunk) {
|
|
10
|
-
let sourceIdx = 0;
|
|
11
|
-
const chunkLen = chunk.byteLength;
|
|
12
|
-
|
|
13
|
-
if (tmp) {
|
|
14
|
-
const nextNewline = chunk.indexOf(10);
|
|
15
|
-
if (nextNewline !== -1) {
|
|
16
|
-
const totalLen = tmp.byteLength + nextNewline;
|
|
17
|
-
const target = new Uint8Array(totalLen)
|
|
18
|
-
target.set(tmp, 0);
|
|
19
|
-
target.set(chunk.subarray(0, nextNewline), tmp.byteLength);
|
|
20
|
-
arena.directAlloc(target, 0, target.length)
|
|
21
|
-
sourceIdx = nextNewline + 1;
|
|
22
|
-
tmp = null;
|
|
23
|
-
} else {
|
|
24
|
-
const newTmp = new Uint8Array(tmp.byteLength + chunkLen);
|
|
25
|
-
newTmp.set(tmp);
|
|
26
|
-
newTmp.set(chunk);
|
|
27
|
-
tmp = newTmp;
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// 2. Schnelle Schleife für den Rest des Chunks
|
|
33
|
-
while (true) {
|
|
34
|
-
const idx = chunk.indexOf(10, sourceIdx);
|
|
35
|
-
if (idx === -1) break;
|
|
36
|
-
|
|
37
|
-
const lineLength = idx - sourceIdx;
|
|
38
|
-
if (lineLength > 0) {
|
|
39
|
-
arena.directAlloc(chunk, 0, lineLength);
|
|
40
|
-
}
|
|
41
|
-
sourceIdx = idx + 1;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (sourceIdx < chunkLen) {
|
|
45
|
-
tmp = chunk.slice(sourceIdx); // slice kopiert hier einmalig den Rest
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
close() {
|
|
49
|
-
if (tmp) {
|
|
50
|
-
arena.directAlloc(tmp, 0, tmp.length)
|
|
51
|
-
tmp = null
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
})
|
|
55
|
-
};
|
|
56
|
-
const arena = new Arena();
|
|
57
|
-
(async () => {
|
|
58
|
-
const start = performance.now()
|
|
59
|
-
for (let i = 0; i < 10; i++) {
|
|
60
|
-
tmp = null
|
|
61
|
-
const arenatransform = getNewArenaTransform()
|
|
62
|
-
const reader = stream()
|
|
63
|
-
|
|
64
|
-
await pipeline(
|
|
65
|
-
reader,
|
|
66
|
-
arenatransform
|
|
67
|
-
)
|
|
68
|
-
const _ptr = arena.label()
|
|
69
|
-
arena.clear()
|
|
70
|
-
}
|
|
71
|
-
const end = performance.now()
|
|
72
|
-
console.log(`${(end - start).toFixed(2)} ms`)
|
|
73
|
-
console.log(process.memoryUsage())
|
|
74
|
-
console.log(`arenasize: ${arena.size() / 1024} KB`)
|
|
75
|
-
})();
|
package/bench/array.ts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { pipeline } from "node:stream/promises";
|
|
2
|
-
import { noopWriter, stream } from "./init.ts"
|
|
3
|
-
|
|
4
|
-
const array: Array<string> = [];
|
|
5
|
-
const decode = new TextDecoder()
|
|
6
|
-
let tmp: Uint8Array | null = null // Rest vom vorherigen Chunk
|
|
7
|
-
function getNewArrayTransformStream() {
|
|
8
|
-
return new WritableStream<Uint8Array>({
|
|
9
|
-
write(chunk) {
|
|
10
|
-
if (tmp) {
|
|
11
|
-
const combined = new Uint8Array(tmp.byteLength + chunk.byteLength)
|
|
12
|
-
combined.set(tmp, 0)
|
|
13
|
-
combined.set(chunk, tmp.byteLength)
|
|
14
|
-
chunk = combined
|
|
15
|
-
tmp = null
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
let firstIdx = 0
|
|
19
|
-
let idx: number
|
|
20
|
-
|
|
21
|
-
while ((idx = chunk.indexOf(10, firstIdx)) !== -1) {
|
|
22
|
-
const lineLength = idx - firstIdx
|
|
23
|
-
if (lineLength > 0) {
|
|
24
|
-
array.push(decode.decode(chunk.subarray(firstIdx, idx)!)!)
|
|
25
|
-
}
|
|
26
|
-
firstIdx = idx + 1
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (firstIdx < chunk.byteLength) {
|
|
30
|
-
tmp = chunk.subarray(firstIdx)
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
close() {
|
|
34
|
-
if (tmp) {
|
|
35
|
-
//@ts-ignore
|
|
36
|
-
array.push(tmp)
|
|
37
|
-
tmp = null
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
})
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
await (async () => {
|
|
45
|
-
const start = performance.now()
|
|
46
|
-
for (let i = 0; i < 100; i++) {
|
|
47
|
-
array.length = 0
|
|
48
|
-
tmp = null
|
|
49
|
-
const arraytransform = getNewArrayTransformStream()
|
|
50
|
-
const reader = stream()
|
|
51
|
-
await pipeline(
|
|
52
|
-
reader,
|
|
53
|
-
arraytransform,
|
|
54
|
-
)
|
|
55
|
-
for (const iter of array) noopWriter(iter)
|
|
56
|
-
}
|
|
57
|
-
const end = performance.now()
|
|
58
|
-
console.log(`${(end - start).toFixed(2)} ms`)
|
|
59
|
-
console.log(`Count: ${array.length}`)
|
|
60
|
-
console.log(process.memoryUsage())
|
|
61
|
-
})();
|
package/bench/init.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { createWriteStream, createReadStream } from "node:fs";
|
|
2
|
-
const testfile = "test3.txt"
|
|
3
|
-
function splitmix32(a: number) {
|
|
4
|
-
return function() {
|
|
5
|
-
a |= 0; a = a + 0x9e3779b9 | 0;
|
|
6
|
-
let t = a ^ a >>> 16; t = Math.imul(t, 0x21f0aaad);
|
|
7
|
-
t = t ^ t >>> 15; t = Math.imul(t, 0x735a2d97);
|
|
8
|
-
return ((t = t ^ t >>> 15) >>> 0) / 4294967296;
|
|
9
|
-
}
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export function noopWriter(_data: any) { return true; };
|
|
13
|
-
|
|
14
|
-
async function generateTestData(seed: number, count: number) {
|
|
15
|
-
const writer = createWriteStream(testfile, { highWaterMark: 16 * 1024 * 1024 })
|
|
16
|
-
const random = splitmix32(seed)
|
|
17
|
-
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
18
|
-
for (let i = 0; i < count; i++) {
|
|
19
|
-
let str = '';
|
|
20
|
-
const len = Math.floor(random() * 5) + 10;
|
|
21
|
-
for (let j = 0; j < len; j++) {
|
|
22
|
-
str += characters.charAt(Math.floor(Math.random() * characters.length));
|
|
23
|
-
}
|
|
24
|
-
if (!writer.write(str + "\n")) {
|
|
25
|
-
await new Promise((res, _rej) => {
|
|
26
|
-
writer.once("drain", res)
|
|
27
|
-
})
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
export const stream = () => {
|
|
33
|
-
return createReadStream(testfile)
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export function generateData(seed: number, count: number) {
|
|
37
|
-
generateTestData(seed, count);
|
|
38
|
-
}
|
package/dist/arena.d.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
export type ArenaLocation = bigint & {
|
|
2
|
-
readonly __data_pointer: unique symbol;
|
|
3
|
-
};
|
|
4
|
-
export interface ArenaOptions {
|
|
5
|
-
initalSize?: number;
|
|
6
|
-
littleEndian?: boolean;
|
|
7
|
-
allignment?: 8 | 16 | 32 | 64;
|
|
8
|
-
bucketOffsets?: number[];
|
|
9
|
-
bucketCapacities?: number[];
|
|
10
|
-
}
|
|
11
|
-
export interface ArenaCustomHeaders {
|
|
12
|
-
header0: number;
|
|
13
|
-
header1: number;
|
|
14
|
-
header2: number;
|
|
15
|
-
}
|
|
16
|
-
export interface ArenaHeaders {
|
|
17
|
-
totalLength: number;
|
|
18
|
-
payloadlength: number;
|
|
19
|
-
deleted: boolean;
|
|
20
|
-
header0: number;
|
|
21
|
-
header1: number;
|
|
22
|
-
header2: number;
|
|
23
|
-
}
|
|
24
|
-
export declare class Arena {
|
|
25
|
-
private HEADER_SIZE_BYTES;
|
|
26
|
-
private _buffer;
|
|
27
|
-
private _view8;
|
|
28
|
-
private _view32;
|
|
29
|
-
private _offset;
|
|
30
|
-
private _emptySpots;
|
|
31
|
-
private _allignMask;
|
|
32
|
-
private _allignShift;
|
|
33
|
-
private _bucketOffsets;
|
|
34
|
-
private _bucketCapacities;
|
|
35
|
-
private _bucketcount;
|
|
36
|
-
constructor(options?: ArenaOptions);
|
|
37
|
-
private _getBucketCount;
|
|
38
|
-
private _setBucketCount;
|
|
39
|
-
private _getBucketOffset;
|
|
40
|
-
private _setBucketOffset;
|
|
41
|
-
private _initBlock;
|
|
42
|
-
alloc(data: Uint8Array, headers?: ArenaCustomHeaders): ArenaLocation;
|
|
43
|
-
read(location: ArenaLocation): Uint8Array | null;
|
|
44
|
-
free(location: ArenaLocation): ArenaLocation;
|
|
45
|
-
private _checkForSpace;
|
|
46
|
-
private _resize;
|
|
47
|
-
size(): number;
|
|
48
|
-
getBuffer(): Uint8Array;
|
|
49
|
-
reserve(size: number): Uint8Array;
|
|
50
|
-
translate(ptr: ArenaLocation): {
|
|
51
|
-
start: number;
|
|
52
|
-
generation: bigint;
|
|
53
|
-
};
|
|
54
|
-
readWithHeaders(ptr: ArenaLocation): Uint8Array | null;
|
|
55
|
-
label(): Array<ArenaLocation>;
|
|
56
|
-
getHeaders(ptr: ArenaLocation): ArenaHeaders;
|
|
57
|
-
estimate(size: number, amnt: number): number;
|
|
58
|
-
}
|
package/dist/arena.js
DELETED
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
var SHIFTOFFSETS;
|
|
2
|
-
(function (SHIFTOFFSETS) {
|
|
3
|
-
SHIFTOFFSETS[SHIFTOFFSETS["BYTE_8"] = 0] = "BYTE_8";
|
|
4
|
-
SHIFTOFFSETS[SHIFTOFFSETS["BYTE_16"] = 1] = "BYTE_16";
|
|
5
|
-
SHIFTOFFSETS[SHIFTOFFSETS["BYTE_32"] = 2] = "BYTE_32";
|
|
6
|
-
SHIFTOFFSETS[SHIFTOFFSETS["BYTE_64"] = 3] = "BYTE_64";
|
|
7
|
-
})(SHIFTOFFSETS || (SHIFTOFFSETS = {}));
|
|
8
|
-
var HEADERS;
|
|
9
|
-
(function (HEADERS) {
|
|
10
|
-
HEADERS[HEADERS["TOTAL_LENGTH_0_32"] = 0] = "TOTAL_LENGTH_0_32";
|
|
11
|
-
HEADERS[HEADERS["PAYLOAD_LENGTH_0_32"] = 1] = "PAYLOAD_LENGTH_0_32";
|
|
12
|
-
HEADERS[HEADERS["GENERATION_BYTE_0_32"] = 2] = "GENERATION_BYTE_0_32";
|
|
13
|
-
HEADERS[HEADERS["DELETED_8"] = 12] = "DELETED_8";
|
|
14
|
-
HEADERS[HEADERS["USER_STATUS_0_8"] = 13] = "USER_STATUS_0_8";
|
|
15
|
-
HEADERS[HEADERS["USER_STATUS_1_8"] = 14] = "USER_STATUS_1_8";
|
|
16
|
-
HEADERS[HEADERS["USER_STATUS_2_8"] = 15] = "USER_STATUS_2_8";
|
|
17
|
-
})(HEADERS || (HEADERS = {}));
|
|
18
|
-
export class Arena {
|
|
19
|
-
constructor(options) {
|
|
20
|
-
this.HEADER_SIZE_BYTES = 16;
|
|
21
|
-
this._buffer = new ArrayBuffer(options?.initalSize || 64 * 1024);
|
|
22
|
-
this._view8 = new Uint8Array(this._buffer);
|
|
23
|
-
this._view32 = new Uint32Array(this._buffer);
|
|
24
|
-
this._offset = 0;
|
|
25
|
-
//@ts-ignore
|
|
26
|
-
this._allignMask = ((options?.allignment || 8) - 1);
|
|
27
|
-
this._allignShift = Math.log2(this._allignMask + 1);
|
|
28
|
-
this._bucketCapacities = options?.bucketCapacities || [1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024];
|
|
29
|
-
this._bucketOffsets = [];
|
|
30
|
-
this._bucketcount = this._bucketCapacities.length;
|
|
31
|
-
let currentOffset = 0;
|
|
32
|
-
for (const cap of this._bucketCapacities) {
|
|
33
|
-
this._bucketOffsets.push(currentOffset);
|
|
34
|
-
currentOffset += (cap + 1);
|
|
35
|
-
}
|
|
36
|
-
this._emptySpots = new Uint32Array(currentOffset);
|
|
37
|
-
}
|
|
38
|
-
_getBucketCount(bucketIdx) {
|
|
39
|
-
return this._emptySpots[this._bucketOffsets[bucketIdx]];
|
|
40
|
-
}
|
|
41
|
-
_setBucketCount(bucketIdx, count) {
|
|
42
|
-
this._emptySpots[this._bucketOffsets[bucketIdx]] = count;
|
|
43
|
-
}
|
|
44
|
-
_getBucketOffset(bucketIdx, slotIdx) {
|
|
45
|
-
return this._emptySpots[this._bucketOffsets[bucketIdx] + slotIdx + 1];
|
|
46
|
-
}
|
|
47
|
-
_setBucketOffset(bucketIdx, slotIdx, offset) {
|
|
48
|
-
this._emptySpots[this._bucketOffsets[bucketIdx] + slotIdx + 1] = offset;
|
|
49
|
-
}
|
|
50
|
-
_initBlock(start, dataLength, { header0, header1, header2 }) {
|
|
51
|
-
const h0 = (header0 || 0) & 0xFF;
|
|
52
|
-
const h1 = (header1 || 0) & 0xFF;
|
|
53
|
-
const h2 = (header2 || 0) & 0xFF;
|
|
54
|
-
const shift32 = start >> SHIFTOFFSETS.BYTE_32;
|
|
55
|
-
this._view32[shift32 + HEADERS.TOTAL_LENGTH_0_32] = (dataLength + this.HEADER_SIZE_BYTES + this._allignMask) & ~this._allignMask;
|
|
56
|
-
this._view32[shift32 + HEADERS.PAYLOAD_LENGTH_0_32] = dataLength;
|
|
57
|
-
this._view32[shift32 + (HEADERS.DELETED_8 >> SHIFTOFFSETS.BYTE_32)] = (h2 << 24) | (h1 << 16) | (h0 << 8) | 0;
|
|
58
|
-
}
|
|
59
|
-
alloc(data, headers) {
|
|
60
|
-
if (headers == undefined)
|
|
61
|
-
headers = { header0: 0, header1: 0, header2: 0 };
|
|
62
|
-
const totalBlockSize = (data.byteLength + this.HEADER_SIZE_BYTES + this._allignMask) & ~this._allignMask;
|
|
63
|
-
const bucketIdx = (totalBlockSize >> this._allignShift) - 1;
|
|
64
|
-
if (bucketIdx >= 0 && bucketIdx < this._bucketcount) {
|
|
65
|
-
const count = this._getBucketCount(bucketIdx);
|
|
66
|
-
if (count > 0) {
|
|
67
|
-
const recycledOffset = this._getBucketOffset(bucketIdx, count - 1);
|
|
68
|
-
this._setBucketCount(bucketIdx, count - 1);
|
|
69
|
-
this._initBlock(recycledOffset, data.length, headers);
|
|
70
|
-
return (BigInt(recycledOffset) << 32n | BigInt(this._view32[(recycledOffset >> SHIFTOFFSETS.BYTE_32) + HEADERS.GENERATION_BYTE_0_32]));
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
const start = this._offset;
|
|
74
|
-
this._checkForSpace(data.byteLength + this.HEADER_SIZE_BYTES) && this._resize();
|
|
75
|
-
this._initBlock(start, data.byteLength, headers || {});
|
|
76
|
-
this._view8.set(data, start + this.HEADER_SIZE_BYTES);
|
|
77
|
-
this._offset = (data.byteLength + this.HEADER_SIZE_BYTES + this._offset + this._allignMask) & ~this._allignMask;
|
|
78
|
-
return (BigInt(start) << 32n | BigInt(this._view32[(start >> SHIFTOFFSETS.BYTE_32) + HEADERS.GENERATION_BYTE_0_32]));
|
|
79
|
-
}
|
|
80
|
-
read(location) {
|
|
81
|
-
let start = Number(BigInt(location) >> 32n);
|
|
82
|
-
const generation = BigInt(location) & 0xffffffffn;
|
|
83
|
-
let length = this._view32[(start >> SHIFTOFFSETS.BYTE_32) + HEADERS.PAYLOAD_LENGTH_0_32];
|
|
84
|
-
const diff = generation ^ BigInt(this._view32[((start >> SHIFTOFFSETS.BYTE_32) + HEADERS.GENERATION_BYTE_0_32)]);
|
|
85
|
-
if (diff !== 0n)
|
|
86
|
-
return null;
|
|
87
|
-
return this._view8.subarray(start + this.HEADER_SIZE_BYTES, start + this.HEADER_SIZE_BYTES + Number(length));
|
|
88
|
-
}
|
|
89
|
-
free(location) {
|
|
90
|
-
let start = Number(BigInt(location) >> 32n);
|
|
91
|
-
const currgen = this._view32[(start >> SHIFTOFFSETS.BYTE_32) + HEADERS.GENERATION_BYTE_0_32];
|
|
92
|
-
this._view32[(start >> SHIFTOFFSETS.BYTE_32) + HEADERS.GENERATION_BYTE_0_32] = currgen + 1; // this part can wrap around
|
|
93
|
-
this._view8[start + HEADERS.DELETED_8] = 1;
|
|
94
|
-
const totalBlockSize = this._view32[(start >> SHIFTOFFSETS.BYTE_32) + HEADERS.TOTAL_LENGTH_0_32];
|
|
95
|
-
const bucketIdx = (totalBlockSize >> this._allignShift) - 1;
|
|
96
|
-
if (bucketIdx >= 0 && bucketIdx < this._bucketcount) {
|
|
97
|
-
const count = this._getBucketCount(bucketIdx);
|
|
98
|
-
const capacity = this._bucketCapacities[bucketIdx];
|
|
99
|
-
if (count < capacity) {
|
|
100
|
-
this._setBucketOffset(bucketIdx, count, start);
|
|
101
|
-
this._setBucketCount(bucketIdx, count + 1);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
return 0n;
|
|
105
|
-
}
|
|
106
|
-
_checkForSpace(size) {
|
|
107
|
-
if (this._buffer.byteLength >= this._offset + size)
|
|
108
|
-
return false;
|
|
109
|
-
return true;
|
|
110
|
-
}
|
|
111
|
-
_resize() {
|
|
112
|
-
//@ts-ignore
|
|
113
|
-
this._buffer = this._buffer.transfer(this._buffer.byteLength * 2);
|
|
114
|
-
this._view8 = new Uint8Array(this._buffer);
|
|
115
|
-
this._view32 = new Uint32Array(this._buffer);
|
|
116
|
-
}
|
|
117
|
-
size() {
|
|
118
|
-
return this._buffer.byteLength;
|
|
119
|
-
}
|
|
120
|
-
getBuffer() {
|
|
121
|
-
return this._view8.subarray(0, this._offset);
|
|
122
|
-
}
|
|
123
|
-
reserve(size) {
|
|
124
|
-
const start = this._offset;
|
|
125
|
-
this._checkForSpace(start + this.HEADER_SIZE_BYTES + size + this._allignMask & ~this._allignMask) && this._resize();
|
|
126
|
-
this._initBlock(start, size, {});
|
|
127
|
-
this._offset = this._offset + size + this.HEADER_SIZE_BYTES + this._allignMask & ~this._allignMask;
|
|
128
|
-
return this._view8.subarray(start + this.HEADER_SIZE_BYTES, start + this.HEADER_SIZE_BYTES + size);
|
|
129
|
-
}
|
|
130
|
-
translate(ptr) {
|
|
131
|
-
let start = Number(BigInt(ptr) >> 32n);
|
|
132
|
-
const generation = BigInt(this._view32[(start >> SHIFTOFFSETS.BYTE_32) + HEADERS.GENERATION_BYTE_0_32]);
|
|
133
|
-
return { start, generation };
|
|
134
|
-
}
|
|
135
|
-
readWithHeaders(ptr) {
|
|
136
|
-
let start = Number(BigInt(ptr) >> 32n);
|
|
137
|
-
const generation = BigInt(ptr) & 0xffffffffn;
|
|
138
|
-
let length = this._view32[(start >> SHIFTOFFSETS.BYTE_32) + HEADERS.PAYLOAD_LENGTH_0_32];
|
|
139
|
-
const diff = generation ^ BigInt(this._view32[((start >> SHIFTOFFSETS.BYTE_32) + HEADERS.GENERATION_BYTE_0_32)]);
|
|
140
|
-
if (diff !== 0n)
|
|
141
|
-
return null;
|
|
142
|
-
return this._view8.subarray(start + 12, start + this.HEADER_SIZE_BYTES + Number(length));
|
|
143
|
-
}
|
|
144
|
-
label() {
|
|
145
|
-
const ptrArray = new Array();
|
|
146
|
-
const limit = this._offset;
|
|
147
|
-
let pos = 0;
|
|
148
|
-
while (pos < limit) {
|
|
149
|
-
const totalLength = this._view32[pos >> SHIFTOFFSETS.BYTE_32];
|
|
150
|
-
if (totalLength === 0) {
|
|
151
|
-
pos = (pos + 16) & ~15;
|
|
152
|
-
continue;
|
|
153
|
-
}
|
|
154
|
-
const isDeleted = this._view8[pos + HEADERS.DELETED_8] === 1;
|
|
155
|
-
if (!isDeleted) {
|
|
156
|
-
ptrArray.push(((BigInt(pos) << 32n) | BigInt(this._view32[(pos >> SHIFTOFFSETS.BYTE_32) + HEADERS.GENERATION_BYTE_0_32])));
|
|
157
|
-
}
|
|
158
|
-
pos += totalLength;
|
|
159
|
-
if (totalLength === 0)
|
|
160
|
-
break;
|
|
161
|
-
}
|
|
162
|
-
return ptrArray;
|
|
163
|
-
}
|
|
164
|
-
getHeaders(ptr) {
|
|
165
|
-
let start = Number(BigInt(ptr) >> 32n);
|
|
166
|
-
return {
|
|
167
|
-
totalLength: Number(this._view32[(start >> SHIFTOFFSETS.BYTE_32) + HEADERS.TOTAL_LENGTH_0_32].toString()),
|
|
168
|
-
payloadlength: Number(this._view32[(start >> SHIFTOFFSETS.BYTE_32) + HEADERS.PAYLOAD_LENGTH_0_32].toString()),
|
|
169
|
-
deleted: this._view8[start + HEADERS.DELETED_8] === 1,
|
|
170
|
-
header0: Number(this._view8[start + HEADERS.USER_STATUS_0_8].toString()),
|
|
171
|
-
header1: Number(this._view8[start + HEADERS.USER_STATUS_1_8].toString()),
|
|
172
|
-
header2: Number(this._view8[start + HEADERS.USER_STATUS_2_8].toString()),
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
estimate(size, amnt) {
|
|
176
|
-
return (((size + this._allignMask) & ~this._allignMask) + this.HEADER_SIZE_BYTES) * amnt;
|
|
177
|
-
}
|
|
178
|
-
}
|
package/test/directAccess.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { Arena } from "../arena"
|
|
2
|
-
|
|
3
|
-
export function TestDirectAlloc(): boolean {
|
|
4
|
-
//Init
|
|
5
|
-
const count = 255
|
|
6
|
-
const a = new Arena()
|
|
7
|
-
const testdata = new Array<Uint8Array>()
|
|
8
|
-
for (let i = 0; i < count; i++) {
|
|
9
|
-
const data = new Uint8Array([i, 1, 2, 3, 4, 5, 6, 7])
|
|
10
|
-
testdata.push(data)
|
|
11
|
-
|
|
12
|
-
a.directAlloc(data, 0, data.length)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const ptrs = a.label()
|
|
16
|
-
if (ptrs.length !== count) throw new Error(`Allocated wrong amount of items!: needed: ${count}, got: ${ptrs.length}`)
|
|
17
|
-
|
|
18
|
-
for (let i = 0; i < ptrs.length; i++) {
|
|
19
|
-
const buf = a.read(ptrs[i])
|
|
20
|
-
if (!buf) throw new Error(`data at index ${i} is Empty.`)
|
|
21
|
-
|
|
22
|
-
for (let j = 0; j < testdata.length; j++) {
|
|
23
|
-
if (buf[j] !== testdata[i][j]) throw new Error(`dataset ${i} at ${j} is broken!`)
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
return true
|
|
27
|
-
}
|
|
28
|
-
|
package/test/ptrs.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { Arena } from "../arena"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export function TestCheckPtrAccess(): boolean {
|
|
5
|
-
//Init
|
|
6
|
-
const a = new Arena()
|
|
7
|
-
const testdata = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7])
|
|
8
|
-
const headers = { header0: 42, header1: 24, header2: 240 };
|
|
9
|
-
|
|
10
|
-
// free Slot reuse
|
|
11
|
-
const ptr = a.alloc(testdata, headers)
|
|
12
|
-
const res = a.read(ptr)
|
|
13
|
-
if (res == null) throw new Error(`${testdata} Could not read data`)
|
|
14
|
-
const resultheaders = a.getHeaders(ptr)
|
|
15
|
-
|
|
16
|
-
if (resultheaders.totalLength !== testdata.byteLength + 16) throw new Error(`totalLength not right: got: ${resultheaders.totalLength} needed: ${testdata.byteLength}`)
|
|
17
|
-
if (resultheaders.payloadlength !== testdata.byteLength) throw new Error(`payloadLength not right: got: ${resultheaders.payloadlength} needed: ${testdata.byteLength}`)
|
|
18
|
-
if (resultheaders.header0 !== headers.header0) throw new Error(`header0: got: ${resultheaders.header0} needed: ${headers.header0}`)
|
|
19
|
-
if (resultheaders.header1 !== headers.header1) throw new Error(`header1: got: ${resultheaders.header1} needed: ${headers.header1}`)
|
|
20
|
-
if (resultheaders.header2 !== headers.header2) throw new Error(`header2: got: ${resultheaders.header2} needed: ${headers.header2}`)
|
|
21
|
-
if (res.toString() !== testdata.toString()) throw new Error(`${testdata} had a problem with data integrety`)
|
|
22
|
-
|
|
23
|
-
return true
|
|
24
|
-
}
|
|
25
|
-
export function TestUseAfterFree(): boolean {
|
|
26
|
-
//Init
|
|
27
|
-
const a = new Arena()
|
|
28
|
-
const testdata = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7])
|
|
29
|
-
|
|
30
|
-
// USE-AFTER-FREE init
|
|
31
|
-
const useAfterFreePtr = a.alloc(testdata)
|
|
32
|
-
a.free(useAfterFreePtr)
|
|
33
|
-
//final use after free with Generation this should never be possible
|
|
34
|
-
if (a.read(useAfterFreePtr) !== null) throw new Error(`Use after Free is Possible, this is a big Problem`)
|
|
35
|
-
|
|
36
|
-
return true
|
|
37
|
-
}
|
package/test/reserve.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { Arena } from "../arena"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export function TestCheckReserve(): boolean {
|
|
5
|
-
//Init
|
|
6
|
-
const count = 1_000
|
|
7
|
-
const a = new Arena()
|
|
8
|
-
const testdata = new Array<Uint8Array>()
|
|
9
|
-
for (let i = 0; i < count; i++) {
|
|
10
|
-
const data = new Uint8Array([i, 1, 2, 3, 4, 5, 6, 7])
|
|
11
|
-
testdata.push(data)
|
|
12
|
-
|
|
13
|
-
const buf = a.reserve(data.byteLength)
|
|
14
|
-
buf.set(data)
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const ptrs = a.label()
|
|
18
|
-
|
|
19
|
-
for (let i = 0; i < ptrs.length; i++) {
|
|
20
|
-
const buf = a.read(ptrs[i])
|
|
21
|
-
if (!buf) throw new Error(`reservation failed at item: ${i}`)
|
|
22
|
-
for (let j = 0; j < ptrs.length; j++) {
|
|
23
|
-
if (buf[j] !== testdata[i][j]) throw new Error(`reservation failed at item ${i}:${j}`)
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return true
|
|
28
|
-
}
|
package/test/test.test.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { describe, test } from "node:test"
|
|
2
|
-
import { equal } from "node:assert"
|
|
3
|
-
import { TestCheckPtrAccess, TestUseAfterFree } from "./ptrs.ts"
|
|
4
|
-
import { TestCheckReserve } from "./reserve.ts"
|
|
5
|
-
import { TestDirectAlloc } from "./directAccess.ts"
|
|
6
|
-
|
|
7
|
-
describe("Arena Checks", () => {
|
|
8
|
-
test("Pointer Access", () => equal(TestCheckPtrAccess(), true, "Data corruption is possible!"))
|
|
9
|
-
test("Use after free", () => equal(TestUseAfterFree(), true, "Use after free is possible!"))
|
|
10
|
-
test("Test Reserving Data", () => equal(TestCheckReserve(), true, "Something with the reservation is not working properly"))
|
|
11
|
-
test("Test allocating data directly", () => equal(TestDirectAlloc(), true, "Direct alloc isnt working properly"))
|
|
12
|
-
})
|
package/tsconfig.json
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"lib": ["ES2020"],
|
|
4
|
-
"target": "ES2020",
|
|
5
|
-
"module": "ESNext",
|
|
6
|
-
"moduleResolution": "bundler",
|
|
7
|
-
"allowJs": true,
|
|
8
|
-
"verbatimModuleSyntax": true,
|
|
9
|
-
"strict": true,
|
|
10
|
-
"skipLibCheck": true,
|
|
11
|
-
"noFallthroughCasesInSwitch": true,
|
|
12
|
-
"noUncheckedIndexedAccess": true,
|
|
13
|
-
"noImplicitOverride": true,
|
|
14
|
-
|
|
15
|
-
// für Build / .d.ts:
|
|
16
|
-
"declaration": true,
|
|
17
|
-
"emitDeclarationOnly": false,
|
|
18
|
-
"outDir": "dist"
|
|
19
|
-
},
|
|
20
|
-
"include": ["arena.ts"]
|
|
21
|
-
}
|