git ssb

0+

wanderer🌟 / js-dfinity-radix-tree



Commit 8c69b0f11212bb2c638df784054352a8f974a63b

100 coverage

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

Files changed

index.jschanged
tests/index.jschanged
index.jsView
@@ -3,8 +3,12 @@
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
10 +
711 const RadixTree = module.exports = class RadixTree {
812 constructor (opts) {
913 this.root = opts.root || {'/': null}
1014 this.dag = opts.dag
@@ -22,8 +26,9 @@
2226
2327 async _get (key) {
2428 let index = 0
2529 let root = this.root
30 + let parent
2631 while (1) {
2732 if (hasExtension(root)) {
2833 let extensionIndex = 0
2934 const extensionLen = getExLength(root)
@@ -57,8 +62,9 @@
5762 root: root,
5863 index: index
5964 }
6065 } else {
66 + parent = root
6167 root = nextRoot
6268 }
6369 } else {
6470 break
@@ -68,15 +74,16 @@
6874 }
6975
7076 let value = getValue(root)
7177
72- if (value.length >= 32) {
73- value = await this.graph.get(root, root.length - 1)
78 + if (value && value['/']) {
79 + value = await this.graph.get(root, VALUE)
7480 }
7581
7682 return {
7783 value: value,
7884 root: root,
85 + parent: parent,
7986 index: index
8087 }
8188 }
8289
@@ -88,90 +95,101 @@
8895
8996 async set (key, value) {
9097 key = RadixTree.formatKey(key)
9198
99 + if (value.length > 32) {
100 + value = {'/': value}
101 + }
102 +
92103 // initial set
93104 if (this.root['/'] === null) {
94- this.root['/'] = createNode(value, key)['/']
95- return
96- }
105 + this.root['/'] = createNode(key, [], value)['/']
106 + } else {
107 + const result = await this._get(key)
108 + let root = result.root
97109
98- const result = await this._get(key)
99- let root = result.root
110 + if (result.value) {
111 + setValue(root, value)
112 + } else {
113 + if (result.extensionIndex !== undefined) {
114 + // split the extension node in two
115 + let extension = getExtension(root)
116 + const extensionKey = extension[result.extensionIndex]
117 + const remExtension = extension.subarray(result.extensionIndex + 1)
118 + extension = extension.subarray(0, result.extensionIndex)
100119
101- if (result.value) {
102- setValue(root, value)
103- return
120 + setExtension(root, remExtension)
121 + const branch = []
122 + branch[extensionKey] = {'/': root['/']}
123 + root['/'] = createNode(extension, branch)['/']
124 + }
125 +
126 + // if there are remaning key segments create an extension node
127 + if (result.index < key.length) {
128 + const keySegment = key[result.index]
129 + const extension = key.subarray(result.index + 1, key.length)
130 + const newNode = createNode(extension, [], value)
131 + const rootBranch = getBranch(root)
132 + rootBranch[keySegment] = newNode
133 + } else {
134 + setValue(root, value)
135 + }
136 + }
104137 }
138 + }
105139
106- if (result.extensionIndex !== undefined) {
107- // split the extension node in two
108- let extension = getExtension(root)
109- const extensionKey = extension[result.extensionIndex]
110- const remExtension = extension.subarray(result.extensionIndex + 1)
111- extension = extension.subarray(0, result.extensionIndex)
140 + async delete (key) {
141 + key = RadixTree.formatKey(key)
142 + const results = await this._get(key)
143 + if (results.value) {
144 + const root = results.root
145 + const parent = results.parent
112146
113- setExtension(root, remExtension)
114- const branch = []
115- branch[extensionKey] = {'/': root['/']}
116- root['/'] = createNode(null, extension, branch)['/']
147 + deleteValue(root)
148 +
149 + if (getBranch(root).length) {
150 + joinNodes(root)
151 + } else {
152 + if (!parent) {
153 + root['/'] = null
154 + } else {
155 + let branch = getBranch(parent)
156 + branch = branch.map(node => node === root ? null : node)
157 + setBranch(parent, branch)
158 +
159 + joinNodes(parent)
160 + }
161 + }
117162 }
118163
119- // if there are remaning key segments create an extension node
120- if (result.index < key.length) {
121- const keySegment = key[result.index]
122- const extension = key.subarray(result.index + 1, key.length)
123- const newNode = createNode(value, extension)
124- const rootBranch = getBranch(root)
125- rootBranch[keySegment] = newNode
126- } else {
127- setValue(root, value)
164 + function joinNodes (root) {
165 + if (getValue(root) === undefined) {
166 + let index
167 + const branch = getBranch(root)
168 + const nodes = branch.filter((node, i) => {
169 + if (node) {
170 + index = i
171 + return true
172 + }
173 + })
174 +
175 + if (nodes.length === 1) {
176 + const child = nodes[0]
177 + const pExtension = getExtension(root)
178 + const childExtension = getExtension(child)
179 + const newExtension = new RadixTree.ArrayConstructor(pExtension.length + childExtension.length + 1)
180 +
181 + newExtension.set(pExtension)
182 + newExtension[pExtension.length] = index
183 + newExtension.set(childExtension, pExtension.length + 1)
184 +
185 + setExtension(child, newExtension)
186 + root['/'] = child['/']
187 + }
188 + }
128189 }
129190 }
130191
131- // async delete (key) {
132- // key = new this.ArrayConstructor(key)
133- // const results = await this._get(key)
134- // const root = results.root
135- // if (results.value) {
136- // if (results.extensionIndex) {
137- // key = key.subarray(-results.extensionIndex)
138- // }
139- // const keySegment = key[key.length - 1]
140- // delete root[keySegment]
141- // if (this.isEmptyNode(root) && results.parent) {
142- // delete results.parent[results.parentKey]
143- // } else if (!root[root.length - 1]) {
144- // let oneNode = false
145- // let rNodeIndex
146- // for (let i = 0; i < root.length - 1; i++) {
147- // const el = root[i]
148- // if (el) {
149- // if (!oneNode) {
150- // rNodeIndex = i
151- // oneNode = true
152- // } else {
153- // oneNode = false
154- // break
155- // }
156- // }
157- // }
158-
159- // if (oneNode) {
160- // let extension = root[rNodeIndex].extension || []
161- // extension = concat([rNodeIndex], extension)
162- // const parentExtenstion = results.parent[results.parentIndex].extension
163- // if (parentExtenstion) {
164- // extension = concat(parentExtenstion, extension)
165- // }
166- // results.parent[results.parentIndex] = {
167- // extension: extension,
168- // root: root
169- // }
170- // }
171- // }
172- // }
173- // }
174192 static formatKey (key) {
175193 if (typeof key === 'string') {
176194 key = encoder.encode(key)
177195 return new RadixTree.ArrayConstructor(key.buffer)
@@ -180,51 +198,60 @@
180198 }
181199 }
182200 }
183201
202 +function setBranch (node, branch) {
203 + node['/'][BRANCH] = branch
204 +}
205 +
184206 function getBranch (node) {
185- return node['/'].branch
207 + return node['/'][BRANCH]
186208 }
187209
188210 function getValue (node) {
189- return node['/'].value
211 + return node['/'][VALUE]
190212 }
191213
214 +function deleteValue (node) {
215 + node['/'].pop()
216 +}
217 +
192218 function hasExtension (node) {
193- return !!node['/'].extension
219 + return node['/'][EXTENSION].length === 2
194220 }
195221
196222 function getExtension (node) {
197- return RadixTree.toTypedArray(node['/'].extension[1]).subarray(0, getExLength(node))
223 + return RadixTree.toTypedArray(node['/'][EXTENSION][1]).subarray(0, getExLength(node))
198224 }
199225
200226 function getExLength (node) {
201- return node['/'].extension[0]
227 + return node['/'][EXTENSION][0]
202228 }
203229
204230 function setExtension (node, ex) {
205231 if (ex && ex.length) {
206- node['/'].extension = [ex.length, new Buffer(ex.buffer)]
232 + node['/'][EXTENSION] = [ex.length, new Buffer(ex.buffer)]
207233 } else {
208- node['/'].extension = null
234 + node['/'][EXTENSION] = []
209235 }
210236 }
211237
212238 function setValue (node, val) {
213- node['/'].value = val
239 + node['/'][VALUE] = val
214240 }
215241
216-function createNode (value, ex, branch = []) {
242 +function createNode (ex, branch, value) {
217243 if (ex && ex.length) {
218244 ex = [ex.length, new Buffer(ex.buffer)]
219245 } else {
220- ex = null
246 + ex = []
221247 }
222248
223- return {
224- '/': {
225- extension: ex,
226- branch: branch,
227- value: value
228- }
249 + const node = {
250 + '/': [ex, branch]
229251 }
252 +
253 + if (value !== undefined) {
254 + node['/'].push(value)
255 + }
256 + return node
230257 }
tests/index.jsView
@@ -17,9 +17,8 @@
1717 let val = await tree.get('test')
1818 t.equals(val, 'cat')
1919 await tree.set('te', 'blop')
2020 val = await tree.get('test')
21- // console.log('val', val)
2221 t.equals(val, 'cat')
2322
2423 val = await tree.get('te')
2524 t.equals(val, 'blop')
@@ -57,10 +56,58 @@
5756 await tree.set(key3, 'test')
5857 val = await tree.get(key3)
5958
6059 t.equals(val, 'test')
60 +
61 + let key4 = new RadixTree.ArrayConstructor([0, 1, 0, 0, 0])
62 + await tree.set(key4, 'dog')
63 + val = await tree.get(key4)
64 + t.equals(val, 'dog')
6165 } catch (e) {
6266 console.log(e)
6367 }
6468 t.end()
6569 })
70 +
71 + tape('delete', async t => {
72 + const tree = new RadixTree({
73 + dag: node.dag
74 + })
75 + try {
76 + await tree.set('test', 'cat')
77 + await tree.set('ter', 'cat3')
78 + await tree.delete('te')
79 + await tree.delete('test')
80 + await tree.delete('ter')
81 + t.equals(tree.root['/'], null)
82 +
83 + // tests delete midle branchs
84 + await tree.set('test', 'cat')
85 + await tree.set('te', 'cat2')
86 + await tree.set('ter', 'cat3')
87 + await tree.delete('te')
88 + let val = await tree.get('test')
89 + t.equals(val, 'cat')
90 +
91 + // tests delete end branchs
92 + await tree.set('te', 'cat2')
93 + await tree.delete('ter')
94 + await tree.delete('te')
95 + await tree.delete('test')
96 + t.equals(tree.root['/'], null)
97 + } catch (e) {
98 + console.log(e)
99 + }
100 + t.end()
101 + })
102 +
103 + tape('large values', async t => {
104 + const tree = new RadixTree({
105 + dag: node.dag
106 + })
107 + const saved = new Buffer(33).fill(1)
108 + await tree.set('test', saved)
109 + const value = await tree.get('test')
110 + t.equals(value.toString(), saved.toString())
111 + t.end()
112 + })
66113 })

Built with git-ssb-web