git ssb

0+

wanderer🌟 / js-dfinity-radix-tree



Tree: f2256af140b4bd89e54041e23499b0902cb869c4

Files: f2256af140b4bd89e54041e23499b0902cb869c4 / index.js

5789 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
7const RadixTree = module.exports = class RadixTree {
8 constructor (opts) {
9 this.root = opts.root || {'/': null}
10 this.dag = opts.dag
11 this.radix = 2
12 this.graph = new Graph(this.dag)
13 }
14
15 static get ArrayConstructor () {
16 return Uint1Array
17 }
18
19 static toTypedArray (array) {
20 return new RadixTree.ArrayConstructor(new Uint8Array(array).buffer)
21 }
22
23 async _get (key) {
24 let index = 0
25 let root = this.root
26 while (1) {
27 if (hasExtension(root)) {
28 let extensionIndex = 0
29 const extensionLen = getExLength(root)
30 const extension = getExtension(root)
31 let subKey
32 subKey = key.slice(index, index + extensionLen)
33
34 // checks the extension against the key
35 while (extensionIndex < extensionLen && extension[extensionIndex] === subKey[extensionIndex]) {
36 extensionIndex++
37 }
38 index += extensionIndex
39 // check if we compelete travered the extension
40 if (extensionIndex !== extensionLen) {
41 return {
42 extensionIndex: extensionIndex,
43 root: root,
44 index: index
45 }
46 }
47 }
48
49 let keySegment = key[index]
50 if (keySegment !== undefined) {
51 const branch = getBranch(root)
52 await this.graph.get(branch, keySegment)
53 // preseves the '/'
54 const nextRoot = branch[keySegment]
55 if (!nextRoot) {
56 return {
57 root: root,
58 index: index
59 }
60 } else {
61 root = nextRoot
62 }
63 } else {
64 break
65 }
66
67 index++
68 }
69
70 let value = getValue(root)
71
72 if (value.length >= 32) {
73 value = await this.graph.get(root, root.length - 1)
74 }
75
76 return {
77 value: value,
78 root: root,
79 index: index
80 }
81 }
82
83 async get (key) {
84 key = RadixTree.formatKey(key)
85 const result = await this._get(key)
86 return result.value
87 }
88
89 async set (key, value) {
90 key = RadixTree.formatKey(key)
91
92 // initial set
93 if (this.root['/'] === null) {
94 this.root['/'] = createNode(value, key)['/']
95 return
96 }
97
98 const result = await this._get(key)
99 let root = result.root
100
101 if (result.value) {
102 setValue(root, value)
103 return
104 }
105
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)
112
113 setExtension(root, remExtension)
114 const branch = []
115 branch[extensionKey] = {'/': root['/']}
116 root['/'] = createNode(null, extension, branch)['/']
117 }
118
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)
128 }
129 }
130
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 // }
174 static formatKey (key) {
175 if (typeof key === 'string') {
176 key = encoder.encode(key)
177 return new RadixTree.ArrayConstructor(key.buffer)
178 } else {
179 return key
180 }
181 }
182}
183
184function getBranch (node) {
185 return node['/'].branch
186}
187
188function getValue (node) {
189 return node['/'].value
190}
191
192function hasExtension (node) {
193 return !!node['/'].extension
194}
195
196function getExtension (node) {
197 return RadixTree.toTypedArray(node['/'].extension[1]).subarray(0, getExLength(node))
198}
199
200function getExLength (node) {
201 return node['/'].extension[0]
202}
203
204function setExtension (node, ex) {
205 if (ex && ex.length) {
206 node['/'].extension = [ex.length, new Buffer(ex.buffer)]
207 } else {
208 node['/'].extension = null
209 }
210}
211
212function setValue (node, val) {
213 node['/'].value = val
214}
215
216function createNode (value, ex, branch = []) {
217 if (ex && ex.length) {
218 ex = [ex.length, new Buffer(ex.buffer)]
219 } else {
220 ex = null
221 }
222
223 return {
224 '/': {
225 extension: ex,
226 branch: branch,
227 value: value
228 }
229 }
230}
231

Built with git-ssb-web