index.jsView |
---|
3 | 3 … | const TextEncoder = require('text-encoding').TextEncoder |
4 | 4 … | |
5 | 5 … | const encoder = new TextEncoder('utf-8') |
6 | 6 … | |
7 | | -module.exports = class RadixTree { |
| 7 … | +const RadixTree = module.exports = class RadixTree { |
8 | 8 … | constructor (opts) { |
9 | 9 … | this.root = opts.root || {'/': null} |
10 | 10 … | this.dag = opts.dag |
11 | | - this.radix = opts.radix || 2 |
| 11 … | + this.radix = 2 |
12 | 12 … | this.graph = new Graph(this.dag) |
13 | 13 … | } |
14 | 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 | | - } |
| 15 … | + static get ArrayConstructor () { |
| 16 … | + return Uint1Array |
26 | 17 … | } |
27 | 18 … | |
28 | | - toTypedArray (array) { |
29 | | - return new this.ArrayConstructor(new Uint8Array(array).buffer) |
| 19 … | + static toTypedArray (array) { |
| 20 … | + return new RadixTree.ArrayConstructor(new Uint8Array(array).buffer) |
30 | 21 … | } |
31 | 22 … | |
32 | 23 … | async _get (key) { |
33 | 24 … | let index = 0 |
34 | 25 … | let root = this.root |
35 | | - while (key.length > index) { |
| 26 … | + while (1) { |
36 | 27 … | if (isExtension(root)) { |
37 | 28 … | let extensionIndex = 0 |
38 | | - const extension = this.toTypedArray(getExtension(root)) |
| 29 … | + const extensionLen = getLength(root) |
| 30 … | + const extension = getExtension(root) |
39 | 31 … | let subKey |
40 | | - |
41 | | - |
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 | | - } |
| 32 … | + subKey = key.slice(index, index + extensionLen) |
| 33 … | + |
51 | 34 … | |
52 | | - while (extensionIndex < extension.length && extension[extensionIndex] === subKey[extensionIndex]) { |
| 35 … | + while (extensionIndex < extensionLen && extension[extensionIndex] === subKey[extensionIndex]) { |
53 | 36 … | extensionIndex++ |
54 | 37 … | } |
55 | | - index += extensionIndex - padLen |
| 38 … | + index += extensionIndex |
56 | 39 … | |
57 | | - if (extensionIndex !== extension.length) { |
| 40 … | + if (extensionIndex !== extensionLen) { |
58 | 41 … | return { |
59 | 42 … | extensionIndex: extensionIndex, |
60 | 43 … | root: root, |
61 | 44 … | index: index |
63 | 46 … | } |
64 | 47 … | } |
65 | 48 … | |
66 | 49 … | let keySegment = key[index] |
67 | | - if (keySegment) { |
| 50 … | + if (keySegment !== undefined) { |
68 | 51 … | const branch = getBranch(root) |
69 | 52 … | await this.graph.get(branch, keySegment) |
| 53 … | + |
70 | 54 … | root = branch[keySegment] |
| 55 … | + } else { |
| 56 … | + break |
71 | 57 … | } |
72 | 58 … | |
73 | 59 … | index++ |
74 | 60 … | } |
76 | 62 … | const node = getBranch(root) |
77 | 63 … | |
78 | 64 … | let value |
79 | 65 … | if (Array.isArray(node)) { |
80 | | - value = node[node.length - 1] |
| 66 … | + value = node[this.radix] |
81 | 67 … | } else { |
82 | 68 … | value = node |
83 | 69 … | } |
84 | 70 … | |
94 | 80 … | } |
95 | 81 … | |
96 | 82 … | async get (key) { |
97 | 83 … | key = this.formatKey(key) |
98 | | - const results = await this._get(key) |
99 | | - return results.value |
| 84 … | + const result = await this._get(key) |
| 85 … | + return result.value |
100 | 86 … | } |
101 | 87 … | |
102 | 88 … | async set (key, value) { |
103 | 89 … | key = this.formatKey(key) |
104 | 90 … | |
105 | 91 … | |
106 | 92 … | if (this.root['/'] === null) { |
107 | | - this.root['/'] = { |
108 | | - extension: new Buffer(key.buffer), |
109 | | - node: value |
110 | | - } |
| 93 … | + this.root['/'] = createExtension(key, value)['/'] |
111 | 94 … | return |
112 | 95 … | } |
113 | 96 … | |
114 | 97 … | const result = await this._get(key) |
116 | 99 … | let keySegment = key[result.index] |
117 | 100 … | |
118 | 101 … | if (result.extensionIndex !== undefined) { |
119 | 102 … | |
120 | | - let extension = this.toTypedArray(getExtension(root)) |
121 | | - const extensionKey = extension[result.extensionIndex + 1] |
122 | | - const remExtension = extension.subarray(1 - result.extensionIndex) |
| 103 … | + let extension = getExtension(root) |
| 104 … | + const extensionKey = extension[result.extensionIndex] |
| 105 … | + const remExtension = extension.subarray(result.extensionIndex + 1) |
123 | 106 … | extension = extension.subarray(0, result.extensionIndex) |
124 | 107 … | |
125 | 108 … | const node = getNode(root) |
126 | 109 … | let newNode |
127 | 110 … | |
128 | 111 … | if (extension.length) { |
129 | | - setExtension(root, new Buffer(extension.buffer)) |
| 112 … | + setExtension(root, extension) |
130 | 113 … | newNode = [] |
131 | 114 … | setNode(root, newNode) |
132 | 115 … | } else { |
133 | 116 … | newNode = root['/'] = [] |
134 | 117 … | } |
135 | 118 … | |
136 | 119 … | |
137 | 120 … | if (remExtension.length) { |
138 | | - newNode[extensionKey] = createExtension(new Buffer(remExtension.buffer), node) |
| 121 … | + newNode[extensionKey] = createExtension(remExtension, node) |
139 | 122 … | } else { |
140 | 123 … | newNode[extensionKey] = node |
141 | 124 … | } |
142 | 125 … | } |
144 | 127 … | let newNode |
145 | 128 … | if (result.index + 1 < key.length) { |
146 | 129 … | |
147 | 130 … | const extension = key.subarray(result.index + 1, key.length) |
148 | | - newNode = createExtension(new Buffer(extension.buffer), value) |
| 131 … | + newNode = createExtension(extension, value) |
149 | 132 … | } else { |
150 | 133 … | newNode = value |
151 | 134 … | } |
152 | 135 … | |
208 | 191 … | } |
209 | 192 … | formatKey (key) { |
210 | 193 … | if (typeof key === 'string') { |
211 | 194 … | key = encoder.encode(key) |
| 195 … | + return new RadixTree.ArrayConstructor(key.buffer) |
| 196 … | + } else { |
| 197 … | + return key |
212 | 198 … | } |
213 | | - return new this.ArrayConstructor(key.buffer) |
214 | 199 … | } |
215 | 200 … | } |
216 | 201 … | |
217 | 202 … | function getBranch (node) { |
218 | 203 … | if (isExtension(node)) { |
219 | 204 … | return getNode(node) |
220 | 205 … | } else { |
221 | | - return root['/'] |
| 206 … | + return node['/'] |
222 | 207 … | } |
223 | 208 … | } |
224 | 209 … | |
225 | 210 … | function isExtension (node) { |
226 | 211 … | return !!node['/'].extension |
227 | 212 … | } |
228 | 213 … | |
229 | 214 … | function getExtension (node) { |
230 | | - return node['/'].extension |
| 215 … | + return RadixTree.toTypedArray(node['/'].extension).subarray(0, getLength(node)) |
231 | 216 … | } |
232 | 217 … | |
233 | 218 … | function getNode (node) { |
234 | 219 … | return node['/'].node |
235 | 220 … | } |
236 | 221 … | |
| 222 … | +function getLength (node) { |
| 223 … | + return node['/'].length |
| 224 … | +} |
| 225 … | + |
237 | 226 … | function setExtension (node, ex) { |
238 | | - node['/'].extension = ex |
| 227 … | + node['/'].extension = new Buffer(ex.buffer) |
| 228 … | + node['/'].length = ex.length |
239 | 229 … | } |
240 | 230 … | |
241 | 231 … | function setNode (node, val) { |
242 | 232 … | node['/'].node = val |
243 | 233 … | } |
244 | 234 … | |
245 | | -function createExtension(ex, node) { |
| 235 … | +function createExtension (ex, node) { |
246 | 236 … | return { |
247 | 237 … | '/': { |
248 | | - extension: ex, |
249 | | - node: node |
| 238 … | + extension: new Buffer(ex.buffer), |
| 239 … | + node: node, |
| 240 … | + length: ex.length |
250 | 241 … | } |
251 | 242 … | } |
252 | 243 … | } |