forked from public/fvtt-cthulhu-eternal
107 lines
2.9 KiB
JavaScript
107 lines
2.9 KiB
JavaScript
|
'use strict'
|
||
|
|
||
|
const { AbstractIterator } = require('abstract-level')
|
||
|
const binding = require('./binding')
|
||
|
|
||
|
const kContext = Symbol('context')
|
||
|
const kCache = Symbol('cache')
|
||
|
const kFinished = Symbol('finished')
|
||
|
const kFirst = Symbol('first')
|
||
|
const kPosition = Symbol('position')
|
||
|
const kHandleNext = Symbol('handleNext')
|
||
|
const kHandleNextv = Symbol('handleNextv')
|
||
|
const kCallback = Symbol('callback')
|
||
|
const empty = []
|
||
|
|
||
|
// Does not implement _all() because the default implementation
|
||
|
// of abstract-level falls back to nextv(1000) and using all()
|
||
|
// on more entries than that probably isn't a realistic use case,
|
||
|
// so it'll typically just make one nextv(1000) call and there's
|
||
|
// no performance gain in overriding _all().
|
||
|
class Iterator extends AbstractIterator {
|
||
|
constructor (db, context, options) {
|
||
|
super(db, options)
|
||
|
|
||
|
this[kContext] = binding.iterator_init(context, options)
|
||
|
this[kHandleNext] = this[kHandleNext].bind(this)
|
||
|
this[kHandleNextv] = this[kHandleNextv].bind(this)
|
||
|
this[kCallback] = null
|
||
|
this[kFirst] = true
|
||
|
this[kCache] = empty
|
||
|
this[kFinished] = false
|
||
|
this[kPosition] = 0
|
||
|
}
|
||
|
|
||
|
_seek (target, options) {
|
||
|
this[kFirst] = true
|
||
|
this[kCache] = empty
|
||
|
this[kFinished] = false
|
||
|
this[kPosition] = 0
|
||
|
|
||
|
binding.iterator_seek(this[kContext], target)
|
||
|
}
|
||
|
|
||
|
_next (callback) {
|
||
|
if (this[kPosition] < this[kCache].length) {
|
||
|
const entry = this[kCache][this[kPosition]++]
|
||
|
process.nextTick(callback, null, entry[0], entry[1])
|
||
|
} else if (this[kFinished]) {
|
||
|
process.nextTick(callback)
|
||
|
} else {
|
||
|
this[kCallback] = callback
|
||
|
|
||
|
if (this[kFirst]) {
|
||
|
// It's common to only want one entry initially or after a seek()
|
||
|
this[kFirst] = false
|
||
|
binding.iterator_nextv(this[kContext], 1, this[kHandleNext])
|
||
|
} else {
|
||
|
// Limit the size of the cache to prevent starving the event loop
|
||
|
// while we're recursively calling process.nextTick().
|
||
|
binding.iterator_nextv(this[kContext], 1000, this[kHandleNext])
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[kHandleNext] (err, items, finished) {
|
||
|
const callback = this[kCallback]
|
||
|
if (err) return callback(err)
|
||
|
|
||
|
this[kCache] = items
|
||
|
this[kFinished] = finished
|
||
|
this[kPosition] = 0
|
||
|
|
||
|
this._next(callback)
|
||
|
}
|
||
|
|
||
|
_nextv (size, options, callback) {
|
||
|
if (this[kFinished]) {
|
||
|
process.nextTick(callback, null, [])
|
||
|
} else {
|
||
|
this[kCallback] = callback
|
||
|
this[kFirst] = false
|
||
|
binding.iterator_nextv(this[kContext], size, this[kHandleNextv])
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[kHandleNextv] (err, items, finished) {
|
||
|
const callback = this[kCallback]
|
||
|
if (err) return callback(err)
|
||
|
this[kFinished] = finished
|
||
|
callback(null, items)
|
||
|
}
|
||
|
|
||
|
_close (callback) {
|
||
|
this[kCache] = empty
|
||
|
this[kCallback] = null
|
||
|
|
||
|
binding.iterator_close(this[kContext], callback)
|
||
|
}
|
||
|
|
||
|
// Undocumented, exposed for tests only
|
||
|
get cached () {
|
||
|
return this[kCache].length - this[kPosition]
|
||
|
}
|
||
|
}
|
||
|
|
||
|
exports.Iterator = Iterator
|