Esempio n. 1
0
  /**
   * At the highest level, we adhere to the classic b-tree insertion algorithm:
   *
   * <p>1. Add to the appropriate leaf 2. Split the leaf if necessary, add the median to the parent
   * 3. Split the parent if necessary, etc.
   *
   * <p>There is one important difference: we don't actually modify the original tree, but copy each
   * node that we modify. Note that every node on the path to the key being inserted or updated will
   * be modified; this implies that at a minimum, the root node will be modified for every update,
   * so every root is a "snapshot" of a tree that can be iterated or sliced without fear of
   * concurrent modifications.
   *
   * <p>The NodeBuilder class handles the details of buffering the copied contents of the original
   * tree and adding in our changes. Since NodeBuilder maintains parent/child references, it also
   * handles parent-splitting (easy enough, since any node affected by the split will already be
   * copied into a NodeBuilder).
   *
   * <p>One other difference from the simple algorithm is that we perform modifications in bulk; we
   * assume @param source has been sorted, e.g. by BTree.update, so the update of each key resumes
   * where the previous left off.
   */
  public <V> Object[] update(
      Object[] btree, Comparator<V> comparator, Iterable<V> source, UpdateFunction<V> updateF) {
    assert updateF != null;

    NodeBuilder current = rootBuilder;
    current.reset(btree, POSITIVE_INFINITY, updateF, comparator);

    for (V key : source) {
      while (true) {
        if (updateF.abortEarly()) {
          rootBuilder.clear();
          return null;
        }
        NodeBuilder next = current.update(key);
        if (next == null) break;
        // we were in a subtree from a previous key that didn't contain this new key;
        // retry against the correct subtree
        current = next;
      }
    }

    // finish copying any remaining keys from the original btree
    while (true) {
      NodeBuilder next = current.finish();
      if (next == null) break;
      current = next;
    }

    // updating with POSITIVE_INFINITY means that current should be back to the root
    assert current.isRoot();

    Object[] r = current.toNode();
    current.clear();
    return r;
  }
Esempio n. 2
0
  public <V> Object[] build(Iterable<V> source, UpdateFunction<V> updateF, int size) {
    assert updateF != null;

    NodeBuilder current = rootBuilder;
    // we descend only to avoid wasting memory; in update() we will often descend into existing
    // trees
    // so here we want to descend also, so we don't have lg max(N) depth in both directions
    while ((size >>= FAN_SHIFT) > 0) current = current.ensureChild();

    current.reset(EMPTY_LEAF, POSITIVE_INFINITY, updateF, null);
    for (V key : source) current.addNewKey(key);

    current = current.ascendToRoot();

    Object[] r = current.toNode();
    current.clear();
    return r;
  }