diff --git a/com.ibm.wala.core/src/com/ibm/wala/util/intset/BitVector.java b/com.ibm.wala.core/src/com/ibm/wala/util/intset/BitVector.java index ef116ff3d..d37a11d40 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/util/intset/BitVector.java +++ b/com.ibm.wala.core/src/com/ibm/wala/util/intset/BitVector.java @@ -18,23 +18,9 @@ import com.ibm.wala.util.debug.Assertions; * @author sfink * */ -public class BitVector implements Cloneable, Serializable { +public final class BitVector extends BitVectorBase { private static final long serialVersionUID = 9087259335807761617L; - private final static int LOG_BITS_PER_UNIT = 5; - private final static int BITS_PER_UNIT = 32; - private final static int MASK = 0xffffffff; - private final static int LOW_MASK = 0x1f; - int bits[]; - - private final static boolean DEBUG = false; - - /** - * Convert bitIndex to a subscript into the bits[] array. - */ - public static int subscript(int bitIndex) { - return bitIndex >> LOG_BITS_PER_UNIT; - } public BitVector() { this(1); @@ -72,15 +58,6 @@ public class BitVector implements Cloneable, Serializable { copyBits(s); } - /** - * Sets all bits. - */ - public final void setAll() { - for (int i = 0; i < bits.length; i++) { - bits[i] = MASK; - } - } - /** * Sets a bit. * @@ -102,15 +79,6 @@ public class BitVector implements Cloneable, Serializable { } } - /** - * Clears all bits. - */ - public final void clearAll() { - for (int i = 0; i < bits.length; i++) { - bits[i] = 0; - } - } - /** * Clears a bit. * @@ -144,15 +112,6 @@ public class BitVector implements Cloneable, Serializable { return ((bits[ss] & (1 << shiftBits)) != 0); } - /** - * Logically NOT this bit string - */ - public final void not() { - for (int i = 0; i < bits.length; i++) { - bits[i] ^= MASK; - } - } - /** * Return the NOT of a bit string */ @@ -287,45 +246,6 @@ public class BitVector implements Cloneable, Serializable { return true; } - /** - * Copies the values of the bits in the specified set into this set. - * - * @param set - * the bit set to copy the bits from - */ - public final void copyBits(BitVector set) { - int setLength = set.bits.length; - bits = new int[setLength]; - for (int i = setLength - 1; i >= 0;) { - bits[i] = set.bits[i]; - i--; - } - - } - - /** - * Gets the hashcode. - */ - public int hashCode() { - int h = 1234; - for (int i = bits.length - 1; i >= 0;) { - h ^= bits[i] * (i + 1); - i--; - } - return h; - } - - /** - * How many bits are set? - */ - public final int populationCount() { - int count = 0; - for (int i = 0; i < bits.length; i++) { - count += Bits.populationCount(bits[i]); - } - return count; - } - /** * Calculates and returns the set's size in bits. The maximum element in the * set is the size - 1st element. @@ -366,135 +286,6 @@ public class BitVector implements Cloneable, Serializable { return true; } - /** - * Compares this object against the specified object. - * - * @param obj - * the object to compare with - * @return true if the objects are the same; false otherwise. - */ - public boolean equals(Object obj) { - if ((obj != null) && (obj instanceof BitVector)) { - if (this == obj) { // should help alias analysis - return true; - } - BitVector set = (BitVector) obj; - return sameBits(set); - } - return false; - } - - public boolean isZero() { - int setLength = bits.length; - for (int i = setLength - 1; i >= 0;) { - if (bits[i] != 0) - return false; - i--; - } - return true; - } - - /** - * Clones the FixedSizeBitVector. - */ - public Object clone() { - BitVector result = null; - try { - result = (BitVector) super.clone(); - } catch (CloneNotSupportedException e) { - // this shouldn't happen, since we are Cloneable - throw new InternalError(); - } - result.bits = new int[bits.length]; - System.arraycopy(bits, 0, result.bits, 0, result.bits.length); - return result; - } - - /** - * Converts the FixedSizeBitVector to a String. - */ - public String toString() { - StringBuffer buffer = new StringBuffer(); - boolean needSeparator = false; - buffer.append('{'); - int limit = length(); - for (int i = 0; i < limit; i++) { - if (get(i)) { - if (needSeparator) { - buffer.append(", "); - } else { - needSeparator = true; - } - buffer.append(i); - } - } - buffer.append('}'); - return buffer.toString(); - } - - /* - * (non-Javadoc) - * - * @see com.ibm.wala.util.intset.IntSet#contains(int) - */ - public boolean contains(int i) { - return get(i); - } - - private static final int[][] masks = new int[][] { - { 0xFFFF0000 }, - { 0xFF000000, 0x0000FF00 }, - { 0xF0000000, 0x00F00000, 0x0000F000, 0x000000F0 }, - { 0xC0000000, 0x0C000000, 0x00C00000, 0x000C0000, 0x0000C000, 0x00000C00, 0x000000C0, 0x0000000C}, - { 0x80000000, 0x20000000, 0x08000000, 0x02000000, 0x00800000, 0x00200000, 0x00080000, 0x00020000, 0x00008000, 0x00002000, 0x00000800, 0x00000200, 0x00000080, 0x00000020, 0x00000008, 0x00000002 } - }; - - public int max() { - int lastWord = bits.length - 1; - int count = lastWord * BITS_PER_UNIT; - - int top = bits[lastWord]; - // Assertions._assert(top != 0); - - int j = 0; - for (int i = 0; i < masks.length; i++) { - if ((top & masks[i][j]) != 0) { - j <<= 1; - } else { - j <<= 1; - j++; - } - } - - return count + (31-j); - } - - /** - * @param start - * @return min j >= start s.t get(j) - */ - public int nextSetBit(int start) { - int word = subscript(start); - int bit = (1 << (start & LOW_MASK)); - while (word < bits.length) { - if (bits[word] != 0) { - do { - if ((bits[word] & bit) != 0) - return start; - bit <<= 1; - start++; - } while (bit != 0); - } else { - start += (BITS_PER_UNIT - (start&LOW_MASK)); - } - - word++; - bit = 1; - } - - return -1; - } - /** * @param other * @return true iff this is a subset of other @@ -527,6 +318,42 @@ public class BitVector implements Cloneable, Serializable { bits[ai] = bits[ai] & (~vector.bits[bi]); } } + + /** + * Compares this object against the specified object. + * + * @param obj + * the object to compare with + * @return true if the objects are the same; false otherwise. + */ + public boolean equals(Object obj) { + if ((obj != null) && (obj instanceof BitVector)) { + if (this == obj) { // should help alias analysis + return true; + } + BitVector set = (BitVector) obj; + return sameBits(set); + } + return false; + } + + /** + * Sets all bits. + */ + public final void setAll() { + for (int i = 0; i < bits.length; i++) { + bits[i] = MASK; + } + } + + /** + * Logically NOT this bit string + */ + public final void not() { + for (int i = 0; i < bits.length; i++) { + bits[i] ^= MASK; + } + } /** * Return a new bit string as the AND of two others. diff --git a/com.ibm.wala.core/src/com/ibm/wala/util/intset/BitVectorBase.java b/com.ibm.wala.core/src/com/ibm/wala/util/intset/BitVectorBase.java new file mode 100644 index 000000000..363c56e13 --- /dev/null +++ b/com.ibm.wala.core/src/com/ibm/wala/util/intset/BitVectorBase.java @@ -0,0 +1,226 @@ +/******************************************************************************* + * Copyright (c) 2002 - 2006 IBM Corporation. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package com.ibm.wala.util.intset; + +import java.io.Serializable; + +import com.ibm.wala.util.debug.Assertions; + +/** + * @author sfink + * @author Julian Dolby (dolby@us.ibm.com) + */ +abstract public class BitVectorBase + implements Cloneable, Serializable +{ + + protected final static boolean DEBUG = false; + + protected final static int LOG_BITS_PER_UNIT = 5; + protected final static int BITS_PER_UNIT = 32; + protected final static int MASK = 0xffffffff; + protected final static int LOW_MASK = 0x1f; + + protected int bits[]; + + public abstract void set(int bit); + + public abstract void clear(int bit); + + public abstract boolean get(int bit); + + public abstract int length(); + + public abstract void and(T other); + + public abstract void andNot(T other); + + public abstract void or(T other); + + public abstract void xor(T other); + + public abstract boolean sameBits(T other); + + public abstract boolean isSubset(T other); + + public abstract boolean intersectionEmpty(T other); + + /** + * Convert bitIndex to a subscript into the bits[] array. + */ + public static int subscript(int bitIndex) { + return bitIndex >> LOG_BITS_PER_UNIT; + } + + /** + * Clears all bits. + */ + public final void clearAll() { + for (int i = 0; i < bits.length; i++) { + bits[i] = 0; + } + } + + + /** + * Gets the hashcode. + */ + public int hashCode() { + int h = 1234; + for (int i = bits.length - 1; i >= 0;) { + h ^= bits[i] * (i + 1); + i--; + } + return h; + } + + /** + * How many bits are set? + */ + public final int populationCount() { + int count = 0; + for (int i = 0; i < bits.length; i++) { + count += Bits.populationCount(bits[i]); + } + return count; + } + + public boolean isZero() { + int setLength = bits.length; + for (int i = setLength - 1; i >= 0;) { + if (bits[i] != 0) + return false; + i--; + } + return true; + } + + /** + * Clones the FixedSizeBitVector. + */ + public Object clone() { + BitVector result = null; + try { + result = (BitVector) super.clone(); + } catch (CloneNotSupportedException e) { + // this shouldn't happen, since we are Cloneable + throw new InternalError(); + } + result.bits = new int[bits.length]; + System.arraycopy(bits, 0, result.bits, 0, result.bits.length); + return result; + } + + /** + * Converts the FixedSizeBitVector to a String. + */ + public String toString() { + StringBuffer buffer = new StringBuffer(); + boolean needSeparator = false; + buffer.append('{'); + int limit = length(); + for (int i = 0; i < limit; i++) { + if (get(i)) { + if (needSeparator) { + buffer.append(", "); + } else { + needSeparator = true; + } + buffer.append(i); + } + } + buffer.append('}'); + return buffer.toString(); + } + + /* + * (non-Javadoc) + * + * @see com.ibm.wala.util.intset.IntSet#contains(int) + */ + public boolean contains(int i) { + return get(i); + } + + private static final int[][] masks = new int[][] { + { 0xFFFF0000 }, + { 0xFF000000, 0x0000FF00 }, + { 0xF0000000, 0x00F00000, 0x0000F000, 0x000000F0 }, + { 0xC0000000, 0x0C000000, 0x00C00000, 0x000C0000, 0x0000C000, 0x00000C00, 0x000000C0, 0x0000000C}, + { 0x80000000, 0x20000000, 0x08000000, 0x02000000, 0x00800000, 0x00200000, 0x00080000, 0x00020000, 0x00008000, 0x00002000, 0x00000800, 0x00000200, 0x00000080, 0x00000020, 0x00000008, 0x00000002 } + }; + + public int max() { + int lastWord = bits.length - 1; + + while (lastWord >= 0 && bits[lastWord] == 0) lastWord--; + + if (lastWord < 0) return -1; + + int count = lastWord * BITS_PER_UNIT; + + int top = bits[lastWord]; + + int j = 0; + for (int i = 0; i < masks.length; i++) { + if ((top & masks[i][j]) != 0) { + j <<= 1; + } else { + j <<= 1; + j++; + } + } + + return count + (31-j); + } + + /** + * @param start + * @return min j >= start s.t get(j) + */ + public int nextSetBit(int start) { + int word = subscript(start); + int bit = (1 << (start & LOW_MASK)); + while (word < bits.length) { + if (bits[word] != 0) { + do { + if ((bits[word] & bit) != 0) + return start; + bit <<= 1; + start++; + } while (bit != 0); + } else { + start += (BITS_PER_UNIT - (start&LOW_MASK)); + } + + word++; + bit = 1; + } + + return -1; + } + + /** + * Copies the values of the bits in the specified set into this set. + * + * @param set + * the bit set to copy the bits from + */ + public void copyBits(BitVectorBase set) { + int setLength = set.bits.length; + bits = new int[setLength]; + for (int i = setLength - 1; i >= 0;) { + bits[i] = set.bits[i]; + i--; + } + + } +} diff --git a/com.ibm.wala.core/src/com/ibm/wala/util/intset/DebuggingMutableIntSetFactory.java b/com.ibm.wala.core/src/com/ibm/wala/util/intset/DebuggingMutableIntSetFactory.java index fb6d0740b..872345e6f 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/util/intset/DebuggingMutableIntSetFactory.java +++ b/com.ibm.wala.core/src/com/ibm/wala/util/intset/DebuggingMutableIntSetFactory.java @@ -56,7 +56,7 @@ public class DebuggingMutableIntSetFactory implements MutableIntSetFactory { Assertions._assert( pr.sameValue( db.primaryImpl ) ); Assertions._assert( sr.sameValue( db.secondaryImpl ) ); - Assertions._assert( sr.sameValue( pr ) ); + Assertions._assert( pr.sameValue( sr ) ); return new DebuggingMutableIntSet(pr, sr); } else { diff --git a/com.ibm.wala.core/src/com/ibm/wala/util/intset/OffsetBitVector.java b/com.ibm.wala.core/src/com/ibm/wala/util/intset/OffsetBitVector.java new file mode 100644 index 000000000..021cabdfa --- /dev/null +++ b/com.ibm.wala.core/src/com/ibm/wala/util/intset/OffsetBitVector.java @@ -0,0 +1,467 @@ +/******************************************************************************* + * Copyright (c) 2002 - 2006 IBM Corporation. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package com.ibm.wala.util.intset; + +import java.io.Serializable; + +import com.ibm.wala.util.debug.Assertions; + +/** + * @author Julian Dolby (dolby@us.ibm.com) + * + */ +public final class OffsetBitVector extends BitVectorBase { + + int offset; + + private int wordDiff(int offset1, int offset2) { + return + (offset1>offset2)? + (offset1-offset2)>>LOG_BITS_PER_UNIT: + - ((offset2-offset1)>>LOG_BITS_PER_UNIT); + } + + /** + * Expand this bit vector to size newCapacity. + */ + private void expand(int newOffset, int newCapacity) { + int wordDiff = wordDiff(newOffset, offset); + + int[] oldbits = bits; + bits = new int[subscript(newCapacity) + 1]; + for (int i = 0; i < oldbits.length; i++) { + bits[i-wordDiff] = oldbits[i]; + } + offset = newOffset; + } + + /** + * @param set + */ + private void ensureCapacity(int newOffset, int newCapacity) { + if (newOffset < offset || newCapacity>(bits.length<< LOG_BITS_PER_UNIT)) { + expand(newOffset, newCapacity); + } + } + + private void ensureCapacity(OffsetBitVector set) { + int newOffset = Math.min(offset, set.offset); + int newCapacity = + Math.max(length(),set.length())-newOffset; + ensureCapacity(newOffset, newCapacity); + } + + public OffsetBitVector() { + this(0, 1); + } + + /** + * Creates an empty string with the specified size. + * + * @param nbits + * the size of the string + */ + public OffsetBitVector(int offset, int nbits) { + offset = (offset&~LOW_MASK); + this.offset = offset; + this.bits = new int[subscript(nbits) + 1]; + } + + /** + * Creates a copy of a Bit String + * + * @param s + * the string to copy + */ + public OffsetBitVector(OffsetBitVector s) { + offset = s.offset; + bits = new int[s.bits.length]; + copyBits(s); + } + + public String toString() { + return super.toString() + "(offset:" + offset + ")"; + } + + public int getOffset() { + return offset; + } + + /** + * Sets a bit. + * + * @param bit + * the bit to be set + */ + public final void set(int bit) { + int shiftBits; + int subscript; + if (bit < offset) { + int newOffset = bit&~LOW_MASK; + expand(newOffset, length()-newOffset); + shiftBits = bit & LOW_MASK; + subscript = 0; + } else { + bit -= offset; + shiftBits = bit & LOW_MASK; + subscript = subscript(bit); + if (subscript >= bits.length) { + expand(offset, bit); + } + } + + try { + bits[subscript] |= (1 << shiftBits); + } catch (RuntimeException e) { + e.printStackTrace(); + throw e; + } + } + + /** + * Clears a bit. + * + * @param bit + * the bit to be cleared + */ + public final void clear(int bit) { + if (bit < offset) { + return; + } + bit -= offset; + + int ss = subscript(bit); + if (ss >= bits.length) { + return; + } + int shiftBits = bit & LOW_MASK; + bits[ss] &= ~(1 << shiftBits); + } + + /** + * Gets a bit. + * + * @param bit + * the bit to be gotten + */ + public final boolean get(int bit) { + if (DEBUG) { + Assertions._assert(bit >= 0); + } + if (bit < offset) { + return false; + } + bit -= offset; + + int ss = subscript(bit); + if (ss >= bits.length) { + return false; + } + int shiftBits = bit & LOW_MASK; + return ((bits[ss] & (1 << shiftBits)) != 0); + } + + /** + * @param start + * @return min j >= start s.t get(j) + */ + public int nextSetBit(int start) { + int nb = super.nextSetBit(Math.max(0, start-offset)); + return nb == -1? -1: offset + nb; + } + + /** + * Logically NOT this bit string + */ + public final void not() { + if (offset != 0) { + expand(0, offset+length()); + } + for (int i = 0; i < bits.length; i++) { + bits[i] ^= MASK; + } + } + + public int max() { + return super.max() + offset; + } + + /** + * Calculates and returns the set's size in bits. The maximum element in the + * set is the size - 1st element. + */ + public final int length() { + return (bits.length << LOG_BITS_PER_UNIT) + offset; + } + + /** + * Sets all bits. + */ + public final void setAll() { + expand(0, length()); + for (int i = 0; i < bits.length; i++) { + bits[i] = MASK; + } + } + + /** + * Compares this object against the specified object. + * + * @param obj + * the object to compare with + * @return true if the objects are the same; false otherwise. + */ + public boolean equals(Object obj) { + if ((obj != null) && (obj instanceof OffsetBitVector)) { + if (this == obj) { // should help alias analysis + return true; + } + OffsetBitVector set = (OffsetBitVector) obj; + return sameBits(set); + } + return false; + } + + /** + * Check if the intersection of the two sets is empty + * + * @param other + * the set to check intersection with + */ + public final boolean intersectionEmpty(OffsetBitVector set) { + if (this == set) { + return isZero(); + } + + int wordDiff = wordDiff(offset, set.offset); + int maxWord = Math.min(bits.length, set.bits.length-wordDiff); + + int i = Math.max(0, -wordDiff); + + for ( ; i < maxWord; i++) { + if ((bits[i] & set.bits[i+wordDiff]) != 0) { + return false; + } + } + + return true; + } + + /** + * Compares this object against the specified object. + * + * @param B + * the object to compare with + * @return true if the objects are the same; false otherwise. + */ + public final boolean sameBits(OffsetBitVector B) { + if (this == B) { // should help alias analysis + return true; + } + int n = Math.min(bits.length, B.bits.length); + if (bits.length > B.bits.length) { + for (int i = n; i < bits.length; i++) { + if (bits[i] != 0) + return false; + } + } else if (B.bits.length > bits.length) { + for (int i = n; i < B.bits.length; i++) { + if (B.bits[i] != 0) + return false; + } + } + for (int i = n - 1; i >= 0;) { + if (bits[i] != B.bits[i]) { + return false; + } + i--; + } + return true; + } + + /** + * @param other + * @return true iff this is a subset of other + */ + public boolean isSubset(OffsetBitVector other) { + if (this == other) { // should help alias analysis + return true; + } + int wordDiff = wordDiff(offset, other.offset); + + int i = 0; + for ( ; i < -wordDiff; i++) { + if (other.bits[i] != 0) { + return false; + } + } + + int min = Math.min(bits.length, other.bits.length+wordDiff); + for ( ; i < min; i++) { + if ((bits[i] & ~other.bits[i-wordDiff]) != 0) { + return false; + } + } + + for ( ; i < bits.length; i++) { + if (bits[i] != 0) { + return false; + } + } + + return true; + } + + /** + * Copies the values of the bits in the specified set into this set. + * + * @param set + * the bit set to copy the bits from + */ + public final void copyBits(OffsetBitVector set) { + super.copyBits(set); + offset = set.offset; + } + + /** + * Logically ANDs this bit set with the specified set of bits. + * + * @param set + * the bit set to be ANDed with + */ + public final void and(OffsetBitVector set) { + if (this == set) { + return; + } + + int wordDiff = wordDiff(offset, set.offset); + int maxWord = Math.min(bits.length, set.bits.length+wordDiff); + + int i = 0; + + for ( ; i < -wordDiff; i++) { + bits[i] = 0; + } + + for ( ; i < maxWord; i++) { + bits[i] &= set.bits[i-wordDiff]; + } + + for ( ; i < bits.length; i++) { + bits[i] = 0; + } + } + + /** + * Logically ORs this bit set with the specified set of bits. + * + * @param set + * the bit set to be ORed with + */ + public final void or(OffsetBitVector set) { + if (this == set) { // should help alias analysis + return; + } + + int newOffset = Math.min(offset, set.offset); + int newCapacity = + Math.max(length(),set.length())-newOffset; + ensureCapacity(newOffset, newCapacity); + + int wordDiff = wordDiff(newOffset, set.offset); + + for (int i = 0; i < set.bits.length; i++) { + bits[i-wordDiff] |= set.bits[i]; + } + } + + /** + * Logically XORs this bit set with the specified set of bits. + * + * @param set + * the bit set to be XORed with + */ + public final void xor(OffsetBitVector set) { + if (this == set) { + clearAll(); + return; + } + + int newOffset = Math.min(offset, set.offset); + int newCapacity = + Math.max(length(),set.length())-newOffset; + ensureCapacity(newOffset, newCapacity); + + int wordDiff = wordDiff(newOffset, set.offset); + + for (int i = 0; i < set.bits.length; i++) { + bits[i-wordDiff] ^= set.bits[i]; + } + } + + /** + * @param vector + */ + public void andNot(OffsetBitVector set) { + if (this == set) { // should help alias analysis + return; + } + + int newOffset = Math.min(offset, set.offset); + int newCapacity = + Math.max(length(),set.length())-newOffset; + ensureCapacity(newOffset, newCapacity); + + int wordDiff = wordDiff(offset, set.offset); + + int n = Math.min(bits.length, set.bits.length+wordDiff); + for (int i = n - 1; i >= 0;) { + bits[i] &= ~(set.bits[i-wordDiff]); + i--; + } + } + + /** + * Return the NOT of a bit string + */ + public static OffsetBitVector not(OffsetBitVector s) { + OffsetBitVector b = new OffsetBitVector(s); + b.not(); + return b; + } + + /** + * Return a new bit string as the AND of two others. + */ + public static OffsetBitVector and(OffsetBitVector b1, OffsetBitVector b2) { + OffsetBitVector b = new OffsetBitVector(b1); + b.and(b2); + return b; + } + + /** + * Return a new FixedSizeBitVector as the OR of two others + */ + public static OffsetBitVector or(OffsetBitVector b1, OffsetBitVector b2) { + OffsetBitVector b = new OffsetBitVector(b1); + b.or(b2); + return b; + } + + /** + * Return a new bit string as the AND of two others. + */ + public static OffsetBitVector andNot(OffsetBitVector b1, OffsetBitVector b2) { + OffsetBitVector b = new OffsetBitVector(b1); + b.andNot(b2); + return b; + } +} diff --git a/com.ibm.wala.core/src/com/ibm/wala/util/intset/SemiSparseMutableIntSet.java b/com.ibm.wala.core/src/com/ibm/wala/util/intset/SemiSparseMutableIntSet.java new file mode 100644 index 000000000..50e4731ac --- /dev/null +++ b/com.ibm.wala.core/src/com/ibm/wala/util/intset/SemiSparseMutableIntSet.java @@ -0,0 +1,512 @@ +/******************************************************************************* + * Copyright (c) 2002 - 2006 IBM Corporation. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package com.ibm.wala.util.intset; + +import com.ibm.wala.util.collections.*; + +public class SemiSparseMutableIntSet implements MutableIntSet { + private static final int SPARSE_INSERT_THRESHOLD = 10; + + private MutableSparseIntSet sparsePart = new MutableSparseIntSet(); + private OffsetBitVector densePart = null; + private int sparseInsertCount = 0; + + private void fixAfterSparseInsert() { + if (sparseInsertCount++ > SPARSE_INSERT_THRESHOLD) { + sparseInsertCount = 0; + IntIterator sparseBits = sparsePart.intIterator(); + int thisBit = sparseBits.next(); + if (densePart == null) { + int maxOffset = -1; + int maxCount = -1; + int maxMax = -1; + int maxBit = -1; + + int offset = thisBit; + int bits = 32; + int count = 1; + while (sparseBits.hasNext()) { + int nextBit = sparseBits.next(); + + int newBits = bits + (nextBit - thisBit); + int newCount = count + 1; + + if (newBits > (32*newCount)) { + count = newCount; + bits = newBits; + } else if (bits > 32*count) { + if (count > maxCount) { + maxOffset = offset; + maxMax = thisBit; + maxCount = count; + } + offset = nextBit; + count = 1; + bits = 32; + } + thisBit = nextBit; + } + + if (maxOffset != -1) { + densePart = new OffsetBitVector(maxOffset, maxMax); + sparseBits = sparsePart.intIterator(); + int bit; + while ((bit = sparseBits.next()) != maxOffset); + for(int i = 0; i < maxCount; i++) { + densePart.set(sparseBits.next()); + } + for(int bit1 = densePart.nextSetBit(0); + bit1 != -1; + bit1 = densePart.nextSetBit(bit1+1)) + { + sparsePart.remove(bit1); + } + } + + } else { + int moveCount = 0; + int newOffset = -1; + int newLength = -1; + + // push stuff just below dense part into it, if it saves space + if (thisBit < densePart.getOffset()) { + newOffset = thisBit; + int bits = 32; + int count = 1; + while (sparseBits.hasNext()) { + int nextBit = sparseBits.next(); + if (nextBit >= densePart.getOffset()) { + if (bits > (32*count)) { + moveCount += count; + break; + } else { + newOffset = -1; + } + } else { + bits += (nextBit - thisBit); + count++; + + if (bits > (32*count)) { + newOffset = nextBit; + count = 1; + bits = 32; + } + } + } + } + + // push stuff just above dense part into it, if it saves space + if (thisBit >= densePart.length()) { + int count = 1; + int bits = (thisBit + 1 - densePart.length()); + if (32*count > bits) { + newLength = thisBit; + } + while (sparseBits.hasNext()) { + thisBit = sparseBits.next(); + count++; + bits = (thisBit + 1 - densePart.length()); + newLength = (32*count > bits)? thisBit: newLength; + } + if (newLength > -1) { + moveCount += count; + } + } + + // actually move bits from sparse to dense + if (newOffset != -1 || newLength != -1) { + int index = 0; + int[] bits = new int[ moveCount ]; + for(sparseBits = sparsePart.intIterator(); sparseBits.hasNext(); ) { + int bit = sparseBits.next(); + if (newOffset!=-1 && bit>=newOffset && bit<=densePart.getOffset()) { + bits[index++] = bit; + } + if (newLength!=-1 && bit>=densePart.length() && bit<=newLength) { + bits[index++] = bit; + } + } + + for(int i = 0; i < moveCount; i++) { + sparsePart.remove(bits[i]); + densePart.set(bits[i]); + } + } + } + } + } + + /** + * @param i + * @return true iff this set contains integer i + */ + public boolean contains(int i) { + return + sparsePart.contains(i) || (densePart != null && densePart.contains(i)); + } + + /** + * @return true iff this set contains integer i + */ + public boolean containsAny(IntSet set) { + if (!sparsePart.isEmpty() && sparsePart.containsAny(set)) { + return true; + } else if (densePart != null) { + int lower = densePart.getOffset(); + for(IntIterator is = set.intIterator(); is.hasNext(); ) { + int i = is.next(); + if (i < lower) continue; + if (densePart.get(i)) { + return true; + } + } + } + + return false; + } + + /** + * This implementation must not despoil the original value of "this" + * + * @return a new IntSet which is the intersection of this and that + */ + public IntSet intersection(IntSet that) { + SemiSparseMutableIntSet newThis = new SemiSparseMutableIntSet(); + for(IntIterator bits = intIterator(); bits.hasNext(); ) { + int bit = bits.next(); + if (that.contains(bit)) { + newThis.add(bit); + } + } + return newThis; + } + + /** + * @return true iff this set is empty + */ + public boolean isEmpty() { + return sparsePart.isEmpty() && (densePart == null || densePart.isZero()); + } + + /** + * @return the number of elements in this set + */ + public int size() { + return sparsePart.size() + (densePart==null? 0: densePart.populationCount()); + } + + /** + * @return a perhaps more efficient iterator + */ + public IntIterator intIterator() { + class DensePartIterator implements IntIterator { + private int i = -1; + + public boolean hasNext() { + return densePart.nextSetBit(i+1) != -1; + } + + public int next() { + int next = densePart.nextSetBit(i+1); + i = next+1; + return next; + } + }; + + if (sparsePart.isEmpty()) { + if (densePart == null || densePart.isZero()) { + return EmptyIntIterator.instance(); + } else { + return new DensePartIterator(); + } + } else { + if (densePart == null || densePart.isZero()) { + return sparsePart.intIterator(); + } else { + return + new CompoundIntIterator( + sparsePart.intIterator(), + new DensePartIterator()); + } + } + } + + /** + * Invoke an action on each element of the Set + * + * @param action + */ + public void foreach(IntSetAction action) { + sparsePart.foreach(action); + if (densePart != null) { + for(int b = densePart.nextSetBit(0); + b != -1; + b = densePart.nextSetBit(b+1)) + { + action.act(b); + } + } + } + + /** + * Invoke an action on each element of the Set, excluding elements of Set X + * + * @param action + */ + public void foreachExcluding(IntSet X, IntSetAction action) { + sparsePart.foreachExcluding(X, action); + if (densePart != null) { + for(int b = densePart.nextSetBit(0); + b != -1; + b = densePart.nextSetBit(b+1)) + { + if (! X.contains(b)) { + action.act(b); + } + } + } + } + + /** + * @return maximum integer in this set. + */ + public int max() { + if (densePart == null) { + return sparsePart.max(); + } else { + return Math.max(sparsePart.max(), densePart.max()); + } + } + + /** + * @return true iff this has the same value as + * that. + */ + public boolean sameValue(IntSet that) { + if (size() != that.size()) { + return false; + } + if (densePart != null) { + for(int bit = densePart.nextSetBit(0); + bit != -1; + bit = densePart.nextSetBit(bit+1)) + { + if (! that.contains(bit)) { + return false; + } + } + } + for(IntIterator bits = sparsePart.intIterator(); bits.hasNext(); ) { + if (! that.contains(bits.next())) { + return false; + } + } + return true; + } + + /** + * @return true iff this is a subset of that. + */ + public boolean isSubset(IntSet that) { + if (size() > that.size()) { + return false; + } + + for(IntIterator bits = sparsePart.intIterator(); bits.hasNext(); ) { + if (! that.contains(bits.next())) { + return false; + } + } + + if (densePart != null) { + for(int b = densePart.nextSetBit(0); + b != -1; + b = densePart.nextSetBit(b+1)) + { + if (! that.contains(b)) { + return false; + } + } + } + + return true; + } + + /** + * Set the value of this to be the same as the value of set + * + * @param set + */ + public void copySet(IntSet set) { + if (set instanceof SemiSparseMutableIntSet) { + SemiSparseMutableIntSet that = (SemiSparseMutableIntSet) set; + sparsePart = new MutableSparseIntSet(that.sparsePart); + if (that.densePart == null) { + densePart = null; + } else { + densePart = new OffsetBitVector(that.densePart); + } + } else { + densePart = null; + sparsePart = new MutableSparseIntSet(); + for(IntIterator bits = set.intIterator(); bits.hasNext(); ) { + add( bits.next() ); + } + } + } + + /** + * Add all members of set to this. + * + * @param set + * @return true iff the value of this changes. + */ + public boolean addAll(IntSet set) { + boolean change = false; + if (set instanceof SemiSparseMutableIntSet) { + SemiSparseMutableIntSet that = (SemiSparseMutableIntSet) set; + + if (densePart == null) { + + // that dense part only + if (that.densePart != null) { + densePart = new OffsetBitVector(that.densePart); + for(int b = densePart.nextSetBit(0); + b != -1; + b = densePart.nextSetBit(b+1)) + { + if (sparsePart.contains(b)) { + sparsePart.remove(b); + } else { + change = true; + } + } + for(IntIterator bits = that.sparsePart.intIterator(); + bits.hasNext(); ) + { + change |= sparsePart.add( bits.next() ); + } + + // no dense part + } else { + for(IntIterator bs = that.sparsePart.intIterator(); bs.hasNext(); ) { + change |= add( bs.next() ); + } + } + + } else { + int oldSize = size(); + + // both dense parts + if (that.densePart != null) { + densePart.or(that.densePart); + + for(IntIterator bs = that.sparsePart.intIterator(); bs.hasNext(); ) { + add( bs.next() ); + } + for(IntIterator bs = sparsePart.intIterator(); bs.hasNext(); ) { + int b = bs.next(); + if (densePart.get(b)) { + sparsePart.remove(b); + } + } + + change = (size() != oldSize); + + // this dense part only + } else { + for(IntIterator bs = that.sparsePart.intIterator(); bs.hasNext(); ) { + change |= add(bs.next()); + } + } + } + } else { + for(IntIterator bs = set.intIterator(); bs.hasNext(); ) { + change |= add(bs.next()); + } + } + + return change; + } + + /** + * Add an integer value to this set. + * + * @param i integer to add + * @return true iff the value of this changes. + */ + public boolean add(int i) { + if (! contains(i)) { + if (densePart!=null && densePart.getOffset()<=i && densePart.length()>i){ + densePart.set(i); + } else { + sparsePart.add(i); + fixAfterSparseInsert(); + } + return true; + } else { + return false; + } + } + + /** + * Remove an integer from this set. + * + * @param i integer to remove + * @return true iff the value of this changes. + */ + public boolean remove(int i) { + if (densePart != null && densePart.get(i)) { + densePart.clear(i); + return true; + } else if (sparsePart.contains(i)) { + sparsePart.remove(i); + return true; + } else { + return false; + } + } + + /** + * Interset this with another set. + * + * @param set + */ + public void intersectWith(IntSet set) { + sparsePart.intersectWith(set); + if (densePart != null) { + for(int b = densePart.nextSetBit(0); + b != -1; + b = densePart.nextSetBit(b+1)) + { + if (! set.contains(b)) { + densePart.clear(b); + } + } + } + } + + /** + * @param other + * @param filter + */ + public boolean addAllInIntersection(IntSet other, IntSet filter) { + boolean change = false; + for(IntIterator bits = other.intIterator(); bits.hasNext(); ) { + int bit = bits.next(); + if (filter.contains(bit)) { + change |= add( bit ); + } + } + + return change; + } +} diff --git a/com.ibm.wala.core/src/com/ibm/wala/util/intset/SemiSparseMutableIntSetFactory.java b/com.ibm.wala.core/src/com/ibm/wala/util/intset/SemiSparseMutableIntSetFactory.java new file mode 100644 index 000000000..ec54fde9d --- /dev/null +++ b/com.ibm.wala.core/src/com/ibm/wala/util/intset/SemiSparseMutableIntSetFactory.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2002 - 2006 IBM Corporation. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package com.ibm.wala.util.intset; + +import java.util.Iterator; +import java.util.TreeSet; + +/** + * @author Julian Dolby (dolby@us.ibm.com) + */ +public class SemiSparseMutableIntSetFactory implements MutableIntSetFactory { + + /** + * @param set + */ + public MutableIntSet make(int[] set) { + if (set.length == 0) { + return new BitVectorIntSet(); + } else { + // XXX not very efficient. + TreeSet T = new TreeSet(); + for (int i = 0; i < set.length; i++) { + T.add(new Integer(set[i])); + } + MutableIntSet result = new SemiSparseMutableIntSet(); + for (Iterator it = T.iterator(); it.hasNext();) { + Integer I = it.next(); + result.add(I.intValue()); + } + return result; + } + } + + /** + * @param string + */ + public MutableIntSet parse(String string) { + int[] data = SparseIntSet.parseIntArray(string); + MutableIntSet result = new SemiSparseMutableIntSet(); + for (int i = 0; i < data.length; i++) + result.add(data[i]); + return result; + } + + /* + * (non-Javadoc) + * + * @see com.ibm.wala.util.intset.MutableIntSetFactory#make(com.ibm.wala.util.intset.IntSet) + */ + public MutableIntSet makeCopy(IntSet x) { + MutableIntSet y = new SemiSparseMutableIntSet(); + y.copySet(x); + return y; + } + + /* + * (non-Javadoc) + * + * @see com.ibm.wala.util.intset.MutableIntSetFactory#make() + */ + public MutableIntSet make() { + return new SemiSparseMutableIntSet(); + } + +}