@@ -1298,12 +1298,7 @@ public static Status.LiveDocStatus testLiveDocs(
12981298 if (liveDocs == null ) {
12991299 throw new CheckIndexException ("segment should have deletions, but liveDocs is null" );
13001300 } else {
1301- int numLive = 0 ;
1302- for (int j = 0 ; j < liveDocs .length (); j ++) {
1303- if (liveDocs .get (j )) {
1304- numLive ++;
1305- }
1306- }
1301+ int numLive = bitsCardinality (liveDocs );
13071302 if (numLive != numDocs ) {
13081303 throw new CheckIndexException (
13091304 "liveDocs count mismatch: info=" + numDocs + ", vs bits=" + numLive );
@@ -1349,6 +1344,28 @@ public static Status.LiveDocStatus testLiveDocs(
13491344 return status ;
13501345 }
13511346
1347+ /**
1348+ * Returns the cardinality of the given {@code Bits}.
1349+ *
1350+ * <p>This method processes bits in batches of 1024 using {@link Bits#applyMask} and {@link
1351+ * FixedBitSet#cardinality}, which is faster than checking bits one by one.
1352+ */
1353+ static int bitsCardinality (Bits bits ) {
1354+ int cardinality = 0 ;
1355+ FixedBitSet copy = new FixedBitSet (1024 );
1356+ for (int offset = 0 ; offset < bits .length (); offset += copy .length ()) {
1357+ int numBitsToCopy = Math .min (bits .length () - offset , copy .length ());
1358+ copy .set (0 , copy .length ());
1359+ if (numBitsToCopy < copy .length ()) {
1360+ // Clear ghost bits
1361+ copy .clear (numBitsToCopy , copy .length ());
1362+ }
1363+ bits .applyMask (copy , offset );
1364+ cardinality += copy .cardinality ();
1365+ }
1366+ return cardinality ;
1367+ }
1368+
13521369 /** Test field infos. */
13531370 public static Status .FieldInfoStatus testFieldInfos (
13541371 CodecReader reader , PrintStream infoStream , boolean failFast ) throws IOException {
0 commit comments