| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.cassandra.utils.btree; |
| |
| import java.util.ArrayDeque; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Comparator; |
| import java.util.Queue; |
| |
| import org.apache.cassandra.utils.ObjectSizes; |
| |
| import static org.apache.cassandra.utils.btree.UpdateFunction.NoOp; |
| |
| public class BTree |
| { |
| /** |
| * Leaf Nodes are a raw array of values: Object[V1, V1, ...,]. |
| * |
| * Branch Nodes: Object[V1, V2, ..., child[<V1.key], child[<V2.key], ..., child[< Inf]], where |
| * each child is another node, i.e., an Object[]. Thus, the value elements in a branch node are the |
| * first half of the array, rounding down. In our implementation, each value must include its own key; |
| * we access these via Comparator, rather than directly. |
| * |
| * So we can quickly distinguish between leaves and branches, we require that leaf nodes are always even number |
| * of elements (padded with a null, if necessary), and branches are always an odd number of elements. |
| * |
| * BTrees are immutable; updating one returns a new tree that reuses unmodified nodes. |
| * |
| * There are no references back to a parent node from its children. (This would make it impossible to re-use |
| * subtrees when modifying the tree, since the modified tree would need new parent references.) |
| * Instead, we store these references in a Path as needed when navigating the tree. |
| */ |
| |
| // The maximum fan factor used for B-Trees |
| static final int FAN_SHIFT; |
| static |
| { |
| int fanfactor = 32; |
| if (System.getProperty("cassandra.btree.fanfactor") != null) |
| fanfactor = Integer.parseInt(System.getProperty("cassandra.btree.fanfactor")); |
| int shift = 1; |
| while (1 << shift < fanfactor) |
| shift += 1; |
| FAN_SHIFT = shift; |
| } |
| // NB we encode Path indexes as Bytes, so this needs to be less than Byte.MAX_VALUE / 2 |
| static final int FAN_FACTOR = 1 << FAN_SHIFT; |
| |
| // An empty BTree Leaf - which is the same as an empty BTree |
| static final Object[] EMPTY_LEAF = new Object[0]; |
| |
| // An empty BTree branch - used only for internal purposes in Modifier |
| static final Object[] EMPTY_BRANCH = new Object[1]; |
| |
| /** |
| * Returns an empty BTree |
| * |
| * @return |
| */ |
| public static Object[] empty() |
| { |
| return EMPTY_LEAF; |
| } |
| |
| public static <V> Object[] build(Collection<V> source, Comparator<V> comparator, boolean sorted, UpdateFunction<V> updateF) |
| { |
| return build(source, source.size(), comparator, sorted, updateF); |
| } |
| |
| /** |
| * Creates a BTree containing all of the objects in the provided collection |
| * |
| * @param source the items to build the tree with |
| * @param comparator the comparator that defines the ordering over the items in the tree |
| * @param sorted if false, the collection will be copied and sorted to facilitate construction |
| * @param <V> |
| * @return |
| */ |
| public static <V> Object[] build(Iterable<V> source, int size, Comparator<V> comparator, boolean sorted, UpdateFunction<V> updateF) |
| { |
| if (size < FAN_FACTOR) |
| { |
| // pad to even length to match contract that all leaf nodes are even |
| V[] values = (V[]) new Object[size + (size & 1)]; |
| { |
| int i = 0; |
| for (V v : source) |
| values[i++] = v; |
| } |
| |
| // inline sorting since we're already calling toArray |
| if (!sorted) |
| Arrays.sort(values, 0, size, comparator); |
| |
| // if updateF is specified |
| if (updateF != null) |
| { |
| for (int i = 0 ; i < size ; i++) |
| values[i] = updateF.apply(values[i]); |
| updateF.allocated(ObjectSizes.sizeOfArray(values)); |
| } |
| return values; |
| } |
| |
| if (!sorted) |
| source = sorted(source, comparator, size); |
| |
| Queue<Builder> queue = modifier.get(); |
| Builder builder = queue.poll(); |
| if (builder == null) |
| builder = new Builder(); |
| Object[] btree = builder.build(source, updateF, size); |
| queue.add(builder); |
| return btree; |
| } |
| |
| /** |
| * Returns a new BTree with the provided set inserting/replacing as necessary any equal items |
| * |
| * @param btree the tree to update |
| * @param comparator the comparator that defines the ordering over the items in the tree |
| * @param updateWith the items to either insert / update |
| * @param updateWithIsSorted if false, updateWith will be copied and sorted to facilitate construction |
| * @param <V> |
| * @return |
| */ |
| public static <V> Object[] update(Object[] btree, Comparator<V> comparator, Collection<V> updateWith, boolean updateWithIsSorted) |
| { |
| return update(btree, comparator, updateWith, updateWithIsSorted, NoOp.<V>instance()); |
| } |
| |
| public static <V> Object[] update(Object[] btree, |
| Comparator<V> comparator, |
| Collection<V> updateWith, |
| boolean updateWithIsSorted, |
| UpdateFunction<V> updateF) |
| { |
| return update(btree, comparator, updateWith, updateWith.size(), updateWithIsSorted, updateF); |
| } |
| |
| /** |
| * Returns a new BTree with the provided set inserting/replacing as necessary any equal items |
| * |
| * @param btree the tree to update |
| * @param comparator the comparator that defines the ordering over the items in the tree |
| * @param updateWith the items to either insert / update |
| * @param updateWithIsSorted if false, updateWith will be copied and sorted to facilitate construction |
| * @param updateF the update function to apply to any pairs we are swapping, and maybe abort early |
| * @param <V> |
| * @return |
| */ |
| public static <V> Object[] update(Object[] btree, |
| Comparator<V> comparator, |
| Iterable<V> updateWith, |
| int updateWithLength, |
| boolean updateWithIsSorted, |
| UpdateFunction<V> updateF) |
| { |
| if (btree.length == 0) |
| return build(updateWith, updateWithLength, comparator, updateWithIsSorted, updateF); |
| |
| if (!updateWithIsSorted) |
| updateWith = sorted(updateWith, comparator, updateWithLength); |
| |
| Queue<Builder> queue = modifier.get(); |
| Builder builder = queue.poll(); |
| if (builder == null) |
| builder = new Builder(); |
| btree = builder.update(btree, comparator, updateWith, updateF); |
| queue.add(builder); |
| return btree; |
| } |
| |
| /** |
| * Returns an Iterator over the entire tree |
| * |
| * @param btree the tree to iterate over |
| * @param forwards if false, the iterator will start at the end and move backwards |
| * @param <V> |
| * @return |
| */ |
| public static <V> Cursor<V, V> slice(Object[] btree, boolean forwards) |
| { |
| Cursor<V, V> r = new Cursor<>(); |
| r.reset(btree, forwards); |
| return r; |
| } |
| |
| /** |
| * Returns an Iterator over a sub-range of the tree |
| * |
| * @param btree the tree to iterate over |
| * @param comparator the comparator that defines the ordering over the items in the tree |
| * @param start the first item to include |
| * @param end the last item to include |
| * @param forwards if false, the iterator will start at end and move backwards |
| * @param <V> |
| * @return |
| */ |
| public static <K, V extends K> Cursor<K, V> slice(Object[] btree, Comparator<K> comparator, K start, K end, boolean forwards) |
| { |
| Cursor<K, V> r = new Cursor<>(); |
| r.reset(btree, comparator, start, end, forwards); |
| return r; |
| } |
| |
| /** |
| * Returns an Iterator over a sub-range of the tree |
| * |
| * @param btree the tree to iterate over |
| * @param comparator the comparator that defines the ordering over the items in the tree |
| * @param start the first item to include |
| * @param end the last item to include |
| * @param forwards if false, the iterator will start at end and move backwards |
| * @param <V> |
| * @return |
| */ |
| public static <K, V extends K> Cursor<K, V> slice(Object[] btree, Comparator<K> comparator, K start, boolean startInclusive, K end, boolean endInclusive, boolean forwards) |
| { |
| Cursor<K, V> r = new Cursor<>(); |
| r.reset(btree, comparator, start, startInclusive, end, endInclusive, forwards); |
| return r; |
| } |
| |
| public static <V> V find(Object[] node, Comparator<V> comparator, V find) |
| { |
| while (true) |
| { |
| int keyEnd = getKeyEnd(node); |
| int i = BTree.find(comparator, find, node, 0, keyEnd); |
| if (i >= 0) |
| { |
| return (V) node[i]; |
| } |
| else if (!isLeaf(node)) |
| { |
| i = -i - 1; |
| node = (Object[]) node[keyEnd + i]; |
| } |
| else |
| { |
| return null; |
| } |
| } |
| } |
| |
| |
| // UTILITY METHODS |
| |
| // same basic semantics as Arrays.binarySearch, but delegates to compare() method to avoid |
| // wrapping generic Comparator with support for Special +/- infinity sentinels |
| static <V> int find(Comparator<V> comparator, Object key, Object[] a, final int fromIndex, final int toIndex) |
| { |
| int low = fromIndex; |
| int high = toIndex - 1; |
| |
| while (low <= high) |
| { |
| int mid = (low + high) / 2; |
| int cmp = comparator.compare((V) key, (V) a[mid]); |
| |
| if (cmp > 0) |
| low = mid + 1; |
| else if (cmp < 0) |
| high = mid - 1; |
| else |
| return mid; // key found |
| } |
| return -(low + 1); // key not found. |
| } |
| |
| // get the upper bound we should search in for keys in the node |
| static int getKeyEnd(Object[] node) |
| { |
| if (isLeaf(node)) |
| return getLeafKeyEnd(node); |
| else |
| return getBranchKeyEnd(node); |
| } |
| |
| // get the last index that is non-null in the leaf node |
| static int getLeafKeyEnd(Object[] node) |
| { |
| int len = node.length; |
| if (len == 0) |
| return 0; |
| else if (node[len - 1] == null) |
| return len - 1; |
| else |
| return len; |
| } |
| |
| // return the boundary position between keys/children for the branch node |
| static int getBranchKeyEnd(Object[] node) |
| { |
| return node.length / 2; |
| } |
| |
| // returns true if the provided node is a leaf, false if it is a branch |
| static boolean isLeaf(Object[] node) |
| { |
| return (node.length & 1) == 0; |
| } |
| |
| public static boolean isEmpty(Object[] tree) |
| { |
| return tree.length == 0; |
| } |
| |
| public static int depth(Object[] tree) |
| { |
| int depth = 1; |
| while (!isLeaf(tree)) |
| { |
| depth++; |
| tree = (Object[]) tree[getKeyEnd(tree)]; |
| } |
| return depth; |
| } |
| |
| // Special class for making certain operations easier, so we can define a +/- Inf |
| static interface Special extends Comparable<Object> { } |
| static final Special POSITIVE_INFINITY = new Special() |
| { |
| public int compareTo(Object o) |
| { |
| return o == this ? 0 : 1; |
| } |
| }; |
| static final Special NEGATIVE_INFINITY = new Special() |
| { |
| public int compareTo(Object o) |
| { |
| return o == this ? 0 : -1; |
| } |
| }; |
| |
| private static final ThreadLocal<Queue<Builder>> modifier = new ThreadLocal<Queue<Builder>>() |
| { |
| @Override |
| protected Queue<Builder> initialValue() |
| { |
| return new ArrayDeque<>(); |
| } |
| }; |
| |
| // return a sorted collection |
| private static <V> Collection<V> sorted(Iterable<V> source, Comparator<V> comparator, int size) |
| { |
| V[] vs = (V[]) new Object[size]; |
| int i = 0; |
| for (V v : source) |
| vs[i++] = v; |
| Arrays.sort(vs, comparator); |
| return Arrays.asList(vs); |
| } |
| |
| /** simple static wrapper to calls to cmp.compare() which checks if either a or b are Special (i.e. represent an infinity) */ |
| // TODO : cheaper to check for POSITIVE/NEGATIVE infinity in callers, rather than here |
| static <V> int compare(Comparator<V> cmp, Object a, Object b) |
| { |
| if (a instanceof Special) |
| return ((Special) a).compareTo(b); |
| if (b instanceof Special) |
| return -((Special) b).compareTo(a); |
| return cmp.compare((V) a, (V) b); |
| } |
| |
| public static boolean isWellFormed(Object[] btree, Comparator<? extends Object> cmp) |
| { |
| return isWellFormed(cmp, btree, true, NEGATIVE_INFINITY, POSITIVE_INFINITY); |
| } |
| |
| private static boolean isWellFormed(Comparator<?> cmp, Object[] node, boolean isRoot, Object min, Object max) |
| { |
| if (cmp != null && !isNodeWellFormed(cmp, node, min, max)) |
| return false; |
| |
| if (isLeaf(node)) |
| { |
| if (isRoot) |
| return node.length <= FAN_FACTOR; |
| return node.length >= FAN_FACTOR / 2 && node.length <= FAN_FACTOR; |
| } |
| |
| int type = 0; |
| int childOffset = getBranchKeyEnd(node); |
| // compare each child node with the branch element at the head of this node it corresponds with |
| for (int i = childOffset; i < node.length; i++) |
| { |
| Object[] child = (Object[]) node[i]; |
| Object localmax = i < node.length - 1 ? node[i - childOffset] : max; |
| if (!isWellFormed(cmp, child, false, min, localmax)) |
| return false; |
| type |= isLeaf(child) ? 1 : 2; |
| min = localmax; |
| } |
| return type < 3; // either all leaves or all branches but not a mix |
| } |
| |
| private static boolean isNodeWellFormed(Comparator<?> cmp, Object[] node, Object min, Object max) |
| { |
| Object previous = min; |
| int end = getKeyEnd(node); |
| for (int i = 0; i < end; i++) |
| { |
| Object current = node[i]; |
| if (compare(cmp, previous, current) >= 0) |
| return false; |
| previous = current; |
| } |
| return compare(cmp, previous, max) < 0; |
| } |
| } |