git ssb

0+

wanderer🌟 / js-dfinity-radix-tree



Commit fb6c040c6945375f945c7613513fc1a550c557fc

readded single array nodes

wanderer committed on 8/4/2017, 8:06:42 PM
Parent: f9516195f8ab298aa8892fc99db3029a7a3d7742

Files changed

index.jschanged
tests/index.jschanged
index.jsView
@@ -3,22 +3,38 @@
33 const TextEncoder = require('text-encoding').TextEncoder
44
55 const encoder = new TextEncoder('utf-8')
66
7-const EXTENSION = 0
8-const BRANCH = 1
9-const VALUE = 2
7 +const LBRANCH = 0
8 +const RBRANCH = 1
9 +const EXTENSION = 2
10 +const VALUE = 3
1011
1112 const RadixTree = module.exports = class RadixTree {
13 + /**
14 + * @param opts
15 + * @param opts.root {object} a merkle root to a radix tree. If none, RadixTree will create an new root.
16 + * @param opts.graph {object} an instance of [ipld-graph-builder](https://github.com/ipld/js-ipld-graph-builder) alternitvly `opts.dag` can be used
17 + * @param opts.dag {object} an instance if [ipfs.dag](https://github.com/ipfs/js-ipfs#dag). If there is no `opts.graph` this will be used to create a new graph instance.
18 + */
1219 constructor (opts) {
1320 this.root = opts.root || {'/': undefined}
1421 this.graph = opts.graph || new Graph(opts.dag)
1522 }
1623
24 + /**
25 + * returns an Uint1Array constructir which is used to repersent keys
26 + * @returns {object}
27 + */
1728 static get ArrayConstructor () {
1829 return Uint1Array
1930 }
2031
32 + /**
33 + * converts a TypedArray or Buffer to an Uint1Array
34 + * @param {TypedArray} array - the array to convert
35 + * @returns {TypedArray}
36 + */
2137 static toTypedArray (array) {
2238 return new RadixTree.ArrayConstructor(new Uint8Array(array).buffer)
2339 }
2440
@@ -27,10 +43,10 @@
2743 let root = this.root
2844 let parent
2945 while (1) {
3046 // load the root
31- await this.graph.get(root, 0, true)
32- if (hasExtension(root)) {
47 + const exNode = await this.graph.get(root, EXTENSION, true)
48 + if (exNode) {
3349 let extensionIndex = 0
3450 const extensionLen = getExLength(root)
3551 const extension = getExtension(root)
3652 let subKey
@@ -86,14 +102,24 @@
86102 index: index
87103 }
88104 }
89105
106 + /**
107 + * gets a value given a key
108 + * @param {*} key
109 + * @return {Promise}
110 + */
90111 async get (key) {
91112 key = RadixTree.formatKey(key)
92113 const result = await this._get(key)
93114 return result.value
94115 }
95116
117 + /**
118 + * stores a value at a given key
119 + * @param {*} key
120 + * @return {Promise}
121 + */
96122 async set (key, value) {
97123 key = RadixTree.formatKey(key)
98124
99125 if (value.length > 32) {
@@ -129,25 +155,32 @@
129155 const extension = key.subarray(result.index + 1, key.length)
130156 const newNode = createNode(extension, [], value)
131157 const rootBranch = getBranch(root)
132158 rootBranch[keySegment] = newNode
159 + setBranch(root, rootBranch)
133160 } else {
134161 setValue(root, value)
135162 }
136163 }
137164 }
138165 }
139166
167 + /**
168 + * deletes a value at a given key
169 + * @param {*} key
170 + * @return {Promise}
171 + */
140172 async delete (key) {
141173 key = RadixTree.formatKey(key)
142174 const results = await this._get(key)
143- if (results.value) {
175 + if (results.value !== undefined) {
144176 const root = results.root
145177 const parent = results.parent
146178
147179 deleteValue(root)
148180
149- if (getBranch(root).length) {
181 + const branch = getBranch(root)
182 + if (branch.some(el => el !== undefined)) {
150183 joinNodes(root)
151184 } else {
152185 if (!parent) {
153186 root['/'] = undefined
@@ -188,26 +221,49 @@
188221 }
189222 }
190223 }
191224
225 + /**
226 + * creates a merkle root for the current tree
227 + * @returns {Promise}
228 + */
192229 createMerkleRoot () {
193230 return this.graph.flush(this.root)
194231 }
195232
196233 static formatKey (key) {
197234 if (typeof key === 'string') {
198235 key = encoder.encode(key)
199236 }
200- return new RadixTree.ArrayConstructor(key.buffer)
237 +
238 + if (key.constructor !== RadixTree.ArrayConstructor) {
239 + return new RadixTree.ArrayConstructor(key.buffer)
240 + } else {
241 + return key
242 + }
201243 }
202244 }
203245
246 +function createNode (ex, branch, value) {
247 + const node = {
248 + '/': []
249 + }
250 +
251 + setValue(node, value)
252 + setExtension(node, ex)
253 + setBranch(node, branch)
254 +
255 + return node
256 +}
257 +
258 +// helper functions for nodes
204259 function setBranch (node, branch) {
205- node['/'][BRANCH] = branch
260 + node['/'][LBRANCH] = branch[0]
261 + node['/'][RBRANCH] = branch[1]
206262 }
207263
208264 function getBranch (node) {
209- return node['/'][BRANCH]
265 + return node['/'].slice(LBRANCH, 2)
210266 }
211267
212268 function getValue (node) {
213269 return node['/'][VALUE]
@@ -216,14 +272,14 @@
216272 function deleteValue (node) {
217273 node['/'].pop()
218274 }
219275
220-function hasExtension (node) {
221- return node['/'][EXTENSION].length === 2
222-}
223-
224276 function getExtension (node) {
225- return RadixTree.toTypedArray(node['/'][EXTENSION][1]).subarray(0, getExLength(node))
277 + if (node['/'][EXTENSION]) {
278 + return RadixTree.toTypedArray(node['/'][EXTENSION][1]).subarray(0, getExLength(node))
279 + } else {
280 + return []
281 + }
226282 }
227283
228284 function getExLength (node) {
229285 return node['/'][EXTENSION][0]
@@ -232,28 +288,17 @@
232288 function setExtension (node, ex) {
233289 if (ex && ex.length) {
234290 node['/'][EXTENSION] = [ex.length, new Buffer(ex.buffer)]
235291 } else {
236- node['/'][EXTENSION] = []
292 + if (getValue(node) === undefined && node['/'][EXTENSION] !== undefined) {
293 + node['/'].pop()
294 + } else if (node['/'][EXTENSION] !== undefined) {
295 + node['/'][EXTENSION] = undefined
296 + }
237297 }
238298 }
239299
240300 function setValue (node, val) {
241- node['/'][VALUE] = val
242-}
243-
244-function createNode (ex, branch, value) {
245- if (ex && ex.length) {
246- ex = [ex.length, new Buffer(ex.buffer)]
247- } else {
248- ex = []
301 + if (val !== undefined) {
302 + node['/'][VALUE] = val
249303 }
250-
251- const node = {
252- '/': [ex, branch]
253- }
254-
255- if (value !== undefined) {
256- node['/'].push(value)
257- }
258- return node
259304 }
tests/index.jsView
@@ -58,38 +58,19 @@
5858 const tree = new RadixTree({
5959 dag: node.dag
6060 })
6161
62- let key0 = new RadixTree.ArrayConstructor([1, 1, 0, 0])
62 + let key0 = new RadixTree.ArrayConstructor([1, 1])
6363 await tree.set(key0, 'cat')
64- let key1 = new RadixTree.ArrayConstructor([0, 1, 0, 1])
64 + let key1 = new RadixTree.ArrayConstructor([0, 1])
6565 await tree.set(key1, 'cat2')
66- let val = await tree.get(key0)
67- t.equals(val, 'cat')
6866
69- val = await tree.get(key1)
70- t.equals(val, 'cat2')
67 + let key2 = new RadixTree.ArrayConstructor([1, 0])
68 + await tree.set(key2, 'cat')
69 + let key3 = new RadixTree.ArrayConstructor([0, 0])
70 + await tree.set(key3, 'cat3')
71 + // console.log(JSON.stringify(tree.root, null, 2))
7172
72- let key3 = new RadixTree.ArrayConstructor([0, 1, 0, 1, 1])
73- await tree.set(key3, 'test')
74- val = await tree.get(key3)
75-
76- t.equals(val, 'test')
77-
78- let key4 = new RadixTree.ArrayConstructor([0, 1, 0, 0, 0])
79- await tree.set(key4, 'dog')
80- val = await tree.get(key4)
81-
82- let key5 = new RadixTree.ArrayConstructor([0, 1, 1, 0, 0])
83- await tree.set(key5, 'dog2')
84- val = await tree.get(key5)
85- t.equals(val, 'dog2')
86-
87- await tree.delete(key0)
88-
89- console.log(JSON.stringify(tree.root, null, 2))
90- val = await tree.get(key5)
91- t.equals(val, 'dog2')
9273 t.end()
9374 })
9475
9576 tape('delete', async t => {
@@ -134,16 +115,30 @@
134115 t.equals(value.toString(), saved.toString())
135116 t.end()
136117 })
137118
138- tape('falures', async t => {
119 + tape('random', async t => {
139120 const tree = new RadixTree({
140121 dag: node.dag
141122 })
142- const key = crypto.createHash('sha256').update((0).toString()).digest().slice(0, 20)
143- await tree.set(key, 0)
144- const value = await tree.get(key)
145- t.equals(value, 0)
123 + const entries = 100
124 + for (let i = 0; i < entries; i++) {
125 + const key = crypto.createHash('sha256').update(i.toString()).digest().slice(0, 20)
126 + await tree.set(key, i)
127 + }
146128
129 + for (let i = 0; i < entries; i++) {
130 + const key = crypto.createHash('sha256').update(i.toString()).digest().slice(0, 20)
131 + const value = await tree.get(key)
132 + t.equals(value, i)
133 + }
134 +
135 + for (let i = 0; i < entries; i++) {
136 + const key = crypto.createHash('sha256').update(i.toString()).digest().slice(0, 20)
137 + await tree.delete(key)
138 + }
139 +
140 + t.equals(tree.root['/'], undefined)
141 +
147142 t.end()
148143 })
149144 })

Built with git-ssb-web