git ssb

0+

wanderer🌟 / js-dfinity-radix-tree



Tree: c5000f3abc5af622b60625081dd851aae564cd02

Files: c5000f3abc5af622b60625081dd851aae564cd02 / index.js

6448 bytesRaw
1const Graph = require('ipld-graph-builder')
2const Uint1Array = require('uint1array')
3const TextEncoder = require('text-encoding').TextEncoder
4
5const encoder = new TextEncoder('utf-8')
6
7module.exports = class RadixTree {
8 constructor (opts) {
9 this.root = opts.root || {'/': null}
10 this.dag = opts.dag
11 this.radix = opts.radix || 2
12 this.graph = new Graph(this.dag)
13 }
14
15 get ArrayConstructor () {
16 switch (this.radix) {
17 case 2:
18 return Uint1Array
19 case 8:
20 return Uint8Array
21 case 16:
22 return Uint16Array
23 case 32:
24 return Uint32Array
25 }
26 }
27
28 toTypedArray (array) {
29 return new this.ArrayConstructor(new Uint8Array(array).buffer)
30 }
31
32 async _get (key) {
33 let index = 0
34 let root = this.root
35 while (key.length > index) {
36 if (isExtension(root)) {
37 let extensionIndex = 0
38 const extension = this.toTypedArray(getExtension(root))
39 let subKey
40 // alll extension are padded to 8 bit alignment. So we need to update
41 // the index later to count for the added padding
42 let padLen
43 if (this.radix === 2) {
44 subKey = key.slice(index, index + extension.length)
45 padLen = subKey.length
46 subKey = new this.ArrayConstructor(subKey.buffer)
47 padLen = subKey.length - padLen
48 } else {
49 subKey = key.subarray(index, extension.length)
50 }
51 // checks the extension against the key
52 while (extensionIndex < extension.length && extension[extensionIndex] === subKey[extensionIndex]) {
53 extensionIndex++
54 }
55 index += extensionIndex - padLen
56 // check if we compelete travered the extension
57 if (extensionIndex !== extension.length) {
58 return {
59 extensionIndex: extensionIndex,
60 root: root,
61 index: index
62 }
63 }
64 }
65
66 let keySegment = key[index]
67 if (keySegment) {
68 const branch = getBranch(root)
69 await this.graph.get(branch, keySegment)
70 root = branch[keySegment]
71 }
72
73 index++
74 }
75
76 const node = getBranch(root)
77 // get the value
78 let value
79 if (Array.isArray(node)) {
80 value = node[node.length - 1]
81 } else {
82 value = node
83 }
84
85 if (value.length >= 32) {
86 value = await this.graph.get(root, root.length - 1)
87 }
88
89 return {
90 value: value,
91 root: root,
92 index: index
93 }
94 }
95
96 async get (key) {
97 key = this.formatKey(key)
98 const results = await this._get(key)
99 return results.value
100 }
101
102 async set (key, value) {
103 key = this.formatKey(key)
104
105 // initial set
106 if (this.root['/'] === null) {
107 this.root['/'] = {
108 extension: new Buffer(key.buffer),
109 node: value
110 }
111 return
112 }
113
114 const result = await this._get(key)
115 let root = result.root
116 let keySegment = key[result.index]
117
118 if (result.extensionIndex !== undefined) {
119 // split the extension node in two
120 let extension = this.toTypedArray(getExtension(root))
121 const extensionKey = extension[result.extensionIndex + 1]
122 const remExtension = extension.subarray(1 - result.extensionIndex)
123 extension = extension.subarray(0, result.extensionIndex)
124
125 const node = getNode(root)
126 let newNode
127 // create the new extension node
128 if (extension.length) {
129 setExtension(root, new Buffer(extension.buffer))
130 newNode = []
131 setNode(root, newNode)
132 } else {
133 newNode = root['/'] = []
134 }
135
136 // save the remainer of the extension node
137 if (remExtension.length) {
138 newNode[extensionKey] = createExtension(new Buffer(remExtension.buffer), node)
139 } else {
140 newNode[extensionKey] = node
141 }
142 }
143
144 let newNode
145 if (result.index + 1 < key.length) {
146 // if there are remaning key segments create an extension node
147 const extension = key.subarray(result.index + 1, key.length)
148 newNode = createExtension(new Buffer(extension.buffer), value)
149 } else {
150 newNode = value
151 }
152
153 let targetNode = getBranch(root)
154
155 // set the value
156 if (keySegment === undefined) {
157 targetNode[this.radix] = newNode
158 } else {
159 targetNode[keySegment] = newNode
160 }
161 }
162
163 // async delete (key) {
164 // key = new this.ArrayConstructor(key)
165 // const results = await this._get(key)
166 // const root = results.root
167 // if (results.value) {
168 // if (results.extensionIndex) {
169 // key = key.subarray(-results.extensionIndex)
170 // }
171 // const keySegment = key[key.length - 1]
172 // delete root[keySegment]
173 // if (this.isEmptyNode(root) && results.parent) {
174 // delete results.parent[results.parentKey]
175 // } else if (!root[root.length - 1]) {
176 // let oneNode = false
177 // let rNodeIndex
178 // for (let i = 0; i < root.length - 1; i++) {
179 // const el = root[i]
180 // if (el) {
181 // if (!oneNode) {
182 // rNodeIndex = i
183 // oneNode = true
184 // } else {
185 // oneNode = false
186 // break
187 // }
188 // }
189 // }
190
191 // if (oneNode) {
192 // let extension = root[rNodeIndex].extension || []
193 // extension = concat([rNodeIndex], extension)
194 // const parentExtenstion = results.parent[results.parentIndex].extension
195 // if (parentExtenstion) {
196 // extension = concat(parentExtenstion, extension)
197 // }
198 // results.parent[results.parentIndex] = {
199 // extension: extension,
200 // root: root
201 // }
202 // }
203 // }
204 // }
205 // }
206 isEmptyNode (node) {
207 return node.evey(el => !el)
208 }
209 formatKey (key) {
210 if (typeof key === 'string') {
211 key = encoder.encode(key)
212 }
213 return new this.ArrayConstructor(key.buffer)
214 }
215}
216
217function getBranch (node) {
218 if (isExtension(node)) {
219 return getNode(node)
220 } else {
221 return root['/']
222 }
223}
224
225function isExtension (node) {
226 return !!node['/'].extension
227}
228
229function getExtension (node) {
230 return node['/'].extension
231}
232
233function getNode (node) {
234 return node['/'].node
235}
236
237function setExtension (node, ex) {
238 node['/'].extension = ex
239}
240
241function setNode (node, val) {
242 node['/'].node = val
243}
244
245function createExtension(ex, node) {
246 return {
247 '/': {
248 extension: ex,
249 node: node
250 }
251 }
252}
253

Built with git-ssb-web