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

org.roaringbitmap.art.Node16 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;
import java.nio.ByteOrder;
import org.roaringbitmap.longlong.LongUtils;

public class Node16 extends Node {

  long firstV = 0L;
  long secondV = 0L;
  Node[] children = new Node[16];

  public Node16(int compressionLength) {
    super(NodeType.NODE16, compressionLength);
  }

  @Override
  public int getChildPos(byte k) {
    byte[] firstBytes = LongUtils.toBDBytes(firstV);
    if (count <= 8) {
      return Node.binarySearch(firstBytes, 0, count, k);
    } else {
      int pos = Node.binarySearch(firstBytes, 0, 8, k);
      if (pos != ILLEGAL_IDX) {
        return pos;
      } else {
        byte[] secondBytes = LongUtils.toBDBytes(secondV);
        pos = Node.binarySearch(secondBytes, 0, (count - 8), k);
        if (pos != ILLEGAL_IDX) {
          return 8 + pos;
        } else {
          return ILLEGAL_IDX;
        }
      }
    }
  }

  @Override
  public SearchResult getNearestChildPos(byte k) {
    byte[] firstBytes = LongUtils.toBDBytes(firstV);
    if (count <= 8) {
      return Node.binarySearchWithResult(firstBytes, 0, count, k);
    } else {
      SearchResult firstResult = Node.binarySearchWithResult(
              firstBytes, 0, 8, k);
      // given the values are "in order" if we found a match or a value larger than
      // the target we are done.
      if (firstResult.outcome == SearchResult.Outcome.FOUND
              || firstResult.hasNextLargerPos()) {
        return firstResult;
      } else {
        byte[] secondBytes = LongUtils.toBDBytes(secondV);
        SearchResult secondResult = Node.binarySearchWithResult(
                secondBytes, 0, (count - 8), k);

        switch(secondResult.outcome) {
          case FOUND:
            return SearchResult.found(8 + secondResult.getKeyPos());
          case NOT_FOUND:
            int lowPos = secondResult.getNextSmallerPos();
            int highPos = secondResult.getNextLargerPos();
            // don't map -1 into the legal range by adding 8!
            if (lowPos>=0){ lowPos += 8;}
            if (highPos>=0){ highPos += 8;}

            if(firstResult.hasNextLargerPos() == false && secondResult.hasNextSmallerPos() == false)
            {
              // this happens when the result is in the gap of the two ranges, the correct
              // "smaller value" is that of first result.
              lowPos = firstResult.getNextSmallerPos();
            }

            return SearchResult.notFound( lowPos, highPos);

          default:
            throw new IllegalStateException("There only two possible search outcomes");
        }
      }
    }
  }

  @Override
  public byte getChildKey(int pos) {
    int posInLong;
    if (pos <= 7) {
      posInLong = pos;
      byte[] firstBytes = LongUtils.toBDBytes(firstV);
      return firstBytes[posInLong];
    } else {
      posInLong = pos - 8;
      byte[] secondBytes = LongUtils.toBDBytes(secondV);
      return secondBytes[posInLong];
    }
  }

  @Override
  public Node getChild(int pos) {
    return children[pos];
  }

  @Override
  public void replaceNode(int pos, Node freshOne) {
    children[pos] = freshOne;
  }

  @Override
  public int getMinPos() {
    return 0;
  }

  @Override
  public int getNextLargerPos(int pos) {
    if (pos == ILLEGAL_IDX) {
      return 0;
    }
    pos++;
    return pos < count ? pos : ILLEGAL_IDX;
  }

  @Override
  public int getMaxPos() {
    return count - 1;
  }

  @Override
  public int getNextSmallerPos(int pos) {
    if (pos == ILLEGAL_IDX) {
      return count - 1;
    }
    pos--;
    return pos >= 0 ? pos : ILLEGAL_IDX;
  }

  /**
   * insert a child into the node with the key byte
   *
   * @param node the node16 to insert into
   * @param child the child node to be inserted
   * @param key the key byte
   * @return the adaptive changed node of the parent node16
   */
  public static Node insert(Node node, Node child, byte key) {
    Node16 currentNode16 = (Node16) node;
    if (currentNode16.count < 8) {
      //first
      byte[] bytes = LongUtils.toBDBytes(currentNode16.firstV);
      bytes[currentNode16.count] = key;
      currentNode16.children[currentNode16.count] = child;
      sortSmallByteArray(bytes, currentNode16.children, 0, currentNode16.count);
      currentNode16.count++;
      currentNode16.firstV = LongUtils.fromBDBytes(bytes);
      return currentNode16;
    } else if (currentNode16.count < 16) {
      //second
      ByteBuffer byteBuffer = ByteBuffer.allocate(16).order(ByteOrder.BIG_ENDIAN);
      byteBuffer.putLong(currentNode16.firstV);
      byteBuffer.putLong(currentNode16.secondV);
      byteBuffer.put(currentNode16.count, key);
      currentNode16.children[currentNode16.count] = child;
      sortSmallByteArray(byteBuffer.array(), currentNode16.children, 0, currentNode16.count);
      currentNode16.count++;
      currentNode16.firstV = byteBuffer.getLong(0);
      currentNode16.secondV = byteBuffer.getLong(8);
      return currentNode16;
    } else {
      Node48 node48 = new Node48(currentNode16.prefixLength);
      for (int i = 0; i < 8; i++) {
        int unsignedIdx = Byte.toUnsignedInt((byte) (currentNode16.firstV >>> ((7 - i) << 3)));
        //i won't be beyond 48
        Node48.setOneByte(unsignedIdx, (byte) i, node48.childIndex);
        node48.children[i] = currentNode16.children[i];
      }
      byte[] secondBytes = LongUtils.toBDBytes(currentNode16.secondV);
      for (int i = 8; i < currentNode16.count; i++) {
        byte v = secondBytes[i - 8];
        int unsignedIdx = Byte.toUnsignedInt(v);
        //i won't be beyond 48
        Node48.setOneByte(unsignedIdx, (byte) i, node48.childIndex);
        node48.children[i] = currentNode16.children[i];
      }
      copyPrefix(currentNode16, node48);
      node48.count = currentNode16.count;
      Node freshOne = Node48.insert(node48, child, key);
      return freshOne;
    }
  }

  @Override
  public Node remove(int pos) {
    children[pos] = null;
    ByteBuffer byteBuffer = ByteBuffer.allocate(16).order(ByteOrder.BIG_ENDIAN);
    byte[] bytes = byteBuffer.putLong(firstV).putLong(secondV).array();
    System.arraycopy(bytes, pos + 1, bytes, pos, (16 - pos - 1));
    System.arraycopy(children, pos + 1, children, pos, (16 - pos - 1));
    firstV = byteBuffer.getLong(0);
    secondV = byteBuffer.getLong(8);
    count--;
    if (count <= 3) {
      //shrink to node4
      Node4 node4 = new Node4(prefixLength);
      //copy the keys
      node4.key = (int) (firstV >> 32);
      System.arraycopy(children, 0, node4.children, 0, count);
      node4.count = count;
      copyPrefix(this, node4);
      return node4;
    }
    return this;
  }

  @Override
  public void serializeNodeBody(DataOutput dataOutput) throws IOException {
    //little endian
    dataOutput.writeLong(Long.reverseBytes(firstV));
    dataOutput.writeLong(Long.reverseBytes(secondV));
  }

  @Override
  public void serializeNodeBody(ByteBuffer byteBuffer) throws IOException {
    byteBuffer.putLong(firstV);
    byteBuffer.putLong(secondV);
  }

  @Override
  public void deserializeNodeBody(DataInput dataInput) throws IOException {
    firstV = Long.reverseBytes(dataInput.readLong());
    secondV = Long.reverseBytes(dataInput.readLong());
  }

  @Override
  public void deserializeNodeBody(ByteBuffer byteBuffer) throws IOException {
    this.firstV = byteBuffer.getLong();
    this.secondV = byteBuffer.getLong();
  }

  @Override
  public int serializeNodeBodySizeInBytes() {
    return 16;
  }

  @Override
  public void replaceChildren(Node[] children) {
    System.arraycopy(children, 0, this.children, 0, count);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy