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

org.roaringbitmap.buffer.BufferBitSetUtil 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.buffer;


import org.roaringbitmap.BitSetUtil;
import org.roaringbitmap.IntIterator;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.CharBuffer;
import java.nio.LongBuffer;
import java.util.BitSet;


/***
 *
 * This class provides convenience functions to manipulate BitSet and MutableRoaringBitmap objects.
 *
 */
public class BufferBitSetUtil {
  // todo: add a method to convert an ImmutableRoaringBitmap to a BitSet using BitSet.valueOf

  // a block consists has a maximum of 1024 words, each representing 64 bits,
  // thus representing at maximum 65536 bits
  static final private int BLOCK_LENGTH = MappeableBitmapContainer.MAX_CAPACITY / Long.SIZE; //
  // 64-bit
  // word

  private static MappeableArrayContainer arrayContainerOf(final int from, final int to,
      final int cardinality, final long[] words) {
    char[] content = BitSetUtil.arrayContainerBufferOf(from, to, cardinality, words);
    return new MappeableArrayContainer(CharBuffer.wrap(content), cardinality);
  }


  /**
   * Generate a MutableRoaringBitmap out of a BitSet
   *
   * @param bitSet original bitset (will not be modified)
   * @return roaring bitmap equivalent to BitSet
   */
  public static MutableRoaringBitmap bitmapOf(final BitSet bitSet) {
    return bitmapOf(bitSet.toLongArray());
  }

  /**
   * Generate a MutableRoaringBitmap out of a long[], each long using little-endian representation
   * of its bits
   *
   * @see BitSet#toLongArray() for an equivalent
   * @param words array of longs (will not be modified)
   * @return roaring bitmap
   */
  public static MutableRoaringBitmap bitmapOf(final long[] words) {
    // split long[] into blocks.
    // each block becomes a single container, if any bit is set
    final MutableRoaringBitmap ans = new MutableRoaringBitmap();
    int containerIndex = 0;
    for (int from = 0; from < words.length; from += BLOCK_LENGTH) {
      final int to = Math.min(from + BLOCK_LENGTH, words.length);
      final int blockCardinality = cardinality(from, to, words);
      if (blockCardinality > 0) {
        ((MutableRoaringArray) ans.highLowContainer).insertNewKeyValueAt(containerIndex++,
            BufferUtil.highbits(from * Long.SIZE),
            BufferBitSetUtil.containerOf(from, to, blockCardinality, words));
      }
    }
    return ans;
  }

  /**
   * Efficiently generate a RoaringBitmap from an uncompressed byte array or ByteBuffer
   * This method tries to minimise all kinds of memory allocation
   *
   * @param bb the uncompressed bitmap
   * @return roaring bitmap
   */
  public static MutableRoaringBitmap bitmapOf(ByteBuffer bb) {
    return bitmapOf(bb, new long[BLOCK_LENGTH]);
  }

  /**
   * Efficiently generate a RoaringBitmap from an uncompressed byte array or ByteBuffer
   * This method tries to minimise all kinds of memory allocation
   * 
* You can provide a cached wordsBuffer for avoiding 8 KB of extra allocation on every call * No reference is kept to the wordsBuffer, so it can be cached as a ThreadLocal * * @param bb the uncompressed bitmap * @param wordsBuffer buffer of length {@link BitSetUtil#BLOCK_LENGTH} * @return roaring bitmap */ public static MutableRoaringBitmap bitmapOf(ByteBuffer bb, long[] wordsBuffer) { if (wordsBuffer.length != BLOCK_LENGTH) { throw new IllegalArgumentException("wordsBuffer length should be " + BLOCK_LENGTH); } bb = bb.slice().order(ByteOrder.LITTLE_ENDIAN); final MutableRoaringBitmap ans = new MutableRoaringBitmap(); // split buffer into blocks of long[] int containerIndex = 0; int blockLength = 0, blockCardinality = 0, offset = 0; long word; while (bb.remaining() >= 8) { word = bb.getLong(); // Add read long to block wordsBuffer[blockLength++] = word; blockCardinality += Long.bitCount(word); // When block is full, add block to bitmap if (blockLength == BLOCK_LENGTH) { // Each block becomes a single container, if any bit is set if (blockCardinality > 0) { ((MutableRoaringArray) ans.highLowContainer).insertNewKeyValueAt(containerIndex++, BufferUtil.highbits(offset), BufferBitSetUtil.containerOf(0, blockLength, blockCardinality, wordsBuffer)); } /* Offset can overflow when bitsets size is more than Integer.MAX_VALUE - 64 It's harmless though, as it will happen after the last block is added */ offset += (BLOCK_LENGTH * Long.SIZE); blockLength = blockCardinality = 0; } } if (bb.remaining() > 0) { // Read remaining (less than 8) bytes // We can do this in while loop also, it will probably slow things down a bit though word = 0; for (int remaining = bb.remaining(), j = 0; j < remaining; j++) { word |= (bb.get() & 0xffL) << (8 * j); } // Add last word to block, only if any bit is set if (word != 0) { wordsBuffer[blockLength++] = word; blockCardinality += Long.bitCount(word); } } // Add block to map, if any bit is set if (blockCardinality > 0) { ((MutableRoaringArray) ans.highLowContainer).insertNewKeyValueAt(containerIndex, BufferUtil.highbits(offset), BufferBitSetUtil.containerOf(0, blockLength, blockCardinality, wordsBuffer)); } return ans; } private static int cardinality(final int from, final int to, final long[] words) { int sum = 0; for (int i = from; i < to; i++) { sum += Long.bitCount(words[i]); } return sum; } private static MappeableContainer containerOf(final int from, final int to, final int blockCardinality, final long[] words) { // find the best container available if (blockCardinality <= MappeableArrayContainer.DEFAULT_MAX_SIZE) { // containers with DEFAULT_MAX_SIZE or less integers should be // ArrayContainers return arrayContainerOf(from, to, blockCardinality, words); } else { // otherwise use bitmap container long[] container = new long[BLOCK_LENGTH]; System.arraycopy(words, from, container, 0, to - from); return new MappeableBitmapContainer(LongBuffer.wrap(container), blockCardinality); } } /** * Compares a RoaringBitmap and a BitSet. They are equal if and only if they contain the same set * of integers. * * @param bitset first object to be compared * @param bitmap second object to be compared * @return whether they are equal */ public static boolean equals(final BitSet bitset, final ImmutableRoaringBitmap bitmap) { if (bitset.cardinality() != bitmap.getCardinality()) { return false; } final IntIterator it = bitmap.getIntIterator(); while (it.hasNext()) { int val = it.next(); if (!bitset.get(val)) { return false; } } return true; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy