Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(175)

Side by Side Diff: pkg/analysis_server/lib/src/index/btree.dart

Issue 314413004: A simple B+Tree implementation. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Better redistribution, random stress test, renames. Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 library index.btree;
6
7
8 /**
9 * A simple B+Tree implementation.
Brian Wilkerson 2014/06/07 16:34:02 "B+Tree" --> "B-Tree"
scheglov 2014/06/07 21:26:22 It is actually not a mistake. It is a B+Tree flavo
Brian Wilkerson 2014/06/07 23:30:38 Cool. I learned something new today. Interestingly
10 */
11 class BTree<K, V> {
12 /**
13 * The [Comparator] to compare keys.
14 */
15 final Comparator<K> _comparator;
16
17 /**
18 * The maximum number of keys in an index node.
19 */
20 final int _maxIndexKeys;
21
22 /**
23 * The maximum number of keys in a leaf node.
24 */
25 final int _maxLeafKeys;
26
27 /**
28 * The root node.
29 */
30 _Node<K, V> _root;
31
32 BTree(this._maxIndexKeys, this._maxLeafKeys, this._comparator) {
33 _root = new _LNode(_maxLeafKeys, _comparator);
34 }
35
36 /**
37 * Returns the value for [key] or `null` if [key] is not in the tree.
38 */
39 V find(K key) {
40 return _root.find(key);
41 }
42
43 /**
44 * Associates the [key] with the given [value].
45 *
46 * If the key was already in the tree, its associated value is changed.
47 * Otherwise the key-value pair is added to the tree.
48 */
49 void insert(K key, V value) {
50 _Split<K, V> result = _root.insert(key, value);
51 if (result != null) {
52 _INode<K, V> newRoot = new _INode<K, V>(_maxIndexKeys, _comparator);
53 newRoot.keys.add(result.key);
54 newRoot.children.add(result.left);
55 newRoot.children.add(result.right);
56 _root = newRoot;
57 }
58 }
59
60 noSuchMethod(Invocation invocation) {
61 if (invocation.memberName == #debugPrint) {
Brian Wilkerson 2014/06/07 16:34:02 Why not just make debugPrint a public method? Does
scheglov 2014/06/07 21:26:22 OK, I think we can make it public again.
62 _debugPrint(invocation.positionalArguments[0]);
63 return null;
64 }
65 return super.noSuchMethod(invocation);
66 }
67
68 /**
69 * Removes the association for the given [key].
70 *
71 * Returns the value associated with [key] in the tree or `null` if [key] is
72 * not in the tree.
73 */
74 V remove(K key) {
75 _Remove<K, V> result = _root.remove(key, null, null, null);
76 if (_root is _INode<K, V>) {
77 List<_Node<K, V>> children = (_root as _INode<K, V>).children;
78 if (children.length == 1) {
79 _root = children[0];
80 }
81 }
82 return result.value;
83 }
84
85 /**
86 * Prints the debug presentation of the tree into [buffer].
87 */
88 void _debugPrint(StringBuffer buffer) {
Brian Wilkerson 2014/06/07 16:34:02 Is there a reason why this isn't the implementatio
scheglov 2014/06/07 21:26:22 IMHO toString() is pretty dangerous to override fo
89 _root._debugPrint(buffer, '');
90 }
91 }
92
93
94 /**
95 * An index node with keys and children references.
96 */
97 class _INode<K, V> extends _Node<K, V> {
Brian Wilkerson 2014/06/07 16:34:02 I have a strong preference for using full names, s
scheglov 2014/06/07 21:26:22 Done.
98 final List<_Node<K, V>> children = new List<_Node<K, V>>();
99 final int maxKeys;
100 final int minKeys;
101
102 _INode(int maxKeys, Comparator<K> comparator)
103 : super(comparator),
104 maxKeys = maxKeys,
105 minKeys = maxKeys ~/ 2;
106
107 @override
108 V find(K key) {
109 int index = findChildIndex(key);
110 return children[index].find(key);
111 }
112
113 /**
114 * Returns the index of the child into which [key] should be inserted.
115 */
116 int findChildIndex(K key) {
117 for (int i = 0; i < keys.length; i++) {
118 if (comparator(keys[i], key) > 0) {
119 return i;
120 }
121 }
122 return keys.length;
123 }
124
125 _Split insert(K key, V value) {
Brian Wilkerson 2014/06/07 16:34:02 "_Split" --> "_Split<K, V>"
scheglov 2014/06/07 21:26:22 Done.
126 // Early split.
127 if (keys.length == maxKeys) {
128 int middle = (maxKeys + 1) ~/ 2;
129 K splitKey = keys[middle];
130 _INode<K, V> sibling = new _INode<K, V>(maxKeys, comparator);
131 sibling.keys.addAll(keys.getRange(middle + 1, keys.length));
132 sibling.children.addAll(children.getRange(middle + 1, children.length));
133 keys.length = middle;
134 children.length = middle + 1;
135 // Prepare split.
136 _Split<K, V> result = new _Split<K, V>(splitKey, this, sibling);
137 if (comparator(key, result.key) < 0) {
138 insertNotFull(key, value);
139 } else {
140 sibling.insertNotFull(key, value);
141 }
142 return result;
143 }
144 // No split.
145 insertNotFull(key, value);
146 return null;
147 }
148
149 void insertNotFull(K key, V value) {
150 int index = findChildIndex(key);
151 _Split<K, V> result = children[index].insert(key, value);
152 if (result != null) {
153 keys.insert(index, result.key);
154 children[index] = result.left;
155 children.insert(index + 1, result.right);
156 }
157 }
158
159 @override
160 _Remove<K, V> remove(K key, _Node<K, V> left, K anchor, _Node<K, V> right) {
161 int index = findChildIndex(key);
162 K thisAnchor = index == 0 ? keys[0] : keys[index - 1];
163 _Node<K, V> child = children[index];
164 bool hasLeft = index != 0;
165 bool hasRight = index < children.length - 1;
166 _Node<K, V> leftChild = hasLeft ? children[index - 1] : null;
167 _Node<K, V> rightChild = hasRight ? children[index + 1] : null;
168 // Ask child to remove.
169 _Remove<K, V> result = child.remove(key, leftChild, thisAnchor, rightChild);
170 V value = result.value;
171 if (value == null) {
172 return new _Remove<K, V>(value);
173 }
174 // Update anchor if borrowed.
175 if (result.leftAnchor != null) {
176 keys[index - 1] = result.leftAnchor;
177 }
178 if (result.rightAnchor != null) {
179 keys[index] = result.rightAnchor;
180 }
181 // Update keys / children if merged.
182 if (result.mergedLeft) {
183 keys.removeAt(index - 1);
184 children.removeAt(index);
185 }
186 if (result.mergedRight) {
187 keys.removeAt(index);
188 children.removeAt(index);
189 }
190 // Perform balancing.
191 if (keys.length < minKeys) {
192 // Try left sibling.
193 if (left is _INode<K, V>) {
194 // Try to redistribute.
195 int leftLength = left.keys.length;
196 if (leftLength > minKeys) {
197 int halfExcess = (leftLength - minKeys + 1) ~/ 2;
198 int newLeftLength = leftLength - halfExcess;
199 keys.insert(0, anchor);
200 keys.insertAll(0, left.keys.getRange(newLeftLength, leftLength));
201 children.insertAll(0, left.children.getRange(newLeftLength, leftLength
202 + 1));
203 K newAnchor = left.keys[newLeftLength - 1];
204 left.keys.length = newLeftLength - 1;
205 left.children.length = newLeftLength;
206 return new _Remove<K, V>.borrowLeft(value, newAnchor);
207 }
208 // Do merge.
209 left.keys.add(anchor);
210 left.keys.addAll(keys);
211 left.children.addAll(children);
212 return new _Remove<K, V>.mergeLeft(value);
213 }
214 // Try right sibling.
215 if (right is _INode<K, V>) {
216 // Try to redistribute.
217 var rightLength = right.keys.length;
218 if (rightLength > minKeys) {
219 int halfExcess = (rightLength - minKeys + 1) ~/ 2;
220 keys.add(anchor);
221 keys.addAll(right.keys.getRange(0, halfExcess - 1));
222 children.addAll(right.children.getRange(0, halfExcess));
223 K newAnchor = right.keys[halfExcess - 1];
224 right.keys.removeRange(0, halfExcess);
225 right.children.removeRange(0, halfExcess);
226 return new _Remove<K, V>.borrowRight(value, newAnchor);
227 }
228 // Do merge.
229 right.keys.insert(0, anchor);
230 right.keys.insertAll(0, keys);
231 right.children.insertAll(0, children);
232 return new _Remove<K, V>.mergeRight(value);
233 }
234 }
235 // No balancing required.
236 return new _Remove<K, V>(value);
237 }
238
239 @override
240 void _debugPrint(StringBuffer buffer, String indent) {
241 buffer.write(indent);
242 buffer.write('INode {\n');
243 for (int i = 0; i < keys.length; i++) {
244 children[i]._debugPrint(buffer, indent + ' ');
245 buffer.write(indent);
246 buffer.write(' ');
247 buffer.write(keys[i]);
248 buffer.write('\n');
249 }
250 children[keys.length]._debugPrint(buffer, indent + ' ');
251 buffer.write(indent);
252 buffer.write('}\n');
253 }
254 }
255
256
257 /**
258 * A leaf node with keys and values.
259 */
260 class _LNode<K, V> extends _Node<K, V> {
261 final int maxKeys;
262 final int minKeys;
263
264 /**
265 * The list of values.
266 */
267 final List<V> values = new List<V>();
268
269 _LNode(int maxKeys, Comparator<K> comparator)
270 : super(comparator),
271 maxKeys = maxKeys,
272 minKeys = maxKeys ~/ 2;
273
274 @override
275 V find(K key) {
276 int index = findKeyIndex(key);
277 if (index < 0) {
278 return null;
279 }
280 if (index >= keys.length) {
281 return null;
282 }
283 if (keys[index] != key) {
284 return null;
285 }
286 return values[index];
287 }
288
289 /**
290 * Returns the index where [key] should be inserted.
291 */
292 int findKeyIndex(K key) {
293 for (int i = 0; i < keys.length; i++) {
294 if (comparator(keys[i], key) >= 0) {
295 return i;
296 }
297 }
298 return keys.length;
299 }
300
301 _Split<K, V> insert(K key, V value) {
302 int index = findKeyIndex(key);
303 // The node is full.
304 if (keys.length == maxKeys) {
305 int middle = (maxKeys + 1) ~/ 2;
306 _LNode<K, V> sibling = new _LNode<K, V>(maxKeys, comparator);
307 sibling.keys.addAll(keys.getRange(middle, keys.length));
308 sibling.values.addAll(values.getRange(middle, values.length));
309 keys.length = middle;
310 values.length = middle;
311 // Insert into the left / right sibling.
312 if (index < middle) {
313 insertNotFull(key, value, index);
314 } else {
315 sibling.insertNotFull(key, value, index - middle);
316 }
317 // Notify the parent about the split.
318 return new _Split<K, V>(sibling.keys[0], this, sibling);
319 }
320 // The node was not full.
321 insertNotFull(key, value, index);
322 return null;
323 }
324
325 void insertNotFull(K key, V value, int index) {
326 if (index < keys.length && keys[index] == key) {
327 values[index] = value;
328 } else {
329 keys.insert(index, key);
330 values.insert(index, value);
331 }
332 }
333
334 @override
335 _Remove<K, V> remove(K key, _Node<K, V> left, K anchor, _Node<K, V> right) {
336 // Find the key.
337 int index = keys.indexOf(key);
338 if (index == -1) {
339 return new _Remove<K, V>(null);
340 }
341 // Key key / value.
342 keys.removeAt(index);
343 V value = values.removeAt(index);
344 // Perform balancing.
345 if (keys.length < minKeys) {
346 // Try left sibling.
347 if (left is _LNode<K, V>) {
348 // Try to redistribute.
349 int leftLength = left.keys.length;
350 if (leftLength > minKeys) {
351 int halfExcess = (leftLength - minKeys + 1) ~/ 2;
352 int newLeftLength = leftLength - halfExcess;
353 keys.insertAll(0, left.keys.getRange(newLeftLength, leftLength));
354 values.insertAll(0, left.values.getRange(newLeftLength, leftLength));
355 left.keys.length = newLeftLength;
356 left.values.length = newLeftLength;
357 return new _Remove<K, V>.borrowLeft(value, keys.first);
358 }
359 // Do merge.
360 left.keys.addAll(keys);
361 left.values.addAll(values);
362 return new _Remove<K, V>.mergeLeft(value);
363 }
364 // Try right sibling.
365 if (right is _LNode<K, V>) {
366 // Try to redistribute.
367 var rightLength = right.keys.length;
368 if (rightLength > minKeys) {
369 int halfExcess = (rightLength - minKeys + 1) ~/ 2;
370 keys.addAll(right.keys.getRange(0, halfExcess));
371 values.addAll(right.values.getRange(0, halfExcess));
372 right.keys.removeRange(0, halfExcess);
373 right.values.removeRange(0, halfExcess);
374 return new _Remove<K, V>.borrowRight(value, right.keys.first);
375 }
376 // Do merge.
377 right.keys.insertAll(0, keys);
378 right.values.insertAll(0, values);
379 return new _Remove<K, V>.mergeRight(value);
380 }
381 }
382 // No balancing required.
383 return new _Remove<K, V>(value);
384 }
385
386 @override
387 void _debugPrint(StringBuffer buffer, String indent) {
388 buffer.write(indent);
389 buffer.write('LNode {');
390 for (int i = 0; i < keys.length; i++) {
391 if (i != 0) {
392 buffer.write(', ');
393 }
394 buffer.write(keys[i]);
395 buffer.write(': ');
396 buffer.write(values[i]);
397 }
398 buffer.write('}\n');
399 }
400 }
401
402
403 /**
404 * An internal or leaf node.
405 */
406 abstract class _Node<K, V> {
Brian Wilkerson 2014/06/07 16:34:02 It is easier for me to read the code in a Dart fil
scheglov 2014/06/07 21:26:22 I agree. And there is another problem with sorting
407 /**
408 * The [Comparator] to compare keys.
409 */
410 Comparator<K> comparator;
411
412 /**
413 * The list of keys.
414 */
415 List<K> keys = new List<K>();
416
417 _Node(this.comparator);
418
419 /**
420 * Looks for [key].
421 *
422 * Returns the associated value if found.
423 * Returns `null` if not found.
424 */
425 V find(K key);
426
427 /**
428 * Inserts the [key] / [value] pair into this [_Node].
429 *
430 * Returns a [_Split] object if split happens, or `null` otherwise.
431 */
432 _Split<K, V> insert(K key, V value);
433
434 /**
435 * Removes the association for the given [key].
436 *
437 * Returns the [_Remove] information about an operation performed.
438 * It may be restructuring or merging, with [left] or [left] siblings.
439 */
440 _Remove<K, V> remove(K key, _Node<K, V> left, K anchor, _Node<K, V> right);
441
442 /**
443 * Prints the debug presentation of the node into [buffer].
444 */
445 void _debugPrint(StringBuffer buffer, String indent);
446 }
447
448
449 /**
450 * A container with information about redistribute / merge.
451 */
452 class _Remove<K, V> {
453 K leftAnchor;
454 bool mergedLeft = false;
455 bool mergedRight = false;
456 K rightAnchor;
457 final V value;
458 _Remove(this.value);
459 _Remove.borrowLeft(this.value, this.leftAnchor);
460 _Remove.borrowRight(this.value, this.rightAnchor);
461 _Remove.mergeLeft(this.value) : mergedLeft = true;
462 _Remove.mergeRight(this.value) : mergedRight = true;
463 }
464
465
466 /**
467 * A container with information about split during insert.
468 */
469 class _Split<K, V> {
470 final K key;
471 final _Node<K, V> left;
472 final _Node<K, V> right;
473 _Split(this.key, this.left, this.right);
474 }
OLDNEW
« no previous file with comments | « no previous file | pkg/analysis_server/test/index/btree_test.dart » ('j') | pkg/analysis_server/test/index/btree_test.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698