/*
* Dijjer - A Peer to Peer HTTP Cache
* Copyright (C) 2004,2005 Change.Tv, Inc
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package freenet.support;
import java.io.DataInput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.BitSet;
import freenet.io.WritableToDataOutputStream;
public class BitArray implements WritableToDataOutputStream {
private int size;
private final BitSet bits;
public BitArray(byte[] data) {
this.bits = BitSet.valueOf(data);
this.size = data.length * 8;
}
public BitArray copy() {
return new BitArray(this);
}
/**
* This constructor does not check for unacceptable sizes, and should only be used on trusted data.
*/
public BitArray(DataInput dis) throws IOException {
this(dis, Integer.MAX_VALUE);
}
public BitArray(DataInput dis, int maxSize) throws IOException {
this.size = dis.readInt();
if (size <= 0 || size > maxSize) {
throw new IOException("Unacceptable bitarray size: " + size);
}
byte[] inputBits = new byte[getByteSize()];
dis.readFully(inputBits);
this.bits = BitSet.valueOf(inputBits);
trimToSize();
}
public BitArray(int size) {
this.size = size;
this.bits = new BitSet(size);
}
public BitArray(BitArray src) {
this.size = src.size;
this.bits = (BitSet)src.bits.clone();
}
public void setBit(int pos, boolean f) {
checkPos(pos);
bits.set(pos, f);
}
public boolean bitAt(int pos) {
checkPos(pos);
return bits.get(pos);
}
static int unsignedByteToInt(byte b) {
return b & 0xFF;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(this.size);
for (int x = 0; x < size; x++) {
if (bitAt(x)) {
sb.append('1');
} else {
sb.append('0');
}
}
return sb.toString();
}
@Override
public void writeToDataOutputStream(DataOutputStream dos) throws IOException {
dos.writeInt(size);
byte[] outputBits = bits.toByteArray();
if (outputBits.length != getByteSize()) {
outputBits = Arrays.copyOf(outputBits, getByteSize());
}
dos.write(outputBits);
}
public static int serializedLength(int size) {
return toByteSize(size) + 4;
}
public int getSize() {
return size;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof BitArray)) {
return false;
}
BitArray ba = (BitArray) o;
if (ba.getSize() != getSize()) {
return false;
}
return bits.equals(ba.bits);
}
@Override
public int hashCode() {
return bits.hashCode() ^ size;
}
public void setAllOnes() {
bits.set(0, size);
}
public int firstOne(int start) {
return bits.nextSetBit(start);
}
public int firstOne() {
return firstOne(0);
}
public int firstZero(int start) {
int result = bits.nextClearBit(start);
if (result >= size) {
return -1;
}
return result;
}
public void setSize(int size) {
this.size = size;
trimToSize();
}
public int lastOne(int start) {
return bits.previousSetBit(start);
}
private void trimToSize() {
bits.clear(size, Integer.MAX_VALUE);
}
private int getByteSize() {
return toByteSize(size);
}
private static int toByteSize(int bitSize) {
return (bitSize + 7) / 8;
}
private void checkPos(int pos) {
if (pos > size || pos < 0) {
throw new ArrayIndexOutOfBoundsException();
}
}
}