uk.co.openkappa.roaringbitmap.buffer.MappeableArrayContainer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bitrules Show documentation
Show all versions of bitrules Show documentation
Rule based classification with explicit support for ambiguity resolution
/*
* (c) the authors Licensed under the Apache License, Version 2.0.
*/
package org.roaringbitmap.buffer;
import org.roaringbitmap.*;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ShortBuffer;
import java.util.Arrays;
import java.util.Iterator;
import static org.roaringbitmap.buffer.BufferUtil.toIntUnsigned;
/**
* Simple container made of an array of 16-bit integers. Unlike org.roaringbitmap.ArrayContainer,
* this class uses a ShortBuffer to store data.
*/
public final class MappeableArrayContainer extends MappeableContainer implements Cloneable {
private static final int DEFAULT_INIT_SIZE = 4;
private static final int ARRAY_LAZY_LOWERBOUND = 1024;
protected static final int DEFAULT_MAX_SIZE = 4096; // containers with DEFAULT_MAX_SZE or less
// integers should be ArrayContainers
private static final long serialVersionUID = 1L;
protected static int getArraySizeInBytes(int cardinality) {
return cardinality * 2;
}
protected static int serializedSizeInBytes(int cardinality) {
return cardinality * 2 + 2;
}
protected int cardinality = 0;
protected ShortBuffer content;
/**
* Create an array container with default capacity
*/
public MappeableArrayContainer() {
this(DEFAULT_INIT_SIZE);
}
public static MappeableArrayContainer empty() {
return new MappeableArrayContainer();
}
/**
* Creates a new container from a non-mappeable one. This copies the data.
*
* @param bc the original container
*/
public MappeableArrayContainer(ArrayContainer bc) {
this.cardinality = bc.getCardinality();
this.content = bc.toShortBuffer();
}
/**
* Create an array container with specified capacity
*
* @param capacity The capacity of the container
*/
public MappeableArrayContainer(final int capacity) {
content = ShortBuffer.allocate(capacity);
}
/**
* Create an array container with a run of ones from firstOfRun to lastOfRun, exclusive. Caller is
* responsible for making sure the range is small enough that ArrayContainer is appropriate.
*
* @param firstOfRun first index
* @param lastOfRun last index (range is exclusive)
*/
public MappeableArrayContainer(final int firstOfRun, final int lastOfRun) {
// TODO: this can be optimized for performance
final int valuesInRange = lastOfRun - firstOfRun;
content = ShortBuffer.allocate(valuesInRange);
short[] sarray = content.array();
for (int i = 0; i < valuesInRange; ++i) {
sarray[i] = (short) (firstOfRun + i);
}
cardinality = valuesInRange;
}
private MappeableArrayContainer(int newCard, ShortBuffer newContent) {
this.cardinality = newCard;
ShortBuffer tmp = newContent.duplicate();// for thread-safety
this.content = ShortBuffer.allocate(Math.max(newCard, tmp.limit()));
tmp.rewind();
this.content.put(tmp);
}
/**
* Construct a new ArrayContainer backed by the provided ShortBuffer. Note that if you modify the
* ArrayContainer a new ShortBuffer may be produced.
*
* @param array ShortBuffer where the data is stored
* @param cardinality cardinality (number of values stored)
*/
public MappeableArrayContainer(final ShortBuffer array, final int cardinality) {
if (array.limit() != cardinality) {
throw new RuntimeException("Mismatch between buffer and cardinality");
}
this.cardinality = cardinality;
this.content = array;
}
@Override
public MappeableContainer add(int begin, int end) {
// TODO: may need to convert to a RunContainer
if (end == begin) {
return clone();
}
if ((begin > end) || (end > (1 << 16))) {
throw new IllegalArgumentException("Invalid range [" + begin + "," + end + ")");
}
int indexstart = BufferUtil.unsignedBinarySearch(content, 0, cardinality, (short) begin);
if (indexstart < 0) {
indexstart = -indexstart - 1;
}
int indexend = BufferUtil.unsignedBinarySearch(content, 0, cardinality, (short) (end - 1));
if (indexend < 0) {
indexend = -indexend - 1;
} else {
indexend++;
}
int rangelength = end - begin;
int newcardinality = indexstart + (cardinality - indexend) + rangelength;
if (newcardinality > DEFAULT_MAX_SIZE) {
MappeableBitmapContainer a = this.toBitmapContainer();
return a.iadd(begin, end);
}
MappeableArrayContainer answer = new MappeableArrayContainer(newcardinality, content);
if (!BufferUtil.isBackedBySimpleArray(answer.content)) {
throw new RuntimeException("Should not happen. Internal bug.");
}
BufferUtil.arraycopy(content, indexend, answer.content, indexstart + rangelength,
cardinality - indexend);
short[] answerarray = answer.content.array();
for (int k = 0; k < rangelength; ++k) {
answerarray[k + indexstart] = (short) (begin + k);
}
answer.cardinality = newcardinality;
return answer;
}
/**
* running time is in O(n) time if insert is not in order.
*/
@Override
// not thread-safe
public MappeableContainer add(final short x) {
if (BufferUtil.isBackedBySimpleArray(this.content)) {
short[] sarray = content.array();
int loc = Util.unsignedBinarySearch(sarray, 0, cardinality, x);
if (loc < 0) {
// Transform the ArrayContainer to a BitmapContainer
// when cardinality exceeds DEFAULT_MAX_SIZE
if (cardinality >= DEFAULT_MAX_SIZE) {
final MappeableBitmapContainer a = this.toBitmapContainer();
a.add(x);
return a;
}
if (cardinality >= sarray.length) {
increaseCapacity();
sarray = content.array();
}
// insertion : shift the elements > x by one
// position to
// the right
// and put x in it's appropriate place
System.arraycopy(sarray, -loc - 1, sarray, -loc, cardinality + loc + 1);
sarray[-loc - 1] = x;
++cardinality;
}
} else {
final int loc = BufferUtil.unsignedBinarySearch(content, 0, cardinality, x);
if (loc < 0) {
// Transform the ArrayContainer to a BitmapContainer
// when cardinality exceeds DEFAULT_MAX_SIZE
if (cardinality >= DEFAULT_MAX_SIZE) {
final MappeableBitmapContainer a = this.toBitmapContainer();
a.add(x);
return a;
}
if (cardinality >= this.content.limit()) {
increaseCapacity();
}
// insertion : shift the elements > x by one
// position to
// the right
// and put x in it's appropriate place
for (int k = cardinality; k > -loc - 1; --k) {
content.put(k, content.get(k - 1));
}
content.put(-loc - 1, x);
++cardinality;
}
}
return this;
}
@Override
public boolean isEmpty() {
return cardinality == 0;
}
private int advance(ShortIterator it) {
if (it.hasNext()) {
return toIntUnsigned(it.next());
} else {
return -1;
}
}
@Override
public MappeableArrayContainer and(final MappeableArrayContainer value2) {
MappeableArrayContainer value1 = this;
final int desiredCapacity = Math.min(value1.getCardinality(), value2.getCardinality());
MappeableArrayContainer answer = new MappeableArrayContainer(desiredCapacity);
if (BufferUtil.isBackedBySimpleArray(this.content)
&& BufferUtil.isBackedBySimpleArray(value2.content)) {
answer.cardinality = org.roaringbitmap.Util.unsignedIntersect2by2(value1.content.array(),
value1.getCardinality(), value2.content.array(), value2.getCardinality(),
answer.content.array());
} else {
answer.cardinality = BufferUtil.unsignedIntersect2by2(value1.content, value1.getCardinality(),
value2.content, value2.getCardinality(), answer.content.array());
}
return answer;
}
@Override
public MappeableContainer and(MappeableBitmapContainer x) {
return x.and(this);
}
@Override
public MappeableContainer and(final MappeableRunContainer value2) {
return value2.and(this);
}
@Override
public MappeableArrayContainer andNot(final MappeableArrayContainer value2) {
final MappeableArrayContainer value1 = this;
final int desiredCapacity = value1.getCardinality();
final MappeableArrayContainer answer = new MappeableArrayContainer(desiredCapacity);
if (BufferUtil.isBackedBySimpleArray(value1.content)
&& BufferUtil.isBackedBySimpleArray(value2.content)) {
answer.cardinality =
org.roaringbitmap.Util.unsignedDifference(value1.content.array(), value1.getCardinality(),
value2.content.array(), value2.getCardinality(), answer.content.array());
} else {
answer.cardinality = BufferUtil.unsignedDifference(value1.content, value1.getCardinality(),
value2.content, value2.getCardinality(), answer.content.array());
}
return answer;
}
@Override
public MappeableArrayContainer andNot(MappeableBitmapContainer value2) {
final MappeableArrayContainer answer = new MappeableArrayContainer(content.limit());
int pos = 0;
short[] sarray = answer.content.array();
if (BufferUtil.isBackedBySimpleArray(this.content)) {
short[] c = content.array();
for (int k = 0; k < cardinality; ++k) {
short v = c[k];
sarray[pos] = v;
pos += 1 - value2.bitValue(v);
}
} else {
for (int k = 0; k < cardinality; ++k) {
short v = this.content.get(k);
sarray[pos] = v;
pos += 1 - value2.bitValue(v);
}
}
answer.cardinality = pos;
return answer;
}
@Override
public MappeableContainer andNot(final MappeableRunContainer x) {
if (x.numberOfRuns() == 0) {
return clone();
} else if (x.isFull()) {
return MappeableArrayContainer.empty();
}
int write = 0;
int read = 0;
MappeableArrayContainer answer = new MappeableArrayContainer(cardinality);
for (int i = 0; i < x.numberOfRuns() && read < cardinality; ++i) {
int runStart = BufferUtil.toIntUnsigned(x.getValue(i));
int runEnd = runStart + BufferUtil.toIntUnsigned(x.getLength(i));
if (BufferUtil.toIntUnsigned(content.get(read)) > runEnd) {
continue;
}
int firstInRun = BufferUtil.iterateUntil(content, read, cardinality, runStart);
int toWrite = firstInRun - read;
BufferUtil.arraycopy(content, read, answer.content, write, toWrite);
write += toWrite;
read = BufferUtil.iterateUntil(content, firstInRun, cardinality, runEnd + 1);
}
BufferUtil.arraycopy(content, read, answer.content, write, cardinality - read);
write += cardinality - read;
answer.cardinality = write;
return answer;
}
@Override
public void clear() {
cardinality = 0;
}
@Override
public MappeableArrayContainer clone() {
return new MappeableArrayContainer(this.cardinality, this.content);
}
@Override
public boolean contains(final short x) {
return BufferUtil.unsignedBinarySearch(content, 0, cardinality, x) >= 0;
}
/**
* Checks whether the container contains the value x.
*
* @param buf underlying buffer
* @param position starting position of the container in the ByteBuffer
* @param x target value x
* @param cardinality container cardinality
* @return whether the container contains the value x
*/
public static boolean contains(ByteBuffer buf, int position, final short x, int cardinality) {
return BufferUtil.unsignedBinarySearch(buf, position, 0, cardinality, x) >= 0;
}
// in order
// not thread-safe
private void emit(short val) {
if (cardinality == content.limit()) {
increaseCapacity(true);
}
content.put(cardinality++, val);
}
@Override
public boolean equals(Object o) {
if (o instanceof MappeableArrayContainer) {
final MappeableArrayContainer srb = (MappeableArrayContainer) o;
if (srb.cardinality != this.cardinality) {
return false;
}
if (BufferUtil.isBackedBySimpleArray(this.content)
&& BufferUtil.isBackedBySimpleArray(srb.content)) {
short[] t = this.content.array();
short[] sr = srb.content.array();
for (int i = 0; i < this.cardinality; ++i) {
if (t[i] != sr[i]) {
return false;
}
}
} else {
for (int i = 0; i < this.cardinality; ++i) {
if (this.content.get(i) != srb.content.get(i)) {
return false;
}
}
}
return true;
} else if (o instanceof MappeableRunContainer) {
return o.equals(this);
}
return false;
}
@Override
public void fillLeastSignificant16bits(int[] x, int i, int mask) {
if (BufferUtil.isBackedBySimpleArray(this.content)) {
short[] c = this.content.array();
for (int k = 0; k < this.cardinality; ++k) {
x[k + i] = toIntUnsigned(c[k]) | mask;
}
} else {
for (int k = 0; k < this.cardinality; ++k) {
x[k + i] = toIntUnsigned(this.content.get(k)) | mask;
}
}
}
@Override
// not thread-safe
public MappeableContainer flip(short x) {
if (BufferUtil.isBackedBySimpleArray(this.content)) {
short[] sarray = content.array();
int loc = Util.unsignedBinarySearch(sarray, 0, cardinality, x);
if (loc < 0) {
// Transform the ArrayContainer to a BitmapContainer
// when cardinality = DEFAULT_MAX_SIZE
if (cardinality >= DEFAULT_MAX_SIZE) {
MappeableBitmapContainer a = this.toBitmapContainer();
a.add(x);
return a;
}
if (cardinality >= sarray.length) {
increaseCapacity();
sarray = content.array();
}
// insertion : shift the elements > x by one position to
// the right
// and put x in it's appropriate place
System.arraycopy(sarray, -loc - 1, sarray, -loc, cardinality + loc + 1);
sarray[-loc - 1] = x;
++cardinality;
} else {
System.arraycopy(sarray, loc + 1, sarray, loc, cardinality - loc - 1);
--cardinality;
}
return this;
} else {
int loc = BufferUtil.unsignedBinarySearch(content, 0, cardinality, x);
if (loc < 0) {
// Transform the ArrayContainer to a BitmapContainer
// when cardinality = DEFAULT_MAX_SIZE
if (cardinality >= DEFAULT_MAX_SIZE) {
MappeableBitmapContainer a = this.toBitmapContainer();
a.add(x);
return a;
}
if (cardinality >= content.limit()) {
increaseCapacity();
}
// insertion : shift the elements > x by one position to
// the right
// and put x in it's appropriate place
for (int k = cardinality; k > -loc - 1; --k) {
content.put(k, content.get(k - 1));
}
content.put(-loc - 1, x);
++cardinality;
} else {
for (int k = loc + 1; k < cardinality; --k) {
content.put(k - 1, content.get(k));
}
--cardinality;
}
return this;
}
}
@Override
protected int getArraySizeInBytes() {
return getArraySizeInBytes(cardinality);
}
@Override
public int getCardinality() {
return cardinality;
}
@Override
public ShortIterator getReverseShortIterator() {
if (this.isArrayBacked()) {
return new RawReverseArrayContainerShortIterator(this);
}
return new ReverseMappeableArrayContainerShortIterator(this);
}
@Override
public PeekableShortIterator getShortIterator() {
if (this.isArrayBacked()) {
return new RawArrayContainerShortIterator(this);
}
return new MappeableArrayContainerShortIterator(this);
}
@Override
public ContainerBatchIterator getBatchIterator() {
return new ArrayBatchIterator(this);
}
@Override
public int getSizeInBytes() {
return this.cardinality * 2;
}
@Override
public int hashCode() {
int hash = 0;
for (int k = 0; k < cardinality; ++k) {
hash += 31 * hash + content.get(k);
}
return hash;
}
@Override
// not thread-safe
public MappeableContainer iadd(int begin, int end) {
// TODO: may need to convert to a RunContainer
if (end == begin) {
return this;
}
if ((begin > end) || (end > (1 << 16))) {
throw new IllegalArgumentException("Invalid range [" + begin + "," + end + ")");
}
int indexstart = BufferUtil.unsignedBinarySearch(content, 0, cardinality, (short) begin);
if (indexstart < 0) {
indexstart = -indexstart - 1;
}
int indexend = BufferUtil.unsignedBinarySearch(content, 0, cardinality, (short) (end - 1));
if (indexend < 0) {
indexend = -indexend - 1;
} else {
indexend++;
}
int rangelength = end - begin;
int newcardinality = indexstart + (cardinality - indexend) + rangelength;
if (newcardinality > DEFAULT_MAX_SIZE) {
MappeableBitmapContainer a = this.toBitmapContainer();
return a.iadd(begin, end);
}
/*
* b - index of begin(indexstart), e - index of end(indexend), |--| is current sequential
* indexes in content. Total 6 cases are possible, listed as below:
*
* case-1) |--------|b-e case-2) |----b---|e case-3) |---b---e---| case-4) b|----e---| case-5)
* b-e|------| case-6) b|-----|e
*
* In case of old approach, we did (1a) Array.copyOf in increaseCapacity ( # of elements copied
* -> cardinality), (1b) then we moved elements using System.arrayCopy ( # of elements copied ->
* cardinality -indexend), (1c) then we set all elements from begin to end ( # of elements set
* -> end - begin)
*
* With new approach, (2a) we set all elements from begin to end ( # of elements set -> end-
* begin), (2b) we only copy elements in current set which are not in range begin-end ( # of
* elements copied -> cardinality - (end-begin) )
*
* why is it faster? Logically we are doing less # of copies. Mathematically proof as below: ->
* 2a is same as 1c, so we can avoid. Assume, 2b < (1a+1b), lets prove this assumption.
* Substitute the values. (cardinality - (end-begin)) < ( 2*cardinality - indexend) , lowest
* possible value of indexend is 0 and equation holds true , hightest possible value of indexend
* is cardinality and equation holds true , hence "<" equation holds true always
*/
if (newcardinality >= this.content.limit()) {
ShortBuffer destination = ShortBuffer.allocate(newcardinality);
BufferUtil.arraycopy(content, 0, destination, 0, indexstart);
if (BufferUtil.isBackedBySimpleArray(content)) {
short[] destinationarray = destination.array();
for (int k = 0; k < rangelength; ++k) {
destinationarray[k + indexstart] = (short) (begin + k);
}
} else {
for (int k = 0; k < rangelength; ++k) {
destination.put(k + indexstart, (short) (begin + k));
}
}
BufferUtil.arraycopy(content, indexend, destination, indexstart + rangelength, cardinality
- indexend);
this.content = destination;
} else {
BufferUtil.arraycopy(content, indexend, content, indexstart + rangelength, cardinality
- indexend);
if (BufferUtil.isBackedBySimpleArray(content)) {
short[] contentarray = content.array();
for (int k = 0; k < rangelength; ++k) {
contentarray[k + indexstart] = (short) (begin + k);
}
} else {
for (int k = 0; k < rangelength; ++k) {
content.put(k + indexstart, (short) (begin + k));
}
}
}
cardinality = newcardinality;
return this;
}
@Override
public MappeableArrayContainer iand(final MappeableArrayContainer value2) {
final MappeableArrayContainer value1 = this;
if (!BufferUtil.isBackedBySimpleArray(value1.content)) {
throw new RuntimeException("Should not happen. Internal bug.");
}
value1.cardinality = BufferUtil.unsignedIntersect2by2(value1.content, value1.getCardinality(),
value2.content, value2.getCardinality(), value1.content.array());
return this;
}
@Override
public MappeableContainer iand(MappeableBitmapContainer value2) {
int pos = 0;
for (int k = 0; k < cardinality; ++k) {
short v = this.content.get(k);
this.content.put(pos, v);
pos += value2.bitValue(v);
}
cardinality = pos;
return this;
}
// Note it is never inplace, may wish to fix
@Override
public MappeableContainer iand(final MappeableRunContainer value2) {
return value2.and(this);
}
@Override
public MappeableArrayContainer iandNot(final MappeableArrayContainer value2) {
if (!BufferUtil.isBackedBySimpleArray(this.content)) {
throw new RuntimeException("Should not happen. Internal bug.");
}
if (BufferUtil.isBackedBySimpleArray(value2.content)) {
this.cardinality =
org.roaringbitmap.Util.unsignedDifference(this.content.array(), this.getCardinality(),
value2.content.array(), value2.getCardinality(), this.content.array());
} else {
this.cardinality = BufferUtil.unsignedDifference(this.content, this.getCardinality(),
value2.content, value2.getCardinality(), this.content.array());
}
return this;
}
@Override
public MappeableArrayContainer iandNot(MappeableBitmapContainer value2) {
if (!BufferUtil.isBackedBySimpleArray(this.content)) {
throw new RuntimeException("Should not happen. Internal bug.");
}
short[] c = this.content.array();
int pos = 0;
for (int k = 0; k < cardinality; ++k) {
short v = c[k];
c[pos] = v;
pos += 1 - value2.bitValue(v);
}
this.cardinality = pos;
return this;
}
@Override
public MappeableContainer iandNot(final MappeableRunContainer value2) { // not inplace, revisit?
return andNot(value2);
}
private void increaseCapacity() {
increaseCapacity(false);
}
// temporarily allow an illegally large size, as long as the operation creating
// the illegal container does not return it.
// not thread safe!
private void increaseCapacity(boolean allowIllegalSize) {
int len = this.content.limit();
int newCapacity = (len == 0) ? DEFAULT_INIT_SIZE
: len < 64 ? len * 2 : this.content.limit() < 1067 ? len * 3 / 2 : len * 5 / 4;
// do not allocate more than we will ever need
if (newCapacity > MappeableArrayContainer.DEFAULT_MAX_SIZE && !allowIllegalSize) {
newCapacity = MappeableArrayContainer.DEFAULT_MAX_SIZE;
}
// if we are within 1/16th of the max., go to max right away to avoid further reallocations
if (newCapacity > MappeableArrayContainer.DEFAULT_MAX_SIZE
- MappeableArrayContainer.DEFAULT_MAX_SIZE / 16 && !allowIllegalSize) {
newCapacity = MappeableArrayContainer.DEFAULT_MAX_SIZE;
}
final ShortBuffer newContent = ShortBuffer.allocate(newCapacity);
this.content.rewind();
newContent.put(this.content);
this.content = newContent;
}
// not thread safe!
private void increaseCapacity(int min) {
final ShortBuffer newContent = ShortBuffer.allocate(calculateCapacity(min));
this.content.rewind();
newContent.put(this.content);
this.content = newContent;
}
private int calculateCapacity(int min){
int len = this.content.limit();
int newCapacity = (len == 0) ? DEFAULT_INIT_SIZE
: len < 64 ? len * 2 : len < 1024 ? len * 3 / 2 : len * 5 / 4;
if (newCapacity < min) {
newCapacity = min;
}
if (newCapacity > MappeableArrayContainer.DEFAULT_MAX_SIZE) {
newCapacity = MappeableArrayContainer.DEFAULT_MAX_SIZE;
}
if (newCapacity > MappeableArrayContainer.DEFAULT_MAX_SIZE
- MappeableArrayContainer.DEFAULT_MAX_SIZE / 16) {
newCapacity = MappeableArrayContainer.DEFAULT_MAX_SIZE;
}
return newCapacity;
}
@Override
// not thread safe! (duh!)
public MappeableContainer inot(final int firstOfRange, final int lastOfRange) {
// TODO: may need to convert to a RunContainer
// TODO: this can be optimized for performance
// determine the span of array indices to be affected
int startIndex = BufferUtil.unsignedBinarySearch(content, 0, cardinality, (short) firstOfRange);
if (startIndex < 0) {
startIndex = -startIndex - 1;
}
int lastIndex =
BufferUtil.unsignedBinarySearch(content, 0, cardinality, (short) (lastOfRange - 1));
if (lastIndex < 0) {
lastIndex = -lastIndex - 1 - 1;
}
final int currentValuesInRange = lastIndex - startIndex + 1;
final int spanToBeFlipped = lastOfRange - firstOfRange;
final int newValuesInRange = spanToBeFlipped - currentValuesInRange;
final ShortBuffer buffer = ShortBuffer.allocate(newValuesInRange);
final int cardinalityChange = newValuesInRange - currentValuesInRange;
final int newCardinality = cardinality + cardinalityChange;
if (cardinalityChange > 0) { // expansion, right shifting needed
if (newCardinality > content.limit()) {
// so big we need a bitmap?
if (newCardinality > DEFAULT_MAX_SIZE) {
return toBitmapContainer().inot(firstOfRange, lastOfRange);
}
final ShortBuffer co = ShortBuffer.allocate(newCardinality);
content.rewind();
co.put(content);
content = co;
}
// slide right the contents after the range
for (int pos = cardinality - 1; pos > lastIndex; --pos) {
content.put(pos + cardinalityChange, content.get(pos));
}
negateRange(buffer, startIndex, lastIndex, firstOfRange, lastOfRange);
} else { // no expansion needed
negateRange(buffer, startIndex, lastIndex, firstOfRange, lastOfRange);
if (cardinalityChange < 0) {
// Leave array oversize
for (int i = startIndex + newValuesInRange; i < newCardinality; ++i) {
content.put(i, content.get(i - cardinalityChange));
}
}
}
cardinality = newCardinality;
return this;
}
@Override
public boolean intersects(MappeableArrayContainer value2) {
MappeableArrayContainer value1 = this;
return BufferUtil.unsignedIntersects(value1.content, value1.getCardinality(), value2.content,
value2.getCardinality());
}
@Override
public boolean intersects(MappeableBitmapContainer x) {
return x.intersects(this);
}
@Override
public boolean intersects(MappeableRunContainer x) {
return x.intersects(this);
}
@Override
public MappeableContainer ior(final MappeableArrayContainer value2) {
final int totalCardinality = getCardinality() + value2.getCardinality();
if (totalCardinality > DEFAULT_MAX_SIZE) {// it could be a bitmap!
final MappeableBitmapContainer bc = new MappeableBitmapContainer();
if (!BufferUtil.isBackedBySimpleArray(bc.bitmap)) {
throw new RuntimeException("Should not happen. Internal bug.");
}
long[] bitArray = bc.bitmap.array();
if (BufferUtil.isBackedBySimpleArray(value2.content)) {
short[] sarray = value2.content.array();
for (int k = 0; k < value2.cardinality; ++k) {
short v = sarray[k];
final int i = toIntUnsigned(v) >>> 6;
bitArray[i] |= (1L << v);
}
} else {
for (int k = 0; k < value2.cardinality; ++k) {
short v2 = value2.content.get(k);
final int i = toIntUnsigned(v2) >>> 6;
bitArray[i] |= (1L << v2);
}
}
if (BufferUtil.isBackedBySimpleArray(content)) {
short[] sarray = content.array();
for (int k = 0; k < cardinality; ++k) {
short v = sarray[k];
final int i = toIntUnsigned(v) >>> 6;
bitArray[i] |= (1L << v);
}
} else {
for (int k = 0; k < cardinality; ++k) {
short v = content.get(k);
final int i = toIntUnsigned(v) >>> 6;
bitArray[i] |= (1L << v);
}
}
bc.cardinality = 0;
int len = bc.bitmap.limit();
for (int index = 0; index < len; ++index) {
bc.cardinality += Long.bitCount(bitArray[index]);
}
if (bc.cardinality <= DEFAULT_MAX_SIZE) {
return bc.toArrayContainer();
} else if (bc.isFull()) {
return MappeableRunContainer.full();
}
return bc;
}
if (totalCardinality >= content.limit()) {
int newCapacity = calculateCapacity(totalCardinality);
ShortBuffer destination = ShortBuffer.allocate(newCapacity);
if (BufferUtil.isBackedBySimpleArray(content)
&& BufferUtil.isBackedBySimpleArray(value2.content)) {
cardinality =
Util.unsignedUnion2by2(content.array(), 0, cardinality, value2.content.array(), 0,
value2.cardinality, destination.array());
} else {
cardinality =
BufferUtil.unsignedUnion2by2(content, 0, cardinality, value2.content, 0,
value2.cardinality, destination.array());
}
this.content = destination;
} else {
BufferUtil.arraycopy(content, 0, content, value2.cardinality, cardinality);
if (BufferUtil.isBackedBySimpleArray(content)
&& BufferUtil.isBackedBySimpleArray(value2.content)) {
cardinality =
Util.unsignedUnion2by2(content.array(), value2.cardinality, cardinality,
value2.content.array(), 0, value2.cardinality, content.array());
} else {
cardinality =
BufferUtil.unsignedUnion2by2(content, value2.cardinality, cardinality, value2.content,
0, value2.cardinality, content.array());
}
}
return this;
}
@Override
public MappeableContainer ior(MappeableBitmapContainer x) {
return x.or(this);
}
@Override
public MappeableContainer ior(final MappeableRunContainer value2) {
// not inplace
return value2.or(this);
}
@Override
public MappeableContainer iremove(int begin, int end) {
if (end == begin) {
return this;
}
if ((begin > end) || (end > (1 << 16))) {
throw new IllegalArgumentException("Invalid range [" + begin + "," + end + ")");
}
int indexstart = BufferUtil.unsignedBinarySearch(content, 0, cardinality, (short) begin);
if (indexstart < 0) {
indexstart = -indexstart - 1;
}
int indexend = BufferUtil.unsignedBinarySearch(content, 0, cardinality, (short) (end - 1));
if (indexend < 0) {
indexend = -indexend - 1;
} else {
indexend++;
}
int rangelength = indexend - indexstart;
BufferUtil.arraycopy(content, indexstart + rangelength, content, indexstart,
cardinality - indexstart - rangelength);
cardinality -= rangelength;
return this;
}
@Override
protected boolean isArrayBacked() {
return BufferUtil.isBackedBySimpleArray(this.content);
}
@Override
public Iterator iterator() {
return new Iterator() {
short pos = 0;
@Override
public boolean hasNext() {
return pos < MappeableArrayContainer.this.cardinality;
}
@Override
public Short next() {
return MappeableArrayContainer.this.content.get(pos++);
}
@Override
public void remove() {
MappeableArrayContainer.this.removeAtIndex(pos - 1);
pos--;
}
};
}
@Override
public MappeableContainer ixor(final MappeableArrayContainer value2) {
return this.xor(value2);
}
@Override
public MappeableContainer ixor(MappeableBitmapContainer x) {
return x.xor(this);
}
@Override
public MappeableContainer ixor(final MappeableRunContainer value2) {
return value2.xor(this);
}
@Override
public MappeableContainer limit(int maxcardinality) {
if (maxcardinality < this.getCardinality()) {
return new MappeableArrayContainer(maxcardinality, this.content);
} else {
return clone();
}
}
protected void loadData(final MappeableBitmapContainer bitmapContainer) {
this.cardinality = bitmapContainer.cardinality;
if (!BufferUtil.isBackedBySimpleArray(this.content)) {
throw new RuntimeException("Should not happen. Internal bug.");
}
bitmapContainer.fillArray(content.array());
}
// for use in inot range known to be nonempty
private void negateRange(final ShortBuffer buffer, final int startIndex, final int lastIndex,
final int startRange, final int lastRange) {
// compute the negation into buffer
int outPos = 0;
int inPos = startIndex; // value here always >= valInRange,
// until it is exhausted
// n.b., we can start initially exhausted.
int valInRange = startRange;
for (; valInRange < lastRange && inPos <= lastIndex; ++valInRange) {
if ((short) valInRange != content.get(inPos)) {
buffer.put(outPos++, (short) valInRange);
} else {
++inPos;
}
}
// if there are extra items (greater than the biggest
// pre-existing one in range), buffer them
for (; valInRange < lastRange; ++valInRange) {
buffer.put(outPos++, (short) valInRange);
}
if (outPos != buffer.limit()) {
throw new RuntimeException(
"negateRange: outPos " + outPos + " whereas buffer.length=" + buffer.limit());
}
assert outPos == buffer.limit();
// copy back from buffer...caller must ensure there is room
int i = startIndex;
int len = buffer.limit();
for (int k = 0; k < len; ++k) {
final short item = buffer.get(k);
content.put(i++, item);
}
}
// shares lots of code with inot; candidate for refactoring
@Override
public MappeableContainer not(final int firstOfRange, final int lastOfRange) {
// TODO: may need to convert to a RunContainer
// TODO: this can be optimized for performance
if (firstOfRange >= lastOfRange) {
return clone(); // empty range
}
// determine the span of array indices to be affected
int startIndex = BufferUtil.unsignedBinarySearch(content, 0, cardinality, (short) firstOfRange);
if (startIndex < 0) {
startIndex = -startIndex - 1;
}
int lastIndex =
BufferUtil.unsignedBinarySearch(content, 0, cardinality, (short) (lastOfRange - 1));
if (lastIndex < 0) {
lastIndex = -lastIndex - 2;
}
final int currentValuesInRange = lastIndex - startIndex + 1;
final int spanToBeFlipped = lastOfRange - firstOfRange;
final int newValuesInRange = spanToBeFlipped - currentValuesInRange;
final int cardinalityChange = newValuesInRange - currentValuesInRange;
final int newCardinality = cardinality + cardinalityChange;
if (newCardinality > DEFAULT_MAX_SIZE) {
return toBitmapContainer().not(firstOfRange, lastOfRange);
}
final MappeableArrayContainer answer = new MappeableArrayContainer(newCardinality);
if (!BufferUtil.isBackedBySimpleArray(answer.content)) {
throw new RuntimeException("Should not happen. Internal bug.");
}
short[] sarray = answer.content.array();
for (int i = 0; i < startIndex; ++i) {
// copy stuff before the active area
sarray[i] = content.get(i);
}
int outPos = startIndex;
int inPos = startIndex; // item at inPos always >= valInRange
int valInRange = firstOfRange;
for (; valInRange < lastOfRange && inPos <= lastIndex; ++valInRange) {
if ((short) valInRange != content.get(inPos)) {
sarray[outPos++] = (short) valInRange;
} else {
++inPos;
}
}
for (; valInRange < lastOfRange; ++valInRange) {
answer.content.put(outPos++, (short) valInRange);
}
// content after the active range
for (int i = lastIndex + 1; i < cardinality; ++i) {
answer.content.put(outPos++, content.get(i));
}
answer.cardinality = newCardinality;
return answer;
}
@Override
int numberOfRuns() {
if (cardinality == 0) {
return 0; // should never happen
}
if (BufferUtil.isBackedBySimpleArray(content)) {
short[] c = content.array();
int numRuns = 1;
int oldv = toIntUnsigned(c[0]);
for (int i = 1; i < cardinality; i++) {
int newv = toIntUnsigned(c[i]);
if (oldv + 1 != newv) {
++numRuns;
}
oldv = newv;
}
return numRuns;
} else {
int numRuns = 1;
int previous = toIntUnsigned(content.get(0));
// we do not proceed like above for fear that calling "get" twice per loop would be too much
for (int i = 1; i < cardinality; i++) {
int val = toIntUnsigned(content.get(i));
if (val != previous + 1) {
++numRuns;
}
previous = val;
}
return numRuns;
}
}
@Override
public MappeableContainer or(final MappeableArrayContainer value2) {
final MappeableArrayContainer value1 = this;
final int totalCardinality = value1.getCardinality() + value2.getCardinality();
if (totalCardinality > DEFAULT_MAX_SIZE) {// it could be a bitmap!
final MappeableBitmapContainer bc = new MappeableBitmapContainer();
if (!BufferUtil.isBackedBySimpleArray(bc.bitmap)) {
throw new RuntimeException("Should not happen. Internal bug.");
}
long[] bitArray = bc.bitmap.array();
if (BufferUtil.isBackedBySimpleArray(value2.content)) {
short[] sarray = value2.content.array();
for (int k = 0; k < value2.cardinality; ++k) {
short v = sarray[k];
final int i = toIntUnsigned(v) >>> 6;
bitArray[i] |= (1L << v);
}
} else {
for (int k = 0; k < value2.cardinality; ++k) {
short v2 = value2.content.get(k);
final int i = toIntUnsigned(v2) >>> 6;
bitArray[i] |= (1L << v2);
}
}
if (BufferUtil.isBackedBySimpleArray(this.content)) {
short[] sarray = this.content.array();
for (int k = 0; k < this.cardinality; ++k) {
short v = sarray[k];
final int i = toIntUnsigned(v) >>> 6;
bitArray[i] |= (1L << v);
}
} else {
for (int k = 0; k < this.cardinality; ++k) {
short v = this.content.get(k);
final int i = toIntUnsigned(v) >>> 6;
bitArray[i] |= (1L << v);
}
}
bc.cardinality = 0;
int len = bc.bitmap.limit();
for (int index = 0; index < len; ++index) {
bc.cardinality += Long.bitCount(bitArray[index]);
}
if (bc.cardinality <= DEFAULT_MAX_SIZE) {
return bc.toArrayContainer();
} else if (bc.isFull()) {
return MappeableRunContainer.full();
}
return bc;
}
final MappeableArrayContainer answer = new MappeableArrayContainer(totalCardinality);
if (BufferUtil.isBackedBySimpleArray(value1.content)
&& BufferUtil.isBackedBySimpleArray(value2.content)) {
answer.cardinality =
Util.unsignedUnion2by2(
value1.content.array(), 0, value1.getCardinality(),
value2.content.array(), 0, value2.getCardinality(),
answer.content.array()
);
} else {
answer.cardinality =
BufferUtil.unsignedUnion2by2(
value1.content, 0, value1.getCardinality(),
value2.content, 0, value2.getCardinality(),
answer.content.array()
);
}
return answer;
}
protected MappeableContainer lazyor(final MappeableArrayContainer value2) {
final MappeableArrayContainer value1 = this;
final int totalCardinality = value1.getCardinality() + value2.getCardinality();
if (totalCardinality > ARRAY_LAZY_LOWERBOUND) {// it could be a bitmap!
final MappeableBitmapContainer bc = new MappeableBitmapContainer();
if (!BufferUtil.isBackedBySimpleArray(bc.bitmap)) {
throw new RuntimeException("Should not happen. Internal bug.");
}
long[] bitArray = bc.bitmap.array();
if (BufferUtil.isBackedBySimpleArray(value2.content)) {
short[] sarray = value2.content.array();
for (int k = 0; k < value2.cardinality; ++k) {
short v = sarray[k];
final int i = toIntUnsigned(v) >>> 6;
bitArray[i] |= (1L << v);
}
} else {
for (int k = 0; k < value2.cardinality; ++k) {
short v2 = value2.content.get(k);
final int i = toIntUnsigned(v2) >>> 6;
bitArray[i] |= (1L << v2);
}
}
if (BufferUtil.isBackedBySimpleArray(this.content)) {
short[] sarray = this.content.array();
for (int k = 0; k < this.cardinality; ++k) {
short v = sarray[k];
final int i = toIntUnsigned(v) >>> 6;
bitArray[i] |= (1L << v);
}
} else {
for (int k = 0; k < this.cardinality; ++k) {
short v = this.content.get(k);
final int i = toIntUnsigned(v) >>> 6;
bitArray[i] |= (1L << v);
}
}
bc.cardinality = -1;
return bc;
}
final MappeableArrayContainer answer = new MappeableArrayContainer(totalCardinality);
if (BufferUtil.isBackedBySimpleArray(value1.content)
&& BufferUtil.isBackedBySimpleArray(value2.content)) {
answer.cardinality =
Util.unsignedUnion2by2(
value1.content.array(), 0, value1.getCardinality(),
value2.content.array(), 0, value2.getCardinality(),
answer.content.array()
);
} else {
answer.cardinality =
BufferUtil.unsignedUnion2by2(
value1.content, 0, value1.getCardinality(),
value2.content, 0, value2.getCardinality(),
answer.content.array()
);
}
return answer;
}
@Override
public MappeableContainer or(MappeableBitmapContainer x) {
return x.or(this);
}
@Override
public MappeableContainer or(final MappeableRunContainer value2) {
return value2.or(this);
}
protected MappeableContainer or(ShortIterator it) {
return or(it, false);
}
/**
* it must return items in (unsigned) sorted order. Possible candidate for Container interface?
**/
private MappeableContainer or(ShortIterator it, boolean exclusive) {
MappeableArrayContainer ac = new MappeableArrayContainer();
int myItPos = 0;
ac.cardinality = 0;
// do a merge. int -1 denotes end of input.
int myHead = (myItPos == cardinality) ? -1 : toIntUnsigned(content.get(myItPos++));
int hisHead = advance(it);
while (myHead != -1 && hisHead != -1) {
if (myHead < hisHead) {
ac.emit((short) myHead);
myHead = (myItPos == cardinality) ? -1 : toIntUnsigned(content.get(myItPos++));
} else if (myHead > hisHead) {
ac.emit((short) hisHead);
hisHead = advance(it);
} else {
if (!exclusive) {
ac.emit((short) hisHead);
}
hisHead = advance(it);
myHead = (myItPos == cardinality) ? -1 : toIntUnsigned(content.get(myItPos++));
}
}
while (myHead != -1) {
ac.emit((short) myHead);
myHead = (myItPos == cardinality) ? -1 : toIntUnsigned(content.get(myItPos++));
}
while (hisHead != -1) {
ac.emit((short) hisHead);
hisHead = advance(it);
}
if (ac.cardinality > DEFAULT_MAX_SIZE) {
return ac.toBitmapContainer();
} else {
return ac;
}
}
@Override
public int rank(short lowbits) {
int answer = BufferUtil.unsignedBinarySearch(content, 0, cardinality, lowbits);
if (answer >= 0) {
return answer + 1;
} else {
return -answer - 1;
}
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
// little endian
this.cardinality = 0xFFFF & Short.reverseBytes(in.readShort());
if (this.content.limit() < this.cardinality) {
this.content = ShortBuffer.allocate(this.cardinality);
}
for (int k = 0; k < this.cardinality; ++k) {
this.content.put(k, Short.reverseBytes(in.readShort()));
}
}
@Override
public MappeableContainer remove(int begin, int end) {
if (end == begin) {
return clone();
}
if ((begin > end) || (end > (1 << 16))) {
throw new IllegalArgumentException("Invalid range [" + begin + "," + end + ")");
}
int indexstart = BufferUtil.unsignedBinarySearch(content, 0, cardinality, (short) begin);
if (indexstart < 0) {
indexstart = -indexstart - 1;
}
int indexend = BufferUtil.unsignedBinarySearch(content, 0, cardinality, (short) (end - 1));
if (indexend < 0) {
indexend = -indexend - 1;
} else {
indexend++;
}
int rangelength = indexend - indexstart;
MappeableArrayContainer answer = clone();
BufferUtil.arraycopy(content, indexstart + rangelength, answer.content, indexstart,
cardinality - indexstart - rangelength);
answer.cardinality = cardinality - rangelength;
return answer;
}
protected void removeAtIndex(final int loc) {
System.arraycopy(content.array(), loc + 1, content.array(), loc, cardinality - loc - 1);
--cardinality;
}
@Override
public MappeableContainer remove(final short x) {
if (BufferUtil.isBackedBySimpleArray(this.content)) {
final int loc = Util.unsignedBinarySearch(content.array(), 0, cardinality, x);
if (loc >= 0) {
removeAtIndex(loc);
}
return this;
} else {
final int loc = BufferUtil.unsignedBinarySearch(content, 0, cardinality, x);
if (loc >= 0) {
// insertion
for (int k = loc + 1; k < cardinality; --k) {
content.put(k - 1, content.get(k));
}
--cardinality;
}
return this;
}
}
@Override
public MappeableContainer repairAfterLazy() {
return this;
}
@Override
public MappeableContainer runOptimize() {
int numRuns = numberOfRuns();
int sizeAsRunContainer = MappeableRunContainer.getArraySizeInBytes(numRuns);
if (getArraySizeInBytes() > sizeAsRunContainer) {
return new MappeableRunContainer(this, numRuns); // this could be
// maybe faster if
// initial
// container is a
// bitmap
} else {
return this;
}
}
@Override
public short select(int j) {
return this.content.get(j);
}
@Override
public int serializedSizeInBytes() {
return serializedSizeInBytes(cardinality);
}
/**
* Copies the data in a bitmap container.
*
* @return the bitmap container
*/
@Override
public MappeableBitmapContainer toBitmapContainer() {
final MappeableBitmapContainer bc = new MappeableBitmapContainer();
bc.loadData(this);
return bc;
}
@Override
public int first() {
assertNonEmpty(cardinality == 0);
return toIntUnsigned(this.select(0));
}
@Override
public int last() {
assertNonEmpty(cardinality == 0);
return toIntUnsigned(select(cardinality - 1));
}
@Override
public Container toContainer() {
return new ArrayContainer(this);
}
/**
* Create a copy of the content of this container as a short array. This creates a copy.
*
* @return copy of the content as a short array
*/
public short[] toShortArray() {
short[] answer = new short[cardinality];
content.rewind();
content.get(answer);
return answer;
}
@Override
public String toString() {
if (this.cardinality == 0) {
return "{}";
}
final StringBuilder sb = new StringBuilder();
sb.append("{");
for (int i = 0; i < this.cardinality - 1; i++) {
sb.append(toIntUnsigned(this.content.get(i)));
sb.append(",");
}
sb.append(toIntUnsigned(this.content.get(this.cardinality - 1)));
sb.append("}");
return sb.toString();
}
@Override
public void trim() {
if (this.content.limit() == this.cardinality) {
return;
}
if (BufferUtil.isBackedBySimpleArray(content)) {
this.content = ShortBuffer.wrap(Arrays.copyOf(content.array(), cardinality));
} else {
final ShortBuffer co = ShortBuffer.allocate(this.cardinality);
// can assume that new co is array backed
short[] x = co.array();
for (int k = 0; k < this.cardinality; ++k) {
x[k] = this.content.get(k);
}
this.content = co;
}
}
@Override
protected void writeArray(DataOutput out) throws IOException {
// little endian
if (BufferUtil.isBackedBySimpleArray(content)) {
short[] a = content.array();
for (int k = 0; k < this.cardinality; ++k) {
out.writeShort(Short.reverseBytes(a[k]));
}
} else {
for (int k = 0; k < this.cardinality; ++k) {
out.writeShort(Short.reverseBytes(content.get(k)));
}
}
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.write(this.cardinality & 0xFF);
out.write((this.cardinality >>> 8) & 0xFF);
if (BufferUtil.isBackedBySimpleArray(content)) {
short[] a = content.array();
for (int k = 0; k < this.cardinality; ++k) {
out.writeShort(Short.reverseBytes(a[k]));
}
} else {
for (int k = 0; k < this.cardinality; ++k) {
out.writeShort(Short.reverseBytes(content.get(k)));
}
}
}
@Override
public MappeableContainer xor(final MappeableArrayContainer value2) {
final MappeableArrayContainer value1 = this;
final int totalCardinality = value1.getCardinality() + value2.getCardinality();
if (totalCardinality > DEFAULT_MAX_SIZE) {// it could be a bitmap!
final MappeableBitmapContainer bc = new MappeableBitmapContainer();
if (!BufferUtil.isBackedBySimpleArray(bc.bitmap)) {
throw new RuntimeException("Should not happen. Internal bug.");
}
long[] bitArray = bc.bitmap.array();
if (BufferUtil.isBackedBySimpleArray(value2.content)) {
short[] sarray = value2.content.array();
for (int k = 0; k < value2.cardinality; ++k) {
short v = sarray[k];
final int i = toIntUnsigned(v) >>> 6;
bitArray[i] ^= (1L << v);
}
} else {
for (int k = 0; k < value2.cardinality; ++k) {
short v2 = value2.content.get(k);
final int i = toIntUnsigned(v2) >>> 6;
bitArray[i] ^= (1L << v2);
}
}
if (BufferUtil.isBackedBySimpleArray(this.content)) {
short[] sarray = this.content.array();
for (int k = 0; k < this.cardinality; ++k) {
short v = sarray[k];
final int i = toIntUnsigned(v) >>> 6;
bitArray[i] ^= (1L << v);
}
} else {
for (int k = 0; k < this.cardinality; ++k) {
short v = this.content.get(k);
final int i = toIntUnsigned(v) >>> 6;
bitArray[i] ^= (1L << v);
}
}
bc.cardinality = 0;
int len = bc.bitmap.limit();
for (int index = 0; index < len; ++index) {
bc.cardinality += Long.bitCount(bitArray[index]);
}
if (bc.cardinality <= DEFAULT_MAX_SIZE) {
return bc.toArrayContainer();
}
return bc;
}
final MappeableArrayContainer answer = new MappeableArrayContainer(totalCardinality);
if (BufferUtil.isBackedBySimpleArray(value1.content)
&& BufferUtil.isBackedBySimpleArray(value2.content)) {
answer.cardinality = org.roaringbitmap.Util.unsignedExclusiveUnion2by2(value1.content.array(),
value1.getCardinality(), value2.content.array(), value2.getCardinality(),
answer.content.array());
} else {
answer.cardinality = BufferUtil.unsignedExclusiveUnion2by2(value1.content,
value1.getCardinality(), value2.content, value2.getCardinality(), answer.content.array());
}
return answer;
}
@Override
public MappeableContainer xor(MappeableBitmapContainer x) {
return x.xor(this);
}
@Override
public MappeableContainer xor(final MappeableRunContainer value2) {
return value2.xor(this);
}
protected MappeableContainer xor(ShortIterator it) {
return or(it, true);
}
@Override
public void forEach(short msb, IntConsumer ic) {
int high = ((int) msb) << 16;
if (BufferUtil.isBackedBySimpleArray(content)) {
short[] c = content.array();
for (int k = 0; k < cardinality; ++k) {
ic.accept((c[k] & 0xFFFF) | high);
}
} else {
for (int k = 0; k < cardinality; ++k) {
ic.accept((content.get(k) & 0xFFFF) | high);
}
}
}
@Override
public int andCardinality(MappeableArrayContainer value2) {
if (BufferUtil.isBackedBySimpleArray(content)
&& BufferUtil.isBackedBySimpleArray(value2.content)) {
return Util.unsignedLocalIntersect2by2Cardinality(content.array(), cardinality,
value2.content.array(), value2.getCardinality());
}
return BufferUtil.unsignedLocalIntersect2by2Cardinality(content, cardinality,
value2.content, value2.getCardinality());
}
@Override
public int andCardinality(MappeableBitmapContainer x) {
return x.andCardinality(this);
}
@Override
// see andNot for an approach that might be better.
public int andCardinality(MappeableRunContainer x) {
return x.andCardinality(this);
}
@Override
protected boolean contains(MappeableRunContainer runContainer) {
if (runContainer.getCardinality() > cardinality) {
return false;
}
int startPos, stopPos = -1;
for (int i = 0; i < runContainer.numberOfRuns(); ++i) {
short start = runContainer.getValue(i);
int stop = toIntUnsigned(start) + toIntUnsigned(runContainer.getLength(i));
startPos = BufferUtil.advanceUntil(content, stopPos, cardinality, start);
stopPos = BufferUtil.advanceUntil(content, stopPos, cardinality, (short)stop);
if(startPos == cardinality) {
return false;
} else if(stopPos - startPos != stop - start
|| content.get(startPos) != start
|| content.get(stopPos) != stop) {
return false;
}
}
return true;
}
@Override
protected boolean contains(MappeableArrayContainer arrayContainer) {
if (cardinality < arrayContainer.cardinality) {
return false;
}
int i1 = 0, i2 = 0;
while(i1 < cardinality && i2 < arrayContainer.cardinality) {
if(content.get(i1) == arrayContainer.content.get(i2)) {
++i1;
++i2;
} else if(BufferUtil.compareUnsigned(content.get(i1), arrayContainer.content.get(i2)) < 0) {
++i1;
} else {
return false;
}
}
return i2 == arrayContainer.cardinality;
}
@Override
protected boolean contains(MappeableBitmapContainer bitmapContainer) {
return false;
}
@Override
public boolean intersects(int minimum, int supremum) {
int pos = BufferUtil.unsignedBinarySearch(content, 0, cardinality, (short)minimum);
int index = pos >= 0 ? pos : -pos - 1;
return index < cardinality && BufferUtil.toIntUnsigned(content.get(index)) < supremum;
}
@Override
public boolean contains(int minimum, int supremum) {
int maximum = supremum - 1;
int start = BufferUtil.advanceUntil(content, -1, cardinality, (short)minimum);
int end = BufferUtil.advanceUntil(content, start - 1, cardinality, (short)maximum);
return start < cardinality
&& end < cardinality
&& end - start == maximum - minimum
&& content.get(start) == (short)minimum
&& content.get(end) == (short)maximum;
}
}
final class MappeableArrayContainerShortIterator implements PeekableShortIterator {
int pos;
MappeableArrayContainer parent;
MappeableArrayContainerShortIterator() {}
MappeableArrayContainerShortIterator(MappeableArrayContainer p) {
wrap(p);
}
@Override
public void advanceIfNeeded(short minval) {
pos = BufferUtil.advanceUntil(parent.content, pos - 1, parent.cardinality, minval);
}
@Override
public PeekableShortIterator clone() {
try {
return (PeekableShortIterator) super.clone();
} catch (CloneNotSupportedException e) {
return null;// will not happen
}
}
@Override
public boolean hasNext() {
return pos < parent.cardinality;
}
@Override
public short next() {
return parent.content.get(pos++);
}
@Override
public int nextAsInt() {
return toIntUnsigned(parent.content.get(pos++));
}
@Override
public short peekNext() {
return parent.content.get(pos);
}
@Override
public void remove() {
parent.removeAtIndex(pos - 1);
pos--;
}
void wrap(MappeableArrayContainer p) {
parent = p;
pos = 0;
}
}
final class RawArrayContainerShortIterator implements PeekableShortIterator {
int pos;
MappeableArrayContainer parent;
short[] content;
RawArrayContainerShortIterator(MappeableArrayContainer p) {
parent = p;
if (!p.isArrayBacked()) {
throw new RuntimeException("internal bug");
}
content = p.content.array();
pos = 0;
}
@Override
public void advanceIfNeeded(short minval) {
pos = Util.advanceUntil(content, pos - 1, parent.cardinality, minval);
}
@Override
public PeekableShortIterator clone() {
try {
return (PeekableShortIterator) super.clone();
} catch (CloneNotSupportedException e) {
return null;// will not happen
}
}
@Override
public boolean hasNext() {
return pos < parent.cardinality;
}
@Override
public short next() {
return content[pos++];
}
@Override
public int nextAsInt() {
return toIntUnsigned(content[pos++]);
}
@Override
public short peekNext() {
return content[pos];
}
@Override
public void remove() {
parent.removeAtIndex(pos - 1);
pos--;
}
}
final class RawReverseArrayContainerShortIterator implements ShortIterator {
int pos;
MappeableArrayContainer parent;
short[] content;
RawReverseArrayContainerShortIterator(MappeableArrayContainer p) {
parent = p;
if (!p.isArrayBacked()) {
throw new RuntimeException("internal bug");
}
content = p.content.array();
pos = parent.cardinality - 1;
}
@Override
public ShortIterator clone() {
try {
return (ShortIterator) super.clone();
} catch (CloneNotSupportedException e) {
return null;// will not happen
}
}
@Override
public boolean hasNext() {
return pos >= 0;
}
@Override
public short next() {
return content[pos--];
}
@Override
public int nextAsInt() {
return toIntUnsigned(content[pos--]);
}
@Override
public void remove() {
parent.removeAtIndex(pos + 1);
pos++;
}
};
final class ReverseMappeableArrayContainerShortIterator implements ShortIterator {
int pos;
MappeableArrayContainer parent;
ReverseMappeableArrayContainerShortIterator() {}
ReverseMappeableArrayContainerShortIterator(MappeableArrayContainer p) {
wrap(p);
}
@Override
public ShortIterator clone() {
try {
return (ShortIterator) super.clone();
} catch (CloneNotSupportedException e) {
return null;// will not happen
}
}
@Override
public boolean hasNext() {
return pos >= 0;
}
@Override
public short next() {
return parent.content.get(pos--);
}
@Override
public int nextAsInt() {
return toIntUnsigned(parent.content.get(pos--));
}
@Override
public void remove() {
parent.removeAtIndex(pos + 1);
pos++;
}
void wrap(MappeableArrayContainer p) {
parent = p;
pos = parent.cardinality - 1;
}
}