LUCENE-4693: FixedBitset might return wrong results if words.length > actual words in the bitset

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1435191 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Simon Willnauer 2013-01-18 15:25:58 +00:00
parent 267b26e182
commit 44f4f8b949
2 changed files with 49 additions and 14 deletions

View File

@ -39,6 +39,7 @@ import org.apache.lucene.search.DocIdSetIterator;
public final class FixedBitSet extends DocIdSet implements Bits { public final class FixedBitSet extends DocIdSet implements Bits {
private final long[] bits; private final long[] bits;
private final int numBits; private final int numBits;
private final int wordLength;
/** returns the number of 64 bit words it would take to hold numBits */ /** returns the number of 64 bit words it would take to hold numBits */
public static int bits2words(int numBits) { public static int bits2words(int numBits) {
@ -52,23 +53,29 @@ public final class FixedBitSet extends DocIdSet implements Bits {
public FixedBitSet(int numBits) { public FixedBitSet(int numBits) {
this.numBits = numBits; this.numBits = numBits;
bits = new long[bits2words(numBits)]; bits = new long[bits2words(numBits)];
wordLength = bits.length;
} }
public FixedBitSet(long[]storedBits,int numBits) { public FixedBitSet(long[] storedBits, int numBits) {
this.wordLength = bits2words(numBits);
if (wordLength > storedBits.length) {
throw new IllegalArgumentException("The given long array is too small to hold " + numBits + " bits");
}
this.numBits = numBits; this.numBits = numBits;
this.bits = storedBits; this.bits = storedBits;
} }
/** Makes full copy. */ /** Makes full copy. */
public FixedBitSet(FixedBitSet other) { public FixedBitSet(FixedBitSet other) {
bits = new long[other.bits.length]; bits = new long[other.wordLength];
System.arraycopy(other.bits, 0, bits, 0, bits.length); System.arraycopy(other.bits, 0, bits, 0, other.wordLength);
numBits = other.numBits; numBits = other.numBits;
wordLength = other.wordLength;
} }
@Override @Override
public DocIdSetIterator iterator() { public DocIdSetIterator iterator() {
return new OpenBitSetIterator(bits, bits.length); return new OpenBitSetIterator(bits, wordLength);
} }
@Override @Override
@ -159,7 +166,7 @@ public final class FixedBitSet extends DocIdSet implements Bits {
return (i<<6) + subIndex + Long.numberOfTrailingZeros(word); return (i<<6) + subIndex + Long.numberOfTrailingZeros(word);
} }
while(++i < bits.length) { while(++i < wordLength) {
word = bits[i]; word = bits[i];
if (word != 0) { if (word != 0) {
return (i<<6) + Long.numberOfTrailingZeros(word); return (i<<6) + Long.numberOfTrailingZeros(word);
@ -211,12 +218,12 @@ public final class FixedBitSet extends DocIdSet implements Bits {
/** this = this OR other */ /** this = this OR other */
public void or(FixedBitSet other) { public void or(FixedBitSet other) {
or(other.bits, other.bits.length); or(other.bits, other.wordLength);
} }
private void or(final long[] otherArr, final int otherLen) { private void or(final long[] otherArr, final int otherLen) {
final long[] thisArr = this.bits; final long[] thisArr = this.bits;
int pos = Math.min(thisArr.length, otherLen); int pos = Math.min(wordLength, otherLen);
while (--pos >= 0) { while (--pos >= 0) {
thisArr[pos] |= otherArr[pos]; thisArr[pos] |= otherArr[pos];
} }
@ -247,17 +254,17 @@ public final class FixedBitSet extends DocIdSet implements Bits {
/** this = this AND other */ /** this = this AND other */
public void and(FixedBitSet other) { public void and(FixedBitSet other) {
and(other.bits, other.bits.length); and(other.bits, other.wordLength);
} }
private void and(final long[] otherArr, final int otherLen) { private void and(final long[] otherArr, final int otherLen) {
final long[] thisArr = this.bits; final long[] thisArr = this.bits;
int pos = Math.min(thisArr.length, otherLen); int pos = Math.min(this.wordLength, otherLen);
while(--pos >= 0) { while(--pos >= 0) {
thisArr[pos] &= otherArr[pos]; thisArr[pos] &= otherArr[pos];
} }
if (thisArr.length > otherLen) { if (this.wordLength > otherLen) {
Arrays.fill(thisArr, otherLen, thisArr.length, 0L); Arrays.fill(thisArr, otherLen, this.wordLength, 0L);
} }
} }
@ -285,7 +292,7 @@ public final class FixedBitSet extends DocIdSet implements Bits {
private void andNot(final long[] otherArr, final int otherLen) { private void andNot(final long[] otherArr, final int otherLen) {
final long[] thisArr = this.bits; final long[] thisArr = this.bits;
int pos = Math.min(thisArr.length, otherLen); int pos = Math.min(this.wordLength, otherLen);
while(--pos >= 0) { while(--pos >= 0) {
thisArr[pos] &= ~otherArr[pos]; thisArr[pos] &= ~otherArr[pos];
} }
@ -418,7 +425,7 @@ public final class FixedBitSet extends DocIdSet implements Bits {
@Override @Override
public int hashCode() { public int hashCode() {
long h = 0; long h = 0;
for (int i = bits.length; --i>=0;) { for (int i = wordLength; --i>=0;) {
h ^= bits[i]; h ^= bits[i];
h = (h << 1) | (h >>> 63); // rotate left h = (h << 1) | (h >>> 63); // rotate left
} }

View File

@ -265,7 +265,18 @@ public class TestFixedBitSet extends LuceneTestCase {
} }
private FixedBitSet makeFixedBitSet(int[] a, int numBits) { private FixedBitSet makeFixedBitSet(int[] a, int numBits) {
FixedBitSet bs = new FixedBitSet(numBits); FixedBitSet bs;
if (random().nextBoolean()) {
int bits2words = FixedBitSet.bits2words(numBits);
long[] words = new long[bits2words + random().nextInt(100)];
for (int i = bits2words; i < words.length; i++) {
words[i] = random().nextLong();
}
bs = new FixedBitSet(words, numBits);
} else {
bs = new FixedBitSet(numBits);
}
for (int e: a) { for (int e: a) {
bs.set(e); bs.set(e);
} }
@ -291,6 +302,23 @@ public class TestFixedBitSet extends LuceneTestCase {
checkPrevSetBitArray(new int[] {0}, 1); checkPrevSetBitArray(new int[] {0}, 1);
checkPrevSetBitArray(new int[] {0,2}, 3); checkPrevSetBitArray(new int[] {0,2}, 3);
} }
private void checkNextSetBitArray(int [] a, int numBits) {
FixedBitSet obs = makeFixedBitSet(a, numBits);
BitSet bs = makeBitSet(a);
doNextSetBit(bs, obs);
}
public void testNextBitSet() {
int[] setBits = new int[0+random().nextInt(1000)];
for (int i = 0; i < setBits.length; i++) {
setBits[i] = random().nextInt(setBits.length);
}
checkNextSetBitArray(setBits, setBits.length + random().nextInt(10));
checkNextSetBitArray(new int[0], setBits.length + random().nextInt(10));
}
} }