forked from Level/classic-level
-
Notifications
You must be signed in to change notification settings - Fork 0
/
iterator.js
106 lines (89 loc) · 2.92 KB
/
iterator.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
'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