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

Unified 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: tweaks 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | pkg/analysis_server/test/index/btree_test.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/analysis_server/lib/src/index/btree.dart
diff --git a/pkg/analysis_server/lib/src/index/btree.dart b/pkg/analysis_server/lib/src/index/btree.dart
new file mode 100644
index 0000000000000000000000000000000000000000..b88789635ed17dc07c9797dc3d252c5f55badbc3
--- /dev/null
+++ b/pkg/analysis_server/lib/src/index/btree.dart
@@ -0,0 +1,467 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library index.btree;
+
+
+/**
+ * A simple B+Tree implementation.
+ */
+class BTree<K, V> {
+ /**
+ * The [Comparator] to compare keys.
+ */
+ final Comparator<K> _comparator;
+
+ /**
+ * The maximum number of keys in an index node.
+ */
+ final int _maxIndexKeys;
+
+ /**
+ * The maximum number of keys in a leaf node.
+ */
+ final int _maxLeafKeys;
+
+ /**
+ * The root node.
+ */
+ _Node<K, V> _root;
+
+ BTree(this._maxIndexKeys, this._maxLeafKeys, this._comparator) {
+ _root = new _LeafNode(_maxLeafKeys, _comparator);
+ }
+
+ /**
+ * Returns the value for [key] or `null` if [key] is not in the tree.
+ */
+ V find(K key) {
+ return _root.find(key);
+ }
+
+ /**
+ * Associates the [key] with the given [value].
+ *
+ * If the key was already in the tree, its associated value is changed.
+ * Otherwise the key-value pair is added to the tree.
+ */
+ void insert(K key, V value) {
+ _Split<K, V> result = _root.insert(key, value);
+ if (result != null) {
+ _IndexNode<K, V> newRoot = new _IndexNode<K, V>(_maxIndexKeys,
+ _comparator);
+ newRoot.keys.add(result.key);
+ newRoot.children.add(result.left);
+ newRoot.children.add(result.right);
+ _root = newRoot;
+ }
+ }
+
+ /**
+ * Removes the association for the given [key].
+ *
+ * Returns the value associated with [key] in the tree or `null` if [key] is
+ * not in the tree.
+ */
+ V remove(K key) {
+ _Remove<K, V> result = _root.remove(key, null, null, null);
+ if (_root is _IndexNode<K, V>) {
+ List<_Node<K, V>> children = (_root as _IndexNode<K, V>).children;
+ if (children.length == 1) {
+ _root = children[0];
+ }
+ }
+ return result.value;
+ }
+
+ /**
+ * Writes a textual presentation of the tree into [buffer].
+ */
+ void writeOn(StringBuffer buffer) {
+ _root.writeOn(buffer, '');
+ }
+}
+
+
+/**
+ * An index node with keys and children references.
+ */
+class _IndexNode<K, V> extends _Node<K, V> {
+ final List<_Node<K, V>> children = new List<_Node<K, V>>();
+ final int maxKeys;
+ final int minKeys;
+
+ _IndexNode(int maxKeys, Comparator<K> comparator)
+ : super(comparator),
+ maxKeys = maxKeys,
+ minKeys = maxKeys ~/ 2;
+
+ @override
+ V find(K key) {
+ int index = findChildIndex(key);
+ return children[index].find(key);
+ }
+
+ /**
+ * Returns the index of the child into which [key] should be inserted.
+ */
+ int findChildIndex(K key) {
+ for (int i = 0; i < keys.length; i++) {
+ if (comparator(keys[i], key) > 0) {
+ return i;
+ }
+ }
+ return keys.length;
+ }
+
+ _Split<K, V> insert(K key, V value) {
+ // Early split.
+ if (keys.length == maxKeys) {
+ int middle = (maxKeys + 1) ~/ 2;
+ K splitKey = keys[middle];
+ _IndexNode<K, V> sibling = new _IndexNode<K, V>(maxKeys, comparator);
+ sibling.keys.addAll(keys.getRange(middle + 1, keys.length));
+ sibling.children.addAll(children.getRange(middle + 1, children.length));
+ keys.length = middle;
+ children.length = middle + 1;
+ // Prepare split.
+ _Split<K, V> result = new _Split<K, V>(splitKey, this, sibling);
+ if (comparator(key, result.key) < 0) {
+ insertNotFull(key, value);
+ } else {
+ sibling.insertNotFull(key, value);
+ }
+ return result;
+ }
+ // No split.
+ insertNotFull(key, value);
+ return null;
+ }
+
+ void insertNotFull(K key, V value) {
+ int index = findChildIndex(key);
+ _Split<K, V> result = children[index].insert(key, value);
+ if (result != null) {
+ keys.insert(index, result.key);
+ children[index] = result.left;
+ children.insert(index + 1, result.right);
+ }
+ }
+
+ @override
+ _Remove<K, V> remove(K key, _Node<K, V> left, K anchor, _Node<K, V> right) {
+ int index = findChildIndex(key);
+ K thisAnchor = index == 0 ? keys[0] : keys[index - 1];
+ _Node<K, V> child = children[index];
+ bool hasLeft = index != 0;
+ bool hasRight = index < children.length - 1;
+ _Node<K, V> leftChild = hasLeft ? children[index - 1] : null;
+ _Node<K, V> rightChild = hasRight ? children[index + 1] : null;
+ // Ask child to remove.
+ _Remove<K, V> result = child.remove(key, leftChild, thisAnchor, rightChild);
+ V value = result.value;
+ if (value == null) {
+ return new _Remove<K, V>(value);
+ }
+ // Update anchor if borrowed.
+ if (result.leftAnchor != null) {
+ keys[index - 1] = result.leftAnchor;
+ }
+ if (result.rightAnchor != null) {
+ keys[index] = result.rightAnchor;
+ }
+ // Update keys / children if merged.
+ if (result.mergedLeft) {
+ keys.removeAt(index - 1);
+ children.removeAt(index);
+ }
+ if (result.mergedRight) {
+ keys.removeAt(index);
+ children.removeAt(index);
+ }
+ // Perform balancing.
+ if (keys.length < minKeys) {
+ // Try left sibling.
+ if (left is _IndexNode<K, V>) {
+ // Try to redistribute.
+ int leftLength = left.keys.length;
+ if (leftLength > minKeys) {
+ int halfExcess = (leftLength - minKeys + 1) ~/ 2;
+ int newLeftLength = leftLength - halfExcess;
+ keys.insert(0, anchor);
+ keys.insertAll(0, left.keys.getRange(newLeftLength, leftLength));
+ children.insertAll(0, left.children.getRange(newLeftLength, leftLength
+ + 1));
+ K newAnchor = left.keys[newLeftLength - 1];
+ left.keys.length = newLeftLength - 1;
+ left.children.length = newLeftLength;
+ return new _Remove<K, V>.borrowLeft(value, newAnchor);
+ }
+ // Do merge.
+ left.keys.add(anchor);
+ left.keys.addAll(keys);
+ left.children.addAll(children);
+ return new _Remove<K, V>.mergeLeft(value);
+ }
+ // Try right sibling.
+ if (right is _IndexNode<K, V>) {
+ // Try to redistribute.
+ var rightLength = right.keys.length;
+ if (rightLength > minKeys) {
+ int halfExcess = (rightLength - minKeys + 1) ~/ 2;
+ keys.add(anchor);
+ keys.addAll(right.keys.getRange(0, halfExcess - 1));
+ children.addAll(right.children.getRange(0, halfExcess));
+ K newAnchor = right.keys[halfExcess - 1];
+ right.keys.removeRange(0, halfExcess);
+ right.children.removeRange(0, halfExcess);
+ return new _Remove<K, V>.borrowRight(value, newAnchor);
+ }
+ // Do merge.
+ right.keys.insert(0, anchor);
+ right.keys.insertAll(0, keys);
+ right.children.insertAll(0, children);
+ return new _Remove<K, V>.mergeRight(value);
+ }
+ }
+ // No balancing required.
+ return new _Remove<K, V>(value);
+ }
+
+ @override
+ void writeOn(StringBuffer buffer, String indent) {
+ buffer.write(indent);
+ buffer.write('INode {\n');
+ for (int i = 0; i < keys.length; i++) {
+ children[i].writeOn(buffer, indent + ' ');
+ buffer.write(indent);
+ buffer.write(' ');
+ buffer.write(keys[i]);
+ buffer.write('\n');
+ }
+ children[keys.length].writeOn(buffer, indent + ' ');
+ buffer.write(indent);
+ buffer.write('}\n');
+ }
+}
+
+
+/**
+ * A leaf node with keys and values.
+ */
+class _LeafNode<K, V> extends _Node<K, V> {
+ final int maxKeys;
+ final int minKeys;
+
+ /**
+ * The list of values.
+ */
+ final List<V> values = new List<V>();
+
+ _LeafNode(int maxKeys, Comparator<K> comparator)
+ : super(comparator),
+ maxKeys = maxKeys,
+ minKeys = maxKeys ~/ 2;
+
+ @override
+ V find(K key) {
+ int index = findKeyIndex(key);
+ if (index < 0) {
+ return null;
+ }
+ if (index >= keys.length) {
+ return null;
+ }
+ if (keys[index] != key) {
+ return null;
+ }
+ return values[index];
+ }
+
+ /**
+ * Returns the index where [key] should be inserted.
+ */
+ int findKeyIndex(K key) {
+ for (int i = 0; i < keys.length; i++) {
+ if (comparator(keys[i], key) >= 0) {
+ return i;
+ }
+ }
+ return keys.length;
+ }
+
+ _Split<K, V> insert(K key, V value) {
+ int index = findKeyIndex(key);
+ // The node is full.
+ if (keys.length == maxKeys) {
+ int middle = (maxKeys + 1) ~/ 2;
+ _LeafNode<K, V> sibling = new _LeafNode<K, V>(maxKeys, comparator);
+ sibling.keys.addAll(keys.getRange(middle, keys.length));
+ sibling.values.addAll(values.getRange(middle, values.length));
+ keys.length = middle;
+ values.length = middle;
+ // Insert into the left / right sibling.
+ if (index < middle) {
+ insertNotFull(key, value, index);
+ } else {
+ sibling.insertNotFull(key, value, index - middle);
+ }
+ // Notify the parent about the split.
+ return new _Split<K, V>(sibling.keys[0], this, sibling);
+ }
+ // The node was not full.
+ insertNotFull(key, value, index);
+ return null;
+ }
+
+ void insertNotFull(K key, V value, int index) {
+ if (index < keys.length && keys[index] == key) {
+ values[index] = value;
+ } else {
+ keys.insert(index, key);
+ values.insert(index, value);
+ }
+ }
+
+ @override
+ _Remove<K, V> remove(K key, _Node<K, V> left, K anchor, _Node<K, V> right) {
+ // Find the key.
+ int index = keys.indexOf(key);
+ if (index == -1) {
+ return new _Remove<K, V>(null);
+ }
+ // Key key / value.
+ keys.removeAt(index);
+ V value = values.removeAt(index);
+ // Perform balancing.
+ if (keys.length < minKeys) {
+ // Try left sibling.
+ if (left is _LeafNode<K, V>) {
+ // Try to redistribute.
+ int leftLength = left.keys.length;
+ if (leftLength > minKeys) {
+ int halfExcess = (leftLength - minKeys + 1) ~/ 2;
+ int newLeftLength = leftLength - halfExcess;
+ keys.insertAll(0, left.keys.getRange(newLeftLength, leftLength));
+ values.insertAll(0, left.values.getRange(newLeftLength, leftLength));
+ left.keys.length = newLeftLength;
+ left.values.length = newLeftLength;
+ return new _Remove<K, V>.borrowLeft(value, keys.first);
+ }
+ // Do merge.
+ left.keys.addAll(keys);
+ left.values.addAll(values);
+ return new _Remove<K, V>.mergeLeft(value);
+ }
+ // Try right sibling.
+ if (right is _LeafNode<K, V>) {
+ // Try to redistribute.
+ var rightLength = right.keys.length;
+ if (rightLength > minKeys) {
+ int halfExcess = (rightLength - minKeys + 1) ~/ 2;
+ keys.addAll(right.keys.getRange(0, halfExcess));
+ values.addAll(right.values.getRange(0, halfExcess));
+ right.keys.removeRange(0, halfExcess);
+ right.values.removeRange(0, halfExcess);
+ return new _Remove<K, V>.borrowRight(value, right.keys.first);
+ }
+ // Do merge.
+ right.keys.insertAll(0, keys);
+ right.values.insertAll(0, values);
+ return new _Remove<K, V>.mergeRight(value);
+ }
+ }
+ // No balancing required.
+ return new _Remove<K, V>(value);
+ }
+
+ @override
+ void writeOn(StringBuffer buffer, String indent) {
+ buffer.write(indent);
+ buffer.write('LNode {');
+ for (int i = 0; i < keys.length; i++) {
+ if (i != 0) {
+ buffer.write(', ');
+ }
+ buffer.write(keys[i]);
+ buffer.write(': ');
+ buffer.write(values[i]);
+ }
+ buffer.write('}\n');
+ }
+}
+
+
+/**
+ * An internal or leaf node.
+ */
+abstract class _Node<K, V> {
+ /**
+ * The [Comparator] to compare keys.
+ */
+ Comparator<K> comparator;
+
+ /**
+ * The list of keys.
+ */
+ List<K> keys = new List<K>();
+
+ _Node(this.comparator);
+
+ /**
+ * Looks for [key].
+ *
+ * Returns the associated value if found.
+ * Returns `null` if not found.
+ */
+ V find(K key);
+
+ /**
+ * Inserts the [key] / [value] pair into this [_Node].
+ *
+ * Returns a [_Split] object if split happens, or `null` otherwise.
+ */
+ _Split<K, V> insert(K key, V value);
+
+ /**
+ * Removes the association for the given [key].
+ *
+ * Returns the [_Remove] information about an operation performed.
+ * It may be restructuring or merging, with [left] or [left] siblings.
+ */
+ _Remove<K, V> remove(K key, _Node<K, V> left, K anchor, _Node<K, V> right);
+
+ /**
+ * Writes a textual presentation of the tree into [buffer].
+ */
+ void writeOn(StringBuffer buffer, String indent);
+}
+
+
+/**
+ * A container with information about redistribute / merge.
+ */
+class _Remove<K, V> {
+ K leftAnchor;
+ bool mergedLeft = false;
+ bool mergedRight = false;
+ K rightAnchor;
+ final V value;
+ _Remove(this.value);
+ _Remove.borrowLeft(this.value, this.leftAnchor);
+ _Remove.borrowRight(this.value, this.rightAnchor);
+ _Remove.mergeLeft(this.value) : mergedLeft = true;
+ _Remove.mergeRight(this.value) : mergedRight = true;
+}
+
+
+/**
+ * A container with information about split during insert.
+ */
+class _Split<K, V> {
+ final K key;
+ final _Node<K, V> left;
+ final _Node<K, V> right;
+ _Split(this.key, this.left, this.right);
+}
« no previous file with comments | « no previous file | pkg/analysis_server/test/index/btree_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698