xitdb 0.13.0 → 0.15.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.
Files changed (61) hide show
  1. package/README.md +129 -15
  2. package/dist/core-buffered-file.d.ts +2 -2
  3. package/dist/core-buffered-file.js +182 -0
  4. package/dist/core-file.d.ts +1 -1
  5. package/dist/core-file.js +105 -0
  6. package/dist/core-memory.d.ts +1 -1
  7. package/dist/core-memory.js +152 -0
  8. package/dist/core.js +1 -0
  9. package/dist/database.d.ts +174 -49
  10. package/dist/database.js +3047 -0
  11. package/dist/exceptions.d.ts +4 -0
  12. package/dist/exceptions.js +62 -0
  13. package/dist/hasher.js +49 -0
  14. package/dist/index.d.ts +30 -26
  15. package/dist/index.js +35 -3923
  16. package/dist/read-array-list.d.ts +4 -3
  17. package/dist/read-array-list.js +41 -0
  18. package/dist/read-counted-hash-map.d.ts +2 -2
  19. package/dist/read-counted-hash-map.js +19 -0
  20. package/dist/read-counted-hash-set.d.ts +2 -2
  21. package/dist/read-counted-hash-set.js +19 -0
  22. package/dist/read-cursor.d.ts +16 -5
  23. package/dist/read-cursor.js +678 -0
  24. package/dist/read-hash-map.d.ts +4 -4
  25. package/dist/read-hash-map.js +65 -0
  26. package/dist/read-hash-set.d.ts +4 -4
  27. package/dist/read-hash-set.js +47 -0
  28. package/dist/read-linked-array-list.d.ts +4 -3
  29. package/dist/read-linked-array-list.js +41 -0
  30. package/dist/read-sorted-map.d.ts +21 -0
  31. package/dist/read-sorted-map.js +73 -0
  32. package/dist/read-sorted-set.d.ts +19 -0
  33. package/dist/read-sorted-set.js +65 -0
  34. package/dist/slot-pointer.d.ts +1 -1
  35. package/dist/slot-pointer.js +11 -0
  36. package/dist/slot.d.ts +2 -2
  37. package/dist/slot.js +41 -0
  38. package/dist/slotted.d.ts +1 -1
  39. package/dist/slotted.js +1 -0
  40. package/dist/tag.d.ts +3 -1
  41. package/dist/tag.js +25 -0
  42. package/dist/write-array-list.d.ts +4 -4
  43. package/dist/write-array-list.js +42 -0
  44. package/dist/write-counted-hash-map.d.ts +2 -2
  45. package/dist/write-counted-hash-map.js +18 -0
  46. package/dist/write-counted-hash-set.d.ts +2 -2
  47. package/dist/write-counted-hash-set.js +18 -0
  48. package/dist/write-cursor.d.ts +5 -5
  49. package/dist/write-cursor.js +124 -0
  50. package/dist/write-hash-map.d.ts +4 -4
  51. package/dist/write-hash-map.js +90 -0
  52. package/dist/write-hash-set.d.ts +4 -4
  53. package/dist/write-hash-set.js +59 -0
  54. package/dist/write-linked-array-list.d.ts +4 -4
  55. package/dist/write-linked-array-list.js +52 -0
  56. package/dist/write-sorted-map.d.ts +12 -0
  57. package/dist/write-sorted-map.js +37 -0
  58. package/dist/write-sorted-set.d.ts +10 -0
  59. package/dist/write-sorted-set.js +29 -0
  60. package/dist/writeable-data.js +68 -0
  61. package/package.json +6 -6
@@ -0,0 +1,678 @@
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, BIT_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
+ // resolve a possibly-negative start index against `size` to a 0-based rank.
325
+ // negatives count from the end (-1 is the last entry); anything out of range
326
+ // returns -1 (yield nothing).
327
+ static resolveStartIndex(index, size) {
328
+ const resolved = index < 0 ? index + size : index;
329
+ if (resolved < 0 || resolved >= size)
330
+ return -1;
331
+ return resolved;
332
+ }
333
+ // start a sorted-map iterator at the entry with rank startIndex (the count descent),
334
+ // iterating in key order from there. negative indexes count from the end.
335
+ static initSortedFromIndex(cursor, startIndex) {
336
+ const it = new CursorIterator(cursor);
337
+ // an unwritten map is NONE (like iterator()): yield nothing
338
+ if (cursor.slotPtr.slot.tag === 0 /* Tag.NONE */) {
339
+ return it;
340
+ }
341
+ const total = cursor.count();
342
+ const idx = CursorIterator.resolveStartIndex(startIndex, total);
343
+ if (idx < 0) {
344
+ return it;
345
+ }
346
+ const rootPtr = CursorIterator.sortedRootPtr(cursor);
347
+ it.size = total;
348
+ it.index = idx;
349
+ it.stack = CursorIterator.sortedStackFromIndex(cursor, rootPtr, idx);
350
+ return it;
351
+ }
352
+ // start an array-list iterator at startIndex, descending the radix trie
353
+ // straight to that index. negatives count from the end; out of range (or an
354
+ // unwritten list) yields nothing.
355
+ static initArrayListFromIndex(cursor, startIndex) {
356
+ const it = new CursorIterator(cursor);
357
+ if (cursor.slotPtr.slot.tag !== 2 /* Tag.ARRAY_LIST */) {
358
+ return it;
359
+ }
360
+ cursor.db.core.seek(Number(cursor.slotPtr.slot.value));
361
+ const reader = cursor.db.core.reader();
362
+ const headerBytes = new Uint8Array(ArrayListHeader.LENGTH);
363
+ reader.readFully(headerBytes);
364
+ const header = ArrayListHeader.fromBytes(headerBytes);
365
+ const idx = CursorIterator.resolveStartIndex(startIndex, header.size);
366
+ if (idx < 0) {
367
+ return it;
368
+ }
369
+ const lastKey = header.size - 1;
370
+ const shift = lastKey < SLOT_COUNT ? 0 : Math.floor(Math.log(lastKey) / Math.log(SLOT_COUNT));
371
+ it.size = header.size;
372
+ it.index = idx;
373
+ it.stack = CursorIterator.arrayListStackFromIndex(cursor, header.ptr, idx, shift);
374
+ return it;
375
+ }
376
+ // start a linked-array-list iterator at startIndex, descending the
377
+ // count-augmented b-tree straight to that index. negatives count from the end.
378
+ static initLinkedArrayListFromIndex(cursor, startIndex) {
379
+ const it = new CursorIterator(cursor);
380
+ if (cursor.slotPtr.slot.tag !== 3 /* Tag.LINKED_ARRAY_LIST */) {
381
+ return it;
382
+ }
383
+ cursor.db.core.seek(Number(cursor.slotPtr.slot.value));
384
+ const reader = cursor.db.core.reader();
385
+ const headerBytes = new Uint8Array(BTreeHeader.LENGTH);
386
+ reader.readFully(headerBytes);
387
+ const header = BTreeHeader.fromBytes(headerBytes);
388
+ const idx = CursorIterator.resolveStartIndex(startIndex, header.size);
389
+ if (idx < 0) {
390
+ return it;
391
+ }
392
+ it.size = header.size;
393
+ it.index = idx;
394
+ it.stack = CursorIterator.btreeStackFromIndex(cursor, header.rootPtr, idx);
395
+ return it;
396
+ }
397
+ // start a sorted-map iterator at the first entry with key >= startKey
398
+ static initSortedFromKey(cursor, startKey) {
399
+ const it = new CursorIterator(cursor);
400
+ if (cursor.slotPtr.slot.tag === 0 /* Tag.NONE */) {
401
+ return it;
402
+ }
403
+ const total = cursor.count();
404
+ const rootPtr = CursorIterator.sortedRootPtr(cursor);
405
+ const built = CursorIterator.sortedStackFromKey(cursor, rootPtr, startKey);
406
+ it.size = total;
407
+ it.index = built.before;
408
+ it.stack = built.stack;
409
+ return it;
410
+ }
411
+ static sortedRootPtr(cursor) {
412
+ switch (cursor.slotPtr.slot.tag) {
413
+ case 14 /* Tag.SORTED_MAP */:
414
+ case 15 /* Tag.SORTED_SET */:
415
+ break;
416
+ default:
417
+ throw new UnexpectedTagException();
418
+ }
419
+ cursor.db.core.seek(Number(cursor.slotPtr.slot.value));
420
+ const reader = cursor.db.core.reader();
421
+ const headerBytes = new Uint8Array(BTreeHeader.LENGTH);
422
+ reader.readFully(headerBytes);
423
+ const header = BTreeHeader.fromBytes(headerBytes);
424
+ return header.rootPtr;
425
+ }
426
+ static sortedStackFromIndex(cursor, rootPtr, startIndex) {
427
+ const stack = [];
428
+ let nodePtr = rootPtr;
429
+ let rem = startIndex;
430
+ while (true) {
431
+ const node = cursor.db.readSortedNode(nodePtr);
432
+ const position = nodePtr + BTREE_NODE_HEADER_SIZE;
433
+ if (node.kind === BTreeNodeKind.LEAF) {
434
+ stack.push(new IteratorLevel(position, node.entries, rem));
435
+ return stack;
436
+ }
437
+ else {
438
+ let i = 0;
439
+ while (i + 1 < node.num && rem >= node.counts[i]) {
440
+ rem -= node.counts[i];
441
+ i++;
442
+ }
443
+ stack.push(new IteratorLevel(position, node.children, i));
444
+ nodePtr = Number(node.children[i].value);
445
+ }
446
+ }
447
+ }
448
+ // descend the array-list radix trie to startIndex, pushing one IteratorLevel
449
+ // per tier with its index set to that tier's child slot. nextInternal then
450
+ // walks forward from there.
451
+ static arrayListStackFromIndex(cursor, rootPtr, startIndex, shift) {
452
+ const stack = [];
453
+ let pos = rootPtr;
454
+ let sh = shift;
455
+ while (true) {
456
+ const block = CursorIterator.readSlotBlock(cursor, pos);
457
+ const i = (startIndex >>> (sh * BIT_COUNT)) & (SLOT_COUNT - 1);
458
+ stack.push(new IteratorLevel(pos, block, i));
459
+ if (sh === 0)
460
+ return stack;
461
+ // every tier above the leaf is a populated INDEX slot for any
462
+ // startIndex < size, so this child always exists
463
+ pos = Number(block[i].value);
464
+ sh -= 1;
465
+ }
466
+ }
467
+ // descend the linked-array-list count b-tree to startIndex; the positional
468
+ // analog of sortedStackFromIndex (no separator keys).
469
+ static btreeStackFromIndex(cursor, rootPtr, startIndex) {
470
+ const stack = [];
471
+ let nodePtr = rootPtr;
472
+ let rem = startIndex;
473
+ while (true) {
474
+ const node = cursor.db.readBTreeNode(nodePtr);
475
+ const position = nodePtr + BTREE_NODE_HEADER_SIZE;
476
+ if (node.kind === BTreeNodeKind.LEAF) {
477
+ stack.push(new IteratorLevel(position, node.values, rem));
478
+ return stack;
479
+ }
480
+ else {
481
+ let i = 0;
482
+ while (i + 1 < node.num && rem >= node.counts[i]) {
483
+ rem -= node.counts[i];
484
+ i++;
485
+ }
486
+ stack.push(new IteratorLevel(position, node.children, i));
487
+ nodePtr = Number(node.children[i].value);
488
+ }
489
+ }
490
+ }
491
+ static sortedStackFromKey(cursor, rootPtr, key) {
492
+ const stack = [];
493
+ let nodePtr = rootPtr;
494
+ let before = 0;
495
+ while (true) {
496
+ const node = cursor.db.readSortedNode(nodePtr);
497
+ const position = nodePtr + BTREE_NODE_HEADER_SIZE;
498
+ if (node.kind === BTreeNodeKind.LEAF) {
499
+ let li = node.num;
500
+ for (let j = 0; j < node.num; j++) {
501
+ const kv = cursor.db.readKvPair(node.entries[j]);
502
+ if (cursor.db.compareKey(kv.keySlot, key) >= 0) {
503
+ li = j;
504
+ break;
505
+ }
506
+ }
507
+ before += li;
508
+ stack.push(new IteratorLevel(position, node.entries, li));
509
+ return { stack, before };
510
+ }
511
+ else {
512
+ let i = 0;
513
+ while (i + 1 < node.num && cursor.db.compareKey(node.separators[i + 1], key) <= 0) {
514
+ before += node.counts[i];
515
+ i++;
516
+ }
517
+ stack.push(new IteratorLevel(position, node.children, i));
518
+ nodePtr = Number(node.children[i].value);
519
+ }
520
+ }
521
+ }
522
+ init() {
523
+ switch (this.cursor.slotPtr.slot.tag) {
524
+ case 0 /* Tag.NONE */:
525
+ this.size = 0;
526
+ this.index = 0;
527
+ this.stack = [];
528
+ break;
529
+ case 2 /* Tag.ARRAY_LIST */: {
530
+ const position = Number(this.cursor.slotPtr.slot.value);
531
+ this.cursor.db.core.seek(position);
532
+ const reader = this.cursor.db.core.reader();
533
+ const headerBytes = new Uint8Array(ArrayListHeader.LENGTH);
534
+ reader.readFully(headerBytes);
535
+ const header = ArrayListHeader.fromBytes(headerBytes);
536
+ this.size = this.cursor.count();
537
+ this.index = 0;
538
+ this.stack = this.initStack(this.cursor, header.ptr);
539
+ break;
540
+ }
541
+ case 3 /* Tag.LINKED_ARRAY_LIST */:
542
+ case 14 /* Tag.SORTED_MAP */:
543
+ case 15 /* Tag.SORTED_SET */: {
544
+ // backed by a b-tree: read the header, then walk from the root node's
545
+ // value/child slots (skipping its kind+num header)
546
+ const position = Number(this.cursor.slotPtr.slot.value);
547
+ this.cursor.db.core.seek(position);
548
+ const reader = this.cursor.db.core.reader();
549
+ const headerBytes = new Uint8Array(BTreeHeader.LENGTH);
550
+ reader.readFully(headerBytes);
551
+ const header = BTreeHeader.fromBytes(headerBytes);
552
+ this.size = this.cursor.count();
553
+ this.index = 0;
554
+ this.stack = this.initStack(this.cursor, header.rootPtr + BTREE_NODE_HEADER_SIZE);
555
+ break;
556
+ }
557
+ case 4 /* Tag.HASH_MAP */:
558
+ case 11 /* Tag.HASH_SET */:
559
+ this.size = 0;
560
+ this.index = 0;
561
+ this.stack = this.initStack(this.cursor, Number(this.cursor.slotPtr.slot.value));
562
+ break;
563
+ case 12 /* Tag.COUNTED_HASH_MAP */:
564
+ case 13 /* Tag.COUNTED_HASH_SET */:
565
+ this.size = 0;
566
+ this.index = 0;
567
+ this.stack = this.initStack(this.cursor, Number(this.cursor.slotPtr.slot.value) + 8);
568
+ break;
569
+ default:
570
+ throw new UnexpectedTagException();
571
+ }
572
+ }
573
+ // read a 16-slot index block (the iterable structures all use 9-byte slots in
574
+ // their index/node blocks)
575
+ static readSlotBlock(cursor, position) {
576
+ cursor.db.core.seek(position);
577
+ const reader = cursor.db.core.reader();
578
+ const indexBlockBytes = new Uint8Array(INDEX_BLOCK_SIZE);
579
+ reader.readFully(indexBlockBytes);
580
+ const indexBlock = new Array(SLOT_COUNT);
581
+ for (let i = 0; i < SLOT_COUNT; i++) {
582
+ const slotBytes = indexBlockBytes.slice(i * Slot.LENGTH, i * Slot.LENGTH + Slot.LENGTH);
583
+ indexBlock[i] = Slot.fromBytes(slotBytes);
584
+ }
585
+ return indexBlock;
586
+ }
587
+ initStack(cursor, position) {
588
+ return [new IteratorLevel(position, CursorIterator.readSlotBlock(cursor, position), 0)];
589
+ }
590
+ hasNext() {
591
+ switch (this.cursor.slotPtr.slot.tag) {
592
+ case 0 /* Tag.NONE */:
593
+ return false;
594
+ case 2 /* Tag.ARRAY_LIST */:
595
+ return this.index < this.size;
596
+ case 3 /* Tag.LINKED_ARRAY_LIST */:
597
+ case 14 /* Tag.SORTED_MAP */:
598
+ case 15 /* Tag.SORTED_SET */:
599
+ return this.index < this.size;
600
+ case 4 /* Tag.HASH_MAP */:
601
+ case 11 /* Tag.HASH_SET */:
602
+ case 12 /* Tag.COUNTED_HASH_MAP */:
603
+ case 13 /* Tag.COUNTED_HASH_SET */:
604
+ if (this.nextCursorMaybe === null) {
605
+ this.nextCursorMaybe = this.nextInternal(0);
606
+ }
607
+ return this.nextCursorMaybe !== null;
608
+ default:
609
+ return false;
610
+ }
611
+ }
612
+ next() {
613
+ switch (this.cursor.slotPtr.slot.tag) {
614
+ case 0 /* Tag.NONE */:
615
+ return null;
616
+ case 2 /* Tag.ARRAY_LIST */:
617
+ if (!(this.hasNext()))
618
+ return null;
619
+ this.index += 1;
620
+ return this.nextInternal(0);
621
+ case 3 /* Tag.LINKED_ARRAY_LIST */:
622
+ case 14 /* Tag.SORTED_MAP */:
623
+ case 15 /* Tag.SORTED_SET */:
624
+ if (!(this.hasNext()))
625
+ return null;
626
+ this.index += 1;
627
+ // b-tree nodes have a kind+num header before their slots, so child pointers
628
+ // are offset by BTREE_NODE_HEADER_SIZE
629
+ return this.nextInternal(BTREE_NODE_HEADER_SIZE);
630
+ case 4 /* Tag.HASH_MAP */:
631
+ case 11 /* Tag.HASH_SET */:
632
+ case 12 /* Tag.COUNTED_HASH_MAP */:
633
+ case 13 /* Tag.COUNTED_HASH_SET */:
634
+ if (this.nextCursorMaybe !== null) {
635
+ const nextCursor = this.nextCursorMaybe;
636
+ this.nextCursorMaybe = null;
637
+ return nextCursor;
638
+ }
639
+ else {
640
+ return this.nextInternal(0);
641
+ }
642
+ default:
643
+ throw new UnexpectedTagException();
644
+ }
645
+ }
646
+ nextInternal(nodeOffset) {
647
+ while (this.stack.length > 0) {
648
+ const level = this.stack[this.stack.length - 1];
649
+ if (level.index === level.block.length) {
650
+ this.stack.pop();
651
+ if (this.stack.length > 0) {
652
+ this.stack[this.stack.length - 1].index += 1;
653
+ }
654
+ continue;
655
+ }
656
+ else {
657
+ const nextSlot = level.block[level.index];
658
+ if (nextSlot.tag === 1 /* Tag.INDEX */) {
659
+ // nodeOffset skips a b-tree node's kind+num header
660
+ const nextPos = Number(nextSlot.value) + nodeOffset;
661
+ this.stack.push(new IteratorLevel(nextPos, CursorIterator.readSlotBlock(this.cursor, nextPos), 0));
662
+ continue;
663
+ }
664
+ else {
665
+ this.stack[this.stack.length - 1].index += 1;
666
+ if (!nextSlot.empty()) {
667
+ const position = level.position + level.index * Slot.LENGTH;
668
+ return new ReadCursor(new SlotPointer(position, nextSlot), this.cursor.db);
669
+ }
670
+ else {
671
+ continue;
672
+ }
673
+ }
674
+ }
675
+ }
676
+ return null;
677
+ }
678
+ }
@@ -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();