org.apache.commons.jre.java.util.BitSet Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gwt-commons-codec Show documentation
Show all versions of gwt-commons-codec Show documentation
The Apache Commons Codec component contains encoders and decoders for
various formats such as Base16, Base32, Base64, digest, and Hexadecimal. In addition to these
widely used encoders and decoders, the codec package also maintains a
collection of phonetic encoding utilities.
This is a port for GWT, which enables program, to use Apache Commons Codec
also in the frontend compiled by the gwt compiler to java-script.
/*
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package java.util;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArrayInteger;
/**
* This implementation uses bit groups of size 32 to keep track of when bits are
* set to true or false. This implementation also uses the sparse nature of
* JavaScript arrays to speed up cases when very few bits are set in a large bit
* set.
*
* Since there is no speed advantage to pre-allocating array sizes in JavaScript
* the underlying array's length is shrunk to Sun's "logical length" whenever
* length() is called. This length is the index of the highest true bit, plus
* one, or zero if there are aren't any. This may cause the size() method to
* return a different size than in a true Java VM.
*/
public class BitSet {
// To speed up certain operations this class also uses the index properties
// of arrays as described in section 15.4 of "Standard ECMA-262" (June 1997),
// which can currently be found here:
// http://www.mozilla.org/js/language/E262.pdf
//
// 15.4 Array Objects
// Array objects give special treatment to a certain class of property names.
// A property name P (in the form of a string value) is an array index if and
// only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal
// to (2^32)-1.
// checks the index range
private static void checkIndex(int bitIndex) {
// we only need to test for negatives, as there is no bit index too high.
if (bitIndex < 0) {
throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex);
}
}
// checks to ensure indexes are not negative and not in reverse order
private static void checkRange(int fromIndex, int toIndex) {
if (fromIndex < 0) {
throw new IndexOutOfBoundsException("fromIndex < 0: " + fromIndex);
}
if (toIndex < 0) {
throw new IndexOutOfBoundsException("toIndex < 0: " + toIndex);
}
if (fromIndex > toIndex) {
throw new IndexOutOfBoundsException("fromIndex: " + fromIndex
+ " > toIndex: " + toIndex);
}
}
// converts from a bit index to a word index
private static int wordIndex(int bitIndex) {
// 32 bits per index
return bitIndex >>> 5;
}
// converts from a word index to a bit index
private static int bitIndex(int wordIndex) {
// 1 word index for every 32 bit indexes
return wordIndex << 5;
}
// gives the word offset for a bit index
private static int bitOffset(int bitIndex) {
return bitIndex & 0x1f;
}
//
// none of the following static method perform any bounds checking
//
// clears one bit
private static void clear(JsArrayInteger array, int bitIndex) {
int index = wordIndex(bitIndex);
int word = getWord(array, index);
if (word != 0) {
// mask the correct bit out
setWord(array, index, word & ~(1 << (bitOffset(bitIndex))));
}
}
// clones the JSArrayInteger array
private static native JsArrayInteger clone(JsArrayInteger array) /*-{
return array.slice(0);
}-*/;
// flips one bit
private static void flip(JsArrayInteger array, int bitIndex) {
// calculate index and offset
int index = wordIndex(bitIndex);
int offset = bitOffset(bitIndex);
// figure out if the bit is on or off
int word = getWord(array, index);
if (((word >>> offset) & 1) == 1) {
// if on, turn it off
setWord(array, index, word & ~(1 << offset));
} else {
// if off, turn it on
array.set(index, word | (1 << offset));
}
};
// gets one bit
private static boolean get(JsArrayInteger array, int bitIndex) {
// retrieve the bits for the given index
int word = getWord(array, wordIndex(bitIndex));
// shift and mask the bit out
return ((word >>> (bitOffset(bitIndex))) & 1) == 1;
}
// sets one bit to true
private static void set(JsArrayInteger array, int bitIndex) {
int index = wordIndex(bitIndex);
array.set(index, getWord(array, index) | (1 << (bitOffset(bitIndex))));
}
// sets all bits to true within the given range
private static void set(JsArrayInteger array, int fromIndex, int toIndex) {
int first = wordIndex(fromIndex);
int last = wordIndex(toIndex);
int startBit = bitOffset(fromIndex);
int endBit = bitOffset(toIndex);
if (first == last) {
// set the bits in between first and last
maskInWord(array, first, startBit, endBit);
} else {
// set the bits from fromIndex to the next 32 bit boundary
if (startBit != 0) {
maskInWord(array, first++, startBit, 32);
}
// set the bits from the last 32 bit boundary to the toIndex
if (endBit != 0) {
maskInWord(array, last, 0, endBit);
}
//
// set everything in between
//
for (int i = first; i < last; i++) {
array.set(i, 0xffffffff);
}
}
}
// copies a subset of the array
private static native JsArrayInteger slice(JsArrayInteger array,
int fromIndex, int toIndex) /*-{
return array.slice(fromIndex, toIndex);
}-*/;
// trims the array to the minimum size it can without losing data
// returns index of the last element in the array, or -1 if empty
private static native int trimToSize(JsArrayInteger array) /*-{
var length = array.length;
if (length === 0) {
return -1;
}
// check if the last bit is false
var last = length - 1;
if (array[last] !== undefined) {
return last;
}
// interleave property checks and linear index checks from the end
var biggestSeen = -1;
for (var property in array) {
// test the index first
if (--last === -1) {
return -1;
}
if (array[last] !== undefined) {
return last;
}
// now check the property
var number = property >>> 0;
if (String(number) == property && number !== 0xffffffff) {
if (number > biggestSeen) {
biggestSeen = number;
}
}
}
array.length = biggestSeen + 1
return biggestSeen;
}-*/;
//
// word methods use the literal index into the array, not the bit index
//
// deletes an element from the array
private static native void deleteWord(JsArrayInteger array, int index) /*-{
delete array[index];
}-*/;
// flips all bits stored at a certain index
private static void flipWord(JsArrayInteger array, int index) {
int word = getWord(array, index);
if (word == 0) {
array.set(index, 0xffffffff);
} else {
word = ~word;
setWord(array, index, word);
}
}
// flips all bits stored at a certain index within the given range
private static void flipMaskedWord(JsArrayInteger array, int index,
int from, int to) {
if (from == to) {
return;
}
// get the bits
int word = getWord(array, index);
// adjust "to" so it will shift out those bits
to = 32 - to;
// create a mask and XOR it in
word ^= (((0xffffffff >>> from) << from) << to) >>> to;
setWord(array, index, word);
}
// returns all bits stored at a certain index
private static native int getWord(JsArrayInteger array, int index) /*-{
// OR converts an undefined to 0
return array[index] | 0;
}-*/;
// sets all bits to true at a certain index within the given bit range
private static void maskInWord(JsArrayInteger array, int index,
int from, int to) {
// shifting by 32 is the same as shifting by 0, this check prevents that
// from happening in addition to the obvious avoidance of extra work
if (from != to) {
// adjust "to" so it will shift out those bits
to = 32 - to;
// create a mask and OR it in
int value = getWord(array, index);
value |= ((0xffffffff >>> from) << (from + to)) >>> to;
array.set(index, value);
}
};
// sets all bits to false at a certain index within the given bit range
private static void maskOutWord(JsArrayInteger array, int index,
int from, int to) {
int word = getWord(array, index);
// something only happens if word has bits set
if (word != 0) {
// create a mask
int mask;
if (from != 0) {
mask = 0xffffffff >>> (32 - from);
} else {
mask = 0;
}
// shifting by 32 is the same as shifting by 0
if (to != 32) {
mask |= 0xffffffff << to;
}
// mask it out
word &= mask;
setWord(array, index, word);
}
}
private static native int nextSetWord(JsArrayInteger array, int index) /*-{
// interleave property checks and linear "index" checks
var length = array.length;
var localMinimum = @java.lang.Integer::MAX_VALUE;
for (var property in array) {
// test the index first
if (array[index] !== undefined) {
return index;
}
if (++index >= length) {
return -1;
}
// now check the property
var number = property >>> 0;
if (String(number) == property && number !== 0xffffffff) {
if (number >= index && number < localMinimum) {
localMinimum = number;
}
}
}
// if local minimum is what we started at, we found nothing
if (localMinimum === @java.lang.Integer::MAX_VALUE) {
return -1;
}
return localMinimum;
}-*/;
// sets all bits at a certain index to the given value
private static void setWord(JsArrayInteger array, int index, int value) {
// keep 0s out of the array
if (value == 0) {
deleteWord(array, index);
} else {
array.set(index, value);
}
}
// sets the array length
private static native void setLengthWords(JsArrayInteger array, int length) /*-{
array.length = length;
}-*/;
// our array of bits
private JsArrayInteger array;
public BitSet() {
// create a new array
array = JavaScriptObject.createArray().cast();
}
public BitSet(int nbits) {
this();
// throw an exception to be consistent
// but (do we want to be consistent?)
if (nbits < 0) {
throw new NegativeArraySizeException("nbits < 0: " + nbits);
}
// even though the array's length is loosely kept to that of Sun's "logical
// length," this might help in some cases where code uses size() to fill in
// bits after constructing a BitSet, or after having one passed in as a
// parameter.
setLengthWords(array, wordIndex(nbits + 31));
}
private BitSet(JsArrayInteger array) {
this.array = array;
}
public void and(BitSet set) {
// a & a is just a
if (this == set) {
return;
}
// trim the second set to avoid extra work
trimToSize(set.array);
// check if the length is longer than otherLength
int otherLength = set.array.length();
if (array.length() > otherLength) {
// shrink the array, effectively ANDing those bits to false
setLengthWords(array, otherLength);
}
// truth table
//
// case | a | b | a & b | change?
// 1 | false | false | false | a is already false
// 2 | false | true | false | a is already false
// 3 | true | false | false | set a to false
// 4 | true | true | true | a is already true
//
// we only need to change something in case 3, so iterate over set a
int index = 0;
while ((index = nextSetWord(array, index)) != -1) {
setWord(array, index,
array.get(index) & getWord(set.array, index));
index++;
}
}
public void andNot(BitSet set) {
// a & !a is false
if (this == set) {
// all falses result in an empty BitSet
clear();
return;
}
// trim the second set to avoid extra work
trimToSize(array);
int length = array.length();
// truth table
//
// case | a | b | !b | a & !b | change?
// 1 | false | false | true | false | a is already false
// 2 | false | true | false | false | a is already false
// 3 | true | false | true | true | a is already true
// 4 | true | true | false | false | set a to false
//
// we only need to change something in case 4
// whenever b is true, a should be false, so iterate over set b
int index = 0;
while ((index = nextSetWord(set.array, index)) != -1) {
setWord(array, index,
getWord(array, index) & ~set.array.get(index));
if (++index >= length) {
// nothing further will affect anything
break;
}
}
}
public native int cardinality() /*-{
var count = 0;
var array = [email protected]::array;
for (var property in array) {
var number = property >>> 0;
if (String(number) == property && number !== 0xffffffff) {
count += @java.lang.Integer::bitCount(I)(array[number]);
}
}
return count;
}-*/;
public void clear() {
// create a new array
array = JavaScriptObject.createArray().cast();
}
public void clear(int bitIndex) {
checkIndex(bitIndex);
clear(array, bitIndex);
}
public void clear(int fromIndex, int toIndex) {
checkRange(fromIndex, toIndex);
int length = length();
if (fromIndex >= length) {
// nothing to do
return;
}
// check to see if toIndex is greater than our array length
if (toIndex >= length) {
// truncate the array by setting it's length
int newLength = wordIndex(fromIndex + 31);
setLengthWords(array, newLength);
// remove the extra bits off the end
if ((bitIndex(newLength)) - fromIndex != 0) {
maskOutWord(array, newLength - 1, bitOffset(fromIndex), 32);
}
} else {
int first = wordIndex(fromIndex);
int last = wordIndex(toIndex);
int startBit = bitOffset(fromIndex);
int endBit = bitOffset(toIndex);
if (first == last) {
// clear the bits in between first and last
maskOutWord(array, first, startBit, endBit);
} else {
// clear the bits from fromIndex to the next 32 bit boundary
if (startBit != 0) {
maskOutWord(array, first++, startBit, 32);
}
// clear the bits from the last 32 bit boundary to the toIndex
if (endBit != 0) {
maskOutWord(array, last, 0, endBit);
}
//
// delete everything in between
//
for (int i = first; i < last; i++) {
deleteWord(array, i);
}
}
}
}
public Object clone() {
return new BitSet(clone(array));
}
@Override
public boolean equals(Object obj) {
if (this != obj) {
if (!(obj instanceof BitSet)) {
return false;
}
BitSet other = (BitSet) obj;
int last = trimToSize(array);
if (last != trimToSize(other.array)) {
return false;
}
int index = 0;
while ((index = nextSetWord(array, index)) != -1) {
if (getWord(array, index) != getWord(other.array, index)) {
return false;
}
index++;
}
}
return true;
}
public void flip(int bitIndex) {
checkIndex(bitIndex);
flip(array, bitIndex);
}
public void flip(int fromIndex, int toIndex) {
checkRange(fromIndex, toIndex);
int length = length();
// if we are flipping bits beyond our length, we are setting them to true
if (fromIndex >= length) {
set(array, fromIndex, toIndex);
return;
}
// check to see if toIndex is greater than our array length
if (toIndex >= length) {
set(array, length, toIndex);
toIndex = length;
}
int first = wordIndex(fromIndex);
int last = wordIndex(toIndex);
int startBit = bitOffset(fromIndex);
int end = bitOffset(toIndex);
if (first == last) {
// flip the bits in between first and last
flipMaskedWord(array, first, startBit, end);
} else {
// clear the bits from fromIndex to the next 32 bit boundary
if (startBit != 0) {
flipMaskedWord(array, first++, startBit, 32);
}
// clear the bits from the last 32 bit boundary to the toIndex
if (end != 0) {
flipMaskedWord(array, last, 0, end);
}
// flip everything in between
for (int i = first; i < last; i++) {
flipWord(array, i);
}
}
}
public boolean get(int bitIndex) {
checkIndex(bitIndex);
return get(array, bitIndex);
}
public BitSet get(int fromIndex, int toIndex) {
checkRange(fromIndex, toIndex);
// no need to go past our length
int length = length();
if (toIndex >= length) {
toIndex = length();
}
// this is the bit shift offset for each group of bits
int rightShift = bitOffset(fromIndex);
if (rightShift == 0) {
int subFrom = wordIndex(fromIndex);
int subTo = wordIndex(toIndex + 31);
JsArrayInteger subSet = slice(array, subFrom, subTo);
int leftOvers = bitOffset(toIndex);
if (leftOvers != 0) {
maskOutWord(subSet, subTo - subFrom - 1, leftOvers, 32);
}
return new BitSet(subSet);
}
BitSet subSet = new BitSet();
int first = wordIndex(fromIndex);
int last = wordIndex(toIndex);
if (first == last) {
// number of bits to cut from the end
int end = 32 - (bitOffset(toIndex));
// raw bits
int word = getWord(array, first);
// shift out those bits
word = ((word << end) >>> end) >>> rightShift;
// set it
if (word != 0) {
subSet.set(0, word);
}
} else {
// this will hold the newly packed bits
int current = 0;
// this is the raw index into the sub set
int subIndex = 0;
// fence post, carry over initial bits
int word = getWord(array, first++);
current = word >>> rightShift;
// a left shift will be used to shift our bits to the top of "current"
int leftShift = 32 - rightShift;
// loop through everything in the middle
for (int i = first; i <= last; i++) {
word = getWord(array, i);
// shift out the bits from the top, OR them into current bits
current |= word << leftShift;
// flush it out
if (current != 0) {
subSet.array.set(subIndex, current);
}
// keep track of our index
subIndex++;
// carry over the unused bits
current = word >>> rightShift;
}
// fence post, flush out the extra bits, but don't go past the "end"
int end = 32 - (bitOffset(toIndex));
current = (current << (rightShift + end)) >>> (rightShift + end);
if (current != 0) {
subSet.array.set(subIndex, current);
}
}
return subSet;
}
/**
* This hash is different than the one described in Sun's documentation. The
* described hash uses 64 bit integers and that's not practical in JavaScript.
*/
@Override
public int hashCode() {
// FNV constants
final int fnvOffset = 0x811c9dc5;
final int fnvPrime = 0x1000193;
// initialize
final int last = trimToSize(array);
int hash = fnvOffset ^ last;
// loop over the data
for (int i = 0; i <= last; i++) {
int value = getWord(array, i);
// hash one byte at a time using FNV1
hash = (hash * fnvPrime) ^ (value & 0xff);
hash = (hash * fnvPrime) ^ ((value >>> 8) & 0xff);
hash = (hash * fnvPrime) ^ ((value >>> 16) & 0xff);
hash = (hash * fnvPrime) ^ (value >>> 24);
}
return hash;
}
public boolean intersects(BitSet set) {
int last = trimToSize(array);
if (this == set) {
// if it has any bits then it intersects itself
return last != -1;
}
int length = set.array.length();
int index = 0;
while ((index = nextSetWord(array, index)) != -1) {
if ((array.get(index) & getWord(set.array, index)) != 0) {
return true;
}
if (++index >= length) {
// nothing further can intersect
break;
}
}
return false;
}
public boolean isEmpty() {
return length() == 0;
}
public int length() {
int last = trimToSize(array);
if (last == -1) {
return 0;
}
// compute the position of the leftmost bit's index
int offsets[] = { 16, 8, 4, 2, 1 };
int bitMasks[] = { 0xffff0000, 0xff00, 0xf0, 0xc, 0x2 };
int position = bitIndex(last) + 1;
int word = getWord(array, last);
for (int i = 0; i < offsets.length; i++) {
if ((word & bitMasks[i]) != 0) {
word >>>= offsets[i];
position += offsets[i];
}
}
return position;
}
public int nextClearBit(int fromIndex) {
checkIndex(fromIndex);
int index = wordIndex(fromIndex);
// special case for first index
int fromBit = fromIndex - (bitIndex(index));
int word = getWord(array, index);
for (int i = fromBit; i < 32; i++) {
if ((word & (1 << i)) == 0) {
return (bitIndex(index)) + i;
}
}
// loop through the rest
while (true) {
index++;
word = getWord(array, index);
if (word != 0xffffffff) {
return (bitIndex(index)) + Integer.numberOfTrailingZeros(~word);
}
}
}
public int nextSetBit(int fromIndex) {
checkIndex(fromIndex);
int index = wordIndex(fromIndex);
// check the current word
int word = getWord(array, index);
if (word != 0) {
for (int i = bitOffset(fromIndex); i < 32; i++) {
if ((word & (1 << i)) != 0) {
return (bitIndex(index)) + i;
}
}
}
index++;
// find the next set word
trimToSize(array);
index = nextSetWord(array, index);
if (index == -1) {
return -1;
}
// return the next set bit
return (bitIndex(index)) + Integer.numberOfTrailingZeros(array.get(index));
};
public void or(BitSet set) {
// a | a is just a
if (this == set) {
return;
}
// truth table
//
// case | a | b | a | b | change?
// 1 | false | false | false | a is already false
// 2 | false | true | true | set a to true
// 3 | true | false | true | a is already true
// 4 | true | true | true | a is already true
//
// we only need to change something in case 2
// case 2 only happens when b is true, so iterate over set b
int index = 0;
while ((index = nextSetWord(set.array, index)) != -1) {
setWord(array, index,
getWord(array, index) | set.array.get(index));
index++;
}
}
public void set(int bitIndex) {
checkIndex(bitIndex);
set(array, bitIndex);
}
public void set(int bitIndex, boolean value) {
if (value == true) {
set(bitIndex);
} else {
clear(bitIndex);
}
}
public void set(int fromIndex, int toIndex) {
checkRange(fromIndex, toIndex);
set(array, fromIndex, toIndex);
}
public void set(int fromIndex, int toIndex, boolean value) {
if (value == true) {
set(fromIndex, toIndex);
} else {
clear(fromIndex, toIndex);
}
}
public int size() {
// the number of bytes that can fit without using "more" memory
return bitIndex(array.length());
}
@Override
public String toString() {
// possibly faster if done in JavaScript and all numerical properties are
// put into an array and sorted
int length = length();
if (length == 0) {
// a "length" of 0 means there are no bits set to true
return "{}";
}
StringBuilder sb = new StringBuilder("{");
// at this point, there is at least one true bit, nextSetBit can not fail
int next = nextSetBit(0);
sb.append(next);
// loop until nextSetBit returns -1
while ((next = nextSetBit(next + 1)) != -1) {
sb.append(", ");
sb.append(next);
}
sb.append("}");
return sb.toString();
}
public void xor(BitSet set) {
// a ^ a is false
if (this == set) {
// this results in an empty BitSet
clear();
return;
}
// truth table
//
// case | a | b | a ^ b | change?
// 1 | false | false | false | a is already false
// 2 | false | true | true | set a to true
// 3 | true | false | true | a is already true
// 4 | true | true | false | set a to false
//
// we need to change something in cases 2 and 4
// cases 2 and 4 only happen when b is true, so iterate over set b
int index = 0;
while ((index = nextSetWord(set.array, index)) != -1) {
setWord(array, index,
getWord(array, index) ^ set.array.get(index));
index++;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy