Skip to content

Commit

Permalink
add userData API (#11)
Browse files Browse the repository at this point in the history
* add user data api

* use correct pointer

* pointer typo

* userData key is a string

* add userData iterator

* do not use slab for key encoding

* user data iterator yields objects
  • Loading branch information
chm-diederichs authored Jun 26, 2024
1 parent 7f30f5a commit 5e92d11
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 3 deletions.
52 changes: 49 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const DATA = {
TREE: 4,
BITFIELD: 5,
BLOCK: 6,
USERDATA: 7
USER_DATA: 7
}

const SLAB = {
Expand Down Expand Up @@ -88,7 +88,11 @@ class WriteBatch {

setDataInfo (info) {
if (info.version !== 0) throw new Error('Version > 0 is not supported')
this.write.tryPut(encodeDataIndex(this.dataPointer, DATA.INFO), encode(m.DataInfo, info))
this.write.tryPut(encodeDataIndex(this.storage.dataPointer, DATA.INFO), encode(m.DataInfo, info))
}

setUserData (key, value) {
this.write.tryPut(encodeUserDataIndex(this.storage.dataPointer, DATA.USER_DATA, key), value)
}

putBlock (index, data) {
Expand Down Expand Up @@ -158,7 +162,11 @@ class ReadBatch {
}

getDataInfo (info) {
return this._get(encodeDataIndex(this.dataPointer, DATA.INFO), m.DataInfo)
return this._get(encodeDataIndex(this.storage.dataPointer, DATA.INFO), m.DataInfo)
}

getUserData (key) {
return this._get(encodeUserDataIndex(this.storage.dataPointer, DATA.USER_DATA, key), null)
}

async hasBlock (index) {
Expand Down Expand Up @@ -345,6 +353,13 @@ class HypercoreStorage {
return new WriteBatch(this, this.db.write())
}

createUserDataStream (opts = {}) {
const r = encodeIndexRange(this.dataPointer, DATA.USER_DATA, opts)
const s = this.db.iterator(r)
s._readableState.map = mapStreamUserData
return s
}

createTreeNodeStream (opts = {}) {
const r = encodeIndexRange(this.dataPointer, DATA.TREE, opts)
const s = this.db.iterator(r)
Expand Down Expand Up @@ -387,6 +402,13 @@ class HypercoreStorage {
return p
}

getUserData (key) {
const b = this.createReadBatch()
const p = b.getUserData(key)
b.tryFlush()
return p
}

getLocalKeyPair () {
const b = this.createReadBatch()
const p = b.getLocalKeyPair()
Expand Down Expand Up @@ -432,6 +454,18 @@ class HypercoreStorage {
}
}

function mapStreamUserData (data) {
const state = { start: 0, end: data.key.byteLength, buffer: data.key }

UINT.decode(state) // TL.DATA
UINT.decode(state) // pointer
UINT.decode(state) // DATA.USER_DATA

const key = c.string.decode(state)

return { key, value: data.value }
}

function mapStreamTreeNode (data) {
return c.decode(m.TreeNode, data.value)
}
Expand Down Expand Up @@ -513,6 +547,18 @@ function encodeDataIndex (pointer, type, index) {
return state.buffer.subarray(start, state.start)
}

function encodeUserDataIndex (pointer, type, key) {
const end = 128 + key.length
const state = { start: 0, end, buffer: Buffer.alloc(end) }
const start = state.start
UINT.encode(state, TL.DATA)
UINT.encode(state, pointer)
UINT.encode(state, type)
c.string.encode(state, key)

return state.buffer.subarray(start, state.start)
}

function encodeDiscoveryKey (discoveryKey) {
const state = ensureSlab(128)
const start = state.start
Expand Down
47 changes: 47 additions & 0 deletions test/basic.js
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,53 @@ test('header', async function (t) {
t.alike(await c2.getCoreHead(), head)
})

test('user data', async function (t) {
const c = await getCore(t)

{
const b = c.createWriteBatch()

b.setUserData('hello', Buffer.from('world'))
b.setUserData('hej', Buffer.from('verden'))

await b.flush()
}

{
const b = c.createReadBatch()
const data1 = b.getUserData('hello')
const data2 = b.getUserData('hej')
b.tryFlush()

t.alike(await data1, Buffer.from('world'))
t.alike(await data2, Buffer.from('verden'))
}

const exp = [
{ key: 'hej', value: Buffer.from('verden') },
{ key: 'hello', value: Buffer.from('world') }
]

const userData = []
for await (const e of c.createUserDataStream()) {
userData.push(e)
}

t.alike(userData, exp)

{
const b = c.createWriteBatch()

b.setUserData('hello', null)
b.setUserData('hej', Buffer.from('verden'))

await b.flush()
}

t.alike(await c.getUserData('hello'), null)
t.alike(await c.getUserData('hej'), Buffer.from('verden'))
})

async function getStorage (t, dir) {
if (!dir) dir = await tmp(t)
const s = new CoreStorage(dir)
Expand Down

0 comments on commit 5e92d11

Please sign in to comment.