-
Notifications
You must be signed in to change notification settings - Fork 1.3k
chore: Seal BitSet to only allow FixedBitSet and SparseFixedBitSet #15161
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,14 +16,10 @@ | |
*/ | ||
package org.apache.lucene.tests.util; | ||
|
||
import com.carrotsearch.randomizedtesting.generators.RandomNumbers; | ||
import java.io.IOException; | ||
import java.util.Collection; | ||
import java.util.Collections; | ||
import java.util.Random; | ||
import org.apache.lucene.search.DocIdSet; | ||
import org.apache.lucene.search.DocIdSetIterator; | ||
import org.apache.lucene.util.Accountable; | ||
import org.apache.lucene.util.BitDocIdSet; | ||
import org.apache.lucene.util.BitSet; | ||
import org.apache.lucene.util.BitSetIterator; | ||
|
@@ -65,7 +61,9 @@ static java.util.BitSet randomSet(int numBits, float percentSet) { | |
return randomSet(numBits, (int) (percentSet * numBits)); | ||
} | ||
|
||
protected void assertEquals(BitSet set1, T set2, int maxDoc) { | ||
protected abstract T fromJavaUtilBitSet(java.util.BitSet set, int numBits); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I worry that copying from a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. tried refactoring it using java.util.Bitset.
|
||
|
||
protected void assertEquals(java.util.BitSet set1, T set2, int maxDoc) { | ||
for (int i = 0; i < maxDoc; ++i) { | ||
assertEquals("Different at " + i, set1.get(i), set2.get(i)); | ||
} | ||
|
@@ -75,20 +73,20 @@ protected void assertEquals(BitSet set1, T set2, int maxDoc) { | |
public void testCardinality() throws IOException { | ||
final int numBits = 1 + random().nextInt(100000); | ||
for (float percentSet : new float[] {0, 0.01f, 0.1f, 0.5f, 0.9f, 0.99f, 1f}) { | ||
BitSet set1 = new JavaUtilBitSet(randomSet(numBits, percentSet), numBits); | ||
T set2 = copyOf(set1, numBits); | ||
assertEquals(set1.cardinality(), set2.cardinality()); | ||
java.util.BitSet jdkSet = randomSet(numBits, percentSet); | ||
T luceneSet = fromJavaUtilBitSet(jdkSet, numBits); | ||
assertEquals(jdkSet.cardinality(), luceneSet.cardinality()); | ||
} | ||
} | ||
|
||
/** Test {@link BitSet#prevSetBit(int)}. */ | ||
public void testPrevSetBit() throws IOException { | ||
final int numBits = 1 + random().nextInt(100000); | ||
for (float percentSet : new float[] {0, 0.01f, 0.1f, 0.5f, 0.9f, 0.99f, 1f}) { | ||
BitSet set1 = new JavaUtilBitSet(randomSet(numBits, percentSet), numBits); | ||
T set2 = copyOf(set1, numBits); | ||
java.util.BitSet jdkSet = randomSet(numBits, percentSet); | ||
T luceneSet = fromJavaUtilBitSet(jdkSet, numBits); | ||
for (int i = 0; i < numBits; ++i) { | ||
assertEquals(Integer.toString(i), set1.prevSetBit(i), set2.prevSetBit(i)); | ||
assertEquals(Integer.toString(i), jdkSet.previousSetBit(i), luceneSet.prevSetBit(i)); | ||
} | ||
} | ||
} | ||
|
@@ -97,27 +95,10 @@ public void testPrevSetBit() throws IOException { | |
public void testNextSetBit() throws IOException { | ||
final int numBits = 1 + random().nextInt(100000); | ||
for (float percentSet : new float[] {0, 0.01f, 0.1f, 0.5f, 0.9f, 0.99f, 1f}) { | ||
BitSet set1 = new JavaUtilBitSet(randomSet(numBits, percentSet), numBits); | ||
T set2 = copyOf(set1, numBits); | ||
java.util.BitSet jdkSet = randomSet(numBits, percentSet); | ||
T luceneSet = fromJavaUtilBitSet(jdkSet, numBits); | ||
for (int i = 0; i < numBits; ++i) { | ||
assertEquals(set1.nextSetBit(i), set2.nextSetBit(i)); | ||
} | ||
} | ||
} | ||
|
||
/** Test {@link BitSet#nextSetBit(int, int)}. */ | ||
public void testNextSetBitInRange() throws IOException { | ||
Random random = random(); | ||
final int numBits = 1 + random().nextInt(100000); | ||
for (float percentSet : new float[] {0, 0.01f, 0.1f, 0.5f, 0.9f, 0.99f, 1f}) { | ||
BitSet set1 = new JavaUtilBitSet(randomSet(numBits, percentSet), numBits); | ||
T set2 = copyOf(set1, numBits); | ||
for (int start = 0; start < numBits; ++start) { | ||
int end = RandomNumbers.randomIntBetween(random, start + 1, numBits); | ||
assertEquals( | ||
"start=" + start + ", end=" + end + ", numBits=" + numBits, | ||
set1.nextSetBit(start, end), | ||
set2.nextSetBit(start, end)); | ||
assertEquals(normalizeJdkNextSetBit(jdkSet.nextSetBit(i)), luceneSet.nextSetBit(i)); | ||
} | ||
} | ||
} | ||
|
@@ -126,47 +107,48 @@ public void testNextSetBitInRange() throws IOException { | |
public void testSet() throws IOException { | ||
Random random = random(); | ||
final int numBits = 1 + random.nextInt(100000); | ||
BitSet set1 = new JavaUtilBitSet(randomSet(numBits, 0), numBits); | ||
T set2 = copyOf(set1, numBits); | ||
java.util.BitSet jdkSet = randomSet(numBits, 0); | ||
T luceneSet = fromJavaUtilBitSet(jdkSet, numBits); | ||
final int iters = 10000 + random.nextInt(10000); | ||
for (int i = 0; i < iters; ++i) { | ||
final int index = random.nextInt(numBits); | ||
set1.set(index); | ||
set2.set(index); | ||
jdkSet.set(index); | ||
luceneSet.set(index); | ||
} | ||
assertEquals(set1, set2, numBits); | ||
assertEquals(jdkSet, luceneSet, numBits); | ||
} | ||
|
||
/** Test the {@link BitSet#getAndSet} method. */ | ||
public void testGetAndSet() throws IOException { | ||
Random random = random(); | ||
final int numBits = 1 + random.nextInt(100000); | ||
BitSet set1 = new JavaUtilBitSet(randomSet(numBits, 0), numBits); | ||
T set2 = copyOf(set1, numBits); | ||
java.util.BitSet jdkSet = randomSet(numBits, 0); | ||
T luceneSet = fromJavaUtilBitSet(jdkSet, numBits); | ||
final int iters = 10000 + random.nextInt(10000); | ||
for (int i = 0; i < iters; ++i) { | ||
final int index = random.nextInt(numBits); | ||
boolean v1 = set1.getAndSet(index); | ||
boolean v2 = set2.getAndSet(index); | ||
boolean v1 = jdkSet.get(index); | ||
jdkSet.set(index); // emulate getAndSet | ||
boolean v2 = luceneSet.getAndSet(index); | ||
assertEquals(v1, v2); | ||
} | ||
assertEquals(set1, set2, numBits); | ||
assertEquals(jdkSet, luceneSet, numBits); | ||
} | ||
|
||
/** Test the {@link BitSet#clear(int)} method. */ | ||
public void testClear() throws IOException { | ||
Random random = random(); | ||
final int numBits = 1 + random.nextInt(100000); | ||
for (float percentSet : new float[] {0, 0.01f, 0.1f, 0.5f, 0.9f, 0.99f, 1f}) { | ||
BitSet set1 = new JavaUtilBitSet(randomSet(numBits, percentSet), numBits); | ||
T set2 = copyOf(set1, numBits); | ||
java.util.BitSet jdkSet = randomSet(numBits, percentSet); | ||
T luceneSet = fromJavaUtilBitSet(jdkSet, numBits); | ||
final int iters = 1 + random.nextInt(numBits * 2); | ||
for (int i = 0; i < iters; ++i) { | ||
final int index = random.nextInt(numBits); | ||
set1.clear(index); | ||
set2.clear(index); | ||
jdkSet.clear(index); | ||
luceneSet.clear(index); | ||
} | ||
assertEquals(set1, set2, numBits); | ||
assertEquals(jdkSet, luceneSet, numBits); | ||
} | ||
} | ||
|
||
|
@@ -175,15 +157,19 @@ public void testClearRange() throws IOException { | |
Random random = random(); | ||
final int numBits = 1 + random.nextInt(100000); | ||
for (float percentSet : new float[] {0, 0.01f, 0.1f, 0.5f, 0.9f, 0.99f, 1f}) { | ||
BitSet set1 = new JavaUtilBitSet(randomSet(numBits, percentSet), numBits); | ||
T set2 = copyOf(set1, numBits); | ||
java.util.BitSet jdkSet = randomSet(numBits, percentSet); | ||
T luceneSet = fromJavaUtilBitSet(jdkSet, numBits); | ||
final int iters = atLeast(random, 10); | ||
for (int i = 0; i < iters; ++i) { | ||
final int from = random.nextInt(numBits); | ||
final int to = random.nextInt(numBits + 1); | ||
set1.clear(from, to); | ||
set2.clear(from, to); | ||
assertEquals(set1, set2, numBits); | ||
if (from > to) { | ||
// JDK would throw, Lucene no-ops: so skip | ||
continue; | ||
} | ||
jdkSet.clear(from, to); | ||
luceneSet.clear(from, to); | ||
assertEquals(jdkSet, luceneSet, numBits); | ||
} | ||
} | ||
} | ||
|
@@ -193,17 +179,21 @@ public void testClearAll() throws IOException { | |
Random random = random(); | ||
final int numBits = 1 + random.nextInt(100000); | ||
for (float percentSet : new float[] {0, 0.01f, 0.1f, 0.5f, 0.9f, 0.99f, 1f}) { | ||
BitSet set1 = new JavaUtilBitSet(randomSet(numBits, percentSet), numBits); | ||
T set2 = copyOf(set1, numBits); | ||
java.util.BitSet jdkSet = randomSet(numBits, percentSet); | ||
T luceneSet = fromJavaUtilBitSet(jdkSet, numBits); | ||
final int iters = atLeast(random, 10); | ||
for (int i = 0; i < iters; ++i) { | ||
set1.clear(); | ||
set2.clear(); | ||
assertEquals(set1, set2, numBits); | ||
jdkSet.clear(); | ||
luceneSet.clear(); | ||
assertEquals(jdkSet, luceneSet, numBits); | ||
} | ||
} | ||
} | ||
|
||
private static int normalizeJdkNextSetBit(int bit) { | ||
return bit == -1 ? DocIdSetIterator.NO_MORE_DOCS : bit; | ||
} | ||
|
||
private DocIdSet randomCopy(BitSet set, int numBits) throws IOException { | ||
switch (random().nextInt(5)) { | ||
case 0: | ||
|
@@ -234,22 +224,31 @@ private DocIdSet randomCopy(BitSet set, int numBits) throws IOException { | |
|
||
private void testOr(float load) throws IOException { | ||
final int numBits = 1 + random().nextInt(100000); | ||
BitSet set1 = new JavaUtilBitSet(randomSet(numBits, 0), numBits); // empty | ||
T set2 = copyOf(set1, numBits); | ||
java.util.BitSet jdkSet = randomSet(numBits, 0); // empty | ||
T luceneSet = fromJavaUtilBitSet(jdkSet, numBits); | ||
|
||
final int iterations = atLeast(10); | ||
for (int iter = 0; iter < iterations; ++iter) { | ||
DocIdSet otherSet = | ||
randomCopy(new JavaUtilBitSet(randomSet(numBits, load), numBits), numBits); | ||
randomCopy(fromJavaUtilBitSet(randomSet(numBits, load), numBits), numBits); | ||
DocIdSetIterator otherIterator = otherSet.iterator(); | ||
if (otherIterator != null) { | ||
set1.or(otherIterator); | ||
set2.or(otherSet.iterator()); | ||
assertEquals(set1, set2, numBits); | ||
jdkSet.or(toJavaUtil(otherIterator, numBits)); | ||
luceneSet.or(otherSet.iterator()); | ||
assertEquals(jdkSet, luceneSet, numBits); | ||
} | ||
} | ||
} | ||
|
||
/** Helper: consume a DocIdSetIterator into a java.util.BitSet. */ | ||
private static java.util.BitSet toJavaUtil(DocIdSetIterator it, int numBits) throws IOException { | ||
java.util.BitSet bs = new java.util.BitSet(numBits); | ||
for (int doc = it.nextDoc(); doc != DocIdSetIterator.NO_MORE_DOCS; doc = it.nextDoc()) { | ||
bs.set(doc); | ||
} | ||
return bs; | ||
} | ||
|
||
/** Test {@link BitSet#or(DocIdSetIterator)} on sparse sets. */ | ||
public void testOrSparse() throws IOException { | ||
testOr(0.001f); | ||
|
@@ -264,89 +263,4 @@ public void testOrDense() throws IOException { | |
public void testOrRandom() throws IOException { | ||
testOr(random().nextFloat()); | ||
} | ||
|
||
private static class JavaUtilBitSet extends BitSet { | ||
|
||
private final java.util.BitSet bitSet; | ||
private final int numBits; | ||
|
||
JavaUtilBitSet(java.util.BitSet bitSet, int numBits) { | ||
this.bitSet = bitSet; | ||
this.numBits = numBits; | ||
} | ||
|
||
@Override | ||
public void clear() { | ||
bitSet.clear(); | ||
} | ||
|
||
@Override | ||
public void clear(int index) { | ||
bitSet.clear(index); | ||
} | ||
|
||
@Override | ||
public boolean get(int index) { | ||
return bitSet.get(index); | ||
} | ||
|
||
@Override | ||
public boolean getAndSet(int index) { | ||
boolean v = get(index); | ||
set(index); | ||
return v; | ||
} | ||
|
||
@Override | ||
public int length() { | ||
return numBits; | ||
} | ||
|
||
@Override | ||
public long ramBytesUsed() { | ||
return -1; | ||
} | ||
|
||
@Override | ||
public Collection<Accountable> getChildResources() { | ||
return Collections.emptyList(); | ||
} | ||
|
||
@Override | ||
public void set(int i) { | ||
bitSet.set(i); | ||
} | ||
|
||
@Override | ||
public void clear(int startIndex, int endIndex) { | ||
if (startIndex >= endIndex) { | ||
return; | ||
} | ||
bitSet.clear(startIndex, endIndex); | ||
} | ||
|
||
@Override | ||
public int cardinality() { | ||
return bitSet.cardinality(); | ||
} | ||
|
||
@Override | ||
public int approximateCardinality() { | ||
return bitSet.cardinality(); | ||
} | ||
|
||
@Override | ||
public int prevSetBit(int index) { | ||
return bitSet.previousSetBit(index); | ||
} | ||
|
||
@Override | ||
public int nextSetBit(int start, int upperBound) { | ||
int next = bitSet.nextSetBit(start); | ||
if (next == -1 || next >= upperBound) { | ||
next = DocIdSetIterator.NO_MORE_DOCS; | ||
} | ||
return next; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a small comment explaining that we're doing this to help call sites of methods on the
BitSet
class be bimorphic at most, to help with performance?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added small comment explaining this.