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

com.artemis.backends.gwt.emu.java.util.BitSet Maven / Gradle / Ivy

There is a newer version: 2.3.0
Show newest version
/*
 * 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.artemis.utils.reflect.ClassReflection;
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 implements Cloneable {
	// 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 (!ClassReflection.isInstance(BitSet.class, obj)) {
				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 - 2025 Weber Informatics LLC | Privacy Policy