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

org.apache.commons.jre.java.util.BitSet Maven / Gradle / Ivy

Go to download

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.

There is a newer version: 1.17.1-1
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.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