You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
144 lines
3.6 KiB
144 lines
3.6 KiB
'use strict'
|
|
|
|
var inherits = require('inherits')
|
|
var AbstractIterator = require('abstract-leveldown').AbstractIterator
|
|
var createKeyRange = require('./util/key-range')
|
|
var deserialize = require('./util/deserialize')
|
|
var noop = function () {}
|
|
|
|
module.exports = Iterator
|
|
|
|
function Iterator (db, location, options) {
|
|
AbstractIterator.call(this, db)
|
|
|
|
this._limit = options.limit
|
|
this._count = 0
|
|
this._callback = null
|
|
this._cache = []
|
|
this._completed = false
|
|
this._aborted = false
|
|
this._error = null
|
|
this._transaction = null
|
|
|
|
this._keys = options.keys
|
|
this._values = options.values
|
|
this._keyAsBuffer = options.keyAsBuffer
|
|
this._valueAsBuffer = options.valueAsBuffer
|
|
|
|
if (this._limit === 0) {
|
|
this._completed = true
|
|
return
|
|
}
|
|
|
|
try {
|
|
var keyRange = createKeyRange(options)
|
|
} catch (e) {
|
|
// The lower key is greater than the upper key.
|
|
// IndexedDB throws an error, but we'll just return 0 results.
|
|
this._completed = true
|
|
return
|
|
}
|
|
|
|
this.createIterator(location, keyRange, options.reverse)
|
|
}
|
|
|
|
inherits(Iterator, AbstractIterator)
|
|
|
|
Iterator.prototype.createIterator = function (location, keyRange, reverse) {
|
|
var self = this
|
|
var transaction = this.db.db.transaction([location], 'readonly')
|
|
var store = transaction.objectStore(location)
|
|
var req = store.openCursor(keyRange, reverse ? 'prev' : 'next')
|
|
|
|
req.onsuccess = function (ev) {
|
|
var cursor = ev.target.result
|
|
if (cursor) self.onItem(cursor)
|
|
}
|
|
|
|
this._transaction = transaction
|
|
|
|
// If an error occurs (on the request), the transaction will abort.
|
|
transaction.onabort = function () {
|
|
self.onAbort(self._transaction.error || new Error('aborted by user'))
|
|
}
|
|
|
|
transaction.oncomplete = function () {
|
|
self.onComplete()
|
|
}
|
|
}
|
|
|
|
Iterator.prototype.onItem = function (cursor) {
|
|
this._cache.push(cursor.key, cursor.value)
|
|
|
|
if (this._limit <= 0 || ++this._count < this._limit) {
|
|
cursor.continue()
|
|
}
|
|
|
|
this.maybeNext()
|
|
}
|
|
|
|
Iterator.prototype.onAbort = function (err) {
|
|
this._aborted = true
|
|
this._error = err
|
|
this.maybeNext()
|
|
}
|
|
|
|
Iterator.prototype.onComplete = function () {
|
|
this._completed = true
|
|
this.maybeNext()
|
|
}
|
|
|
|
Iterator.prototype.maybeNext = function () {
|
|
if (this._callback) {
|
|
this._next(this._callback)
|
|
this._callback = null
|
|
}
|
|
}
|
|
|
|
Iterator.prototype._next = function (callback) {
|
|
if (this._aborted) {
|
|
// The error should be picked up by either next() or end().
|
|
var err = this._error
|
|
this._error = null
|
|
this._nextTick(callback, err)
|
|
} else if (this._cache.length > 0) {
|
|
var key = this._cache.shift()
|
|
var value = this._cache.shift()
|
|
|
|
if (this._keys && key !== undefined) {
|
|
key = this._deserializeKey(key, this._keyAsBuffer)
|
|
} else {
|
|
key = undefined
|
|
}
|
|
|
|
if (this._values && value !== undefined) {
|
|
value = this._deserializeValue(value, this._valueAsBuffer)
|
|
} else {
|
|
value = undefined
|
|
}
|
|
|
|
this._nextTick(callback, null, key, value)
|
|
} else if (this._completed) {
|
|
this._nextTick(callback)
|
|
} else {
|
|
this._callback = callback
|
|
}
|
|
}
|
|
|
|
// Exposed for the v4 to v5 upgrade utility
|
|
Iterator.prototype._deserializeKey = deserialize
|
|
Iterator.prototype._deserializeValue = deserialize
|
|
|
|
Iterator.prototype._end = function (callback) {
|
|
if (this._aborted || this._completed) {
|
|
return this._nextTick(callback, this._error)
|
|
}
|
|
|
|
// Don't advance the cursor anymore, and the transaction will complete
|
|
// on its own in the next tick. This approach is much cleaner than calling
|
|
// transaction.abort() with its unpredictable event order.
|
|
this.onItem = noop
|
|
this.onAbort = callback
|
|
this.onComplete = callback
|
|
}
|