org.roaringbitmap.art.Node Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of RoaringBitmap Show documentation
Show all versions of RoaringBitmap Show documentation
Roaring bitmaps are compressed bitmaps (also called bitsets) which tend to outperform
conventional compressed bitmaps such as WAH or Concise.
package org.roaringbitmap.art;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.nio.ByteBuffer;
public abstract class Node {
//node type
protected NodeType nodeType;
//length of compressed path(prefix)
protected byte prefixLength;
//the compressed path path (prefix)
protected byte[] prefix;
//number of non-null children, the largest value will not beyond 255
//to benefit calculation,we keep the value as a short type
protected short count;
public static final int ILLEGAL_IDX = -1;
/**
* constructor
*
* @param nodeType the node type
* @param compressedPrefixSize the prefix byte array size,less than or equal to 6
*/
public Node(NodeType nodeType, int compressedPrefixSize) {
this.nodeType = nodeType;
this.prefixLength = (byte) compressedPrefixSize;
prefix = new byte[prefixLength];
count = 0;
}
/**
* sort the small arrays through the insertion sort alg.
*/
protected static byte[] sortSmallByteArray(byte[] key, Node[] children, int left, int right) {//x
for (int i = left, j = i; i < right; j = ++i) {
byte ai = key[i + 1];
Node child = children[i + 1];
int unsignedByteAi = Byte.toUnsignedInt(ai);
while (unsignedByteAi < Byte.toUnsignedInt(key[j])) {
key[j + 1] = key[j];
children[j + 1] = children[j];
if (j-- == left) {
break;
}
}
key[j + 1] = ai;
children[j + 1] = child;
}
return key;
}
/**
* get the position of a child corresponding to the input key 'k'
* @param k a key value of the byte range
* @return the child position corresponding to the key 'k'
*/
public abstract int getChildPos(byte k);
/**
* get the position of a child corresponding to the input key 'k' if present
*
* if 'k' is not in the child, return the positions of the neighbouring nodes instead
*
* @param key a key value of the byte range
* @return a result indicating whether or not the key was found and the positions of the
* child corresponding to it or its neighbours
*/
public abstract SearchResult getNearestChildPos(byte key);
/**
* get the corresponding key byte of the requested position
* @param pos the position
* @return the corresponding key byte
*/
public abstract byte getChildKey(int pos);
/**
* get the child at the specified position in the node, the 'pos' range from 0 to count
* @param pos the position
* @return a Node corresponding to the input position
*/
public abstract Node getChild(int pos);
/**
* replace the position child to the fresh one
* @param pos the position
* @param freshOne the fresh node to replace the old one
*/
public abstract void replaceNode(int pos, Node freshOne);
/**
* get the position of the min element in current node.
* @return the minimum key's position
*/
public abstract int getMinPos();
/**
* get the next position in the node
*
* @param pos current position,-1 to start from the min one
* @return the next larger byte key's position which is close to 'pos' position,-1 for end
*/
public abstract int getNextLargerPos(int pos);
/**
* get the max child's position
* @return the max byte key's position
*/
public abstract int getMaxPos();
/**
* get the next smaller element's position
* @param pos the position,-1 to start from the largest one
* @return the next smaller key's position which is close to input 'pos' position,-1 for end
*/
public abstract int getNextSmallerPos(int pos);
/**
* remove the specified position child
* @param pos the position to remove
* @return an adaptive changed fresh node of the current node
*/
public abstract Node remove(int pos);
/**
* serialize
*
* @param dataOutput the DataOutput
* @throws IOException signal a exception happened while the serialization
*/
public void serialize(DataOutput dataOutput) throws IOException {
serializeHeader(dataOutput);
serializeNodeBody(dataOutput);
}
/**
* serialize
*
* @param byteBuffer the ByteBuffer
* @throws IOException signal a exception happened while the serialization
*/
public void serialize(ByteBuffer byteBuffer) throws IOException {
serializeHeader(byteBuffer);
serializeNodeBody(byteBuffer);
}
/**
* the serialized size in bytes of this node
*
* @return the size in bytes
*/
public int serializeSizeInBytes() {
int size = 0;
size += serializeHeaderSizeInBytes();
size += serializeNodeBodySizeInBytes();
return size;
}
/**
* deserialize into a typed node from the byte stream
*
* @param dataInput the input byte stream
* @return the typed node
* @throws IOException indicate a exception happened
*/
public static Node deserialize(DataInput dataInput) throws IOException {
Node node = deserializeHeader(dataInput);
if (node != null) {
node.deserializeNodeBody(dataInput);
return node;
}
return null;
}
/**
* deserialize into a typed node
* @param byteBuffer the ByteBuffer
* @return the typed node
* @throws IOException indicate a exception happened
*/
public static Node deserialize(ByteBuffer byteBuffer) throws IOException {
Node node = deserializeHeader(byteBuffer);
if (node != null) {
node.deserializeNodeBody(byteBuffer);
return node;
}
return null;
}
/**
* replace the node's children according to the given children parameter while doing the
* deserialization phase.
* @param children all the not null children nodes in key byte ascending order,no null element
*/
abstract void replaceChildren(Node[] children);
/**
* serialize the node's body content
* @param dataOutput the DataOutput
* @throws IOException exception indicates serialization errors
*/
abstract void serializeNodeBody(DataOutput dataOutput) throws IOException;
/**
* serialize the node's body content
* @param byteBuffer the ByteBuffer
* @throws IOException exception indicates serialization errors
*/
abstract void serializeNodeBody(ByteBuffer byteBuffer) throws IOException;
/**
* deserialize the node's body content
* @param dataInput the DataInput
* @throws IOException exception indicates deserialization errors
*/
abstract void deserializeNodeBody(DataInput dataInput) throws IOException;
/**
* deserialize the node's body content
* @param byteBuffer the ByteBuffer
* @throws IOException exception indicates deserialization errors
*/
abstract void deserializeNodeBody(ByteBuffer byteBuffer) throws IOException;
/**
* the serialized size except the common node header part
*
* @return the size in bytes
*/
public abstract int serializeNodeBodySizeInBytes();
/**
* insert the LeafNode as a child of the current internal node
*
* @param current current internal node
* @param childNode the leaf node
* @param key the key byte reference to the child leaf node
* @return an adaptive changed node of the input 'current' node
*/
public static Node insertLeaf(Node current, LeafNode childNode, byte key) {
switch (current.nodeType) {
case NODE4:
return Node4.insert(current, childNode, key);
case NODE16:
return Node16.insert(current, childNode, key);
case NODE48:
return Node48.insert(current, childNode, key);
case NODE256:
return Node256.insert(current, childNode, key);
default:
throw new IllegalArgumentException("Not supported node type!");
}
}
/**
* copy the prefix between two nodes
* @param src the source node
* @param dst the destination node
*/
public static void copyPrefix(Node src, Node dst) {
dst.prefixLength = src.prefixLength;
System.arraycopy(src.prefix, 0, dst.prefix, 0, src.prefixLength);
}
/**
* search the position of the input byte key in the node's key byte array part
*
* @param key the input key byte array
* @param fromIndex inclusive
* @param toIndex exclusive
* @param k the target key byte value
* @return the array offset of the target input key 'k' or -1 to not found
*/
public static int binarySearch(byte[] key, int fromIndex, int toIndex,
byte k) {
int inputUnsignedByte = Byte.toUnsignedInt(k);
int low = fromIndex;
int high = toIndex - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
int midVal = Byte.toUnsignedInt(key[mid]);
if (midVal < inputUnsignedByte) {
low = mid + 1;
} else if (midVal > inputUnsignedByte) {
high = mid - 1;
} else {
return mid; // key found
}
}
// key not found.
return ILLEGAL_IDX;
}
static SearchResult binarySearchWithResult(byte[] key, int fromIndex, int toIndex,
byte k) {
int inputUnsignedByte = Byte.toUnsignedInt(k);
int low = fromIndex;
int high = toIndex - 1;
while (low != high) {
int mid = (low + high + 1) >>> 1; // ceil
int midVal = Byte.toUnsignedInt(key[mid]);
if (midVal > inputUnsignedByte) {
high = mid - 1;
} else {
low = mid;
}
}
int val = Byte.toUnsignedInt(key[low]);
if (val == inputUnsignedByte) {
return SearchResult.found(low);
} else if (val < inputUnsignedByte) {
int highIndex = low + 1;
return SearchResult.notFound(low, highIndex < toIndex ? highIndex : Node.ILLEGAL_IDX);
} else {
return SearchResult.notFound(low - 1, low); // low - 1 == ILLEGAL_IDX if low == 0
}
}
private void serializeHeader(DataOutput dataOutput) throws IOException {
//first byte: node type
dataOutput.writeByte((byte) this.nodeType.ordinal());
//non null object count
dataOutput.writeShort(Short.reverseBytes(this.count));
dataOutput.writeByte(this.prefixLength);
if (prefixLength > 0) {
dataOutput.write(this.prefix, 0, this.prefixLength);
}
}
private void serializeHeader(ByteBuffer byteBuffer) throws IOException {
byteBuffer.put((byte) this.nodeType.ordinal());
byteBuffer.putShort(this.count);
byteBuffer.put(this.prefixLength);
if (prefixLength > 0) {
byteBuffer.put(this.prefix, 0, prefixLength);
}
}
private int serializeHeaderSizeInBytes() {
int size = 1 + 2 + 1;
if (prefixLength > 0) {
size = size + prefixLength;
}
return size;
}
private static Node deserializeHeader(DataInput dataInput) throws IOException {
int nodeTypeOrdinal = dataInput.readByte();
short count = Short.reverseBytes(dataInput.readShort());
byte prefixLength = dataInput.readByte();
byte[] prefix = new byte[0];
if (prefixLength > 0) {
prefix = new byte[prefixLength];
dataInput.readFully(prefix);
}
if (nodeTypeOrdinal == NodeType.NODE4.ordinal()) {
Node4 node4 = new Node4(prefixLength);
node4.prefixLength = prefixLength;
node4.prefix = prefix;
node4.count = count;
return node4;
}
if (nodeTypeOrdinal == NodeType.NODE16.ordinal()) {
Node16 node16 = new Node16(prefixLength);
node16.prefixLength = prefixLength;
node16.prefix = prefix;
node16.count = count;
return node16;
}
if (nodeTypeOrdinal == NodeType.NODE48.ordinal()) {
Node48 node48 = new Node48(prefixLength);
node48.prefixLength = prefixLength;
node48.prefix = prefix;
node48.count = count;
return node48;
}
if (nodeTypeOrdinal == NodeType.NODE256.ordinal()) {
Node256 node256 = new Node256(prefixLength);
node256.prefixLength = prefixLength;
node256.prefix = prefix;
node256.count = count;
return node256;
}
if (nodeTypeOrdinal == NodeType.LEAF_NODE.ordinal()) {
LeafNode leafNode = new LeafNode(0L, 0);
leafNode.prefixLength = prefixLength;
leafNode.prefix = prefix;
leafNode.count = count;
return leafNode;
}
return null;
}
private static Node deserializeHeader(ByteBuffer byteBuffer) throws IOException {
int nodeTypeOrdinal = byteBuffer.get();
short count = byteBuffer.getShort();
byte prefixLength = byteBuffer.get();
byte[] prefix = new byte[0];
if (prefixLength > 0) {
prefix = new byte[prefixLength];
byteBuffer.get(prefix);
}
if (nodeTypeOrdinal == NodeType.NODE4.ordinal()) {
Node4 node4 = new Node4(prefixLength);
node4.prefixLength = prefixLength;
node4.prefix = prefix;
node4.count = count;
return node4;
}
if (nodeTypeOrdinal == NodeType.NODE16.ordinal()) {
Node16 node16 = new Node16(prefixLength);
node16.prefixLength = prefixLength;
node16.prefix = prefix;
node16.count = count;
return node16;
}
if (nodeTypeOrdinal == NodeType.NODE48.ordinal()) {
Node48 node48 = new Node48(prefixLength);
node48.prefixLength = prefixLength;
node48.prefix = prefix;
node48.count = count;
return node48;
}
if (nodeTypeOrdinal == NodeType.NODE256.ordinal()) {
Node256 node256 = new Node256(prefixLength);
node256.prefixLength = prefixLength;
node256.prefix = prefix;
node256.count = count;
return node256;
}
if (nodeTypeOrdinal == NodeType.LEAF_NODE.ordinal()) {
LeafNode leafNode = new LeafNode(0L, 0);
leafNode.prefixLength = prefixLength;
leafNode.prefix = prefix;
leafNode.count = count;
return leafNode;
}
return null;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy