xitdb 0.13.0 → 0.14.0
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/README.md +16 -15
- package/dist/core-buffered-file.d.ts +2 -2
- package/dist/core-buffered-file.js +182 -0
- package/dist/core-file.d.ts +1 -1
- package/dist/core-file.js +105 -0
- package/dist/core-memory.d.ts +1 -1
- package/dist/core-memory.js +152 -0
- package/dist/core.js +1 -0
- package/dist/database.d.ts +174 -49
- package/dist/database.js +3047 -0
- package/dist/exceptions.d.ts +4 -0
- package/dist/exceptions.js +62 -0
- package/dist/hasher.js +49 -0
- package/dist/index.d.ts +30 -26
- package/dist/index.js +35 -3923
- package/dist/read-array-list.d.ts +3 -3
- package/dist/read-array-list.js +35 -0
- package/dist/read-counted-hash-map.d.ts +2 -2
- package/dist/read-counted-hash-map.js +19 -0
- package/dist/read-counted-hash-set.d.ts +2 -2
- package/dist/read-counted-hash-set.js +19 -0
- package/dist/read-cursor.d.ts +11 -5
- package/dist/read-cursor.js +577 -0
- package/dist/read-hash-map.d.ts +4 -4
- package/dist/read-hash-map.js +65 -0
- package/dist/read-hash-set.d.ts +4 -4
- package/dist/read-hash-set.js +47 -0
- package/dist/read-linked-array-list.d.ts +3 -3
- package/dist/read-linked-array-list.js +35 -0
- package/dist/read-sorted-map.d.ts +21 -0
- package/dist/read-sorted-map.js +73 -0
- package/dist/read-sorted-set.d.ts +19 -0
- package/dist/read-sorted-set.js +65 -0
- package/dist/slot-pointer.d.ts +1 -1
- package/dist/slot-pointer.js +11 -0
- package/dist/slot.d.ts +2 -2
- package/dist/slot.js +41 -0
- package/dist/slotted.d.ts +1 -1
- package/dist/slotted.js +1 -0
- package/dist/tag.d.ts +3 -1
- package/dist/tag.js +25 -0
- package/dist/write-array-list.d.ts +4 -4
- package/dist/write-array-list.js +42 -0
- package/dist/write-counted-hash-map.d.ts +2 -2
- package/dist/write-counted-hash-map.js +18 -0
- package/dist/write-counted-hash-set.d.ts +2 -2
- package/dist/write-counted-hash-set.js +18 -0
- package/dist/write-cursor.d.ts +5 -5
- package/dist/write-cursor.js +124 -0
- package/dist/write-hash-map.d.ts +4 -4
- package/dist/write-hash-map.js +90 -0
- package/dist/write-hash-set.d.ts +4 -4
- package/dist/write-hash-set.js +59 -0
- package/dist/write-linked-array-list.d.ts +4 -4
- package/dist/write-linked-array-list.js +52 -0
- package/dist/write-sorted-map.d.ts +12 -0
- package/dist/write-sorted-map.js +37 -0
- package/dist/write-sorted-set.d.ts +10 -0
- package/dist/write-sorted-set.js +29 -0
- package/dist/writeable-data.js +68 -0
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -97,18 +97,18 @@ const moment = new ReadHashMap(momentCursor!);
|
|
|
97
97
|
// the cursor to "foo" and then calling readBytes on it
|
|
98
98
|
const fooCursor = moment.getCursor('foo');
|
|
99
99
|
const fooValue = fooCursor!.readBytes(MAX_READ_BYTES);
|
|
100
|
-
|
|
100
|
+
assert.strictEqual(new TextDecoder().decode(fooValue), 'foo');
|
|
101
101
|
|
|
102
102
|
// to get the "fruits" list, we get the cursor to it and
|
|
103
103
|
// then pass it to the ReadArrayList constructor
|
|
104
104
|
const fruitsCursor = moment.getCursor('fruits');
|
|
105
105
|
const fruits = new ReadArrayList(fruitsCursor!);
|
|
106
|
-
|
|
106
|
+
assert.strictEqual(fruits.count(), 3);
|
|
107
107
|
|
|
108
108
|
// now we can get the first item from the fruits list and read it
|
|
109
109
|
const appleCursor = fruits.getCursor(0);
|
|
110
110
|
const appleValue = appleCursor!.readBytes(MAX_READ_BYTES);
|
|
111
|
-
|
|
111
|
+
assert.strictEqual(new TextDecoder().decode(appleValue), 'apple');
|
|
112
112
|
```
|
|
113
113
|
|
|
114
114
|
## Initializing a Database
|
|
@@ -132,8 +132,9 @@ In xitdb there are a variety of immutable data structures that you can nest arbi
|
|
|
132
132
|
* `CountedHashMap` and `CountedHashSet` are just a `HashMap` and `HashSet` that maintain a count of their contents
|
|
133
133
|
* `ArrayList` is a growable array
|
|
134
134
|
* `LinkedArrayList` is like an `ArrayList` that can also be efficiently sliced and concatenated
|
|
135
|
+
* `SortedMap` and `SortedSet` are like a `HashMap` and `HashSet` where the keys are byte arrays kept in lexicographic order
|
|
135
136
|
|
|
136
|
-
|
|
137
|
+
The `Hash`-based data structures and the `Arraylist` use the hash array mapped trie, invented by Phil Bagwell (originally made immutable and widely available by Rich Hickey in Clojure). The `LinkedArrayList`, `SortedMap`, and `SortedSet` are based on a B-tree.
|
|
137
138
|
|
|
138
139
|
There are also scalar types you can store in the above-mentioned data structures:
|
|
139
140
|
|
|
@@ -157,7 +158,7 @@ Then, you can read it like this:
|
|
|
157
158
|
```typescript
|
|
158
159
|
const randomNumberCursor = moment.getCursor('random-number');
|
|
159
160
|
const randomNumber = randomNumberCursor!.readBytesObject(MAX_READ_BYTES);
|
|
160
|
-
|
|
161
|
+
assert.strictEqual(new TextDecoder().decode(randomNumber.formatTag!), 'bi');
|
|
161
162
|
const randomBigInt = randomNumber.value;
|
|
162
163
|
```
|
|
163
164
|
|
|
@@ -191,12 +192,12 @@ const moment = new ReadHashMap(momentCursor!);
|
|
|
191
192
|
// the food list includes the fruits
|
|
192
193
|
const foodCursor = moment.getCursor('food');
|
|
193
194
|
const food = new ReadArrayList(foodCursor!);
|
|
194
|
-
|
|
195
|
+
assert.strictEqual(food.count(), 6);
|
|
195
196
|
|
|
196
197
|
// ...but the fruits list hasn't been changed
|
|
197
198
|
const fruitsCursor = moment.getCursor('fruits');
|
|
198
199
|
const fruits = new ReadArrayList(fruitsCursor!);
|
|
199
|
-
|
|
200
|
+
assert.strictEqual(fruits.count(), 3);
|
|
200
201
|
```
|
|
201
202
|
|
|
202
203
|
Before we continue, let's save the latest history index, so we can revert back to this moment of the database later:
|
|
@@ -232,12 +233,12 @@ const moment = new ReadHashMap(momentCursor!);
|
|
|
232
233
|
// the cities list contains all four
|
|
233
234
|
const citiesCursor = moment.getCursor('cities');
|
|
234
235
|
const cities = new ReadArrayList(citiesCursor!);
|
|
235
|
-
|
|
236
|
+
assert.strictEqual(cities.count(), 4);
|
|
236
237
|
|
|
237
238
|
// ..but so does big-cities! we did not intend to mutate this
|
|
238
239
|
const bigCitiesCursor = moment.getCursor('big-cities');
|
|
239
240
|
const bigCities = new ReadArrayList(bigCitiesCursor!);
|
|
240
|
-
|
|
241
|
+
assert.strictEqual(bigCities.count(), 4);
|
|
241
242
|
```
|
|
242
243
|
|
|
243
244
|
The reason that `big-cities` was mutated is because all data in a given transaction is temporarily mutable. This is a very important optimization, but in this case, it's not what we want.
|
|
@@ -278,12 +279,12 @@ const moment = new ReadHashMap(momentCursor!);
|
|
|
278
279
|
// the cities list contains all four
|
|
279
280
|
const citiesCursor = moment.getCursor('cities');
|
|
280
281
|
const cities = new ReadArrayList(citiesCursor!);
|
|
281
|
-
|
|
282
|
+
assert.strictEqual(cities.count(), 4);
|
|
282
283
|
|
|
283
284
|
// and big-cities only contains the original two
|
|
284
285
|
const bigCitiesCursor = moment.getCursor('big-cities');
|
|
285
286
|
const bigCities = new ReadArrayList(bigCitiesCursor!);
|
|
286
|
-
|
|
287
|
+
assert.strictEqual(bigCities.count(), 2);
|
|
287
288
|
```
|
|
288
289
|
|
|
289
290
|
## Large Byte Arrays
|
|
@@ -315,7 +316,7 @@ for (let n; (n = cursorReader.read(buf)) > 0; ) {
|
|
|
315
316
|
}
|
|
316
317
|
}
|
|
317
318
|
if (line.length > 0) lineCount++;
|
|
318
|
-
|
|
319
|
+
assert.strictEqual(lineCount, 50);
|
|
319
320
|
```
|
|
320
321
|
|
|
321
322
|
## Iterators
|
|
@@ -377,7 +378,7 @@ The size of the hash in bytes will be stored in the database's header. If you tr
|
|
|
377
378
|
```typescript
|
|
378
379
|
core.seek(0);
|
|
379
380
|
const header = Header.read(core);
|
|
380
|
-
|
|
381
|
+
assert.strictEqual(header.hashSize, 20);
|
|
381
382
|
```
|
|
382
383
|
|
|
383
384
|
The hash size alone does not disambiguate hashing algorithms, though. In addition, xitdb reserves four bytes in the header that you can use to put the name of the algorithm. You must provide it in the `Hasher` constructor:
|
|
@@ -391,7 +392,7 @@ The hash id is only written to the database header when it is first initialized.
|
|
|
391
392
|
```typescript
|
|
392
393
|
core.seek(0);
|
|
393
394
|
const header = Header.read(core);
|
|
394
|
-
|
|
395
|
+
assert.strictEqual(Hasher.idToString(header.hashId), "sha1");
|
|
395
396
|
```
|
|
396
397
|
|
|
397
398
|
If you want to use SHA-256, I recommend using `sha2` as the hash id. You can then distinguish between SHA-256 and SHA-512 using the hash size, like this:
|
|
@@ -431,7 +432,7 @@ const compactDb = db.compact(compactCore);
|
|
|
431
432
|
|
|
432
433
|
// read from the new compacted db
|
|
433
434
|
const history = new ReadArrayList(compactDb.rootCursor());
|
|
434
|
-
|
|
435
|
+
assert.strictEqual(history.count(), 1);
|
|
435
436
|
```
|
|
436
437
|
|
|
437
438
|
This compacted database will be in a separate file. If you want to delete the original database and replace it with this one, you'll need to do that yourself. It is not possible to compact a database in-place (using the same file as the target database); doing so would fail and would render your original database unreadable.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Core, DataReader, DataWriter } from './core';
|
|
2
|
-
import { CoreFile } from './core-file';
|
|
1
|
+
import type { Core, DataReader, DataWriter } from './core.js';
|
|
2
|
+
import { CoreFile } from './core-file.js';
|
|
3
3
|
export declare class CoreBufferedFile implements Core {
|
|
4
4
|
file: RandomAccessBufferedFile;
|
|
5
5
|
constructor(filePath: string, bufferSize?: number);
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { CoreFile } from './core-file.js';
|
|
2
|
+
import { CoreMemory } from './core-memory.js';
|
|
3
|
+
export class CoreBufferedFile {
|
|
4
|
+
file;
|
|
5
|
+
constructor(filePath, bufferSize) {
|
|
6
|
+
this.file = new RandomAccessBufferedFile(filePath, bufferSize);
|
|
7
|
+
}
|
|
8
|
+
reader() {
|
|
9
|
+
return this.file;
|
|
10
|
+
}
|
|
11
|
+
writer() {
|
|
12
|
+
return this.file;
|
|
13
|
+
}
|
|
14
|
+
length() {
|
|
15
|
+
return this.file.length();
|
|
16
|
+
}
|
|
17
|
+
seek(pos) {
|
|
18
|
+
this.file.seek(pos);
|
|
19
|
+
}
|
|
20
|
+
position() {
|
|
21
|
+
return this.file.position();
|
|
22
|
+
}
|
|
23
|
+
setLength(len) {
|
|
24
|
+
this.file.setLength(len);
|
|
25
|
+
}
|
|
26
|
+
flush() {
|
|
27
|
+
this.file.flush();
|
|
28
|
+
}
|
|
29
|
+
sync() {
|
|
30
|
+
this.file.sync();
|
|
31
|
+
}
|
|
32
|
+
[Symbol.dispose]() {
|
|
33
|
+
this.file.file[Symbol.dispose]();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const DEFAULT_BUFFER_SIZE = 8 * 1024 * 1024; // 8MB
|
|
37
|
+
class RandomAccessBufferedFile {
|
|
38
|
+
file;
|
|
39
|
+
memory;
|
|
40
|
+
bufferSize; // flushes when the memory is >= this size
|
|
41
|
+
filePos;
|
|
42
|
+
memoryPos;
|
|
43
|
+
constructor(filePath, bufferSize = DEFAULT_BUFFER_SIZE) {
|
|
44
|
+
this.file = new CoreFile(filePath);
|
|
45
|
+
this.memory = new CoreMemory();
|
|
46
|
+
this.bufferSize = bufferSize;
|
|
47
|
+
this.filePos = 0;
|
|
48
|
+
this.memoryPos = 0;
|
|
49
|
+
}
|
|
50
|
+
seek(pos) {
|
|
51
|
+
// flush if we are going past the end of the in-memory buffer
|
|
52
|
+
if (pos > this.memoryPos + this.memory.length()) {
|
|
53
|
+
this.flush();
|
|
54
|
+
}
|
|
55
|
+
this.filePos = pos;
|
|
56
|
+
// if the buffer is empty, set its position to this offset as well
|
|
57
|
+
if (this.memory.length() === 0) {
|
|
58
|
+
this.memoryPos = pos;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
length() {
|
|
62
|
+
return Math.max(this.memoryPos + this.memory.length(), this.file.length());
|
|
63
|
+
}
|
|
64
|
+
position() {
|
|
65
|
+
return this.filePos;
|
|
66
|
+
}
|
|
67
|
+
setLength(len) {
|
|
68
|
+
this.flush();
|
|
69
|
+
this.file.setLength(len);
|
|
70
|
+
this.filePos = Math.min(len, this.filePos);
|
|
71
|
+
}
|
|
72
|
+
flush() {
|
|
73
|
+
if (this.memory.length() > 0) {
|
|
74
|
+
this.file.seek(this.memoryPos);
|
|
75
|
+
this.file.writer().write(this.memory.memory.toByteArray());
|
|
76
|
+
this.memoryPos = 0;
|
|
77
|
+
this.memory.memory.reset();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
sync() {
|
|
81
|
+
this.flush();
|
|
82
|
+
this.file.sync();
|
|
83
|
+
}
|
|
84
|
+
// DataWriter interface
|
|
85
|
+
write(buffer) {
|
|
86
|
+
if (this.memory.length() + buffer.length > this.bufferSize) {
|
|
87
|
+
this.flush();
|
|
88
|
+
}
|
|
89
|
+
if (this.filePos >= this.memoryPos && this.filePos <= this.memoryPos + this.memory.length()) {
|
|
90
|
+
this.memory.seek(this.filePos - this.memoryPos);
|
|
91
|
+
this.memory.memory.write(buffer);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
// a direct disk write that overlaps the buffered region would be
|
|
95
|
+
// clobbered by a later flush of stale buffer bytes, so flush first
|
|
96
|
+
if (this.filePos < this.memoryPos + this.memory.length() && this.filePos + buffer.length > this.memoryPos) {
|
|
97
|
+
this.flush();
|
|
98
|
+
}
|
|
99
|
+
// Write directly to file
|
|
100
|
+
this.file.seek(this.filePos);
|
|
101
|
+
this.file.writer().write(buffer);
|
|
102
|
+
}
|
|
103
|
+
this.filePos += buffer.length;
|
|
104
|
+
}
|
|
105
|
+
writeByte(v) {
|
|
106
|
+
this.write(new Uint8Array([v & 0xff]));
|
|
107
|
+
}
|
|
108
|
+
writeShort(v) {
|
|
109
|
+
const buffer = new ArrayBuffer(2);
|
|
110
|
+
const view = new DataView(buffer);
|
|
111
|
+
view.setInt16(0, v & 0xffff, false); // big-endian
|
|
112
|
+
this.write(new Uint8Array(buffer));
|
|
113
|
+
}
|
|
114
|
+
writeLong(v) {
|
|
115
|
+
const buffer = new ArrayBuffer(8);
|
|
116
|
+
const view = new DataView(buffer);
|
|
117
|
+
view.setBigInt64(0, BigInt(v), false);
|
|
118
|
+
this.write(new Uint8Array(buffer));
|
|
119
|
+
}
|
|
120
|
+
// DataReader interface
|
|
121
|
+
readFully(buffer) {
|
|
122
|
+
let pos = 0;
|
|
123
|
+
// read from the disk -- before the in-memory buffer
|
|
124
|
+
if (this.filePos < this.memoryPos) {
|
|
125
|
+
const sizeBeforeMem = Math.min(this.memoryPos - this.filePos, buffer.length);
|
|
126
|
+
const tempBuffer = new Uint8Array(sizeBeforeMem);
|
|
127
|
+
this.file.seek(this.filePos);
|
|
128
|
+
this.file.reader().readFully(tempBuffer);
|
|
129
|
+
buffer.set(tempBuffer, pos);
|
|
130
|
+
pos += sizeBeforeMem;
|
|
131
|
+
this.filePos += sizeBeforeMem;
|
|
132
|
+
}
|
|
133
|
+
if (pos === buffer.length)
|
|
134
|
+
return;
|
|
135
|
+
// read from the in-memory buffer
|
|
136
|
+
if (this.filePos >= this.memoryPos && this.filePos < this.memoryPos + this.memory.length()) {
|
|
137
|
+
const memPos = this.filePos - this.memoryPos;
|
|
138
|
+
const sizeInMem = Math.min(this.memory.length() - memPos, buffer.length - pos);
|
|
139
|
+
this.memory.seek(memPos);
|
|
140
|
+
const memBuffer = new Uint8Array(sizeInMem);
|
|
141
|
+
this.memory.memory.readFully(memBuffer);
|
|
142
|
+
buffer.set(memBuffer, pos);
|
|
143
|
+
pos += sizeInMem;
|
|
144
|
+
this.filePos += sizeInMem;
|
|
145
|
+
}
|
|
146
|
+
if (pos === buffer.length)
|
|
147
|
+
return;
|
|
148
|
+
// read from the disk -- after the in-memory buffer
|
|
149
|
+
if (this.filePos >= this.memoryPos + this.memory.length()) {
|
|
150
|
+
const sizeAfterMem = buffer.length - pos;
|
|
151
|
+
const tempBuffer = new Uint8Array(sizeAfterMem);
|
|
152
|
+
this.file.seek(this.filePos);
|
|
153
|
+
this.file.reader().readFully(tempBuffer);
|
|
154
|
+
buffer.set(tempBuffer, pos);
|
|
155
|
+
pos += sizeAfterMem;
|
|
156
|
+
this.filePos += sizeAfterMem;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
readByte() {
|
|
160
|
+
const bytes = new Uint8Array(1);
|
|
161
|
+
this.readFully(bytes);
|
|
162
|
+
return bytes[0];
|
|
163
|
+
}
|
|
164
|
+
readShort() {
|
|
165
|
+
const bytes = new Uint8Array(2);
|
|
166
|
+
this.readFully(bytes);
|
|
167
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
168
|
+
return view.getInt16(0, false); // big-endian
|
|
169
|
+
}
|
|
170
|
+
readInt() {
|
|
171
|
+
const bytes = new Uint8Array(4);
|
|
172
|
+
this.readFully(bytes);
|
|
173
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
174
|
+
return view.getInt32(0, false); // big-endian
|
|
175
|
+
}
|
|
176
|
+
readLong() {
|
|
177
|
+
const bytes = new Uint8Array(8);
|
|
178
|
+
this.readFully(bytes);
|
|
179
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
180
|
+
return Number(view.getBigInt64(0, false));
|
|
181
|
+
}
|
|
182
|
+
}
|
package/dist/core-file.d.ts
CHANGED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
export class CoreFile {
|
|
3
|
+
filePath;
|
|
4
|
+
_position = 0;
|
|
5
|
+
fd;
|
|
6
|
+
constructor(filePath) {
|
|
7
|
+
this.filePath = filePath;
|
|
8
|
+
// Create file if it doesn't exist
|
|
9
|
+
try {
|
|
10
|
+
fs.accessSync(filePath);
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
fs.writeFileSync(filePath, new Uint8Array(0));
|
|
14
|
+
}
|
|
15
|
+
// Open file for reading and writing
|
|
16
|
+
this.fd = fs.openSync(filePath, 'r+');
|
|
17
|
+
}
|
|
18
|
+
reader() {
|
|
19
|
+
return new FileDataReader(this);
|
|
20
|
+
}
|
|
21
|
+
writer() {
|
|
22
|
+
return new FileDataWriter(this);
|
|
23
|
+
}
|
|
24
|
+
length() {
|
|
25
|
+
const stats = fs.fstatSync(this.fd);
|
|
26
|
+
return stats.size;
|
|
27
|
+
}
|
|
28
|
+
seek(pos) {
|
|
29
|
+
this._position = pos;
|
|
30
|
+
}
|
|
31
|
+
position() {
|
|
32
|
+
return this._position;
|
|
33
|
+
}
|
|
34
|
+
setLength(len) {
|
|
35
|
+
fs.ftruncateSync(this.fd, len);
|
|
36
|
+
}
|
|
37
|
+
flush() {
|
|
38
|
+
}
|
|
39
|
+
sync() {
|
|
40
|
+
fs.fsyncSync(this.fd);
|
|
41
|
+
}
|
|
42
|
+
[Symbol.dispose]() {
|
|
43
|
+
fs.closeSync(this.fd);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
class FileDataReader {
|
|
47
|
+
core;
|
|
48
|
+
constructor(core) {
|
|
49
|
+
this.core = core;
|
|
50
|
+
}
|
|
51
|
+
readFully(b) {
|
|
52
|
+
const position = this.core.position();
|
|
53
|
+
fs.readSync(this.core.fd, b, 0, b.length, position);
|
|
54
|
+
this.core.seek(position + b.length);
|
|
55
|
+
}
|
|
56
|
+
readByte() {
|
|
57
|
+
const bytes = new Uint8Array(1);
|
|
58
|
+
this.readFully(bytes);
|
|
59
|
+
return bytes[0];
|
|
60
|
+
}
|
|
61
|
+
readShort() {
|
|
62
|
+
const bytes = new Uint8Array(2);
|
|
63
|
+
this.readFully(bytes);
|
|
64
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
65
|
+
return view.getInt16(0, false);
|
|
66
|
+
}
|
|
67
|
+
readInt() {
|
|
68
|
+
const bytes = new Uint8Array(4);
|
|
69
|
+
this.readFully(bytes);
|
|
70
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
71
|
+
return view.getInt32(0, false);
|
|
72
|
+
}
|
|
73
|
+
readLong() {
|
|
74
|
+
const bytes = new Uint8Array(8);
|
|
75
|
+
this.readFully(bytes);
|
|
76
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
77
|
+
return Number(view.getBigInt64(0, false));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
class FileDataWriter {
|
|
81
|
+
core;
|
|
82
|
+
constructor(core) {
|
|
83
|
+
this.core = core;
|
|
84
|
+
}
|
|
85
|
+
write(buffer) {
|
|
86
|
+
const position = this.core.position();
|
|
87
|
+
fs.writeSync(this.core.fd, buffer, 0, buffer.length, position);
|
|
88
|
+
this.core.seek(position + buffer.length);
|
|
89
|
+
}
|
|
90
|
+
writeByte(v) {
|
|
91
|
+
this.write(new Uint8Array([v & 0xff]));
|
|
92
|
+
}
|
|
93
|
+
writeShort(v) {
|
|
94
|
+
const buffer = new ArrayBuffer(2);
|
|
95
|
+
const view = new DataView(buffer);
|
|
96
|
+
view.setInt16(0, v, false);
|
|
97
|
+
this.write(new Uint8Array(buffer));
|
|
98
|
+
}
|
|
99
|
+
writeLong(v) {
|
|
100
|
+
const buffer = new ArrayBuffer(8);
|
|
101
|
+
const view = new DataView(buffer);
|
|
102
|
+
view.setBigInt64(0, BigInt(v), false);
|
|
103
|
+
this.write(new Uint8Array(buffer));
|
|
104
|
+
}
|
|
105
|
+
}
|
package/dist/core-memory.d.ts
CHANGED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
export class CoreMemory {
|
|
2
|
+
memory;
|
|
3
|
+
constructor() {
|
|
4
|
+
this.memory = new RandomAccessMemory();
|
|
5
|
+
}
|
|
6
|
+
reader() {
|
|
7
|
+
return this.memory;
|
|
8
|
+
}
|
|
9
|
+
writer() {
|
|
10
|
+
return this.memory;
|
|
11
|
+
}
|
|
12
|
+
length() {
|
|
13
|
+
return this.memory.size();
|
|
14
|
+
}
|
|
15
|
+
seek(pos) {
|
|
16
|
+
this.memory.seek(pos);
|
|
17
|
+
}
|
|
18
|
+
position() {
|
|
19
|
+
return this.memory.getPosition();
|
|
20
|
+
}
|
|
21
|
+
setLength(len) {
|
|
22
|
+
this.memory.setLength(len);
|
|
23
|
+
}
|
|
24
|
+
flush() {
|
|
25
|
+
// no-op for in-memory
|
|
26
|
+
}
|
|
27
|
+
sync() {
|
|
28
|
+
// no-op for in-memory
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
class RandomAccessMemory {
|
|
32
|
+
buffer;
|
|
33
|
+
_position = 0;
|
|
34
|
+
_count = 0;
|
|
35
|
+
constructor(initialSize = 1024) {
|
|
36
|
+
this.buffer = new Uint8Array(initialSize);
|
|
37
|
+
}
|
|
38
|
+
ensureCapacity(minCapacity) {
|
|
39
|
+
if (minCapacity > this.buffer.length) {
|
|
40
|
+
let newCapacity = this.buffer.length * 2;
|
|
41
|
+
if (newCapacity < minCapacity) {
|
|
42
|
+
newCapacity = minCapacity;
|
|
43
|
+
}
|
|
44
|
+
const newBuffer = new Uint8Array(newCapacity);
|
|
45
|
+
newBuffer.set(this.buffer.subarray(0, this._count));
|
|
46
|
+
this.buffer = newBuffer;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
size() {
|
|
50
|
+
return this._count;
|
|
51
|
+
}
|
|
52
|
+
seek(pos) {
|
|
53
|
+
if (pos > this._count) {
|
|
54
|
+
this._position = this._count;
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
this._position = pos;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
getPosition() {
|
|
61
|
+
return this._position;
|
|
62
|
+
}
|
|
63
|
+
setLength(len) {
|
|
64
|
+
if (len === 0) {
|
|
65
|
+
this.reset();
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
if (len > this._count)
|
|
69
|
+
throw new Error('Cannot extend length');
|
|
70
|
+
this._count = len;
|
|
71
|
+
if (this._position > len) {
|
|
72
|
+
this._position = len;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
reset() {
|
|
77
|
+
this._count = 0;
|
|
78
|
+
this._position = 0;
|
|
79
|
+
}
|
|
80
|
+
toByteArray() {
|
|
81
|
+
return this.buffer.slice(0, this._count);
|
|
82
|
+
}
|
|
83
|
+
// DataWriter interface
|
|
84
|
+
write(data) {
|
|
85
|
+
const pos = this._position;
|
|
86
|
+
if (pos < this._count) {
|
|
87
|
+
const bytesBeforeEnd = Math.min(data.length, this._count - pos);
|
|
88
|
+
for (let i = 0; i < bytesBeforeEnd; i++) {
|
|
89
|
+
this.buffer[pos + i] = data[i];
|
|
90
|
+
}
|
|
91
|
+
if (bytesBeforeEnd < data.length) {
|
|
92
|
+
const bytesAfterEnd = data.length - bytesBeforeEnd;
|
|
93
|
+
this.ensureCapacity(this._count + bytesAfterEnd);
|
|
94
|
+
this.buffer.set(data.subarray(bytesBeforeEnd), this._count);
|
|
95
|
+
this._count += bytesAfterEnd;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
this.ensureCapacity(this._count + data.length);
|
|
100
|
+
this.buffer.set(data, this._count);
|
|
101
|
+
this._count += data.length;
|
|
102
|
+
}
|
|
103
|
+
this._position = pos + data.length;
|
|
104
|
+
}
|
|
105
|
+
writeByte(v) {
|
|
106
|
+
this.write(new Uint8Array([v & 0xff]));
|
|
107
|
+
}
|
|
108
|
+
writeShort(v) {
|
|
109
|
+
const buffer = new ArrayBuffer(2);
|
|
110
|
+
const view = new DataView(buffer);
|
|
111
|
+
view.setInt16(0, v, false); // big-endian
|
|
112
|
+
this.write(new Uint8Array(buffer));
|
|
113
|
+
}
|
|
114
|
+
writeLong(v) {
|
|
115
|
+
const buffer = new ArrayBuffer(8);
|
|
116
|
+
const view = new DataView(buffer);
|
|
117
|
+
view.setBigInt64(0, BigInt(v), false);
|
|
118
|
+
this.write(new Uint8Array(buffer));
|
|
119
|
+
}
|
|
120
|
+
// DataReader interface
|
|
121
|
+
readFully(b) {
|
|
122
|
+
const pos = this._position;
|
|
123
|
+
if (pos + b.length > this._count) {
|
|
124
|
+
throw new Error('End of stream');
|
|
125
|
+
}
|
|
126
|
+
b.set(this.buffer.subarray(pos, pos + b.length));
|
|
127
|
+
this._position = pos + b.length;
|
|
128
|
+
}
|
|
129
|
+
readByte() {
|
|
130
|
+
const bytes = new Uint8Array(1);
|
|
131
|
+
this.readFully(bytes);
|
|
132
|
+
return bytes[0];
|
|
133
|
+
}
|
|
134
|
+
readShort() {
|
|
135
|
+
const bytes = new Uint8Array(2);
|
|
136
|
+
this.readFully(bytes);
|
|
137
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
138
|
+
return view.getInt16(0, false); // big-endian
|
|
139
|
+
}
|
|
140
|
+
readInt() {
|
|
141
|
+
const bytes = new Uint8Array(4);
|
|
142
|
+
this.readFully(bytes);
|
|
143
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
144
|
+
return view.getInt32(0, false); // big-endian
|
|
145
|
+
}
|
|
146
|
+
readLong() {
|
|
147
|
+
const bytes = new Uint8Array(8);
|
|
148
|
+
this.readFully(bytes);
|
|
149
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
150
|
+
return Number(view.getBigInt64(0, false));
|
|
151
|
+
}
|
|
152
|
+
}
|
package/dist/core.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|