| /* |
| * 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 java.util.Iterator; |
| |
| 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.getLeafKeyEnd; |
| import static org.apache.cassandra.utils.btree.BTree.isLeaf; |
| |
| /** |
| * An extension of Path which provides a public interface for iterating over or counting a subrange of the tree |
| * |
| * @param <V> |
| */ |
| public final class Cursor<K, V extends K> extends Path implements Iterator<V> |
| { |
| /* |
| * Conceptually, a Cursor derives two Paths, one for the first object in the slice requested (inclusive), |
| * and one for the last (exclusive). Then hasNext just checks, have we reached the last yet, and next |
| * calls successor() to get to the next item in the Tree. |
| * |
| * To optimize memory use, we summarize the last Path as just endNode/endIndex, and inherit from Path for |
| * |
| * the first one. |
| */ |
| |
| // the last node covered by the requested range |
| private Object[] endNode; |
| // the index within endNode that signals we're finished -- that is, endNode[endIndex] is NOT part of the Cursor |
| private byte endIndex; |
| |
| private boolean forwards; |
| |
| /** |
| * Reset this cursor for the provided tree, to iterate over its entire range |
| * |
| * @param btree the tree to iterate over |
| * @param forwards if false, the cursor will start at the end and move backwards |
| */ |
| public void reset(Object[] btree, boolean forwards) |
| { |
| _reset(btree, null, NEGATIVE_INFINITY, false, POSITIVE_INFINITY, false, forwards); |
| } |
| |
| /** |
| * Reset this cursor for the provided tree, to iterate between the provided start and end |
| * |
| * @param btree the tree to iterate over |
| * @param comparator the comparator that defines the ordering over the items in the tree |
| * @param lowerBound the first item to include, inclusive |
| * @param upperBound the last item to include, exclusive |
| * @param forwards if false, the cursor will start at the end and move backwards |
| */ |
| public void reset(Object[] btree, Comparator<K> comparator, K lowerBound, K upperBound, boolean forwards) |
| { |
| _reset(btree, comparator, lowerBound, true, upperBound, false, forwards); |
| } |
| |
| /** |
| * Reset this cursor for the provided tree, to iterate between the provided start and end |
| * |
| * @param btree the tree to iterate over |
| * @param comparator the comparator that defines the ordering over the items in the tree |
| * @param lowerBound the first item to include |
| * @param inclusiveLowerBound should include start in the iterator, if present in the tree |
| * @param upperBound the last item to include |
| * @param inclusiveUpperBound should include end in the iterator, if present in the tree |
| * @param forwards if false, the cursor will start at the end and move backwards |
| */ |
| public void reset(Object[] btree, Comparator<K> comparator, K lowerBound, boolean inclusiveLowerBound, K upperBound, boolean inclusiveUpperBound, boolean forwards) |
| { |
| _reset(btree, comparator, lowerBound, inclusiveLowerBound, upperBound, inclusiveUpperBound, forwards); |
| } |
| |
| private void _reset(Object[] btree, Comparator<K> comparator, Object lowerBound, boolean inclusiveLowerBound, Object upperBound, boolean inclusiveUpperBound, boolean forwards) |
| { |
| init(btree); |
| if (lowerBound == null) |
| lowerBound = NEGATIVE_INFINITY; |
| if (upperBound == null) |
| upperBound = POSITIVE_INFINITY; |
| |
| this.forwards = forwards; |
| |
| Path findLast = new Path(this.path.length, btree); |
| if (forwards) |
| { |
| findLast.find(comparator, upperBound, inclusiveUpperBound ? Op.HIGHER : Op.CEIL, true); |
| find(comparator, lowerBound, inclusiveLowerBound ? Op.CEIL : Op.HIGHER, true); |
| } |
| else |
| { |
| findLast.find(comparator, lowerBound, inclusiveLowerBound ? Op.LOWER : Op.FLOOR, false); |
| find(comparator, upperBound, inclusiveUpperBound ? Op.FLOOR : Op.LOWER, false); |
| } |
| int c = this.compareTo(findLast, forwards); |
| if (forwards ? c > 0 : c < 0) |
| { |
| endNode = currentNode(); |
| endIndex = currentIndex(); |
| } |
| else |
| { |
| endNode = findLast.currentNode(); |
| endIndex = findLast.currentIndex(); |
| } |
| } |
| |
| public boolean hasNext() |
| { |
| return path[depth] != endNode || indexes[depth] != endIndex; |
| } |
| |
| public V next() |
| { |
| Object r = currentKey(); |
| if (forwards) |
| successor(); |
| else |
| predecessor(); |
| return (V) r; |
| } |
| |
| public int count() |
| { |
| if (!forwards) |
| throw new IllegalStateException("Count can only be run on forward cursors"); |
| int count = 0; |
| int next; |
| while ((next = consumeNextLeaf()) >= 0) |
| count += next; |
| return count; |
| } |
| |
| /** |
| * @return the number of objects consumed by moving out of the next (possibly current) leaf |
| */ |
| private int consumeNextLeaf() |
| { |
| Object[] node = currentNode(); |
| int r = 0; |
| |
| if (!isLeaf(node)) |
| { |
| // if we're not in a leaf, then calling successor once will take us to a leaf, since the next |
| // key will be in the leftmost subtree of whichever branch is next. For instance, if we |
| // are in the root node of the tree depicted by http://cis.stvincent.edu/html/tutorials/swd/btree/btree1.gif, |
| // successor() will take us to the leaf containing N and O. |
| int i = currentIndex(); |
| if (node == endNode && i == endIndex) |
| return -1; |
| r = 1; |
| successor(); |
| node = currentNode(); |
| } |
| |
| if (node == endNode) |
| { |
| // only count up to endIndex, and don't call successor() |
| if (currentIndex() == endIndex) |
| return r > 0 ? r : -1; |
| r += endIndex - currentIndex(); |
| setIndex(endIndex); |
| return r; |
| } |
| |
| // count the remaining objects in this leaf |
| int keyEnd = getLeafKeyEnd(node); |
| r += keyEnd - currentIndex(); |
| setIndex(keyEnd); |
| successor(); |
| return r; |
| } |
| |
| public void remove() |
| { |
| throw new UnsupportedOperationException(); |
| } |
| } |