| /* |
| * 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.Comparator; |
| |
| import static org.apache.cassandra.utils.btree.BTree.NEGATIVE_INFINITY; |
| import static org.apache.cassandra.utils.btree.BTree.POSITIVE_INFINITY; |
| import static org.apache.cassandra.utils.btree.BTree.getBranchKeyEnd; |
| import static org.apache.cassandra.utils.btree.BTree.getKeyEnd; |
| import static org.apache.cassandra.utils.btree.BTree.getLeafKeyEnd; |
| import static org.apache.cassandra.utils.btree.BTree.isLeaf; |
| |
| /** |
| * An internal class for searching and iterating through a tree. As it traverses the tree, |
| * it adds the nodes visited to a stack. This allows us to backtrack from a child node |
| * to its parent. |
| * |
| * As we navigate the tree, we destructively modify this stack. |
| * |
| * Path is only intended to be used via Cursor. |
| */ |
| class Path |
| { |
| // operations corresponding to the ones in NavigableSet |
| static enum Op |
| { |
| CEIL, // the least element greater than or equal to the given element |
| FLOOR, // the greatest element less than or equal to the given element |
| HIGHER, // the least element strictly greater than the given element |
| LOWER // the greatest element strictly less than the given element |
| } |
| |
| // the path to the searched-for key |
| Object[][] path; |
| // the index within the node of our path at a given depth |
| byte[] indexes; |
| // current depth. nothing in path[i] for i > depth is valid. |
| byte depth; |
| |
| Path() { } |
| Path(int depth) |
| { |
| this.path = new Object[depth][]; |
| this.indexes = new byte[depth]; |
| } |
| |
| void ensureDepth(Object[] btree) |
| { |
| int depth = BTree.depth(btree); |
| if (path == null || path.length < depth) |
| { |
| path = new Object[depth][]; |
| indexes = new byte[depth]; |
| } |
| } |
| |
| void moveEnd(Object[] node, boolean forwards) |
| { |
| push(node, getKeyEnd(node)); |
| if (!forwards) |
| predecessor(); |
| } |
| |
| void moveStart(Object[] node, boolean forwards) |
| { |
| push(node, -1); |
| if (forwards) |
| successor(); |
| } |
| |
| /** |
| * Find the provided key in the tree rooted at node, and store the root to it in the path |
| * |
| * @param node the tree to search in |
| * @param comparator the comparator defining the order on the tree |
| * @param target the key to search for |
| * @param mode the type of search to perform |
| * @param forwards if the path should be setup for forward or backward iteration |
| * @param <V> |
| */ |
| <V> void find(Object[] node, Comparator<V> comparator, Object target, Op mode, boolean forwards) |
| { |
| // TODO : should not require parameter 'forwards' - consider modifying index to represent both |
| // child and key position, as opposed to just key position (which necessitates a different value depending |
| // on which direction you're moving in. Prerequisite for making Path public and using to implement general |
| // search |
| |
| depth = -1; |
| if (target instanceof BTree.Special) |
| { |
| if (target == POSITIVE_INFINITY) |
| moveEnd(node, forwards); |
| else if (target == NEGATIVE_INFINITY) |
| moveStart(node, forwards); |
| else |
| throw new AssertionError(); |
| return; |
| } |
| |
| while (true) |
| { |
| int keyEnd = getKeyEnd(node); |
| |
| // search for the target in the current node |
| int i = BTree.find(comparator, target, node, 0, keyEnd); |
| if (i >= 0) |
| { |
| // exact match. transform exclusive bounds into the correct index by moving back or forwards one |
| push(node, i); |
| switch (mode) |
| { |
| case HIGHER: |
| successor(); |
| break; |
| case LOWER: |
| predecessor(); |
| } |
| return; |
| } |
| i = -i - 1; |
| |
| // traverse into the appropriate child |
| if (!isLeaf(node)) |
| { |
| push(node, forwards ? i - 1 : i); |
| node = (Object[]) node[keyEnd + i]; |
| continue; |
| } |
| |
| // bottom of the tree and still not found. pick the right index to satisfy Op |
| switch (mode) |
| { |
| case FLOOR: |
| case LOWER: |
| i--; |
| } |
| |
| if (i < 0) |
| { |
| push(node, 0); |
| predecessor(); |
| } |
| else if (i >= keyEnd) |
| { |
| push(node, keyEnd - 1); |
| successor(); |
| } |
| else |
| { |
| push(node, i); |
| } |
| |
| return; |
| } |
| } |
| |
| private boolean isRoot() |
| { |
| return depth == 0; |
| } |
| |
| private void pop() |
| { |
| depth--; |
| } |
| |
| Object[] currentNode() |
| { |
| return path[depth]; |
| } |
| |
| byte currentIndex() |
| { |
| return indexes[depth]; |
| } |
| |
| private void push(Object[] node, int index) |
| { |
| path[++depth] = node; |
| indexes[depth] = (byte) index; |
| } |
| |
| void setIndex(int index) |
| { |
| indexes[depth] = (byte) index; |
| } |
| |
| // move to the next key in the tree |
| void successor() |
| { |
| Object[] node = currentNode(); |
| int i = currentIndex(); |
| |
| if (!isLeaf(node)) |
| { |
| // if we're on a key in a branch, we MUST have a descendant either side of us, |
| // so we always go down the left-most child until we hit a leaf |
| node = (Object[]) node[getBranchKeyEnd(node) + i + 1]; |
| while (!isLeaf(node)) |
| { |
| push(node, -1); |
| node = (Object[]) node[getBranchKeyEnd(node)]; |
| } |
| push(node, 0); |
| return; |
| } |
| |
| // if we haven't reached the end of this leaf, just increment our index and return |
| i += 1; |
| if (i < getLeafKeyEnd(node)) |
| { |
| // moved to the next key in the same leaf |
| setIndex(i); |
| return; |
| } |
| |
| // we've reached the end of this leaf, |
| // so go up until we reach something we've not finished visiting |
| while (!isRoot()) |
| { |
| pop(); |
| i = currentIndex() + 1; |
| node = currentNode(); |
| if (i < getKeyEnd(node)) |
| { |
| setIndex(i); |
| return; |
| } |
| } |
| |
| // we've visited the last key in the root node, so we're done |
| setIndex(getKeyEnd(node)); |
| } |
| |
| // move to the previous key in the tree |
| void predecessor() |
| { |
| Object[] node = currentNode(); |
| int i = currentIndex(); |
| |
| if (!isLeaf(node)) |
| { |
| // if we're on a key in a branch, we MUST have a descendant either side of us |
| // so we always go down the right-most child until we hit a leaf |
| node = (Object[]) node[getBranchKeyEnd(node) + i]; |
| while (!isLeaf(node)) |
| { |
| i = getBranchKeyEnd(node); |
| push(node, i); |
| node = (Object[]) node[i * 2]; |
| } |
| push(node, getLeafKeyEnd(node) - 1); |
| return; |
| } |
| |
| // if we haven't reached the beginning of this leaf, just decrement our index and return |
| i -= 1; |
| if (i >= 0) |
| { |
| setIndex(i); |
| return; |
| } |
| |
| // we've reached the beginning of this leaf, |
| // so go up until we reach something we've not finished visiting |
| while (!isRoot()) |
| { |
| pop(); |
| i = currentIndex() - 1; |
| if (i >= 0) |
| { |
| setIndex(i); |
| return; |
| } |
| } |
| |
| // we've visited the last key in the root node, so we're done |
| setIndex(-1); |
| } |
| |
| Object currentKey() |
| { |
| return currentNode()[currentIndex()]; |
| } |
| |
| int compareTo(Path that, boolean forwards) |
| { |
| int d = Math.min(this.depth, that.depth); |
| for (int i = 0; i <= d; i++) |
| { |
| int c = this.indexes[i] - that.indexes[i]; |
| if (c != 0) |
| return c; |
| } |
| // identical indices up to depth, so if somebody is lower depth they are on a later item if iterating forwards |
| // and an earlier item if iterating backwards, as the node at max common depth must be a branch if they are |
| // different depths, and branches that are currently descended into lag the child index they are in when iterating forwards, |
| // i.e. if they are in child 0 they record an index of -1 forwards, or 0 when backwards |
| d = this.depth - that.depth; |
| return forwards ? d : -d; |
| } |
| } |
| |