diff --git a/lib/ranges.js b/lib/ranges.js index 3ef166e..d831b92 100644 --- a/lib/ranges.js +++ b/lib/ranges.js @@ -119,6 +119,7 @@ class RangeIterator { // TODO: dbl check this for off-by-ones with the offset and keys and children let limit = this.limit + // TODO: if limit === -1, don't return early here if (limit < this.tree.context.minKeys) return const parent = this.stack[this.stack.length - 1] @@ -128,17 +129,21 @@ class RangeIterator { this.prefetching = pv for (let i = parent.offset >> 1; i < pv.children.length; i++) { - const k = pv.keys.get(i) + // If the preceding key in parent is beyond upper bound, + // stop fetching child nodes. + if (i > 0) { + const k = pv.keys.get(i - 1) - const cmp = this.reverse - ? this.start - ? b4a.compare(this.start, k.key) - : -1 - : this.end - ? b4a.compare(k.key, this.end) - : -1 + const cmp = this.reverse + ? this.start + ? b4a.compare(this.start, k.key) + : -1 + : this.end + ? b4a.compare(k.key, this.end) + : -1 - if (cmp > this.compare) break + if (cmp > this.compare) return + } const c = pv.children.get(i) if (!c.value) this.tree.inflate(c, this.config).catch(noop) diff --git a/test/basic.js b/test/basic.js index bc4b352..052d015 100644 --- a/test/basic.js +++ b/test/basic.js @@ -556,3 +556,35 @@ test('lock to avoid building concurrent batches', async function (t) { t.alike((await db.get(name)).value, b4a.from('WORLD!')) }) + +test('RangeIterator.prefetchNext with upper bound', async function (t) { + const db = await create(t) + + function encodeUint32(n) { + const buf = new ArrayBuffer(4) + const view = new DataView(buf) + view.setUint32(0, n, false) + return b4a.from(buf) + } + + const ENTRIES = 256 + + const w = db.write() + for (let i = 0; i < ENTRIES; i++) { + w.tryPut(encodeUint32(i), encodeUint32(i)) + } + await w.flush() + + const opt = { + prefetch: true, + lt: encodeUint32(ENTRIES), + // Needs a limit > minKeys to avoid early exit in prefetchNext + // (separate bug that can hide this one) + limit: ENTRIES * 2 + } + let count = 0 + for await (const _ of db.createReadStream(opt)) { + count++ + } + t.alike(count, ENTRIES) +})