blob: 7a96cd80c0005e43cd5291a18bc04301bf8d1925 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005 The Regents of the University of California.
* This material was produced under U.S. Government contract W-7405-ENG-36
* for Los Alamos National Laboratory, which is operated by the University
* of California for the U.S. Department of Energy. The U.S. Government has
* rights to use, reproduce, and distribute this software. NEITHER THE
* GOVERNMENT NOR THE UNIVERSITY MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR
* ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified
* to produce derivative works, such modified software should be clearly marked,
* so as not to confuse it with the version available from LANL.
*
* Additionally, 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
*
* LA-CC 04-115
*******************************************************************************/
package org.eclipse.ptp.core.util;
public class BitList {
/**
* Return a human readable string representation of a {@code BitList}
*
* @return string representation of {@code BitList}
*/
public static String showBitList(BitList b) {
if (b == null) {
return "{null}"; //$NON-NLS-1$
}
int[] array = b.toArray();
if (array.length == 0) {
return "{}"; //$NON-NLS-1$
}
String msg = "{"; //$NON-NLS-1$
int preTask = array[0];
msg += preTask;
boolean isContinue = false;
for (int i = 1; i < array.length; i++) {
if (preTask == (array[i] - 1)) {
preTask = array[i];
isContinue = true;
if (i == (array.length - 1)) {
msg += "-" + array[i]; //$NON-NLS-1$
break;
}
continue;
}
if (isContinue) {
msg += "-" + preTask; //$NON-NLS-1$
}
msg += "," + array[i]; //$NON-NLS-1$
isContinue = false;
preTask = array[i];
}
return msg + "}"; //$NON-NLS-1$
}
private byte[] bits;
private int nBits;
private int nBytes;
/** Cache the hash code for the BitList */
private int hash = 0;
public BitList(int nbits) {
this.init(nbits);
}
public BitList(int nbits, String str) {
this.init(nbits);
this.fromString(str);
}
private BitList(int nbits, byte[] bits) {
this.init(nbits);
for (int i = 0; i < this.nBytes; i++) {
this.bits[i] = bits[i];
}
}
/**
* Perform the operation:
*
* d = d & s
*
* for each corresponding element in the source and destination {@code BitList}s.
*
* If source {@code BitList} is smaller, we assume missing bits are zero
*
* @param s source {@code BitList}
*/
public void and(BitList s) {
for (int i = 0; i < this.nBytes; i++) {
if (i >= s.nBytes) {
this.bits[i] = 0;
} else {
this.bits[i] &= s.bits[i];
}
}
}
/**
* Perform the operation:
*
* d = d & ~s
*
* for each corresponding element in the source and destination {@code BitList}s.
*
* If source {@code BitList} is smaller, we assume missing bits are zero
*
* @param s source {@code BitList}
*/
public void andNot(BitList s) {
for (int i = 0; i < this.nBytes; i++) {
if (i < s.nBytes)
this.bits[i] &= ~s.bits[i];
}
}
/**
* Return the number of set bits in the {@code BitList}
*
* @return number of set bits
*/
public int cardinality() {
int n = 0;
for (int i = 0; i < this.nBytes; i++) {
n += countBits(this.bits[i]);
}
return n;
}
/**
* Clear the bit at the position given by index.
*
* @param index index of bit to clear
* @throws IndexOutOfBoundsException
*/
public void clear(int index) throws IndexOutOfBoundsException {
if (index >= this.nBits || index < 0) {
throw new IndexOutOfBoundsException();
}
int b = bytePos(index);
this.bits[b] &= ~(byte)(1 << bitInByte(index));
}
/**
* Clear the bits at each position given by the array of indices
*
* @param indexes array of indices to clear
*/
public void clear(int[] indices) throws IndexOutOfBoundsException {
for (int i = 0; i < indices.length; i++) {
this.clear(indices[i]);
}
}
/* (non-Javadoc)
* @see java.lang.Object#clone()
*/
public Object clone() {
return (Object)this.copy();
}
/**
* Create a copy of the {@code BitList}
*
* @return a copy of the {@code BitList}
*/
public BitList copy() {
return new BitList(this.nBits, this.bits);
}
/**
* Compares this BitList to the specified object.
*
* @param obj The object to compare this {@code BitList} against
* @return {@code true} if the given object represents a {@code BitList}
* equivalent to this BitList, {@code false} otherwise
*/
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof BitList) {
BitList anotherBitList = (BitList)obj;
if (nBits != anotherBitList.nBits || nBytes != anotherBitList.nBytes)
return false;
for (int i = 0; i < nBytes; i++) {
if (bits[i] != anotherBitList.bits[i])
return false;
}
return true;
}
return false;
}
/**
* Return the value of the bit at the position given by index
*
* @param index index of bit
* @return boolean representing the value of the bit ({@code true} = set, {@code false} = cleared)
* @throws IndexOutOfBoundsException
*/
public boolean get(int index) throws IndexOutOfBoundsException {
if (index >= this.nBits || index < 0) {
throw new IndexOutOfBoundsException();
}
int b = bytePos(index);
byte mask = (byte)(1 << bitInByte(index));
return (this.bits[b] & mask) == mask;
}
/**
* Returns a hash code for this BitList.
* (The hash value of the empty BitList is zero.)
* @return a hash code value for this object.
*/
public int hashCode() {
int h = hash;
if (h == 0) {
for (int i = 0; i < nBytes; i++) {
h = 31*h + bits[i];
}
hash = h;
}
return h;
}
/**
* Test if the source and destination {@code BitList}s have one or more
* bits set at the same index.
*
* @param s source {@code BitList}
* @return true if the source and destination {@code BitList}s intersect
*/
public boolean intersects(BitList s) {
for (int i = 0; i < this.nBytes; i++) {
if (i >= s.nBytes) {
break;
}
if ((this.bits[i] & s.bits[i]) != 0) {
return true;
}
}
return false;
}
/**
* Test if the {@code BitList} contains no set bits
*
* @return true if not bits are set
*/
public boolean isEmpty() {
boolean res = true;
for (int i = 0; i < this.nBytes; i++) {
if (this.bits[i] != 0)
res = false;
}
return res;
}
/**
* Given a starting bit position, find the position of the next highest bit that is set.
*
* @param index starting bit position
* @return position of the next highest bit that is set
*/
public int nextSetBit(int index) {
if (index < 0) {
throw new IndexOutOfBoundsException();
}
if (index >= this.nBits) {
return -1;
}
int i = index;
int start = bitInByte(index);
for (int p = bytePos(index); p < this.nBytes; p++) {
byte val = this.bits[p];
if (val != 0) {
for (int b = start; b < 8; b++, i++) {
byte mask = (byte) (1 << b);
if ((val & mask) == mask) {
return i;
}
}
} else
i += 8;
start = 0;
}
return -1;
}
/**
* Perform the operation:
*
* d = d | s
*
* for each corresponding element in the source and destination {@code BitList}s.
*
* If source {@code BitList} is smaller, we assume missing bits are zero
*
* @param s source {@code BitList}
*/
public void or(BitList s) {
for (int i = 0; i < this.nBytes; i++) {
if (i >= s.nBytes)
break;
this.bits[i] |= s.bits[i];
}
}
/**
* Set the bit at the position given by index.
*
* @param index index of bit to set
* @throws IndexOutOfBoundsException
*/
public void set(int index) throws IndexOutOfBoundsException {
if (index >= this.nBits || index < 0) {
throw new IndexOutOfBoundsException();
}
int b = bytePos(index);
this.bits[b] |= (byte)(1 << bitInByte(index));
}
/**
* Set all the bits starting from the position given by 'from' up to, but
* not including, the position given by 'to'. The relation
* 0 <= from <= to <= size() must hold.
*
* @param from starting bit position
* @param to ending bit position + 1
*/
public void set(int from, int to) {
if (from >= this.nBits || from < 0 || to > this.nBits || from >= to) {
throw new IndexOutOfBoundsException();
}
int last = to - 1;
/*
* Deal with single bit case
*/
if (from == last) {
this.set(from);
return;
}
int p1 = bytePos(from);
int p2 = bytePos(last);
int b1 = bitInByte(from);
int b2 = bitInByte(last);
/*
* Need to do something special if 'from' and 'to' refer
* to a single byte
*/
if (p1 == p2) {
byte mask = (byte) (0xff >> (7 - b2));
this.bits[p1] = (byte) (mask & (0xff << b1));
return;
}
byte hiMask = (byte) (0xff >> (7 - b2));
byte lowMask = (byte) (0xff << b1);
for (int p = p1+1; p < p2; p++) {
this.bits[p] = (byte) 0xff;
}
this.bits[p1] |= lowMask;
this.bits[p2] |= hiMask;
}
/**
* Set the bits at each position given by the array of indices
*
* @param indices array of indices to clear
*/
public void set(int[] indices) throws IndexOutOfBoundsException {
for (int i = 0; i < indices.length; i++) {
this.set(indices[i]);
}
}
/**
* Find the number of bits that can be represented by this {@code BitList}
*
* @return number of bits
*/
public int size() {
return this.nBits;
}
/**
* Convert the {@code BitList} to an array containing the indices of all set bits. If
* no bits are set, an empty array is returned.
*
* @return array containing the indices of all set bits.
*/
public int[] toArray() {
int[] retValue = new int[this.cardinality()];
for(int i = this.nextSetBit(0), j = 0; i >= 0; i = this.nextSetBit(i+1), j++) {
retValue[j] = i;
}
return retValue;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
public String toString() {
String res = ""; //$NON-NLS-1$
if (this.nBits == 0) {
res = "0"; //$NON-NLS-1$
} else {
for (int i = this.nBytes-1 ; i >= 0; i--) {
res += Integer.toHexString((this.bits[i] >> 4) & 0xf);
res += Integer.toHexString(this.bits[i] & 0xf);
}
}
return res;
}
/**
* Convert an index into a bit position within a byte
*
* @param idx index
* @return bit position within a byte
*/
private int bitInByte(int idx) {
return idx - (bytePos(idx) << 3);
}
/**
* Convert an index into a position within the byte array
*
* @param idx index
* @return position within byte array
*/
private int bytePos(int idx) {
return idx >> 3;
}
/**
* Count the number of set bits in a byte
*
* @param b byte
* @return number of set bits in the byte
*/
private int countBits(byte b) {
int n = 0;
while (b != 0) {
n++;
b &= (b-1);
}
return n;
}
/**
* Convert a hex representation of a byte to it's actual value
*
* @param b hex representation of a byte
* @return byte
*/
private byte fromHex(byte b) {
if (b >= 48 && b <= 57)
return (byte) (b - 48);
if (b >= 65 && b <= 70)
return (byte) (b - 65 + 10);
if (b >= 97 && b <= 102)
return (byte) (b - 97 + 10);
return (byte)0;
}
/**
* Set the bits specified in a hex representation of the {@code BitList}
*
* @param str hex representation of the {@code BitList}
*/
private void fromString(String str) {
if (this.nBytes == 0) {
return;
}
byte[] strBits = str.getBytes();
int last = strBits.length - this.nBytes * 2;
if (last < 0) {
last = 0;
}
byte mask = (byte) (0xff >> (7 - bitInByte(this.nBits - 1)));
for (int i = strBits.length-1, b = 0; i >= last; i -= 2, b++) {
byte c = fromHex(strBits[i]);
if (i-1 >= 0) {
c |= fromHex(strBits[i-1]) << 4;
}
this.bits[b] = c;
}
/*
* Mask out high bits of last byte
*/
this.bits[this.nBytes-1] &= mask;
}
/**
* Initialize the {@code BitList}
*
* @param nbits number of bits in the {@code BitList}
* @throws NegativeArraySizeException
*/
private void init(int nbits) throws NegativeArraySizeException {
if (nbits < 0)
throw new NegativeArraySizeException();
if (nbits == 0) {
this.nBits = 0;
this.nBytes = 0;
} else {
this.nBits = nbits;
this.nBytes = ((nbits-1) >> 3) + 1;
this.bits = new byte[this.nBytes];
}
}
}