memory-level
Version:
In-memory abstract-level database for Node.js and browsers
277 lines (221 loc) • 6.72 kB
JavaScript
'use strict'
const {
AbstractIterator,
AbstractKeyIterator,
AbstractValueIterator
} = require('abstract-level')
const compare = require('./compare')
const breathe = require('./breathe')
const kNone = Symbol('none')
const kIterator = Symbol('iterator')
const kLowerBound = Symbol('lowerBound')
const kUpperBound = Symbol('upperBound')
const kOutOfRange = Symbol('outOfRange')
const kReverse = Symbol('reverse')
const kOptions = Symbol('options')
const kTest = Symbol('test')
const kAdvance = Symbol('advance')
const kInit = Symbol('init')
function gt (value) {
return compare(value, this[kUpperBound]) > 0
}
function gte (value) {
return compare(value, this[kUpperBound]) >= 0
}
function lt (value) {
return compare(value, this[kUpperBound]) < 0
}
function lte (value) {
return compare(value, this[kUpperBound]) <= 0
}
class MemoryEntryIterator extends AbstractIterator {
constructor (db, tree, options) {
super(db, options)
this[kInit](tree, options)
}
async _next () {
if (!this[kIterator].valid) return undefined
const key = this[kIterator].key
const value = this[kIterator].value
if (!this[kTest](key)) return undefined
this[kIterator][this[kAdvance]]()
return [key, value]
}
async _nextv (size, options) {
const it = this[kIterator]
const entries = []
while (it.valid && entries.length < size && this[kTest](it.key)) {
entries.push([it.key, it.value])
it[this[kAdvance]]()
}
return entries
}
async _all (options) {
const size = this.limit - this.count
const it = this[kIterator]
const entries = []
while (it.valid && entries.length < size && this[kTest](it.key)) {
entries.push([it.key, it.value])
it[this[kAdvance]]()
}
return entries
}
}
class MemoryKeyIterator extends AbstractKeyIterator {
constructor (db, tree, options) {
super(db, options)
this[kInit](tree, options)
}
async _next () {
if (!this[kIterator].valid) return undefined
const key = this[kIterator].key
if (!this[kTest](key)) return undefined
this[kIterator][this[kAdvance]]()
return key
}
async _nextv (size, options) {
const it = this[kIterator]
const keys = []
while (it.valid && keys.length < size && this[kTest](it.key)) {
keys.push(it.key)
it[this[kAdvance]]()
}
return keys
}
async _all (options) {
const size = this.limit - this.count
const it = this[kIterator]
const keys = []
while (it.valid && keys.length < size && this[kTest](it.key)) {
keys.push(it.key)
it[this[kAdvance]]()
}
return keys
}
}
class MemoryValueIterator extends AbstractValueIterator {
constructor (db, tree, options) {
super(db, options)
this[kInit](tree, options)
}
async _next (options) {
if (!this[kIterator].valid) return undefined
const key = this[kIterator].key
const value = this[kIterator].value
if (!this[kTest](key)) return undefined
this[kIterator][this[kAdvance]]()
return value
}
async _nextv (size, options) {
const it = this[kIterator]
const values = []
while (it.valid && values.length < size && this[kTest](it.key)) {
values.push(it.value)
it[this[kAdvance]]()
}
return values
}
async _all (options) {
const size = this.limit - this.count
const it = this[kIterator]
const values = []
while (it.valid && values.length < size && this[kTest](it.key)) {
values.push(it.value)
it[this[kAdvance]]()
}
return values
}
}
class MemoryClearIterator extends AbstractKeyIterator {
constructor (db, tree, options) {
super(db, options)
this[kInit](tree, options)
}
// This is not an abstract-level API
async visit (visitor) {
const limit = this.limit
const it = this[kIterator]
let count = 0
while (true) {
for (let i = 0; i < 500; i++) {
if (++count > limit) return
if (!it.valid || !this[kTest](it.key)) return
visitor(it.key)
it[this[kAdvance]]()
}
// Some time to breathe
await breathe()
}
}
}
for (const Ctor of [MemoryEntryIterator, MemoryKeyIterator, MemoryValueIterator, MemoryClearIterator]) {
Ctor.prototype[kInit] = function (tree, options) {
this[kReverse] = options.reverse
this[kOptions] = options
if (!this[kReverse]) {
this[kAdvance] = 'next'
this[kLowerBound] = 'gte' in options ? options.gte : 'gt' in options ? options.gt : kNone
this[kUpperBound] = 'lte' in options ? options.lte : 'lt' in options ? options.lt : kNone
if (this[kLowerBound] === kNone) {
this[kIterator] = tree.begin
} else if ('gte' in options) {
this[kIterator] = tree.ge(this[kLowerBound])
} else {
this[kIterator] = tree.gt(this[kLowerBound])
}
if (this[kUpperBound] !== kNone) {
this[kTest] = 'lte' in options ? lte : lt
}
} else {
this[kAdvance] = 'prev'
this[kLowerBound] = 'lte' in options ? options.lte : 'lt' in options ? options.lt : kNone
this[kUpperBound] = 'gte' in options ? options.gte : 'gt' in options ? options.gt : kNone
if (this[kLowerBound] === kNone) {
this[kIterator] = tree.end
} else if ('lte' in options) {
this[kIterator] = tree.le(this[kLowerBound])
} else {
this[kIterator] = tree.lt(this[kLowerBound])
}
if (this[kUpperBound] !== kNone) {
this[kTest] = 'gte' in options ? gte : gt
}
}
}
Ctor.prototype[kTest] = function () {
return true
}
Ctor.prototype[kOutOfRange] = function (target) {
if (!this[kTest](target)) {
return true
} else if (this[kLowerBound] === kNone) {
return false
} else if (!this[kReverse]) {
if ('gte' in this[kOptions]) {
return compare(target, this[kLowerBound]) < 0
} else {
return compare(target, this[kLowerBound]) <= 0
}
} else {
if ('lte' in this[kOptions]) {
return compare(target, this[kLowerBound]) > 0
} else {
return compare(target, this[kLowerBound]) >= 0
}
}
}
Ctor.prototype._seek = function (target, options) {
if (this[kOutOfRange](target)) {
this[kIterator] = this[kIterator].tree.end
this[kIterator].next()
} else if (this[kReverse]) {
this[kIterator] = this[kIterator].tree.le(target)
} else {
this[kIterator] = this[kIterator].tree.ge(target)
}
}
}
exports.MemoryEntryIterator = MemoryEntryIterator
exports.MemoryKeyIterator = MemoryKeyIterator
exports.MemoryValueIterator = MemoryValueIterator
exports.MemoryClearIterator = MemoryClearIterator