io.sirix.index.redblacktree.RBTreeReader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sirix-core Show documentation
Show all versions of sirix-core Show documentation
SirixDB is a hybrid on-disk and in-memory document oriented, versioned database system. It has a lightweight buffer manager, stores everything in a huge persistent and durable tree and allows efficient reconstruction of every revision. Furthermore, SirixDB implements change tracking, diffing and supports time travel queries.
package io.sirix.index.redblacktree;
import com.google.common.collect.AbstractIterator;
import io.sirix.api.NodeCursor;
import io.sirix.api.PageReadOnlyTrx;
import io.sirix.api.PageTrx;
import io.sirix.cache.Cache;
import io.sirix.cache.RBIndexKey;
import io.sirix.exception.SirixIOException;
import io.sirix.index.IndexType;
import io.sirix.index.SearchMode;
import io.sirix.index.redblacktree.interfaces.References;
import io.sirix.node.NodeKind;
import io.sirix.node.NullNode;
import io.sirix.node.interfaces.Node;
import io.sirix.node.interfaces.StructNode;
import io.sirix.node.interfaces.immutable.ImmutableNode;
import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.nullness.qual.NonNull;
import io.sirix.settings.Fixed;
import java.io.PrintStream;
import java.util.ArrayDeque;
import java.util.Comparator;
import java.util.Deque;
import java.util.Optional;
import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;
/**
* Simple Red-black tree reader (balanced binary search-tree -- based on BaseX(.org) version).
*
* @param the key to search for or insert
* @param the value
* @author Johannes Lichtenberger, University of Konstanz
*/
@SuppressWarnings({ "ConstantConditions", "unchecked" })
public final class RBTreeReader, V extends References> implements NodeCursor {
/**
* Cache.
*/
private final Cache cache;
/**
* The index type.
*/
final IndexType indexType;
/**
* The index number.
*/
private final int indexNumber;
/**
* The revision number.
*/
private final int revisionNumber;
/**
* Determines if tree is closed or not.
*/
private boolean isClosed;
/**
* Strong reference to currently selected node.
*/
private Node currentNode;
/**
* {@link PageReadOnlyTrx} for persistent storage.
*/
final PageReadOnlyTrx pageReadOnlyTrx;
/**
* Index number.
*/
final int index;
/**
* Determines movement of the internal cursor.
*/
public enum MoveCursor {
/**
* Cursor should be moved document root.
*/
TO_DOCUMENT_ROOT,
/**
* Cursor should not be moved.
*/
NO_MOVE
}
/**
* Get a new instance.
*
* @param key instance which extends comparable
* @param value
* @param cache a cache shared between all read-only tree readers
* @param pageReadTrx {@link PageReadOnlyTrx} for persistent storage
* @param type type of index
* @param index index
* @return new tree instance
*/
public static , V extends References> RBTreeReader getInstance(
final Cache cache, final PageReadOnlyTrx pageReadTrx, final IndexType type,
@NonNegative final int index) {
return new RBTreeReader<>(cache, pageReadTrx, type, index);
}
/**
* Private constructor.
*
* @param cache a cache shared between all read-only tree readers
* @param pageReadOnlyTrx {@link PageReadOnlyTrx} for persistent storage
* @param indexType kind of indexType
* @param indexNumber the indexNumber number
*/
private RBTreeReader(final Cache cache, final PageReadOnlyTrx pageReadOnlyTrx,
final IndexType indexType, final int indexNumber) {
this.cache = requireNonNull(cache);
this.pageReadOnlyTrx = requireNonNull(pageReadOnlyTrx);
this.indexType = requireNonNull(indexType);
this.indexNumber = indexNumber;
revisionNumber = pageReadOnlyTrx.getRevisionNumber();
isClosed = false;
this.index = indexNumber;
currentNode =
this.pageReadOnlyTrx.getRecord(Fixed.DOCUMENT_NODE_KEY.getStandardProperty(), indexType, indexNumber);
if (currentNode == null) {
throw new IllegalStateException("Node couldn't be fetched from persistent storage!");
}
// TODO: move this / constructor of course should not do any work!
if (!(pageReadOnlyTrx instanceof PageTrx)) {
for (RBNodeIterator it = new RBNodeIterator(0); it.hasNext(); ) {
RBNodeKey node = it.next();
assert node.getNodeKey() != 0;
this.cache.put(new RBIndexKey(node.getNodeKey(), revisionNumber, indexType, indexNumber), getCurrentNodeAsRBNodeKey());
}
setCurrentNode(currentNode);
}
}
/**
* Dump the AVLTree in preorder.
*
* @param out {@link PrintStream} to print to
*/
public void dump(final PrintStream out) {
assertNotClosed();
moveToDocumentRoot();
if (((StructNode) getNode()).hasFirstChild()) {
moveToFirstChild();
} else {
return;
}
internalDump(out);
}
// Internal function to dump data to a PrintStream instance.
private void internalDump(final PrintStream out) {
out.println(getCurrentNodeAsRBNodeKey());
@SuppressWarnings("ConstantConditions") final long nodeKey = getCurrentNodeAsRBNodeKey().getNodeKey();
if (getCurrentNodeAsRBNodeKey().hasLeftChild()) {
moveToFirstChild();
internalDump(out);
}
moveTo(nodeKey);
if (getCurrentNodeAsRBNodeKey().hasRightChild()) {
moveToLastChild();
internalDump(out);
}
}
/**
* Get the {@link RBNodeKey}.
*
* @return {@link RBNodeKey} instance
*/
public RBNodeKey getCurrentNodeAsRBNodeKey() {
assertNotClosed();
//noinspection rawtypes
if (currentNode instanceof RBNodeKey rbNodeKey) {
return rbNodeKey;
}
return null;
}
/**
* Get the {@link RBNodeKey}.
*
* @return {@link RBNodeKey} instance
*/
public RBNodeValue getCurrentNodeAsRBNodeValue() {
assertNotClosed();
//noinspection rawtypes
if (currentNode instanceof RBNodeValue rbNodeValue) {
return rbNodeValue;
}
return null;
}
/**
* Set the current node.
*
* @param node the node to set
* @return the node instance
*/
Node setCurrentNode(final Node node) {
assertNotClosed();
currentNode = node;
return node;
}
/**
* Finds the specified key in the index and returns its value.
*
* @param startNodeKey the key of the node to start from
* @param key key to be found
* @param mode the search mode
* @return {@link Optional} reference (with the found value, or a reference which indicates that the
* value hasn't been found)
*/
public Optional get(final long startNodeKey, final K key, final SearchMode mode) {
assertNotClosed();
final boolean movedToStartNode = moveTo(startNodeKey);
if (!movedToStartNode) {
return Optional.empty();
}
moveToFirstChild();
RBNodeKey node = getCurrentNodeAsRBNodeKey();
return getNode(key, mode, node);
}
@NonNull
private Optional getNode(K key, SearchMode mode, RBNodeKey node) {
while (true) {
final int c = mode.compare(key, node.getKey());
if (c == 0) {
final long valueNodeKey = node.getValueNodeKey();
moveTo(valueNodeKey);
final var value = Optional.ofNullable(getCurrentNodeAsRBNodeValue().getValue());
setCurrentNode(node);
return value;
}
boolean moved;
if (c < 0) {
if (node.getLeftChild() != null) {
node = node.getLeftChild();
currentNode = node;
moved = true;
} else if (node.hasLeftChild()) {
node = pageReadOnlyTrx instanceof PageTrx
? null
: (RBNodeKey) cache.get(new RBIndexKey(node.getLeftChildKey(),
revisionNumber,
indexType,
indexNumber));
if (node == null) {
moved = moveToFirstChild();
if (moved) {
node = getCurrentNodeAsRBNodeKey();
}
} else {
currentNode = node;
moved = true;
}
} else {
moved = false;
}
} else {
if (node.getRightChild() != null) {
node = node.getRightChild();
currentNode = node;
moved = true;
} else if (node.hasRightChild()) {
node = pageReadOnlyTrx instanceof PageTrx
? null
: (RBNodeKey) cache.get(new RBIndexKey(node.getRightChildKey(),
revisionNumber,
indexType,
indexNumber));
if (node == null) {
moved = moveToLastChild();
if (moved) {
node = getCurrentNodeAsRBNodeKey();
}
} else {
currentNode = node;
moved = true;
}
} else {
moved = false;
}
}
if (!moved) {
break;
}
}
return Optional.empty();
}
/**
* Finds the specified key in the index and returns its value.
*
* @param key key to be found
* @param mode the search mode
* @return {@link Optional} reference (with the found value, or a reference which indicates that the
* value hasn't been found)
*/
public Optional get(final K key, final SearchMode mode) {
assertNotClosed();
moveToDocumentRoot();
if (!((StructNode) getNode()).hasFirstChild()) {
return Optional.empty();
}
moveToFirstChild();
RBNodeKey node = getCurrentNodeAsRBNodeKey();
return getNode(key, mode, node);
}
/**
* Finds the specified key in the index starting from the specified node key and returns its
* AVLNode.
*
* @param startNodeKey specified node
* @param key key to be found
* @param mode the search mode
* @return Optional {@link RBNodeKey} reference
*/
public Optional> getCurrentNodeAsRBNodeKey(final long startNodeKey, final K key, final SearchMode mode) {
assertNotClosed();
final boolean movedToStartNode = moveTo(startNodeKey);
if (!movedToStartNode) {
return Optional.empty();
}
RBNodeKey node = getCurrentNodeAsRBNodeKey();
return getTheSearchedNode(key, mode, node);
}
@NonNull
private Optional> getTheSearchedNode(K key, SearchMode mode, RBNodeKey node) {
while (true) {
final int c = key.compareTo(node.getKey());
if (mode != SearchMode.EQUAL && mode.compare(key, node.getKey()) == 0) {
return Optional.ofNullable(node);
}
if (c == 0) {
if (mode == SearchMode.EQUAL) {
return Optional.ofNullable(node);
}
}
final boolean moved = c < 0 ? moveToFirstChild() : moveToLastChild();
if (moved) {
node = getCurrentNodeAsRBNodeKey();
} else {
break;
}
}
return Optional.empty();
}
/**
* Finds the specified key in the index and returns its AVLNode.
*
* @param key key to be found
* @param mode the search mode
* @return Optional {@link RBNodeKey} reference
*/
public Optional> getCurrentNodeAsRBNodeKey(final K key, final SearchMode mode) {
assertNotClosed();
moveToDocumentRoot();
if (!((StructNode) getNode()).hasFirstChild()) {
return Optional.empty();
}
moveToFirstChild();
RBNodeKey node = getCurrentNodeAsRBNodeKey();
return getTheSearchedNode(key, mode, node);
}
/**
* Finds the specified key in the index and returns its AVLNode.
*
* @param key key to be found
* @param mode the search mode
* @param comp comparator to be used to compare keys
* @return Optional {@link RBNodeKey} reference
*/
public Optional> getCurrentNodeAsRBNodeKey(final K key, final SearchMode mode, final Comparator super K> comp) {
assertNotClosed();
moveToDocumentRoot();
if (!((StructNode) getNode()).hasFirstChild()) {
return Optional.empty();
}
moveToFirstChild();
RBNodeKey node = getCurrentNodeAsRBNodeKey();
while (true) {
final int c = key.compareTo(node.getKey());
if (mode.compare(key, node.getKey(), comp) == 0) {
return Optional.ofNullable(node);
}
final boolean moved = c < 0 ? moveToFirstChild() : moveToLastChild();
if (moved) {
node = getCurrentNodeAsRBNodeKey();
} else {
break;
}
}
return Optional.empty();
}
/**
* Returns the number of index entries.
*
* @return number of index entries
*/
public long size() {
moveToDocumentRoot();
return getStructuralNode().getDescendantCount();
}
@Override
public void close() {
isClosed = true;
}
/**
* Make sure that the path summary is not yet closed when calling this method.
*/
void assertNotClosed() {
if (isClosed) {
throw new IllegalStateException("Tree reader is already closed.");
}
}
/**
* Get the structural node of the current node.
*
* @return structural node
*/
private StructNode getStructuralNode() {
assertNotClosed();
if (currentNode instanceof StructNode) {
return (StructNode) currentNode;
} else {
return new NullNode(currentNode);
}
}
@Override
public boolean hasNode(final long key) {
assertNotClosed();
final long currKey = currentNode.getNodeKey();
final boolean moved = moveTo(key);
final boolean movedCursor = moveTo(currKey);
assert movedCursor : "Must be movable back!";
return moved;
}
@Override
public boolean hasParent() {
assertNotClosed();
return currentNode.hasParent();
}
@Override
public boolean hasFirstChild() {
assertNotClosed();
return getStructuralNode().hasFirstChild();
}
@Override
public boolean hasLastChild() {
assertNotClosed();
if (currentNode instanceof RBNodeKey) {
return getCurrentNodeAsRBNodeKey().hasRightChild();
}
return false;
}
@Override
public boolean hasLeftSibling() {
assertNotClosed();
return getStructuralNode().hasLeftSibling();
}
@Override
public boolean hasRightSibling() {
assertNotClosed();
return getStructuralNode().hasRightSibling();
}
@Override
public boolean moveTo(final long nodeKey) {
assertNotClosed();
if (nodeKey == Fixed.NULL_NODE_KEY.getStandardProperty()) {
return false;
}
// Remember old node and fetch new one.
final Node oldNode = currentNode;
Node newNode;
try {
// Immediately return node from item list if node key negative.
newNode = pageReadOnlyTrx.getRecord(nodeKey, indexType, index);
} catch (final SirixIOException e) {
newNode = null;
}
if (newNode == null) {
currentNode = oldNode;
return false;
} else {
currentNode = newNode;
return true;
}
}
@Override
public boolean moveToDocumentRoot() {
assertNotClosed();
return moveTo(Fixed.DOCUMENT_NODE_KEY.getStandardProperty());
}
@Override
public boolean moveToParent() {
assertNotClosed();
return moveTo(currentNode.getParentKey());
}
@Override
public boolean moveToFirstChild() {
assertNotClosed();
if (currentNode instanceof RBNodeKey) {
final RBNodeKey node = getCurrentNodeAsRBNodeKey();
if (!node.hasLeftChild()) {
return false;
}
final var move = moveTo(node.getLeftChildKey());
@SuppressWarnings("unchecked") final var currentNode = (RBNodeKey) this.currentNode;
node.setLeftChild(currentNode);
currentNode.setParent(node);
return move;
}
return moveTo(((StructNode) currentNode).getFirstChildKey());
}
@Override
public boolean moveToLastChild() {
assertNotClosed();
if (currentNode instanceof RBNodeKey) {
final RBNodeKey node = getCurrentNodeAsRBNodeKey();
if (!node.hasRightChild()) {
return false;
}
final var move = moveTo(node.getRightChildKey());
@SuppressWarnings("unchecked") final var currentNode = (RBNodeKey) this.currentNode;
node.setRightChild(currentNode);
currentNode.setParent(node);
return move;
}
return false;
}
@Override
public boolean moveToPrevious() {
assertNotClosed();
return moveToParent();
}
@Override
public boolean moveToNext() {
assertNotClosed();
if (currentNode instanceof RBNodeKey) {
final RBNodeKey node = (RBNodeKey) currentNode;
if (node.hasLeftChild()) {
moveToFirstChild();
} else if (node.hasRightChild()) {
moveToLastChild();
} else {
do {
moveToParent();
} while (getNode() instanceof RBNodeKey && !hasLastChild());
if (getNode() instanceof RBNodeKey) {
moveToLastChild();
return true;
} else {
return false;
}
}
}
return moveToFirstChild();
}
@Override
public boolean moveToLeftSibling() {
assertNotClosed();
return false;
}
@Override
public boolean moveToRightSibling() {
assertNotClosed();
return false;
}
@Override
public long getNodeKey() {
assertNotClosed();
return currentNode.getNodeKey();
}
@Override
public NodeKind getRightSiblingKind() {
assertNotClosed();
return NodeKind.UNKNOWN;
}
@Override
public NodeKind getLeftSiblingKind() {
assertNotClosed();
return NodeKind.UNKNOWN;
}
@Override
public NodeKind getFirstChildKind() {
assertNotClosed();
final NodeKind firstChildKind;
if (moveToFirstChild()) {
firstChildKind = getNode().getKind();
moveToParent();
} else {
firstChildKind = NodeKind.UNKNOWN;
}
return firstChildKind;
}
@Override
public NodeKind getLastChildKind() {
assertNotClosed();
final NodeKind lastChildKind;
if (moveToLastChild()) {
lastChildKind = getNode().getKind();
moveToParent();
} else {
lastChildKind = NodeKind.UNKNOWN;
}
return lastChildKind;
}
@Override
public NodeKind getParentKind() {
assertNotClosed();
if (hasParent()) {
final long nodeKey = currentNode.getNodeKey();
moveToParent();
final NodeKind parentKind = getKind();
moveTo(nodeKey);
return parentKind;
}
return NodeKind.UNKNOWN;
}
@Override
public NodeKind getKind() {
assertNotClosed();
return currentNode.getKind();
}
@Override
public ImmutableNode getNode() {
assertNotClosed();
return currentNode;
}
@Override
public boolean moveToNextFollowing() {
throw new UnsupportedOperationException();
}
@Override
public long getLeftSiblingKey() {
assertNotClosed();
return Fixed.NULL_NODE_KEY.getStandardProperty();
}
@Override
public long getRightSiblingKey() {
assertNotClosed();
return Fixed.NULL_NODE_KEY.getStandardProperty();
}
@Override
public long getFirstChildKey() {
assertNotClosed();
return ((StructNode) currentNode).getFirstChildKey();
}
@Override
public long getLastChildKey() {
assertNotClosed();
return ((StructNode) currentNode).getLastChildKey();
}
@Override
public long getParentKey() {
return getNode().getParentKey();
}
/**
* Iterator supporting different search modes.
*
* @author Johannes Lichtenberger
*/
public final class RBNodeIterator extends AbstractIterator> {
/**
* Determines if it's the first call.
*/
private boolean first;
/**
* All AVLNode keys which are part of the result sequence.
*/
private final Deque keys;
/**
* Start node key.
*/
private final long key;
/**
* Constructor.
*
* @param nodeKey node key to start from, root node of AVLTree is selected if
* {@code Fixed.DOCUMENT_NODE_KEY.getStandardProperty} is specified.
*/
public RBNodeIterator(final long nodeKey) {
first = true;
keys = new ArrayDeque<>();
checkArgument(nodeKey >= 0, "nodeKey must be >= 0!");
key = nodeKey;
}
@Override
protected RBNodeKey computeNext() {
if (!first) {
if (!keys.isEmpty()) {
// Subsequent results.
moveTo(keys.pop());
final RBNodeKey node = getCurrentNodeAsRBNodeKey();
stackOperation(node);
return node;
}
return endOfData();
}
// First search.
first = false;
boolean moved = moveTo(key);
if (key == Fixed.DOCUMENT_NODE_KEY.getStandardProperty()) {
moved = moveToFirstChild();
}
if (moved) {
final RBNodeKey node = getCurrentNodeAsRBNodeKey();
stackOperation(node);
return node;
}
return endOfData();
}
private void stackOperation(final RBNodeKey node) {
if (node.hasRightChild()) {
moveToLastChild();
final RBNodeKey right = getCurrentNodeAsRBNodeKey();
keys.push(right.getNodeKey());
}
moveTo(node.getNodeKey());
if (node.hasLeftChild()) {
moveToFirstChild();
final RBNodeKey left = getCurrentNodeAsRBNodeKey();
keys.push(left.getNodeKey());
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy