LeafNode.java

package org.roaringbitmap.art;

import org.roaringbitmap.longlong.LongUtils;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class LeafNode extends Node {

  // key are saved as the lazy expanding logic,here we only care about the
  // high 48 bit data,so only the high 48 bit is valuable
  // - the top 32 bits of these 48
  private int keyHigh;
  // - the lower 16 bits of these 48. we use a char as its unsigned 16 bits
  private char keyLow;
  long containerIdx;
  public static final int LEAF_NODE_KEY_LENGTH_IN_BYTES = 6;

  /**
   * constructor
   *
   * @param key the 48 bit
   * @param containerIdx the corresponding container index
   */
  public LeafNode(byte[] key, long containerIdx) {
    super();
    setKeyFromShifted(LongUtils.fromKey(key));
    this.containerIdx = containerIdx;
  }

  /**
   * constructor
   * @param key a long value,only the high 48 bit is valuable
   * @param containerIdx the corresponding container index
   */
  public LeafNode(long key, long containerIdx) {
    super();
    setKeyFromShifted(key);
    this.containerIdx = containerIdx;
  }

  @Override
  public void serializeNodeBody(DataOutput dataOutput) throws IOException {
    dataOutput.writeInt(keyHigh);
    dataOutput.writeShort(keyLow);
    dataOutput.writeLong(Long.reverseBytes(containerIdx));
  }

  @Override
  public void serializeNodeBody(ByteBuffer byteBuffer) throws IOException {
    if (byteBuffer.order() == ByteOrder.BIG_ENDIAN) {
      byteBuffer.putInt(keyHigh);
      byteBuffer.putChar(keyLow);
    } else {
      byteBuffer.putInt(Integer.reverseBytes(keyHigh));
      byteBuffer.putChar(Character.reverseBytes(keyLow));
    }
    byteBuffer.putLong(containerIdx);
  }

  @Override
  public void deserializeNodeBody(DataInput dataInput) throws IOException {
    keyHigh = dataInput.readInt();
    keyLow = dataInput.readChar();
    this.containerIdx = Long.reverseBytes(dataInput.readLong());
  }

  @Override
  public void deserializeNodeBody(ByteBuffer byteBuffer) throws IOException {
    if (byteBuffer.order() == ByteOrder.BIG_ENDIAN) {
      keyHigh = byteBuffer.getInt();
      keyLow = byteBuffer.getChar();
    } else {
      keyHigh = Integer.reverseBytes(byteBuffer.getInt());
      keyLow = Character.reverseBytes(byteBuffer.getChar());
    }
    this.containerIdx = byteBuffer.getLong();
  }

  @Override
  public int serializeNodeBodySizeInBytes() {
    return LEAF_NODE_KEY_LENGTH_IN_BYTES + 8;
  }

  public long getContainerIdx() {
    return containerIdx;
  }

  public byte[] getKeyBytes() {
    return LongUtils.highPart(getKey() << 16);
  }

  public long getKey() {
    return (((long) keyHigh) & 0xFFFFFFFFL) << 16 | (((long)keyLow) & 0xFFFFL);
  }

    /**
     * Sets the key from a long value, only the high 48 bits are used.
     *
     * @param key the long value representing the key
     */
  private void setKeyFromShifted(long key) {
    this.keyHigh = (int) (key >> 32);
    this.keyLow = (char) (key >> 16);
  }

  @Override
  protected void serializeHeader(DataOutput dataOutput) throws IOException {
    // first byte: node type
    dataOutput.writeByte((byte) NodeType.LEAF_NODE.ordinal());
    // non null object count
    dataOutput.writeShort(0);
    dataOutput.writeByte(0);
  }

  @Override
  protected void serializeHeader(ByteBuffer byteBuffer) throws IOException {
    byteBuffer.put((byte) NodeType.LEAF_NODE.ordinal());
    byteBuffer.putShort((short)0);
    byteBuffer.put((byte)0);
  }

  @Override
  public String toString() {
    return "LeafNode{" +
            "key=" + Long.toHexString(getKey()) +
            ", containerIdx=" + containerIdx +
            '}';
  }

}