index.jsView |
---|
3 | 3 … | const TextEncoder = require('text-encoding').TextEncoder |
4 | 4 … | |
5 | 5 … | const encoder = new TextEncoder('utf-8') |
6 | 6 … | |
| 7 … | +const EXTENSION = 0 |
| 8 … | +const BRANCH = 1 |
| 9 … | +const VALUE = 2 |
| 10 … | + |
7 | 11 … | const RadixTree = module.exports = class RadixTree { |
8 | 12 … | constructor (opts) { |
9 | 13 … | this.root = opts.root || {'/': null} |
10 | 14 … | this.dag = opts.dag |
22 | 26 … | |
23 | 27 … | async _get (key) { |
24 | 28 … | let index = 0 |
25 | 29 … | let root = this.root |
| 30 … | + let parent |
26 | 31 … | while (1) { |
27 | 32 … | if (hasExtension(root)) { |
28 | 33 … | let extensionIndex = 0 |
29 | 34 … | const extensionLen = getExLength(root) |
57 | 62 … | root: root, |
58 | 63 … | index: index |
59 | 64 … | } |
60 | 65 … | } else { |
| 66 … | + parent = root |
61 | 67 … | root = nextRoot |
62 | 68 … | } |
63 | 69 … | } else { |
64 | 70 … | break |
68 | 74 … | } |
69 | 75 … | |
70 | 76 … | let value = getValue(root) |
71 | 77 … | |
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) |
74 | 80 … | } |
75 | 81 … | |
76 | 82 … | return { |
77 | 83 … | value: value, |
78 | 84 … | root: root, |
| 85 … | + parent: parent, |
79 | 86 … | index: index |
80 | 87 … | } |
81 | 88 … | } |
82 | 89 … | |
88 | 95 … | |
89 | 96 … | async set (key, value) { |
90 | 97 … | key = RadixTree.formatKey(key) |
91 | 98 … | |
| 99 … | + if (value.length > 32) { |
| 100 … | + value = {'/': value} |
| 101 … | + } |
| 102 … | + |
92 | 103 … | |
93 | 104 … | 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 |
97 | 109 … | |
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 … | + |
| 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) |
100 | 119 … | |
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 … | + |
| 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 … | + } |
104 | 137 … | } |
| 138 … | + } |
105 | 139 … | |
106 | | - if (result.extensionIndex !== undefined) { |
107 | | - |
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 |
112 | 146 … | |
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 … | + } |
117 | 162 … | } |
118 | 163 … | |
119 | | - |
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 … | + } |
128 | 189 … | } |
129 | 190 … | } |
130 | 191 … | |
131 | | - |
132 | | - |
133 | | - |
134 | | - |
135 | | - |
136 | | - |
137 | | - |
138 | | - |
139 | | - |
140 | | - |
141 | | - |
142 | | - |
143 | | - |
144 | | - |
145 | | - |
146 | | - |
147 | | - |
148 | | - |
149 | | - |
150 | | - |
151 | | - |
152 | | - |
153 | | - |
154 | | - |
155 | | - |
156 | | - |
157 | | - |
158 | | - |
159 | | - |
160 | | - |
161 | | - |
162 | | - |
163 | | - |
164 | | - |
165 | | - |
166 | | - |
167 | | - |
168 | | - |
169 | | - |
170 | | - |
171 | | - |
172 | | - |
173 | | - |
174 | 192 … | static formatKey (key) { |
175 | 193 … | if (typeof key === 'string') { |
176 | 194 … | key = encoder.encode(key) |
177 | 195 … | return new RadixTree.ArrayConstructor(key.buffer) |
180 | 198 … | } |
181 | 199 … | } |
182 | 200 … | } |
183 | 201 … | |
| 202 … | +function setBranch (node, branch) { |
| 203 … | + node['/'][BRANCH] = branch |
| 204 … | +} |
| 205 … | + |
184 | 206 … | function getBranch (node) { |
185 | | - return node['/'].branch |
| 207 … | + return node['/'][BRANCH] |
186 | 208 … | } |
187 | 209 … | |
188 | 210 … | function getValue (node) { |
189 | | - return node['/'].value |
| 211 … | + return node['/'][VALUE] |
190 | 212 … | } |
191 | 213 … | |
| 214 … | +function deleteValue (node) { |
| 215 … | + node['/'].pop() |
| 216 … | +} |
| 217 … | + |
192 | 218 … | function hasExtension (node) { |
193 | | - return !!node['/'].extension |
| 219 … | + return node['/'][EXTENSION].length === 2 |
194 | 220 … | } |
195 | 221 … | |
196 | 222 … | 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)) |
198 | 224 … | } |
199 | 225 … | |
200 | 226 … | function getExLength (node) { |
201 | | - return node['/'].extension[0] |
| 227 … | + return node['/'][EXTENSION][0] |
202 | 228 … | } |
203 | 229 … | |
204 | 230 … | function setExtension (node, ex) { |
205 | 231 … | if (ex && ex.length) { |
206 | | - node['/'].extension = [ex.length, new Buffer(ex.buffer)] |
| 232 … | + node['/'][EXTENSION] = [ex.length, new Buffer(ex.buffer)] |
207 | 233 … | } else { |
208 | | - node['/'].extension = null |
| 234 … | + node['/'][EXTENSION] = [] |
209 | 235 … | } |
210 | 236 … | } |
211 | 237 … | |
212 | 238 … | function setValue (node, val) { |
213 | | - node['/'].value = val |
| 239 … | + node['/'][VALUE] = val |
214 | 240 … | } |
215 | 241 … | |
216 | | -function createNode (value, ex, branch = []) { |
| 242 … | +function createNode (ex, branch, value) { |
217 | 243 … | if (ex && ex.length) { |
218 | 244 … | ex = [ex.length, new Buffer(ex.buffer)] |
219 | 245 … | } else { |
220 | | - ex = null |
| 246 … | + ex = [] |
221 | 247 … | } |
222 | 248 … | |
223 | | - return { |
224 | | - '/': { |
225 | | - extension: ex, |
226 | | - branch: branch, |
227 | | - value: value |
228 | | - } |
| 249 … | + const node = { |
| 250 … | + '/': [ex, branch] |
229 | 251 … | } |
| 252 … | + |
| 253 … | + if (value !== undefined) { |
| 254 … | + node['/'].push(value) |
| 255 … | + } |
| 256 … | + return node |
230 | 257 … | } |