Skip to content

Commit

Permalink
drafts are working
Browse files Browse the repository at this point in the history
  • Loading branch information
mafintosh committed Sep 23, 2024
1 parent 95b904a commit f272784
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 11 deletions.
8 changes: 4 additions & 4 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ module.exports = class Hypercore extends EventEmitter {
this.opened = false
this.closed = false
this.snapshotted = !!opts.snapshot
this.dryRun = !!opts.dryRun
this.draft = !!opts.draft
this.sparse = opts.sparse !== false
this.sessions = opts._sessions || [this]
this.autoClose = !!opts.autoClose
Expand Down Expand Up @@ -268,7 +268,7 @@ module.exports = class Hypercore extends EventEmitter {
this.writable = this._isWritable()
this.autoClose = o.autoClose

if (o.state) this.state = this.dryRun ? o.state.memoryOverlay() : this.snapshotted ? o.state.snapshot() : o.state.ref()
if (o.state) this.state = this.draft ? o.state.memoryOverlay() : this.snapshotted ? o.state.snapshot() : o.state.ref()

if (o.core) this.tracer.setParent(o.core.tracer)

Expand Down Expand Up @@ -805,7 +805,7 @@ module.exports = class Hypercore extends EventEmitter {
if (this.opened === false) await this.opening
if (!isValidIndex(bytes)) throw ASSERTION('seek is invalid')

const tree = (opts && opts.tree) || this.core.tree
const tree = (opts && opts.tree) || this.state.tree
const s = tree.seek(bytes, this.padding)

const offset = await s.update()
Expand Down Expand Up @@ -1037,7 +1037,7 @@ module.exports = class Hypercore extends EventEmitter {
async treeHash (length) {
if (length === undefined) {
await this.ready()
length = this.core.tree.length
length = this.state.tree.length
}

const roots = await this.state.tree.getRoots(length)
Expand Down
7 changes: 4 additions & 3 deletions lib/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,15 +185,16 @@ class SessionState {
}

memoryOverlay () {
const storage = new MemoryOverlay(this.storage)
const s = new SessionState(
this.core,
this.storage,
this.blocks,
this.tree,
this.tree.clone(storage),
this.bitfield,
this.treeLength,
null,
new MemoryOverlay(this.storage)
storage
)

return s
Expand Down Expand Up @@ -871,7 +872,7 @@ module.exports = class Core {

const promises = []

const reader = state.storage.createReadBatch()
const reader = state.createReadBatch()
for (let i = treeLength; i < length; i++) promises.push(reader.getBlock(i))
reader.tryFlush()

Expand Down
15 changes: 13 additions & 2 deletions lib/memory-overlay.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class MemoryOverlay {
this.bitfields = null
}

createReadBatch (read) {
createReadBatch (read = this.storage.createReadBatch()) {
return new MemoryOverlayReadBatch(this, read)
}

Expand All @@ -42,6 +42,10 @@ class TipList {
this.data = []
}

end () {
return this.offset + this.data.length
}

put (index, value) {
if (this.data.length === 0) {
this.offset = index
Expand All @@ -62,6 +66,12 @@ class TipList {
if (index >= this.data.length) return null
return this.data[index]
}

* [Symbol.iterator] () {
for (let i = 0; i < this.data.length; i++) {
yield [i + this.offset, this.data[i]]
}
}
}

module.exports = MemoryOverlay
Expand Down Expand Up @@ -233,7 +243,8 @@ function mergeMap (a, b) {

function mergeTip (a, b) {
if (a === null) return b
if (a.offset + a.data.length !== b.offset) throw ASSERTION('Cannot merge tip list')
while (a.end() !== b.offset && b.offset >= a.offset && b.end() >= a.end()) a.data.pop()
if (a.end() !== b.offset) throw ASSERTION('Cannot merge tip list')
for (const data of b.data) a.data.push(data)
return a
}
15 changes: 13 additions & 2 deletions lib/merkle-tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,10 @@ module.exports = class MerkleTree {
return u.finalise()
}

clone (storage) {
return new MerkleTree(storage, this.roots.slice(0), this.fork, this.signature, this.prologue)
}

batch () {
return new MerkleTreeBatch(this)
}
Expand Down Expand Up @@ -683,7 +687,7 @@ module.exports = class MerkleTree {
get (index, error = true, readBatch = null) {
if (readBatch) return readBatch.getTreeNode(index, error)

return this.storage.getTreeNode(index, error)
return getTreeNode(this.storage, index, error)
}

clear (writer) {
Expand Down Expand Up @@ -847,7 +851,7 @@ module.exports = class MerkleTree {

const roots = []
for (const index of flat.fullRoots(2 * length)) {
roots.push(unslabNode(await storage.getTreeNode(index, true)))
roots.push(unslabNode(await getTreeNode(storage, index, true)))
}

return new MerkleTree(storage, roots, opts.fork || 0, opts.signature || null, opts.prologue || null)
Expand Down Expand Up @@ -1274,6 +1278,13 @@ function normalizeIndexed (block, hash) {
return null
}

function getTreeNode (storage, index, error) {
const batch = storage.createReadBatch()
const node = batch.getTreeNode(index, error)
batch.tryFlush()
return node
}

async function settleProof (p) {
const result = [
p.node && Promise.all(p.node),
Expand Down
1 change: 1 addition & 0 deletions test/all.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ async function runTests () {
// await import('./compat.js') // todo: how to test compat?
await import('./conflicts.js')
await import('./core.js')
await import('./draft.js')
await import('./encodings.js')
await import('./encryption.js')
await import('./extension.js')
Expand Down
50 changes: 50 additions & 0 deletions test/draft.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
const { create } = require('./helpers')
const test = require('brittle')
const b4a = require('b4a')

test('draft', async function (t) {
const core = await create(t)

await core.append('hello')
await core.append('world')

const draft = core.session({ draft: true })

await draft.append('edits!')

t.alike(await draft.get(0), b4a.from('hello'))
t.alike(await draft.get(1), b4a.from('world'))
t.alike(await draft.get(2), b4a.from('edits!'))
t.alike(await draft.seek(11), [2, 1])
t.alike(draft.byteLength, 16)
t.alike(draft.length, 3)

await draft.close()

// nothing changed as it was a draft
t.alike(core.byteLength, 10)
t.alike(core.length, 2)

await core.close()
})

test('draft and then undraft', async function (t) {
const core = await create(t)

await core.append('hello')
await core.append('world')

const draft = core.session({ draft: true })

await draft.append('edits!')

await core.core.commit(draft.state)

await draft.close()

// nothing changed as it was a draft
t.alike(core.byteLength, 16)
t.alike(core.length, 3)

await core.close()
})

0 comments on commit f272784

Please sign in to comment.