org.roaringbitmap.buffer.MappeableArrayContainer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of RoaringBitmap Show documentation
Show all versions of RoaringBitmap Show documentation
Roaring bitmaps are compressed bitmaps (also called bitsets) which tend to outperform
conventional compressed bitmaps such as WAH or Concise.
/*
* (c) the authors Licensed under the Apache License, Version 2.0.
*/
package org.roaringbitmap.buffer;
import org.roaringbitmap.*;
import java.io.DataOutput;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.CharBuffer;
import java.util.Arrays;
import java.util.Iterator;
import static org.roaringbitmap.buffer.MappeableBitmapContainer.MAX_CAPACITY;
/**
* Simple container made of an array of 16-bit integers. Unlike org.roaringbitmap.ArrayContainer,
* this class uses a CharBuffer 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 CharBuffer 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.toCharBuffer();
}
/**
* Create an array container with specified capacity
*
* @param capacity The capacity of the container
*/
public MappeableArrayContainer(final int capacity) {
content = CharBuffer.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) {
final int valuesInRange = lastOfRun - firstOfRun;
content = CharBuffer.allocate(valuesInRange);
char[] sarray = content.array();
for (int i = 0; i < valuesInRange; ++i) {
sarray[i] = (char) (firstOfRun + i);
}
cardinality = valuesInRange;
}
private MappeableArrayContainer(int newCard, CharBuffer newContent) {
this.cardinality = newCard;
CharBuffer tmp = newContent.duplicate();// for thread-safety
this.content = CharBuffer.allocate(Math.max(newCard, tmp.limit()));
tmp.rewind();
this.content.put(tmp);
}
/**
* Construct a new ArrayContainer backed by the provided CharBuffer. Note that if you modify the
* ArrayContainer a new CharBuffer may be produced.
*
* @param array CharBuffer where the data is stored
* @param cardinality cardinality (number of values stored)
*/
public MappeableArrayContainer(final CharBuffer 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, (char) begin);
if (indexstart < 0) {
indexstart = -indexstart - 1;
}
int indexend = BufferUtil.unsignedBinarySearch(content, indexstart, cardinality,
(char) (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);
char[] answerarray = answer.content.array();
for (int k = 0; k < rangelength; ++k) {
answerarray[k + indexstart] = (char) (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 char x) {
if (BufferUtil.isBackedBySimpleArray(this.content)) {
char[] sarray = content.array();
if (cardinality == 0 || (cardinality > 0
&& (x) > (sarray[cardinality - 1]))) {
if (cardinality >= DEFAULT_MAX_SIZE) {
return toBitmapContainer().add(x);
}
if (cardinality >= sarray.length) {
increaseCapacity();
sarray = content.array();
}
sarray[cardinality++] = x;
} else {
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) {
return toBitmapContainer().add(x);
}
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 {
if (cardinality == 0 || (cardinality > 0
&& (x) > (content.get(cardinality - 1)))) {
if (cardinality >= DEFAULT_MAX_SIZE) {
return toBitmapContainer().add(x);
}
if (cardinality >= content.limit()) {
increaseCapacity();
}
content.put(cardinality++, x);
}
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;
}
@Override
public boolean isFull() {
return false;
}
@Override
public void orInto(long[] bits) {
for (int i = 0; i < this.getCardinality(); ++i) {
char value = content.get(i);
bits[value >>> 6] |= (1L << value);
}
}
@Override
public void andInto(long[] bits) {
int prev = 0;
for (int i = 0; i < this.getCardinality(); ++i) {
int value = content.get(i);
Util.resetBitmapRange(bits, prev, value);
prev = value + 1;
}
Util.resetBitmapRange(bits, prev, MAX_CAPACITY);
}
@Override
public void removeFrom(long[] bits) {
for (int i = 0; i < this.getCardinality(); ++i) {
int value = content.get(i);
bits[value >>> 6] &= ~(1L << value);
}
}
private int advance(CharIterator it) {
if (it.hasNext()) {
return (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;
char[] sarray = answer.content.array();
if (BufferUtil.isBackedBySimpleArray(this.content)) {
char[] c = content.array();
for (int k = 0; k < cardinality; ++k) {
char v = c[k];
sarray[pos] = v;
pos += 1 - (int)value2.bitValue(v);
}
} else {
for (int k = 0; k < cardinality; ++k) {
char v = this.content.get(k);
sarray[pos] = v;
pos += 1 - (int)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 = (x.getValue(i));
int runEnd = runStart + (x.getLength(i));
if ((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 char 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 char x, int cardinality) {
return BufferUtil.unsignedBinarySearch(buf, position, 0, cardinality, x) >= 0;
}
// in order
// not thread-safe
private void emit(char 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)) {
char[] t = this.content.array();
char[] 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)) {
char[] c = this.content.array();
for (int k = 0; k < this.cardinality; ++k) {
x[k + i] = (c[k]) | mask;
}
} else {
for (int k = 0; k < this.cardinality; ++k) {
x[k + i] = (this.content.get(k)) | mask;
}
}
}
@Override
// not thread-safe
public MappeableContainer flip(char x) {
if (BufferUtil.isBackedBySimpleArray(this.content)) {
char[] 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 CharIterator getReverseCharIterator() {
if (this.isArrayBacked()) {
return new RawReverseArrayContainerCharIterator(this);
}
return new ReverseMappeableArrayContainerCharIterator(this);
}
@Override
public PeekableCharIterator getCharIterator() {
if (this.isArrayBacked()) {
return new RawArrayContainerCharIterator(this);
}
return new MappeableArrayContainerCharIterator(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, (char) begin);
if (indexstart < 0) {
indexstart = -indexstart - 1;
}
int indexend = BufferUtil.unsignedBinarySearch(content, indexstart, cardinality,
(char) (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()) {
CharBuffer destination = CharBuffer.allocate(newcardinality);
BufferUtil.arraycopy(content, 0, destination, 0, indexstart);
if (BufferUtil.isBackedBySimpleArray(content)) {
char[] destinationarray = destination.array();
for (int k = 0; k < rangelength; ++k) {
destinationarray[k + indexstart] = (char) (begin + k);
}
} else {
for (int k = 0; k < rangelength; ++k) {
destination.put(k + indexstart, (char) (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)) {
char[] contentarray = content.array();
for (int k = 0; k < rangelength; ++k) {
contentarray[k + indexstart] = (char) (begin + k);
}
} else {
for (int k = 0; k < rangelength; ++k) {
content.put(k + indexstart, (char) (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) {
char v = this.content.get(k);
this.content.put(pos, v);
pos += (int)value2.bitValue(v);
}
cardinality = pos;
return this;
}
@Override
public MappeableContainer iand(final MappeableRunContainer value2) {
PeekableCharIterator it = value2.getCharIterator();
int removed = 0;
for (int i = 0; i < cardinality; i++) {
it.advanceIfNeeded(content.get(i));
if (it.peekNext() == content.get(i)) {
content.put(i - removed, content.get(i));
} else {
removed++;
}
}
cardinality -= removed;
return 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.");
}
char[] c = this.content.array();
int pos = 0;
for (int k = 0; k < cardinality; ++k) {
char v = c[k];
c[pos] = v;
pos += 1 - (int)value2.bitValue(v);
}
this.cardinality = pos;
return this;
}
@Override
public MappeableContainer iandNot(final MappeableRunContainer value2) {
PeekableCharIterator it = value2.getCharIterator();
int removed = 0;
for (int i = 0; i < cardinality; i++) {
it.advanceIfNeeded(content.get(i));
if (it.peekNext() != content.get(i)) {
content.put(i - removed, content.get(i));
} else {
removed++;
}
}
cardinality -= removed;
return this;
}
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 newCapacity = calculateCapacity();
// 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 CharBuffer newContent = CharBuffer.allocate(newCapacity);
this.content.rewind();
newContent.put(this.content);
this.content = newContent;
}
private int calculateCapacity() {
int len = this.content.limit();
int newCapacity = (len == 0) ? DEFAULT_INIT_SIZE
: len < 64 ? len * 2 : len < 1067 ? len * 3 / 2 : len * 5 / 4;
return newCapacity;
}
private int calculateCapacity(int min){
int newCapacity = calculateCapacity();
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, (char) firstOfRange);
if (startIndex < 0) {
startIndex = -startIndex - 1;
}
int lastIndex =
BufferUtil.unsignedBinarySearch(content, startIndex, cardinality, (char) (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 CharBuffer buffer = CharBuffer.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 CharBuffer co = CharBuffer.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)) {
char[] sarray = value2.content.array();
for (int k = 0; k < value2.cardinality; ++k) {
char v = sarray[k];
final int i = (v) >>> 6;
bitArray[i] |= (1L << v);
}
} else {
for (int k = 0; k < value2.cardinality; ++k) {
char v2 = value2.content.get(k);
final int i = (v2) >>> 6;
bitArray[i] |= (1L << v2);
}
}
if (BufferUtil.isBackedBySimpleArray(content)) {
char[] sarray = content.array();
for (int k = 0; k < cardinality; ++k) {
char v = sarray[k];
final int i = (v) >>> 6;
bitArray[i] |= (1L << v);
}
} else {
for (int k = 0; k < cardinality; ++k) {
char v = content.get(k);
final int i = (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);
CharBuffer destination = CharBuffer.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, (char) begin);
if (indexstart < 0) {
indexstart = -indexstart - 1;
}
int indexend = BufferUtil.unsignedBinarySearch(content, indexstart, cardinality,
(char) (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() {
char pos = 0;
@Override
public boolean hasNext() {
return pos < MappeableArrayContainer.this.cardinality;
}
@Override
public Character 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();
}
}
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 CharBuffer 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 ((char) valInRange != content.get(inPos)) {
buffer.put(outPos++, (char) 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++, (char) valInRange);
}
if (outPos != buffer.limit()) {
throw new RuntimeException(
"negateRange: outPos " + outPos + " whereas buffer.length=" + 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 char 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, (char) firstOfRange);
if (startIndex < 0) {
startIndex = -startIndex - 1;
}
int lastIndex =
BufferUtil.unsignedBinarySearch(content, startIndex, cardinality, (char) (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.");
}
char[] 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 ((char) valInRange != content.get(inPos)) {
sarray[outPos++] = (char) valInRange;
} else {
++inPos;
}
}
for (; valInRange < lastOfRange; ++valInRange) {
answer.content.put(outPos++, (char) 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)) {
char[] c = content.array();
int numRuns = 1;
int oldv = (c[0]);
for (int i = 1; i < cardinality; i++) {
int newv = (c[i]);
if (oldv + 1 != newv) {
++numRuns;
}
oldv = newv;
}
return numRuns;
} else {
int numRuns = 1;
int previous = (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 = (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)) {
char[] sarray = value2.content.array();
for (int k = 0; k < value2.cardinality; ++k) {
char v = sarray[k];
final int i = (v) >>> 6;
bitArray[i] |= (1L << v);
}
} else {
for (int k = 0; k < value2.cardinality; ++k) {
char v2 = value2.content.get(k);
final int i = (v2) >>> 6;
bitArray[i] |= (1L << v2);
}
}
if (BufferUtil.isBackedBySimpleArray(this.content)) {
char[] sarray = this.content.array();
for (int k = 0; k < this.cardinality; ++k) {
char v = sarray[k];
final int i = (v) >>> 6;
bitArray[i] |= (1L << v);
}
} else {
for (int k = 0; k < this.cardinality; ++k) {
char v = this.content.get(k);
final int i = (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)) {
char[] sarray = value2.content.array();
for (int k = 0; k < value2.cardinality; ++k) {
char v = sarray[k];
final int i = (v) >>> 6;
bitArray[i] |= (1L << v);
}
} else {
for (int k = 0; k < value2.cardinality; ++k) {
char v2 = value2.content.get(k);
final int i = (v2) >>> 6;
bitArray[i] |= (1L << v2);
}
}
if (BufferUtil.isBackedBySimpleArray(this.content)) {
char[] sarray = this.content.array();
for (int k = 0; k < this.cardinality; ++k) {
char v = sarray[k];
final int i = (v) >>> 6;
bitArray[i] |= (1L << v);
}
} else {
for (int k = 0; k < this.cardinality; ++k) {
char v = this.content.get(k);
final int i = (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(CharIterator it) {
return or(it, false);
}
/**
* it must return items in (unsigned) sorted order. Possible candidate for Container interface?
**/
private MappeableContainer or(CharIterator 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 : (content.get(myItPos++));
int hisHead = advance(it);
while (myHead != -1 && hisHead != -1) {
if (myHead < hisHead) {
ac.emit((char) myHead);
myHead = (myItPos == cardinality) ? -1 : (content.get(myItPos++));
} else if (myHead > hisHead) {
ac.emit((char) hisHead);
hisHead = advance(it);
} else {
if (!exclusive) {
ac.emit((char) hisHead);
}
hisHead = advance(it);
myHead = (myItPos == cardinality) ? -1 : (content.get(myItPos++));
}
}
while (myHead != -1) {
ac.emit((char) myHead);
myHead = (myItPos == cardinality) ? -1 : (content.get(myItPos++));
}
while (hisHead != -1) {
ac.emit((char) hisHead);
hisHead = advance(it);
}
if (ac.cardinality > DEFAULT_MAX_SIZE) {
return ac.toBitmapContainer();
} else {
return ac;
}
}
@Override
public int rank(char 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 {
// little endian
this.cardinality = 0xFFFF & Character.reverseBytes(in.readChar());
if (this.content.limit() < this.cardinality) {
this.content = CharBuffer.allocate(this.cardinality);
}
for (int k = 0; k < this.cardinality; ++k) {
this.content.put(k, Character.reverseBytes(in.readChar()));
}
}
@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, (char) begin);
if (indexstart < 0) {
indexstart = -indexstart - 1;
}
int indexend = BufferUtil.unsignedBinarySearch(content, indexstart, cardinality,
(char) (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;
}
void removeAtIndex(final int loc) {
System.arraycopy(content.array(), loc + 1, content.array(), loc, cardinality - loc - 1);
--cardinality;
}
@Override
public MappeableContainer remove(final char 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 char 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 (this.select(0));
}
@Override
public int last() {
assertNonEmpty(cardinality == 0);
return (select(cardinality - 1));
}
@Override
public int nextValue(char fromValue) {
int index = BufferUtil.advanceUntil(content, -1, cardinality, fromValue);
if (index == cardinality) {
return fromValue == content.get(cardinality - 1) ? (fromValue) : -1;
}
return (content.get(index));
}
@Override
public int previousValue(char fromValue) {
int index = BufferUtil.advanceUntil(content, -1, cardinality, fromValue);
if (index != cardinality && content.get(index) == fromValue) {
return (content.get(index));
}
return index == 0 ? -1 : (content.get(index - 1));
}
@Override
public int nextAbsentValue(char fromValue) {
int index = BufferUtil.advanceUntil(content, -1, cardinality, fromValue);
if (index >= cardinality) {
return fromValue;
}
if (index == cardinality - 1) {
return fromValue == content.get(cardinality - 1) ? fromValue + 1 : fromValue;
}
if (content.get(index) != fromValue) {
return fromValue;
}
if (content.get(index + 1) > fromValue + 1) {
return fromValue + 1;
}
int low = index;
int high = cardinality;
while (low + 1 < high) {
int mid = (high + low) >>> 1;
if (mid - index < (content.get(mid)) - fromValue) {
high = mid;
} else {
low = mid;
}
}
if (low == cardinality - 1) {
return (content.get(cardinality - 1)) + 1;
}
assert (content.get(low)) + 1 < (content.get(high));
assert (content.get(low)) == fromValue + (low - index);
return (content.get(low)) + 1;
}
@Override
public int previousAbsentValue(char fromValue) {
int index = BufferUtil.advanceUntil(content, -1, cardinality, fromValue);
if (index >= cardinality) {
return fromValue;
}
if (index == 0) {
return fromValue == content.get(0) ? fromValue - 1 : fromValue;
}
if (content.get(index) != fromValue) {
return fromValue;
}
if (content.get(index - 1) < fromValue - 1) {
return fromValue - 1;
}
int low = -1;
int high = index;
// Binary search for the first index which differs by at least 2 from its
// successor
while (low + 1 < high) {
int mid = (high + low) >>> 1;
if (index - mid < fromValue - (content.get(mid))) {
low = mid;
} else {
high = mid;
}
}
if (high == 0) {
return (content.get(0)) - 1;
}
assert (content.get(low)) + 1 < (content.get(high));
assert (content.get(high)) == fromValue - (index - high);
return (content.get(high)) - 1;
}
@Override
public Container toContainer() {
return new ArrayContainer(this);
}
/**
* Create a copy of the content of this container as a char array. This creates a copy.
*
* @return copy of the content as a char array
*/
public char[] toShortArray() {
char[] answer = new char[cardinality];
content.rewind();
content.get(answer);
return answer;
}
@Override
public String toString() {
if (this.cardinality == 0) {
return "{}";
}
final StringBuilder sb = new StringBuilder("{}".length() + "-123456789,".length()
* cardinality);
sb.append('{');
for (int i = 0; i < this.cardinality - 1; i++) {
sb.append((int)(this.content.get(i)));
sb.append(',');
}
sb.append((int)(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 = CharBuffer.wrap(Arrays.copyOf(content.array(), cardinality));
} else {
final CharBuffer co = CharBuffer.allocate(this.cardinality);
// can assume that new co is array backed
char[] 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)) {
char[] a = content.array();
for (int k = 0; k < this.cardinality; ++k) {
out.writeShort(Character.reverseBytes(a[k]));
}
} else {
for (int k = 0; k < this.cardinality; ++k) {
out.writeShort(Character.reverseBytes(content.get(k)));
}
}
}
@Override
protected void writeArray(ByteBuffer buffer) {
assert buffer.order() == ByteOrder.LITTLE_ENDIAN;
CharBuffer target = buffer.asCharBuffer();
CharBuffer source = content.duplicate();
source.position(0);
source.limit(cardinality);
target.put(source);
int bytesWritten = 2 * cardinality;
buffer.position(buffer.position() + bytesWritten);
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.write(this.cardinality & 0xFF);
out.write((this.cardinality >>> 8) & 0xFF);
if (BufferUtil.isBackedBySimpleArray(content)) {
char[] a = content.array();
for (int k = 0; k < this.cardinality; ++k) {
out.writeShort(Character.reverseBytes(a[k]));
}
} else {
for (int k = 0; k < this.cardinality; ++k) {
out.writeShort(Character.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)) {
char[] sarray = value2.content.array();
for (int k = 0; k < value2.cardinality; ++k) {
char v = sarray[k];
final int i = (v) >>> 6;
bitArray[i] ^= (1L << v);
}
} else {
for (int k = 0; k < value2.cardinality; ++k) {
char v2 = value2.content.get(k);
final int i = (v2) >>> 6;
bitArray[i] ^= (1L << v2);
}
}
if (BufferUtil.isBackedBySimpleArray(this.content)) {
char[] sarray = this.content.array();
for (int k = 0; k < this.cardinality; ++k) {
char v = sarray[k];
final int i = (v) >>> 6;
bitArray[i] ^= (1L << v);
}
} else {
for (int k = 0; k < this.cardinality; ++k) {
char v = this.content.get(k);
final int i = (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(CharIterator it) {
return or(it, true);
}
@Override
public void forEach(char msb, IntConsumer ic) {
int high = ((int) msb) << 16;
if (BufferUtil.isBackedBySimpleArray(content)) {
char[] 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;
}
for (int i = 0; i < runContainer.numberOfRuns(); ++i) {
int start = (runContainer.getValue(i));
int length = (runContainer.getLength(i));
if (!contains(start, start + length)) {
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 (content.get(i1) < arrayContainer.content.get(i2)) {
++i1;
} else {
return false;
}
}
return i2 == arrayContainer.cardinality;
}
@Override
protected boolean contains(MappeableBitmapContainer bitmapContainer) {
return false;
}
@Override
public boolean intersects(int minimum, int supremum) {
if((minimum < 0) || (supremum < minimum) || (supremum > (1<<16))) {
throw new RuntimeException("This should never happen (bug).");
}
int pos = BufferUtil.unsignedBinarySearch(content, 0, cardinality, (char)minimum);
int index = pos >= 0 ? pos : -pos - 1;
return index < cardinality && (content.get(index)) < supremum;
}
@Override
public boolean contains(int minimum, int supremum) {
int maximum = supremum - 1;
int start = BufferUtil.advanceUntil(content, -1, cardinality, (char)minimum);
int end = BufferUtil.advanceUntil(content, start - 1, cardinality, (char)maximum);
return start < cardinality
&& end < cardinality
&& end - start == maximum - minimum
&& content.get(start) == (char)minimum
&& content.get(end) == (char)maximum;
}
}
final class MappeableArrayContainerCharIterator implements PeekableCharIterator {
int pos;
private MappeableArrayContainer parent;
MappeableArrayContainerCharIterator() {
}
MappeableArrayContainerCharIterator(MappeableArrayContainer p) {
wrap(p);
}
@Override
public void advanceIfNeeded(char minval) {
pos = BufferUtil.advanceUntil(parent.content, pos - 1, parent.cardinality, minval);
}
@Override
public PeekableCharIterator clone() {
try {
return (PeekableCharIterator) super.clone();
} catch (CloneNotSupportedException e) {
return null;// will not happen
}
}
@Override
public boolean hasNext() {
return pos < parent.cardinality;
}
@Override
public char next() {
return parent.content.get(pos++);
}
@Override
public int nextAsInt() {
return (parent.content.get(pos++));
}
@Override
public char 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 RawArrayContainerCharIterator implements PeekableCharIterator {
int pos;
private MappeableArrayContainer parent;
char[] content;
RawArrayContainerCharIterator(MappeableArrayContainer p) {
parent = p;
if (!p.isArrayBacked()) {
throw new RuntimeException("internal bug");
}
content = p.content.array();
pos = 0;
}
@Override
public void advanceIfNeeded(char minval) {
pos = Util.advanceUntil(content, pos - 1, parent.cardinality, minval);
}
@Override
public PeekableCharIterator clone() {
try {
return (PeekableCharIterator) super.clone();
} catch (CloneNotSupportedException e) {
return null;// will not happen
}
}
@Override
public boolean hasNext() {
return pos < parent.cardinality;
}
@Override
public char next() {
return content[pos++];
}
@Override
public int nextAsInt() {
return (content[pos++]);
}
@Override
public char peekNext() {
return content[pos];
}
@Override
public void remove() {
parent.removeAtIndex(pos - 1);
pos--;
}
}
final class RawReverseArrayContainerCharIterator implements CharIterator {
int pos;
private MappeableArrayContainer parent;
char[] content;
RawReverseArrayContainerCharIterator(MappeableArrayContainer p) {
parent = p;
if (!p.isArrayBacked()) {
throw new RuntimeException("internal bug");
}
content = p.content.array();
pos = parent.cardinality - 1;
}
@Override
public CharIterator clone() {
try {
return (CharIterator) super.clone();
} catch (CloneNotSupportedException e) {
return null;// will not happen
}
}
@Override
public boolean hasNext() {
return pos >= 0;
}
@Override
public char next() {
return content[pos--];
}
@Override
public int nextAsInt() {
return (content[pos--]);
}
@Override
public void remove() {
parent.removeAtIndex(pos + 1);
pos++;
}
}
final class ReverseMappeableArrayContainerCharIterator implements CharIterator {
int pos;
private MappeableArrayContainer parent;
ReverseMappeableArrayContainerCharIterator() {
}
ReverseMappeableArrayContainerCharIterator(MappeableArrayContainer p) {
wrap(p);
}
@Override
public CharIterator clone() {
try {
return (CharIterator) super.clone();
} catch (CloneNotSupportedException e) {
return null;// will not happen
}
}
@Override
public boolean hasNext() {
return pos >= 0;
}
@Override
public char next() {
return parent.content.get(pos--);
}
@Override
public int nextAsInt() {
return (parent.content.get(pos--));
}
@Override
public void remove() {
parent.removeAtIndex(pos + 1);
pos++;
}
void wrap(MappeableArrayContainer p) {
parent = p;
pos = parent.cardinality - 1;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy