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
|
@@ -0,0 +1,577 @@
|
|
|
1
|
+
import { Slot } from './slot.js';
|
|
2
|
+
import { SlotPointer } from './slot-pointer.js';
|
|
3
|
+
import { WriteMode, ArrayListHeader, BTreeHeader, KeyValuePair, INDEX_BLOCK_SIZE, BTREE_NODE_HEADER_SIZE, BTreeNodeKind, SLOT_COUNT, } from './database.js';
|
|
4
|
+
import { UnexpectedTagException, StreamTooLongException, EndOfStreamException, InvalidOffsetException, KeyNotFoundException, ExpectedUnsignedLongException, } from './exceptions.js';
|
|
5
|
+
import { Bytes } from './writeable-data.js';
|
|
6
|
+
export class KeyValuePairCursor {
|
|
7
|
+
valueCursor;
|
|
8
|
+
keyCursor;
|
|
9
|
+
hash;
|
|
10
|
+
constructor(valueCursor, keyCursor, hash) {
|
|
11
|
+
this.valueCursor = valueCursor;
|
|
12
|
+
this.keyCursor = keyCursor;
|
|
13
|
+
this.hash = hash;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export class ReadCursor {
|
|
17
|
+
slotPtr;
|
|
18
|
+
db;
|
|
19
|
+
constructor(slotPtr, db) {
|
|
20
|
+
this.slotPtr = slotPtr;
|
|
21
|
+
this.db = db;
|
|
22
|
+
}
|
|
23
|
+
slot() {
|
|
24
|
+
return this.slotPtr.slot;
|
|
25
|
+
}
|
|
26
|
+
readPath(path) {
|
|
27
|
+
try {
|
|
28
|
+
const slotPtr = this.db.readSlotPointer(WriteMode.READ_ONLY, path, 0, this.slotPtr);
|
|
29
|
+
return new ReadCursor(slotPtr, this.db);
|
|
30
|
+
}
|
|
31
|
+
catch (e) {
|
|
32
|
+
if (e instanceof KeyNotFoundException) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
throw e;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
readPathSlot(path) {
|
|
39
|
+
try {
|
|
40
|
+
const slotPtr = this.db.readSlotPointer(WriteMode.READ_ONLY, path, 0, this.slotPtr);
|
|
41
|
+
if (!slotPtr.slot.empty()) {
|
|
42
|
+
return slotPtr.slot;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
if (e instanceof KeyNotFoundException) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
throw e;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
readUint() {
|
|
56
|
+
if (this.slotPtr.slot.tag !== 8 /* Tag.UINT */) {
|
|
57
|
+
throw new UnexpectedTagException();
|
|
58
|
+
}
|
|
59
|
+
if (this.slotPtr.slot.value < 0n)
|
|
60
|
+
throw new ExpectedUnsignedLongException();
|
|
61
|
+
return Number(this.slotPtr.slot.value);
|
|
62
|
+
}
|
|
63
|
+
readInt() {
|
|
64
|
+
if (this.slotPtr.slot.tag !== 9 /* Tag.INT */) {
|
|
65
|
+
throw new UnexpectedTagException();
|
|
66
|
+
}
|
|
67
|
+
return Number(this.slotPtr.slot.value);
|
|
68
|
+
}
|
|
69
|
+
readFloat() {
|
|
70
|
+
if (this.slotPtr.slot.tag !== 10 /* Tag.FLOAT */) {
|
|
71
|
+
throw new UnexpectedTagException();
|
|
72
|
+
}
|
|
73
|
+
const buffer = new ArrayBuffer(8);
|
|
74
|
+
const view = new DataView(buffer);
|
|
75
|
+
// Write value as 8 bytes big-endian (using BigInt operations)
|
|
76
|
+
view.setBigInt64(0, this.slotPtr.slot.value, false);
|
|
77
|
+
return view.getFloat64(0, false);
|
|
78
|
+
}
|
|
79
|
+
readBytes(maxSizeMaybe = null) {
|
|
80
|
+
const bytesObj = this.readBytesObject(maxSizeMaybe);
|
|
81
|
+
return bytesObj.value;
|
|
82
|
+
}
|
|
83
|
+
readBytesObject(maxSizeMaybe = null) {
|
|
84
|
+
const reader = this.db.core.reader();
|
|
85
|
+
switch (this.slotPtr.slot.tag) {
|
|
86
|
+
case 0 /* Tag.NONE */:
|
|
87
|
+
return new Bytes(new Uint8Array(0));
|
|
88
|
+
case 6 /* Tag.BYTES */: {
|
|
89
|
+
this.db.core.seek(Number(this.slotPtr.slot.value));
|
|
90
|
+
const valueSize = reader.readLong();
|
|
91
|
+
if (maxSizeMaybe !== null && valueSize > maxSizeMaybe) {
|
|
92
|
+
throw new StreamTooLongException();
|
|
93
|
+
}
|
|
94
|
+
const startPosition = this.db.core.position();
|
|
95
|
+
const value = new Uint8Array(valueSize);
|
|
96
|
+
reader.readFully(value);
|
|
97
|
+
let formatTag = null;
|
|
98
|
+
if (this.slotPtr.slot.full) {
|
|
99
|
+
this.db.core.seek(startPosition + valueSize);
|
|
100
|
+
formatTag = new Uint8Array(2);
|
|
101
|
+
reader.readFully(formatTag);
|
|
102
|
+
}
|
|
103
|
+
return new Bytes(value, formatTag);
|
|
104
|
+
}
|
|
105
|
+
case 7 /* Tag.SHORT_BYTES */: {
|
|
106
|
+
const buffer = new ArrayBuffer(8);
|
|
107
|
+
const view = new DataView(buffer);
|
|
108
|
+
// Write value as 8 bytes big-endian (using BigInt operations)
|
|
109
|
+
view.setBigInt64(0, this.slotPtr.slot.value, false);
|
|
110
|
+
const bytes = new Uint8Array(buffer);
|
|
111
|
+
const totalSize = this.slotPtr.slot.full ? bytes.length - 2 : bytes.length;
|
|
112
|
+
let valueSize = 0;
|
|
113
|
+
for (const b of bytes) {
|
|
114
|
+
if (b === 0 || valueSize === totalSize)
|
|
115
|
+
break;
|
|
116
|
+
valueSize += 1;
|
|
117
|
+
}
|
|
118
|
+
if (maxSizeMaybe !== null && valueSize > maxSizeMaybe) {
|
|
119
|
+
throw new StreamTooLongException();
|
|
120
|
+
}
|
|
121
|
+
let formatTag = null;
|
|
122
|
+
if (this.slotPtr.slot.full) {
|
|
123
|
+
formatTag = bytes.slice(totalSize, bytes.length);
|
|
124
|
+
}
|
|
125
|
+
return new Bytes(bytes.slice(0, valueSize), formatTag);
|
|
126
|
+
}
|
|
127
|
+
default:
|
|
128
|
+
throw new UnexpectedTagException();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
readKeyValuePair() {
|
|
132
|
+
const reader = this.db.core.reader();
|
|
133
|
+
if (this.slotPtr.slot.tag !== 5 /* Tag.KV_PAIR */) {
|
|
134
|
+
throw new UnexpectedTagException();
|
|
135
|
+
}
|
|
136
|
+
this.db.core.seek(Number(this.slotPtr.slot.value));
|
|
137
|
+
const kvPairBytes = new Uint8Array(KeyValuePair.length(this.db.header.hashSize));
|
|
138
|
+
reader.readFully(kvPairBytes);
|
|
139
|
+
const kvPair = KeyValuePair.fromBytes(kvPairBytes, this.db.header.hashSize);
|
|
140
|
+
const hashPos = Number(this.slotPtr.slot.value);
|
|
141
|
+
const keySlotPos = hashPos + this.db.header.hashSize;
|
|
142
|
+
const valueSlotPos = keySlotPos + Slot.LENGTH;
|
|
143
|
+
return new KeyValuePairCursor(new ReadCursor(new SlotPointer(valueSlotPos, kvPair.valueSlot), this.db), new ReadCursor(new SlotPointer(keySlotPos, kvPair.keySlot), this.db), kvPair.hash);
|
|
144
|
+
}
|
|
145
|
+
reader() {
|
|
146
|
+
const reader = this.db.core.reader();
|
|
147
|
+
switch (this.slotPtr.slot.tag) {
|
|
148
|
+
case 6 /* Tag.BYTES */: {
|
|
149
|
+
this.db.core.seek(Number(this.slotPtr.slot.value));
|
|
150
|
+
const size = reader.readLong();
|
|
151
|
+
const startPosition = this.db.core.position();
|
|
152
|
+
return new Reader(this, size, startPosition, 0);
|
|
153
|
+
}
|
|
154
|
+
case 7 /* Tag.SHORT_BYTES */: {
|
|
155
|
+
const buffer = new ArrayBuffer(8);
|
|
156
|
+
const view = new DataView(buffer);
|
|
157
|
+
// Write value as 8 bytes big-endian (using BigInt operations)
|
|
158
|
+
view.setBigInt64(0, this.slotPtr.slot.value, false);
|
|
159
|
+
const bytes = new Uint8Array(buffer);
|
|
160
|
+
const totalSize = this.slotPtr.slot.full ? bytes.length - 2 : bytes.length;
|
|
161
|
+
let valueSize = 0;
|
|
162
|
+
for (const b of bytes) {
|
|
163
|
+
if (b === 0 || valueSize === totalSize)
|
|
164
|
+
break;
|
|
165
|
+
valueSize += 1;
|
|
166
|
+
}
|
|
167
|
+
const startPosition = this.slotPtr.position + 1;
|
|
168
|
+
return new Reader(this, valueSize, startPosition, 0);
|
|
169
|
+
}
|
|
170
|
+
default:
|
|
171
|
+
throw new UnexpectedTagException();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
count() {
|
|
175
|
+
const reader = this.db.core.reader();
|
|
176
|
+
switch (this.slotPtr.slot.tag) {
|
|
177
|
+
case 0 /* Tag.NONE */:
|
|
178
|
+
return 0;
|
|
179
|
+
case 2 /* Tag.ARRAY_LIST */: {
|
|
180
|
+
this.db.core.seek(Number(this.slotPtr.slot.value));
|
|
181
|
+
const headerBytes = new Uint8Array(ArrayListHeader.LENGTH);
|
|
182
|
+
reader.readFully(headerBytes);
|
|
183
|
+
const header = ArrayListHeader.fromBytes(headerBytes);
|
|
184
|
+
return header.size;
|
|
185
|
+
}
|
|
186
|
+
case 3 /* Tag.LINKED_ARRAY_LIST */:
|
|
187
|
+
case 14 /* Tag.SORTED_MAP */:
|
|
188
|
+
case 15 /* Tag.SORTED_SET */: {
|
|
189
|
+
this.db.core.seek(Number(this.slotPtr.slot.value));
|
|
190
|
+
const headerBytes = new Uint8Array(BTreeHeader.LENGTH);
|
|
191
|
+
reader.readFully(headerBytes);
|
|
192
|
+
const header = BTreeHeader.fromBytes(headerBytes);
|
|
193
|
+
return header.size;
|
|
194
|
+
}
|
|
195
|
+
case 6 /* Tag.BYTES */: {
|
|
196
|
+
this.db.core.seek(Number(this.slotPtr.slot.value));
|
|
197
|
+
return reader.readLong();
|
|
198
|
+
}
|
|
199
|
+
case 7 /* Tag.SHORT_BYTES */: {
|
|
200
|
+
const buffer = new ArrayBuffer(8);
|
|
201
|
+
const view = new DataView(buffer);
|
|
202
|
+
// Write value as 8 bytes big-endian (using BigInt operations)
|
|
203
|
+
view.setBigInt64(0, this.slotPtr.slot.value, false);
|
|
204
|
+
const bytes = new Uint8Array(buffer);
|
|
205
|
+
const totalSize = this.slotPtr.slot.full ? bytes.length - 2 : bytes.length;
|
|
206
|
+
let size = 0;
|
|
207
|
+
for (const b of bytes) {
|
|
208
|
+
if (b === 0 || size === totalSize)
|
|
209
|
+
break;
|
|
210
|
+
size += 1;
|
|
211
|
+
}
|
|
212
|
+
return size;
|
|
213
|
+
}
|
|
214
|
+
case 12 /* Tag.COUNTED_HASH_MAP */:
|
|
215
|
+
case 13 /* Tag.COUNTED_HASH_SET */: {
|
|
216
|
+
this.db.core.seek(Number(this.slotPtr.slot.value));
|
|
217
|
+
return reader.readLong();
|
|
218
|
+
}
|
|
219
|
+
default:
|
|
220
|
+
throw new UnexpectedTagException();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
*[Symbol.iterator]() {
|
|
224
|
+
const iterator = this.iterator();
|
|
225
|
+
while (iterator.hasNext()) {
|
|
226
|
+
const next = iterator.next();
|
|
227
|
+
if (next !== null) {
|
|
228
|
+
yield next;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
iterator() {
|
|
233
|
+
const iterator = new CursorIterator(this);
|
|
234
|
+
iterator.init();
|
|
235
|
+
return iterator;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
export class Reader {
|
|
239
|
+
parent;
|
|
240
|
+
size;
|
|
241
|
+
startPosition;
|
|
242
|
+
relativePosition;
|
|
243
|
+
constructor(parent, size, startPosition, relativePosition) {
|
|
244
|
+
this.parent = parent;
|
|
245
|
+
this.size = size;
|
|
246
|
+
this.startPosition = startPosition;
|
|
247
|
+
this.relativePosition = relativePosition;
|
|
248
|
+
}
|
|
249
|
+
read(buffer) {
|
|
250
|
+
if (this.size < this.relativePosition)
|
|
251
|
+
throw new EndOfStreamException();
|
|
252
|
+
this.parent.db.core.seek(this.startPosition + this.relativePosition);
|
|
253
|
+
const readSize = Math.min(buffer.length, this.size - this.relativePosition);
|
|
254
|
+
if (readSize === 0)
|
|
255
|
+
return -1;
|
|
256
|
+
const reader = this.parent.db.core.reader();
|
|
257
|
+
const tempBuffer = new Uint8Array(readSize);
|
|
258
|
+
reader.readFully(tempBuffer);
|
|
259
|
+
buffer.set(tempBuffer);
|
|
260
|
+
this.relativePosition += readSize;
|
|
261
|
+
return readSize;
|
|
262
|
+
}
|
|
263
|
+
readFully(buffer) {
|
|
264
|
+
if (this.size < this.relativePosition || this.size - this.relativePosition < buffer.length) {
|
|
265
|
+
throw new EndOfStreamException();
|
|
266
|
+
}
|
|
267
|
+
this.parent.db.core.seek(this.startPosition + this.relativePosition);
|
|
268
|
+
const reader = this.parent.db.core.reader();
|
|
269
|
+
reader.readFully(buffer);
|
|
270
|
+
this.relativePosition += buffer.length;
|
|
271
|
+
}
|
|
272
|
+
readByte() {
|
|
273
|
+
const bytes = new Uint8Array(1);
|
|
274
|
+
this.readFully(bytes);
|
|
275
|
+
return bytes[0];
|
|
276
|
+
}
|
|
277
|
+
readShort() {
|
|
278
|
+
const readSize = 2;
|
|
279
|
+
const bytes = new Uint8Array(readSize);
|
|
280
|
+
this.readFully(bytes);
|
|
281
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
282
|
+
return view.getInt16(0, false);
|
|
283
|
+
}
|
|
284
|
+
readInt() {
|
|
285
|
+
const readSize = 4;
|
|
286
|
+
const bytes = new Uint8Array(readSize);
|
|
287
|
+
this.readFully(bytes);
|
|
288
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
289
|
+
return view.getInt32(0, false);
|
|
290
|
+
}
|
|
291
|
+
readLong() {
|
|
292
|
+
const readSize = 8;
|
|
293
|
+
const bytes = new Uint8Array(readSize);
|
|
294
|
+
this.readFully(bytes);
|
|
295
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
296
|
+
return Number(view.getBigInt64(0, false));
|
|
297
|
+
}
|
|
298
|
+
seek(position) {
|
|
299
|
+
if (position > this.size) {
|
|
300
|
+
throw new InvalidOffsetException();
|
|
301
|
+
}
|
|
302
|
+
this.relativePosition = position;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
class IteratorLevel {
|
|
306
|
+
position;
|
|
307
|
+
block;
|
|
308
|
+
index;
|
|
309
|
+
constructor(position, block, index) {
|
|
310
|
+
this.position = position;
|
|
311
|
+
this.block = block;
|
|
312
|
+
this.index = index;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
export class CursorIterator {
|
|
316
|
+
cursor;
|
|
317
|
+
size = 0;
|
|
318
|
+
index = 0;
|
|
319
|
+
stack = [];
|
|
320
|
+
nextCursorMaybe = null;
|
|
321
|
+
constructor(cursor) {
|
|
322
|
+
this.cursor = cursor;
|
|
323
|
+
}
|
|
324
|
+
// start a sorted-map iterator at the entry with rank startIndex (the count descent),
|
|
325
|
+
// iterating in key order from there
|
|
326
|
+
static initSortedFromIndex(cursor, startIndex) {
|
|
327
|
+
const total = cursor.count();
|
|
328
|
+
const it = new CursorIterator(cursor);
|
|
329
|
+
// an unwritten map is NONE (like iterator()): yield nothing
|
|
330
|
+
if (cursor.slotPtr.slot.tag === 0 /* Tag.NONE */ || startIndex >= total) {
|
|
331
|
+
return it;
|
|
332
|
+
}
|
|
333
|
+
const rootPtr = CursorIterator.sortedRootPtr(cursor);
|
|
334
|
+
it.size = total;
|
|
335
|
+
it.index = startIndex;
|
|
336
|
+
it.stack = CursorIterator.sortedStackFromIndex(cursor, rootPtr, startIndex);
|
|
337
|
+
return it;
|
|
338
|
+
}
|
|
339
|
+
// start a sorted-map iterator at the first entry with key >= startKey
|
|
340
|
+
static initSortedFromKey(cursor, startKey) {
|
|
341
|
+
const it = new CursorIterator(cursor);
|
|
342
|
+
if (cursor.slotPtr.slot.tag === 0 /* Tag.NONE */) {
|
|
343
|
+
return it;
|
|
344
|
+
}
|
|
345
|
+
const total = cursor.count();
|
|
346
|
+
const rootPtr = CursorIterator.sortedRootPtr(cursor);
|
|
347
|
+
const built = CursorIterator.sortedStackFromKey(cursor, rootPtr, startKey);
|
|
348
|
+
it.size = total;
|
|
349
|
+
it.index = built.before;
|
|
350
|
+
it.stack = built.stack;
|
|
351
|
+
return it;
|
|
352
|
+
}
|
|
353
|
+
static sortedRootPtr(cursor) {
|
|
354
|
+
switch (cursor.slotPtr.slot.tag) {
|
|
355
|
+
case 14 /* Tag.SORTED_MAP */:
|
|
356
|
+
case 15 /* Tag.SORTED_SET */:
|
|
357
|
+
break;
|
|
358
|
+
default:
|
|
359
|
+
throw new UnexpectedTagException();
|
|
360
|
+
}
|
|
361
|
+
cursor.db.core.seek(Number(cursor.slotPtr.slot.value));
|
|
362
|
+
const reader = cursor.db.core.reader();
|
|
363
|
+
const headerBytes = new Uint8Array(BTreeHeader.LENGTH);
|
|
364
|
+
reader.readFully(headerBytes);
|
|
365
|
+
const header = BTreeHeader.fromBytes(headerBytes);
|
|
366
|
+
return header.rootPtr;
|
|
367
|
+
}
|
|
368
|
+
static sortedStackFromIndex(cursor, rootPtr, startIndex) {
|
|
369
|
+
const stack = [];
|
|
370
|
+
let nodePtr = rootPtr;
|
|
371
|
+
let rem = startIndex;
|
|
372
|
+
while (true) {
|
|
373
|
+
const node = cursor.db.readSortedNode(nodePtr);
|
|
374
|
+
const position = nodePtr + BTREE_NODE_HEADER_SIZE;
|
|
375
|
+
if (node.kind === BTreeNodeKind.LEAF) {
|
|
376
|
+
stack.push(new IteratorLevel(position, node.entries, rem));
|
|
377
|
+
return stack;
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
let i = 0;
|
|
381
|
+
while (i + 1 < node.num && rem >= node.counts[i]) {
|
|
382
|
+
rem -= node.counts[i];
|
|
383
|
+
i++;
|
|
384
|
+
}
|
|
385
|
+
stack.push(new IteratorLevel(position, node.children, i));
|
|
386
|
+
nodePtr = Number(node.children[i].value);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
static sortedStackFromKey(cursor, rootPtr, key) {
|
|
391
|
+
const stack = [];
|
|
392
|
+
let nodePtr = rootPtr;
|
|
393
|
+
let before = 0;
|
|
394
|
+
while (true) {
|
|
395
|
+
const node = cursor.db.readSortedNode(nodePtr);
|
|
396
|
+
const position = nodePtr + BTREE_NODE_HEADER_SIZE;
|
|
397
|
+
if (node.kind === BTreeNodeKind.LEAF) {
|
|
398
|
+
let li = node.num;
|
|
399
|
+
for (let j = 0; j < node.num; j++) {
|
|
400
|
+
const kv = cursor.db.readKvPair(node.entries[j]);
|
|
401
|
+
if (cursor.db.compareKey(kv.keySlot, key) >= 0) {
|
|
402
|
+
li = j;
|
|
403
|
+
break;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
before += li;
|
|
407
|
+
stack.push(new IteratorLevel(position, node.entries, li));
|
|
408
|
+
return { stack, before };
|
|
409
|
+
}
|
|
410
|
+
else {
|
|
411
|
+
let i = 0;
|
|
412
|
+
while (i + 1 < node.num && cursor.db.compareKey(node.separators[i + 1], key) <= 0) {
|
|
413
|
+
before += node.counts[i];
|
|
414
|
+
i++;
|
|
415
|
+
}
|
|
416
|
+
stack.push(new IteratorLevel(position, node.children, i));
|
|
417
|
+
nodePtr = Number(node.children[i].value);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
init() {
|
|
422
|
+
switch (this.cursor.slotPtr.slot.tag) {
|
|
423
|
+
case 0 /* Tag.NONE */:
|
|
424
|
+
this.size = 0;
|
|
425
|
+
this.index = 0;
|
|
426
|
+
this.stack = [];
|
|
427
|
+
break;
|
|
428
|
+
case 2 /* Tag.ARRAY_LIST */: {
|
|
429
|
+
const position = Number(this.cursor.slotPtr.slot.value);
|
|
430
|
+
this.cursor.db.core.seek(position);
|
|
431
|
+
const reader = this.cursor.db.core.reader();
|
|
432
|
+
const headerBytes = new Uint8Array(ArrayListHeader.LENGTH);
|
|
433
|
+
reader.readFully(headerBytes);
|
|
434
|
+
const header = ArrayListHeader.fromBytes(headerBytes);
|
|
435
|
+
this.size = this.cursor.count();
|
|
436
|
+
this.index = 0;
|
|
437
|
+
this.stack = this.initStack(this.cursor, header.ptr);
|
|
438
|
+
break;
|
|
439
|
+
}
|
|
440
|
+
case 3 /* Tag.LINKED_ARRAY_LIST */:
|
|
441
|
+
case 14 /* Tag.SORTED_MAP */:
|
|
442
|
+
case 15 /* Tag.SORTED_SET */: {
|
|
443
|
+
// backed by a b-tree: read the header, then walk from the root node's
|
|
444
|
+
// value/child slots (skipping its kind+num header)
|
|
445
|
+
const position = Number(this.cursor.slotPtr.slot.value);
|
|
446
|
+
this.cursor.db.core.seek(position);
|
|
447
|
+
const reader = this.cursor.db.core.reader();
|
|
448
|
+
const headerBytes = new Uint8Array(BTreeHeader.LENGTH);
|
|
449
|
+
reader.readFully(headerBytes);
|
|
450
|
+
const header = BTreeHeader.fromBytes(headerBytes);
|
|
451
|
+
this.size = this.cursor.count();
|
|
452
|
+
this.index = 0;
|
|
453
|
+
this.stack = this.initStack(this.cursor, header.rootPtr + BTREE_NODE_HEADER_SIZE);
|
|
454
|
+
break;
|
|
455
|
+
}
|
|
456
|
+
case 4 /* Tag.HASH_MAP */:
|
|
457
|
+
case 11 /* Tag.HASH_SET */:
|
|
458
|
+
this.size = 0;
|
|
459
|
+
this.index = 0;
|
|
460
|
+
this.stack = this.initStack(this.cursor, Number(this.cursor.slotPtr.slot.value));
|
|
461
|
+
break;
|
|
462
|
+
case 12 /* Tag.COUNTED_HASH_MAP */:
|
|
463
|
+
case 13 /* Tag.COUNTED_HASH_SET */:
|
|
464
|
+
this.size = 0;
|
|
465
|
+
this.index = 0;
|
|
466
|
+
this.stack = this.initStack(this.cursor, Number(this.cursor.slotPtr.slot.value) + 8);
|
|
467
|
+
break;
|
|
468
|
+
default:
|
|
469
|
+
throw new UnexpectedTagException();
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
// read a 16-slot index block (the iterable structures all use 9-byte slots in
|
|
473
|
+
// their index/node blocks)
|
|
474
|
+
readSlotBlock(cursor, position) {
|
|
475
|
+
cursor.db.core.seek(position);
|
|
476
|
+
const reader = cursor.db.core.reader();
|
|
477
|
+
const indexBlockBytes = new Uint8Array(INDEX_BLOCK_SIZE);
|
|
478
|
+
reader.readFully(indexBlockBytes);
|
|
479
|
+
const indexBlock = new Array(SLOT_COUNT);
|
|
480
|
+
for (let i = 0; i < SLOT_COUNT; i++) {
|
|
481
|
+
const slotBytes = indexBlockBytes.slice(i * Slot.LENGTH, i * Slot.LENGTH + Slot.LENGTH);
|
|
482
|
+
indexBlock[i] = Slot.fromBytes(slotBytes);
|
|
483
|
+
}
|
|
484
|
+
return indexBlock;
|
|
485
|
+
}
|
|
486
|
+
initStack(cursor, position) {
|
|
487
|
+
return [new IteratorLevel(position, this.readSlotBlock(cursor, position), 0)];
|
|
488
|
+
}
|
|
489
|
+
hasNext() {
|
|
490
|
+
switch (this.cursor.slotPtr.slot.tag) {
|
|
491
|
+
case 0 /* Tag.NONE */:
|
|
492
|
+
return false;
|
|
493
|
+
case 2 /* Tag.ARRAY_LIST */:
|
|
494
|
+
return this.index < this.size;
|
|
495
|
+
case 3 /* Tag.LINKED_ARRAY_LIST */:
|
|
496
|
+
case 14 /* Tag.SORTED_MAP */:
|
|
497
|
+
case 15 /* Tag.SORTED_SET */:
|
|
498
|
+
return this.index < this.size;
|
|
499
|
+
case 4 /* Tag.HASH_MAP */:
|
|
500
|
+
case 11 /* Tag.HASH_SET */:
|
|
501
|
+
case 12 /* Tag.COUNTED_HASH_MAP */:
|
|
502
|
+
case 13 /* Tag.COUNTED_HASH_SET */:
|
|
503
|
+
if (this.nextCursorMaybe === null) {
|
|
504
|
+
this.nextCursorMaybe = this.nextInternal(0);
|
|
505
|
+
}
|
|
506
|
+
return this.nextCursorMaybe !== null;
|
|
507
|
+
default:
|
|
508
|
+
return false;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
next() {
|
|
512
|
+
switch (this.cursor.slotPtr.slot.tag) {
|
|
513
|
+
case 0 /* Tag.NONE */:
|
|
514
|
+
return null;
|
|
515
|
+
case 2 /* Tag.ARRAY_LIST */:
|
|
516
|
+
if (!(this.hasNext()))
|
|
517
|
+
return null;
|
|
518
|
+
this.index += 1;
|
|
519
|
+
return this.nextInternal(0);
|
|
520
|
+
case 3 /* Tag.LINKED_ARRAY_LIST */:
|
|
521
|
+
case 14 /* Tag.SORTED_MAP */:
|
|
522
|
+
case 15 /* Tag.SORTED_SET */:
|
|
523
|
+
if (!(this.hasNext()))
|
|
524
|
+
return null;
|
|
525
|
+
this.index += 1;
|
|
526
|
+
// b-tree nodes have a kind+num header before their slots, so child pointers
|
|
527
|
+
// are offset by BTREE_NODE_HEADER_SIZE
|
|
528
|
+
return this.nextInternal(BTREE_NODE_HEADER_SIZE);
|
|
529
|
+
case 4 /* Tag.HASH_MAP */:
|
|
530
|
+
case 11 /* Tag.HASH_SET */:
|
|
531
|
+
case 12 /* Tag.COUNTED_HASH_MAP */:
|
|
532
|
+
case 13 /* Tag.COUNTED_HASH_SET */:
|
|
533
|
+
if (this.nextCursorMaybe !== null) {
|
|
534
|
+
const nextCursor = this.nextCursorMaybe;
|
|
535
|
+
this.nextCursorMaybe = null;
|
|
536
|
+
return nextCursor;
|
|
537
|
+
}
|
|
538
|
+
else {
|
|
539
|
+
return this.nextInternal(0);
|
|
540
|
+
}
|
|
541
|
+
default:
|
|
542
|
+
throw new UnexpectedTagException();
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
nextInternal(nodeOffset) {
|
|
546
|
+
while (this.stack.length > 0) {
|
|
547
|
+
const level = this.stack[this.stack.length - 1];
|
|
548
|
+
if (level.index === level.block.length) {
|
|
549
|
+
this.stack.pop();
|
|
550
|
+
if (this.stack.length > 0) {
|
|
551
|
+
this.stack[this.stack.length - 1].index += 1;
|
|
552
|
+
}
|
|
553
|
+
continue;
|
|
554
|
+
}
|
|
555
|
+
else {
|
|
556
|
+
const nextSlot = level.block[level.index];
|
|
557
|
+
if (nextSlot.tag === 1 /* Tag.INDEX */) {
|
|
558
|
+
// nodeOffset skips a b-tree node's kind+num header
|
|
559
|
+
const nextPos = Number(nextSlot.value) + nodeOffset;
|
|
560
|
+
this.stack.push(new IteratorLevel(nextPos, this.readSlotBlock(this.cursor, nextPos), 0));
|
|
561
|
+
continue;
|
|
562
|
+
}
|
|
563
|
+
else {
|
|
564
|
+
this.stack[this.stack.length - 1].index += 1;
|
|
565
|
+
if (!nextSlot.empty()) {
|
|
566
|
+
const position = level.position + level.index * Slot.LENGTH;
|
|
567
|
+
return new ReadCursor(new SlotPointer(position, nextSlot), this.cursor.db);
|
|
568
|
+
}
|
|
569
|
+
else {
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
return null;
|
|
576
|
+
}
|
|
577
|
+
}
|
package/dist/read-hash-map.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Slot } from './slot';
|
|
2
|
-
import type { Slotted } from './slotted';
|
|
3
|
-
import { ReadCursor, CursorIterator, KeyValuePairCursor } from './read-cursor';
|
|
4
|
-
import { Bytes } from './writeable-data';
|
|
1
|
+
import { Slot } from './slot.js';
|
|
2
|
+
import type { Slotted } from './slotted.js';
|
|
3
|
+
import { ReadCursor, CursorIterator, KeyValuePairCursor } from './read-cursor.js';
|
|
4
|
+
import { Bytes } from './writeable-data.js';
|
|
5
5
|
export declare class ReadHashMap implements Slotted {
|
|
6
6
|
cursor: ReadCursor;
|
|
7
7
|
constructor();
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { HashMapGet, HashMapGetValue, HashMapGetKey, HashMapGetKVPair } from './database.js';
|
|
2
|
+
import { UnexpectedTagException } from './exceptions.js';
|
|
3
|
+
export class ReadHashMap {
|
|
4
|
+
cursor;
|
|
5
|
+
constructor(cursor) {
|
|
6
|
+
if (cursor) {
|
|
7
|
+
switch (cursor.slotPtr.slot.tag) {
|
|
8
|
+
case 0 /* Tag.NONE */:
|
|
9
|
+
case 4 /* Tag.HASH_MAP */:
|
|
10
|
+
case 11 /* Tag.HASH_SET */:
|
|
11
|
+
this.cursor = cursor;
|
|
12
|
+
break;
|
|
13
|
+
default:
|
|
14
|
+
throw new UnexpectedTagException();
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
slot() {
|
|
19
|
+
return this.cursor.slot();
|
|
20
|
+
}
|
|
21
|
+
iterator() {
|
|
22
|
+
return this.cursor.iterator();
|
|
23
|
+
}
|
|
24
|
+
*[Symbol.iterator]() {
|
|
25
|
+
yield* this.cursor;
|
|
26
|
+
}
|
|
27
|
+
getCursor(key) {
|
|
28
|
+
const hash = this.resolveHash(key);
|
|
29
|
+
return this.cursor.readPath([new HashMapGet(new HashMapGetValue(hash))]);
|
|
30
|
+
}
|
|
31
|
+
getSlot(key) {
|
|
32
|
+
const hash = this.resolveHash(key);
|
|
33
|
+
return this.cursor.readPathSlot([new HashMapGet(new HashMapGetValue(hash))]);
|
|
34
|
+
}
|
|
35
|
+
getKeyCursor(key) {
|
|
36
|
+
const hash = this.resolveHash(key);
|
|
37
|
+
return this.cursor.readPath([new HashMapGet(new HashMapGetKey(hash))]);
|
|
38
|
+
}
|
|
39
|
+
getKeySlot(key) {
|
|
40
|
+
const hash = this.resolveHash(key);
|
|
41
|
+
return this.cursor.readPathSlot([new HashMapGet(new HashMapGetKey(hash))]);
|
|
42
|
+
}
|
|
43
|
+
getKeyValuePair(key) {
|
|
44
|
+
const hash = this.resolveHash(key);
|
|
45
|
+
const cursor = this.cursor.readPath([new HashMapGet(new HashMapGetKVPair(hash))]);
|
|
46
|
+
if (cursor === null) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
return cursor.readKeyValuePair();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Helper to resolve key to hash
|
|
54
|
+
resolveHash(key) {
|
|
55
|
+
if (key instanceof Uint8Array) {
|
|
56
|
+
return key;
|
|
57
|
+
}
|
|
58
|
+
else if (typeof key === 'string') {
|
|
59
|
+
return this.cursor.db.hasher.digest(new TextEncoder().encode(key));
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
return this.cursor.db.hasher.digest(key.value);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
package/dist/read-hash-set.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Slot } from './slot';
|
|
2
|
-
import type { Slotted } from './slotted';
|
|
3
|
-
import { ReadCursor, CursorIterator } from './read-cursor';
|
|
4
|
-
import { Bytes } from './writeable-data';
|
|
1
|
+
import { Slot } from './slot.js';
|
|
2
|
+
import type { Slotted } from './slotted.js';
|
|
3
|
+
import { ReadCursor, CursorIterator } from './read-cursor.js';
|
|
4
|
+
import { Bytes } from './writeable-data.js';
|
|
5
5
|
export declare class ReadHashSet implements Slotted {
|
|
6
6
|
cursor: ReadCursor;
|
|
7
7
|
constructor();
|