All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.roaringbitmap.art.Node Maven / Gradle / Ivy

Go to download

Roaring bitmaps are compressed bitmaps (also called bitsets) which tend to outperform conventional compressed bitmaps such as WAH or Concise.

There is a newer version: 1.3.0
Show newest version
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