fix: multi-repo local dev (file:../ deps, package exports, missing deps)
This commit is contained in:
+220
@@ -0,0 +1,220 @@
|
||||
const b4a = require('b4a')
|
||||
|
||||
class SubTree {
|
||||
constructor(node, parent) {
|
||||
this.node = node
|
||||
this.parent = parent
|
||||
|
||||
this.isKey = node.children.length === 0
|
||||
this.i = this.isKey ? 1 : 0
|
||||
this.n = 0
|
||||
|
||||
const child = this.isKey ? null : this.node.children[0]
|
||||
this.seq = child !== null ? child.seq : this.node.keys[0].seq
|
||||
this.offset = child !== null ? child.offset : 0
|
||||
}
|
||||
|
||||
next() {
|
||||
this.i++
|
||||
this.isKey = (this.i & 1) === 1
|
||||
if (!this.isKey && !this.node.children.length) this.i++
|
||||
return this.update()
|
||||
}
|
||||
|
||||
async bisect(key, incl) {
|
||||
let s = 0
|
||||
let e = this.node.keys.length
|
||||
let c
|
||||
|
||||
while (s < e) {
|
||||
const mid = (s + e) >> 1
|
||||
c = cmp(key, await this.node.getKey(mid))
|
||||
|
||||
if (c === 0) {
|
||||
if (incl) this.i = mid * 2 + 1
|
||||
else this.i = mid * 2 + (this.node.children.length ? 2 : 3)
|
||||
return true
|
||||
}
|
||||
|
||||
if (c < 0) e = mid
|
||||
else s = mid + 1
|
||||
}
|
||||
|
||||
const i = c < 0 ? e : s
|
||||
this.i = 2 * i + (this.node.children.length ? 0 : 1)
|
||||
return this.node.children.length === 0
|
||||
}
|
||||
|
||||
update() {
|
||||
this.isKey = (this.i & 1) === 1
|
||||
this.n = this.i >> 1
|
||||
if (this.n >= (this.isKey ? this.node.keys.length : this.node.children.length)) return false
|
||||
const child = this.isKey ? null : this.node.children[this.n]
|
||||
this.seq = child !== null ? child.seq : this.node.keys[this.n].seq
|
||||
this.offset = child !== null ? child.offset : 0
|
||||
return true
|
||||
}
|
||||
|
||||
async key() {
|
||||
return this.n < this.node.keys.length
|
||||
? this.node.getKey(this.n)
|
||||
: this.parent && this.parent.key()
|
||||
}
|
||||
|
||||
async compare(tree) {
|
||||
const [a, b] = await Promise.all([this.key(), tree.key()])
|
||||
return cmp(a, b)
|
||||
}
|
||||
}
|
||||
|
||||
class TreeIterator {
|
||||
constructor(batch, opts) {
|
||||
this.batch = batch
|
||||
this.stack = []
|
||||
this.lt = opts.lt || opts.lte || null
|
||||
this.lte = !!opts.lte
|
||||
this.gt = opts.gt || opts.gte || null
|
||||
this.gte = !!opts.gte
|
||||
this.seeking = !!this.gt
|
||||
this.encoding = opts.encoding || batch.encoding
|
||||
}
|
||||
|
||||
async open() {
|
||||
const node = await this.batch.getRoot(false)
|
||||
if (!node || !node.keys.length) return
|
||||
const tree = new SubTree(node, null)
|
||||
if (this.seeking && !(await this._seek(tree))) return
|
||||
this.stack.push(tree)
|
||||
}
|
||||
|
||||
async _seek(tree) {
|
||||
const done = await tree.bisect(this.gt, this.gte)
|
||||
const oob = !tree.update()
|
||||
if (done || oob) {
|
||||
this.seeking = false
|
||||
if (oob) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
peek() {
|
||||
if (!this.stack.length) return null
|
||||
return this.stack[this.stack.length - 1]
|
||||
}
|
||||
|
||||
skip() {
|
||||
if (!this.stack.length) return
|
||||
if (!this.stack[this.stack.length - 1].next()) this.stack.pop()
|
||||
}
|
||||
|
||||
async nextKey() {
|
||||
let n = null
|
||||
while (this.stack.length && n === null) n = await this.next()
|
||||
if (n === null) return null
|
||||
if (!this.lt) return n.final(this.encoding)
|
||||
|
||||
const c = cmp(n.key, this.lt)
|
||||
if (this.lte ? c <= 0 : c < 0) return n.final(this.encoding)
|
||||
this.stack = []
|
||||
return null
|
||||
}
|
||||
|
||||
async next() {
|
||||
if (!this.stack.length) return null
|
||||
|
||||
const top = this.stack[this.stack.length - 1]
|
||||
const { isKey, n, seq } = top
|
||||
|
||||
if (!top.next()) {
|
||||
this.stack.pop()
|
||||
}
|
||||
|
||||
if (isKey) {
|
||||
this.seeking = false
|
||||
return this.batch.getBlock(seq)
|
||||
}
|
||||
|
||||
const child = await top.node.getChildNode(n)
|
||||
top.node.children[n] = null // unlink to save memory
|
||||
const tree = new SubTree(child, top)
|
||||
if (this.seeking && !(await this._seek(tree))) return null
|
||||
this.stack.push(tree)
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
close() {
|
||||
return this.batch._closeSnapshot()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = class DiffIterator {
|
||||
constructor(left, right, opts = {}) {
|
||||
this.left = new TreeIterator(left, opts)
|
||||
this.right = new TreeIterator(right, opts)
|
||||
this.limit = typeof opts.limit === 'number' ? opts.limit : -1
|
||||
}
|
||||
|
||||
async open() {
|
||||
await Promise.all([this.left.open(), this.right.open()])
|
||||
}
|
||||
|
||||
async next() {
|
||||
if (this.limit === 0) return null
|
||||
const res = await this._next()
|
||||
if (!res || (res.left === null && res.right === null)) return null
|
||||
this.limit--
|
||||
return res
|
||||
}
|
||||
|
||||
async _next() {
|
||||
const a = this.left
|
||||
const b = this.right
|
||||
|
||||
while (true) {
|
||||
const [l, r] = await Promise.all([a.peek(), b.peek()])
|
||||
|
||||
if (!l && !r) return null
|
||||
if (!l) return { left: null, right: await b.nextKey() }
|
||||
if (!r) return { left: await a.nextKey(), right: null }
|
||||
|
||||
if (l.seq === r.seq && l.isKey === r.isKey && l.offset === r.offset) {
|
||||
a.skip()
|
||||
b.skip()
|
||||
continue
|
||||
}
|
||||
|
||||
const c = await l.compare(r)
|
||||
|
||||
if (l.isKey && !r.isKey) {
|
||||
await b.next()
|
||||
continue
|
||||
}
|
||||
|
||||
if (!l.isKey && r.isKey) {
|
||||
await a.next()
|
||||
continue
|
||||
}
|
||||
|
||||
if (l.isKey && r.isKey) {
|
||||
if (c === 0) return { left: await a.nextKey(), right: await b.nextKey() }
|
||||
if (c < 0) return { left: await a.nextKey(), right: null }
|
||||
return { left: null, right: await b.nextKey() }
|
||||
}
|
||||
|
||||
if (c === 0) await Promise.all([a.next(), b.next()])
|
||||
else if (c < 0) await b.next()
|
||||
else await a.next()
|
||||
}
|
||||
}
|
||||
|
||||
async close() {
|
||||
await Promise.all([this.left.close(), this.right.close()])
|
||||
}
|
||||
}
|
||||
|
||||
function cmp(a, b) {
|
||||
if (!a) return b ? 1 : 0
|
||||
if (!b) return a ? -1 : 0
|
||||
return b4a.compare(a, b)
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
module.exports = class HistoryIterator {
|
||||
constructor(batch, opts = {}) {
|
||||
this.batch = batch
|
||||
this.options = opts
|
||||
this.live = !!opts.live
|
||||
this.gte = 0
|
||||
this.lt = 0
|
||||
this.reverse = !!opts.reverse
|
||||
this.limit = typeof opts.limit === 'number' ? opts.limit : -1
|
||||
this.encoding = opts.encoding || batch.encoding
|
||||
if (this.live && this.reverse) {
|
||||
throw new Error('Cannot have both live and reverse enabled')
|
||||
}
|
||||
}
|
||||
|
||||
async open() {
|
||||
await this.batch.getRoot(false) // does the update dance
|
||||
this.gte = gte(this.options, this.batch.version)
|
||||
this.lt = this.live ? Infinity : lt(this.options, this.batch.version)
|
||||
}
|
||||
|
||||
async next() {
|
||||
if (this.limit === 0) return null
|
||||
if (this.limit > 0) this.limit--
|
||||
|
||||
if (this.gte >= this.lt) return null
|
||||
|
||||
if (this.reverse) {
|
||||
if (this.lt <= 1) return null
|
||||
return final(await this.batch.getBlock(--this.lt), this.encoding)
|
||||
}
|
||||
|
||||
return final(await this.batch.getBlock(this.gte++), this.encoding)
|
||||
}
|
||||
|
||||
close() {
|
||||
return this.batch._closeSnapshot()
|
||||
}
|
||||
}
|
||||
|
||||
function final(node, encoding) {
|
||||
const type = node.isDeletion() ? 'del' : 'put'
|
||||
return { type, ...node.final(encoding) }
|
||||
}
|
||||
|
||||
function gte(opts, version) {
|
||||
if (opts.gt) return (opts.gt < 0 ? opts.gt + version : opts.gt) + 1
|
||||
const gte = opts.gte || opts.since || 1
|
||||
return gte < 0 ? gte + version : gte
|
||||
}
|
||||
|
||||
function lt(opts, version) {
|
||||
if (opts.lte === 0 || opts.lt === 0 || opts.end === 0) return 0
|
||||
if (opts.lte) return (opts.lte < 0 ? opts.lte + version : opts.lte) + 1
|
||||
const lt = opts.lt || opts.end || version
|
||||
return lt < 0 ? lt + version : lt
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
module.exports = class LocalBlocksIterator {
|
||||
constructor(batch, opts = {}) {
|
||||
this.batch = batch
|
||||
this.options = opts
|
||||
this.gte = 0
|
||||
this.lt = 0
|
||||
this.limit = typeof opts.limit === 'number' ? opts.limit : -1
|
||||
}
|
||||
|
||||
async open() {
|
||||
await this.batch.getRoot(false) // does the update dance
|
||||
this.gte = gte(this.options, this.batch.version)
|
||||
this.lt = lt(this.options, this.batch.version)
|
||||
}
|
||||
|
||||
async next() {
|
||||
if (this.limit === 0) return null
|
||||
if (this.limit > 0) this.limit--
|
||||
|
||||
while (this.gte < this.lt) {
|
||||
try {
|
||||
return await this.batch.getBlock(this.gte++)
|
||||
} catch {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
close() {
|
||||
return this.batch._closeSnapshot()
|
||||
}
|
||||
}
|
||||
|
||||
function gte(opts, version) {
|
||||
if (opts.gt) return (opts.gt < 0 ? opts.gt + version : opts.gt) + 1
|
||||
const gte = opts.gte || opts.since || 1
|
||||
return gte < 0 ? gte + version : gte
|
||||
}
|
||||
|
||||
function lt(opts, version) {
|
||||
if (opts.lte === 0 || opts.lt === 0 || opts.end === 0) return 0
|
||||
if (opts.lte) return (opts.lte < 0 ? opts.lte + version : opts.lte) + 1
|
||||
const lt = opts.lt || opts.end || version
|
||||
return lt < 0 ? lt + version : lt
|
||||
}
|
||||
+207
@@ -0,0 +1,207 @@
|
||||
const b4a = require('b4a')
|
||||
|
||||
module.exports = class RangeIterator {
|
||||
constructor(batch, encoding, opts = {}) {
|
||||
this.batch = batch
|
||||
this.stack = []
|
||||
this.opened = false
|
||||
this.encoding = encoding || batch.encoding
|
||||
|
||||
this._limit = typeof opts.limit === 'number' ? opts.limit : -1
|
||||
this._gIncl = !opts.gt
|
||||
this._gKey = opts.gt || opts.gte || null
|
||||
this._lIncl = !opts.lt
|
||||
this._lKey = opts.lt || opts.lte || null
|
||||
this._reverse = !!opts.reverse
|
||||
this._version = 0
|
||||
this._checkpoint = opts.checkpoint && opts.checkpoint.length ? opts.checkpoint : null
|
||||
this._nexting = false
|
||||
this._closed = false
|
||||
}
|
||||
|
||||
snapshot(version = this.batch.version) {
|
||||
const checkpoint = []
|
||||
for (const s of this.stack) {
|
||||
let { node, i } = s
|
||||
if (this._nexting && s === this.stack[this.stack.length - 1]) {
|
||||
i = this._reverse ? i + 1 : i - 1
|
||||
}
|
||||
if (!node.block) continue
|
||||
if (i < 0) continue
|
||||
checkpoint.push(node.block.seq, node.offset, i)
|
||||
}
|
||||
|
||||
return {
|
||||
version,
|
||||
gte: this._gIncl ? this._gKey : null,
|
||||
gt: this._gIncl ? null : this._gKey,
|
||||
lte: this._lIncl ? this._lKey : null,
|
||||
lt: this._lIncl ? null : this._lKey,
|
||||
limit: this._limit,
|
||||
reverse: this._reverse,
|
||||
ended: this.opened && !checkpoint.length,
|
||||
checkpoint: this.opened ? checkpoint : []
|
||||
}
|
||||
}
|
||||
|
||||
async open() {
|
||||
await this._open()
|
||||
this.opened = true
|
||||
}
|
||||
|
||||
async _open() {
|
||||
if (this._checkpoint) {
|
||||
for (let j = 0; j < this._checkpoint.length; j += 3) {
|
||||
const seq = this._checkpoint[j]
|
||||
const offset = this._checkpoint[j + 1]
|
||||
const i = this._checkpoint[j + 2]
|
||||
this.stack.push({
|
||||
node: (await this.batch.getBlock(seq)).getTreeNode(offset),
|
||||
i
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
this._nexting = true
|
||||
|
||||
let node = await this.batch.getRoot(false)
|
||||
if (!node) {
|
||||
this._nexting = false
|
||||
return
|
||||
}
|
||||
|
||||
const incl = this._reverse ? this._lIncl : this._gIncl
|
||||
const start = this._reverse ? this._lKey : this._gKey
|
||||
|
||||
if (!start) {
|
||||
this.stack.push({ node, i: this._reverse ? node.keys.length << 1 : 0 })
|
||||
this._nexting = false
|
||||
this._preloadQuery()
|
||||
return
|
||||
}
|
||||
|
||||
while (true) {
|
||||
const entry = { node, i: this._reverse ? node.keys.length << 1 : 0 }
|
||||
|
||||
let s = 0
|
||||
let e = node.keys.length
|
||||
let c
|
||||
|
||||
while (s < e) {
|
||||
const mid = (s + e) >> 1
|
||||
c = b4a.compare(start, await node.getKey(mid))
|
||||
|
||||
if (c === 0) {
|
||||
if (incl) entry.i = mid * 2 + 1
|
||||
else entry.i = mid * 2 + (this._reverse ? 0 : 2)
|
||||
this.stack.push(entry)
|
||||
this._nexting = false
|
||||
this._preloadQuery()
|
||||
return
|
||||
}
|
||||
|
||||
if (c < 0) e = mid
|
||||
else s = mid + 1
|
||||
}
|
||||
|
||||
const i = c < 0 ? e : s
|
||||
entry.i = 2 * i + (this._reverse ? -1 : 1)
|
||||
|
||||
if (entry.i >= 0 && entry.i <= node.keys.length << 1) this.stack.push(entry)
|
||||
if (!node.children.length) {
|
||||
this._nexting = false
|
||||
this._preloadQuery()
|
||||
return
|
||||
}
|
||||
|
||||
node = await node.getChildNode(i)
|
||||
}
|
||||
}
|
||||
|
||||
_preloadQuery() {
|
||||
const max = { nodes: 0, max: 2048 }
|
||||
|
||||
if (this._limit === -1 && !this._reverse && this.stack.length) {
|
||||
for (const s of this.stack) {
|
||||
const k = (s.i - (s.i & 1)) / 2
|
||||
this._preload(s.node, k + 1, this._lKey, max).catch(noop)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async _preload(node, i, end, max) {
|
||||
for (; i < node.keys.length; i++) {
|
||||
const key = node.keys[i]
|
||||
const block = await this.batch.getBlock(key.seq)
|
||||
if (this._closed) return
|
||||
const c = end ? b4a.compare(block.key, end) : -1
|
||||
if (c >= 0 || max.nodes >= max.max || i >= node.children.length) return
|
||||
max.nodes++
|
||||
const next = await node.getChildNode(i)
|
||||
if (this._closed) return
|
||||
this._preload(next, 0, end, max).catch(noop)
|
||||
}
|
||||
|
||||
if (node.children.length && max.nodes < max.max) {
|
||||
const next = await node.getChildNode(node.children.length - 1)
|
||||
if (this._closed) return
|
||||
this._preload(next, 0, end, max).catch(noop)
|
||||
}
|
||||
}
|
||||
|
||||
async next() {
|
||||
// TODO: this nexting flag is only needed if someone asks for a snapshot during
|
||||
// a lookup (ie the extension, pretty important...).
|
||||
// A better solution would be to refactor this so top.i is incremented eagerly
|
||||
// to get the current block instead of the way it is done now (++i vs i++)
|
||||
this._nexting = true
|
||||
|
||||
const end = this._reverse ? this._gKey : this._lKey
|
||||
const incl = this._reverse ? this._gIncl : this._lIncl
|
||||
|
||||
while (this.stack.length && (this._limit === -1 || this._limit > 0)) {
|
||||
const top = this.stack[this.stack.length - 1]
|
||||
const isKey = (top.i & 1) === 1
|
||||
const n = this._reverse ? (top.i < 0 ? top.node.keys.length : top.i-- >> 1) : top.i++ >> 1
|
||||
|
||||
if (!isKey) {
|
||||
if (!top.node.children.length) continue
|
||||
const node = await top.node.getChildNode(n)
|
||||
if (top.node.block.seq < this.batch.core.length) {
|
||||
top.node.children[n].value = null // unlink it to save memory
|
||||
}
|
||||
this.stack.push({ i: this._reverse ? node.keys.length << 1 : 0, node })
|
||||
continue
|
||||
}
|
||||
|
||||
if (n >= top.node.keys.length) {
|
||||
this.stack.pop()
|
||||
continue
|
||||
}
|
||||
|
||||
const key = top.node.keys[n]
|
||||
const block = await this.batch.getBlock(key.seq)
|
||||
if (end) {
|
||||
const c = b4a.compare(block.key, end)
|
||||
if (c === 0 ? !incl : this._reverse ? c < 0 : c > 0) {
|
||||
this._limit = 0
|
||||
break
|
||||
}
|
||||
}
|
||||
if (this._limit > 0) this._limit--
|
||||
this._nexting = false
|
||||
return block.final(this.encoding)
|
||||
}
|
||||
|
||||
this._nexting = false
|
||||
return null
|
||||
}
|
||||
|
||||
close() {
|
||||
this._closed = true
|
||||
return this.batch._closeSnapshot()
|
||||
}
|
||||
}
|
||||
|
||||
function noop() {}
|
||||
Reference in New Issue
Block a user