diff --git a/iterator.js b/iterator.js index b9f46a5..cc8d29d 100644 --- a/iterator.js +++ b/iterator.js @@ -76,10 +76,18 @@ class Iterator extends AbstractIterator { } } - // TODO (v2): read from cache first? async _nextv (size, options) { this[kFirst] = false + // If next() was called then empty the cache first + if (this[kPosition] < this[kCache].length) { + const length = Math.min(size, this[kCache].length - this[kPosition]) + const chunk = this[kCache].slice(this[kPosition], this[kPosition] + length) + + this[kPosition] += length + return chunk + } + // Avoid iterator_nextv() call if end was already reached if ((this[kState][0] & STATE_ENDED) !== 0) { return [] diff --git a/test/iterator-test.js b/test/iterator-test.js index fe764d3..fd2e2b9 100644 --- a/test/iterator-test.js +++ b/test/iterator-test.js @@ -37,3 +37,37 @@ test('iterator optimized for seek', async function (t) { await ite.close() return db.close() }) + +// TODO: move to abstract-level +for (const slice of [false, true]) { + test(`nextv() after next() respects cache (slice=${slice})`, async function (t) { + const db = testCommon.factory() + + await db.open() + await db.batch() + .put('a', 'value') + .put('b', 'value') + .put('c', 'value') + .put('d', 'value') + .write() + + const it = db.iterator() + + // Two called are needed to populate the cache + t.same(await it.next(), ['a', 'value'], 'entry a ok') + t.same(await it.next(), ['b', 'value'], 'entry b ok') + t.is(it.cached, 2) + + if (slice) { + t.same(await it.nextv(1), [['c', 'value']], 'entries ok (1)') + t.same(await it.nextv(1), [['d', 'value']], 'entries ok (2)') + t.same(await it.nextv(1), [], 'empty') + } else { + t.same(await it.nextv(10), [['c', 'value'], ['d', 'value']], 'entries ok') + t.same(await it.nextv(10), [], 'empty') + } + + await it.close() + return db.close() + }) +}