package com.limegroup.gnutella.routing; import java.util.Iterator; import org.limewire.collection.BitSet; import org.limewire.collection.UnmodifiableIterator; /** * Implementation of QRTTableStorage which uses a BitSet. */ class BitSetQRTTableStorage implements QRTTableStorage { private final BitSet bitSet; private final int bitTableLength; private BitSet cachedResizedSet; private int cachedResizedSetLength; BitSetQRTTableStorage(int bitTableLength) { this.bitTableLength = bitTableLength; this.bitSet = new BitSet(); } /** * copy constructor. */ private BitSetQRTTableStorage(BitSet bitSet, int bitTableLength) { this.bitSet = bitSet; this.bitTableLength = bitTableLength; } public double getPercentFull() { return bitSet.cardinality() * 100.0 / bitTableLength; } @Override public QRTTableStorage clone() throws CloneNotSupportedException { return new BitSetQRTTableStorage((BitSet)bitSet.clone(), bitTableLength); } public void clear(int hash) { cachedResizedSet = null; bitSet.clear(hash); } public void compact() { bitSet.compact(); } public int getUnitsInUse() { return bitSet.getUnitsInUse(); } public int getUnusedUnits() { return bitSet.unusedUnits(); } public int numUnitsWithLoad(int load) { return bitSet.numUnitsWithLoad(load); } public void or(QRTTableStorage other) { if (other instanceof BitSetQRTTableStorage) { BitSetQRTTableStorage optimized = (BitSetQRTTableStorage) other; bitSet.or(optimized.bitSet); } else { for (int i : other) bitSet.set(i); } } public QRTTableStorage resize(int newSize) { // if this bitTable is already the correct size, // return it if (bitTableLength == newSize) return this; // if we already have a cached resizedQRT and // it is the correct size, then use it. if (cachedResizedSet != null && cachedResizedSetLength == newSize) return new BitSetQRTTableStorage(cachedResizedSet, cachedResizedSetLength); cachedResizedSet = new BitSet(); cachedResizedSetLength = newSize; //This algorithm scales between tables of different lengths. //Refer to the query routing paper for a full explanation. //(The below algorithm, contributed by Philippe Verdy, // uses integer values instead of decimal values // as both double & float can cause precision problems on machines // with odd setups, causing the wrong values to be set in tables) final int m = this.bitTableLength; final int m2 = cachedResizedSetLength; for (int i = this.bitSet.nextSetBit(0); i >= 0; i = this.bitSet.nextSetBit(i + 1)) { // floor(i*m2/m) final int firstSet = (int)(((long)i * m2) / m); i = this.bitSet.nextClearBit(i + 1); // ceil(i*m2/m) final int lastNotSet = (int)(((long)i * m2 - 1) / m + 1); cachedResizedSet.set(firstSet, lastNotSet); } return new BitSetQRTTableStorage(cachedResizedSet, cachedResizedSetLength); } public void set(int hash) { cachedResizedSet = null; bitSet.set(hash); } public void xor(QRTTableStorage other) { if (other instanceof BitSetQRTTableStorage) { BitSetQRTTableStorage optimized = (BitSetQRTTableStorage) other; bitSet.xor(optimized.bitSet); } else { for (int i=0; i < bitTableLength; i++) bitSet.set(i,bitSet.get(i) != other.get(i)); } } public Iterator<Integer> iterator() { return new BitSetIterator(); } public int cardinality() { return bitSet.cardinality(); } public boolean get(int i) { return bitSet.get(i); } public int maxSize() { return bitTableLength; } public int nextClearBit(int i) { return bitSet.nextClearBit(i); } public int nextSetBit(int i) { return bitSet.nextSetBit(i); } @Override public boolean equals(Object o) { if (! (o instanceof QRTTableStorage)) return false; if (o instanceof BitSetQRTTableStorage) { BitSetQRTTableStorage other = (BitSetQRTTableStorage)o; return bitSet.equals(other.bitSet); } QRTTableStorage other = (QRTTableStorage)o; if (cardinality() != other.cardinality()) return false; for (int i : other) { if (!get(i)) return false; } return true; } private class BitSetIterator extends UnmodifiableIterator<Integer> { private int current = bitSet.nextSetBit(0); public boolean hasNext() { return current >= 0; } public Integer next() { int ret = current; current = bitSet.nextSetBit(ret + 1); return ret; } } }