This is an automated email from the ASF dual-hosted git repository.

jt2594838 pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/tsfile.git


The following commit(s) were added to refs/heads/develop by this push:
     new 3c831b779 Add range mark query APIs to BitMap (#884)
3c831b779 is described below

commit 3c831b779999b5be905255b018ac3a5858dabc84
Author: Jiang Tian <[email protected]>
AuthorDate: Fri Jul 31 12:02:35 2026 +0800

    Add range mark query APIs to BitMap (#884)
    
    * add range test interface
    
    * push-down implementation
---
 .../main/java/org/apache/tsfile/utils/BitMap.java  |  29 +++
 .../org/apache/tsfile/utils/BitMapArrayImpl.java   |  72 ++++++-
 .../java/org/apache/tsfile/utils/BitMapImpl.java   |  15 ++
 .../org/apache/tsfile/utils/BitMapLongImpl.java    |  33 +++-
 .../apache/tsfile/utils/BitMapPerformanceTest.java | 217 +++++++++++++++++++++
 .../java/org/apache/tsfile/utils/BitMapTest.java   |  49 +++++
 6 files changed, 398 insertions(+), 17 deletions(-)

diff --git a/java/common/src/main/java/org/apache/tsfile/utils/BitMap.java 
b/java/common/src/main/java/org/apache/tsfile/utils/BitMap.java
index 629cda9c0..337bec90f 100644
--- a/java/common/src/main/java/org/apache/tsfile/utils/BitMap.java
+++ b/java/common/src/main/java/org/apache/tsfile/utils/BitMap.java
@@ -54,6 +54,35 @@ public class BitMap implements Accountable {
     return implementation.isMarked(position);
   }
 
+  /**
+   * Returns whether at least one bit in the specified range is marked. An 
empty range returns
+   * {@code false}.
+   *
+   * @throws IndexOutOfBoundsException if the range is outside this bitmap
+   */
+  public boolean isRangeAnyMarked(int start, int length) {
+    return implementation.isRangeAnyMarked(start, length);
+  }
+
+  /**
+   * Returns whether every bit in the specified range is marked. An empty 
range returns {@code
+   * true}.
+   *
+   * @throws IndexOutOfBoundsException if the range is outside this bitmap
+   */
+  public boolean isRangeAllMarked(int start, int length) {
+    return implementation.isRangeAllMarked(start, length);
+  }
+
+  /**
+   * Returns whether no bit in the specified range is marked. An empty range 
returns {@code true}.
+   *
+   * @throws IndexOutOfBoundsException if the range is outside this bitmap
+   */
+  public boolean isRangeNoneMarked(int start, int length) {
+    return implementation.isRangeNoneMarked(start, length);
+  }
+
   /** mark as 1 at all positions. */
   public void markAll() {
     implementation.markAll();
diff --git 
a/java/common/src/main/java/org/apache/tsfile/utils/BitMapArrayImpl.java 
b/java/common/src/main/java/org/apache/tsfile/utils/BitMapArrayImpl.java
index 9e8a40910..2a8a4d3f1 100644
--- a/java/common/src/main/java/org/apache/tsfile/utils/BitMapArrayImpl.java
+++ b/java/common/src/main/java/org/apache/tsfile/utils/BitMapArrayImpl.java
@@ -19,8 +19,6 @@
 
 package org.apache.tsfile.utils;
 
-import org.apache.tsfile.i18n.Messages;
-
 import java.util.Arrays;
 
 class BitMapArrayImpl extends BitMapImpl {
@@ -76,6 +74,47 @@ class BitMapArrayImpl extends BitMapImpl {
     return (bits[position / Byte.SIZE] & BIT_UTIL[position % Byte.SIZE]) != 0;
   }
 
+  @Override
+  boolean isRangeAnyMarked(int start, int length) {
+    checkRange(start, length);
+    return isRangeAnyMarkedUnchecked(start, length);
+  }
+
+  @Override
+  boolean isRangeAllMarked(int start, int length) {
+    checkRange(start, length);
+    if (length == 0) {
+      return true;
+    }
+
+    int end = start + length;
+    int firstByte = start >>> 3;
+    int lastByte = (end - 1) >>> 3;
+    if (firstByte == lastByte) {
+      int mask = ((1 << length) - 1) << (start & 7);
+      return (bits[firstByte] & mask) == mask;
+    }
+
+    int firstMask = (0xFF << (start & 7)) & 0xFF;
+    if ((bits[firstByte] & firstMask) != firstMask) {
+      return false;
+    }
+    for (int index = firstByte + 1; index < lastByte; index++) {
+      if (bits[index] != (byte) 0xFF) {
+        return false;
+      }
+    }
+    int lastBitCount = end & 7;
+    int lastMask = lastBitCount == 0 ? 0xFF : (1 << lastBitCount) - 1;
+    return (bits[lastByte] & lastMask) == lastMask;
+  }
+
+  @Override
+  boolean isRangeNoneMarked(int start, int length) {
+    checkRange(start, length);
+    return !isRangeAnyMarkedUnchecked(start, length);
+  }
+
   @Override
   void markAll() {
     Arrays.fill(bits, (byte) 0XFF);
@@ -288,11 +327,30 @@ class BitMapArrayImpl extends BitMapImpl {
     return INSTANCE_SIZE + RamUsageEstimator.sizeOfByteArray(bits.length);
   }
 
-  private void checkRange(int startPosition, int length) {
-    if (startPosition < 0 || startPosition + length > size) {
-      throw new IndexOutOfBoundsException(
-          Messages.format(
-              "error.common.bitmap_start_length_out_of_range", startPosition, 
length, size));
+  private boolean isRangeAnyMarkedUnchecked(int start, int length) {
+    if (length == 0) {
+      return false;
+    }
+
+    int end = start + length;
+    int firstByte = start >>> 3;
+    int lastByte = (end - 1) >>> 3;
+    if (firstByte == lastByte) {
+      int mask = ((1 << length) - 1) << (start & 7);
+      return (bits[firstByte] & mask) != 0;
+    }
+
+    int firstMask = (0xFF << (start & 7)) & 0xFF;
+    if ((bits[firstByte] & firstMask) != 0) {
+      return true;
+    }
+    for (int index = firstByte + 1; index < lastByte; index++) {
+      if (bits[index] != 0) {
+        return true;
+      }
     }
+    int lastBitCount = end & 7;
+    int lastMask = lastBitCount == 0 ? 0xFF : (1 << lastBitCount) - 1;
+    return (bits[lastByte] & lastMask) != 0;
   }
 }
diff --git a/java/common/src/main/java/org/apache/tsfile/utils/BitMapImpl.java 
b/java/common/src/main/java/org/apache/tsfile/utils/BitMapImpl.java
index d9f296fe2..a798af56a 100644
--- a/java/common/src/main/java/org/apache/tsfile/utils/BitMapImpl.java
+++ b/java/common/src/main/java/org/apache/tsfile/utils/BitMapImpl.java
@@ -19,6 +19,8 @@
 
 package org.apache.tsfile.utils;
 
+import org.apache.tsfile.i18n.Messages;
+
 abstract class BitMapImpl {
 
   protected int size;
@@ -39,6 +41,12 @@ abstract class BitMapImpl {
 
   abstract boolean isMarked(int position);
 
+  abstract boolean isRangeAnyMarked(int start, int length);
+
+  abstract boolean isRangeAllMarked(int start, int length);
+
+  abstract boolean isRangeNoneMarked(int start, int length);
+
   abstract void markAll();
 
   abstract void mark(int position);
@@ -102,4 +110,11 @@ abstract class BitMapImpl {
   abstract BitMapImpl extend(int newSize);
 
   abstract long getRetainedSizeInBytes();
+
+  final void checkRange(int start, int length) {
+    if (start < 0 || length < 0 || start > size - length) {
+      throw new IndexOutOfBoundsException(
+          Messages.format("error.common.bitmap_start_length_out_of_range", 
start, length, size));
+    }
+  }
 }
diff --git 
a/java/common/src/main/java/org/apache/tsfile/utils/BitMapLongImpl.java 
b/java/common/src/main/java/org/apache/tsfile/utils/BitMapLongImpl.java
index 452c7f096..633a6ea97 100644
--- a/java/common/src/main/java/org/apache/tsfile/utils/BitMapLongImpl.java
+++ b/java/common/src/main/java/org/apache/tsfile/utils/BitMapLongImpl.java
@@ -19,8 +19,6 @@
 
 package org.apache.tsfile.utils;
 
-import org.apache.tsfile.i18n.Messages;
-
 class BitMapLongImpl extends BitMapImpl {
 
   private static final long ALL_BITS_MARKED = -1L;
@@ -79,6 +77,25 @@ class BitMapLongImpl extends BitMapImpl {
     return (bits & (1L << position)) != 0;
   }
 
+  @Override
+  boolean isRangeAnyMarked(int start, int length) {
+    checkRange(start, length);
+    return (bits & rangeMask(start, length)) != 0L;
+  }
+
+  @Override
+  boolean isRangeAllMarked(int start, int length) {
+    checkRange(start, length);
+    long mask = rangeMask(start, length);
+    return (bits & mask) == mask;
+  }
+
+  @Override
+  boolean isRangeNoneMarked(int start, int length) {
+    checkRange(start, length);
+    return (bits & rangeMask(start, length)) == 0L;
+  }
+
   @Override
   void markAll() {
     bits = ALL_BITS_MARKED;
@@ -219,6 +236,10 @@ class BitMapLongImpl extends BitMapImpl {
     return length == Long.SIZE ? -1L : (1L << length) - 1;
   }
 
+  private static long rangeMask(int start, int length) {
+    return lowerBitsMask(length) << start;
+  }
+
   private byte[] getExtendedByteArray(int newSize) {
     byte[] bytes = new byte[BitMap.getSizeOfBytes(newSize)];
     for (int i = 0; i < Long.BYTES; i++) {
@@ -227,12 +248,4 @@ class BitMapLongImpl extends BitMapImpl {
     bytes[Long.BYTES] = paddingByte;
     return bytes;
   }
-
-  private void checkRange(int startPosition, int length) {
-    if (startPosition < 0 || startPosition + length > size) {
-      throw new IndexOutOfBoundsException(
-          Messages.format(
-              "error.common.bitmap_start_length_out_of_range", startPosition, 
length, size));
-    }
-  }
 }
diff --git 
a/java/tsfile/src/test/java/org/apache/tsfile/utils/BitMapPerformanceTest.java 
b/java/tsfile/src/test/java/org/apache/tsfile/utils/BitMapPerformanceTest.java
index 7f3d239c0..faaa41d06 100644
--- 
a/java/tsfile/src/test/java/org/apache/tsfile/utils/BitMapPerformanceTest.java
+++ 
b/java/tsfile/src/test/java/org/apache/tsfile/utils/BitMapPerformanceTest.java
@@ -22,6 +22,7 @@ package org.apache.tsfile.utils;
 import org.junit.Assume;
 import org.junit.Test;
 
+import java.lang.management.ManagementFactory;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -35,6 +36,8 @@ public class BitMapPerformanceTest {
   private static final int WARMUP_ROUNDS = 3;
   private static final int MEASUREMENT_ROUNDS = 7;
   private static final int CHEAP_OPERATION_COUNT = 2_000_000;
+  private static final int RANGE_OPERATION_COUNT = 10_000_000;
+  private static final int RANGE_BITMAP_COUNT = 64;
   private static final int ALLOCATION_OPERATION_COUNT = 200_000;
   private static final int COMPOSITE_OPERATION_COUNT = 50_000;
   private static final int BULK_INSTANCE_COUNT = 1_000_000;
@@ -145,6 +148,103 @@ public class BitMapPerformanceTest {
     printMemoryUsage();
   }
 
+  @Test
+  public void testRangeMarkedQueryImplementations() {
+    Assume.assumeTrue(
+        "Set -Dtsfile.runPerformanceTests=true to run the performance test",
+        Boolean.getBoolean(RUN_PERFORMANCE_TEST_PROPERTY));
+
+    java.lang.management.ThreadMXBean baseThreadBean = 
ManagementFactory.getThreadMXBean();
+    if (!(baseThreadBean instanceof com.sun.management.ThreadMXBean 
threadBean)) {
+      Assume.assumeTrue("Current-thread allocation metrics are unavailable", 
false);
+      return;
+    }
+    Assume.assumeTrue(
+        "Current-thread CPU time is unavailable", 
threadBean.isCurrentThreadCpuTimeSupported());
+    Assume.assumeTrue(
+        "Current-thread allocation metrics are unavailable",
+        threadBean.isThreadAllocatedMemorySupported());
+    if (!threadBean.isThreadCpuTimeEnabled()) {
+      threadBean.setThreadCpuTimeEnabled(true);
+    }
+    if (!threadBean.isThreadAllocatedMemoryEnabled()) {
+      threadBean.setThreadAllocatedMemoryEnabled(true);
+    }
+
+    List<RangeBenchmarkResult> results = new ArrayList<>();
+    results.add(
+        benchmarkRangeAnyMarked(
+            "array-backed prefix", createRangeBitMaps(false, 128, 0, 64), 0, 
64, threadBean));
+    results.add(
+        benchmarkRangeAnyMarked(
+            "array-backed partial", createRangeBitMaps(false, 128, 1, 63), 1, 
63, threadBean));
+    results.add(
+        benchmarkRangeAnyMarked(
+            "array-backed next block", createRangeBitMaps(false, 128, 64, 64), 
64, 64, threadBean));
+    results.add(
+        benchmarkRangeAnyMarked(
+            "long-backed prefix", createRangeBitMaps(true, 64, 0, 64), 0, 64, 
threadBean));
+    results.add(
+        benchmarkRangeAnyMarked(
+            "long-backed partial", createRangeBitMaps(true, 64, 1, 63), 1, 63, 
threadBean));
+
+    printRangeResults(results);
+  }
+
+  private static RangeBenchmarkResult benchmarkRangeAnyMarked(
+      String name,
+      BitMap[] bitMaps,
+      int start,
+      int length,
+      com.sun.management.ThreadMXBean threadBean) {
+    Workload currentHelper = currentIoTDBRangeAnyMarkedWorkload(bitMaps, 
start, length);
+    Workload newApi = rangeAnyMarkedWorkload(bitMaps, start, length);
+    for (int round = 0; round < WARMUP_ROUNDS; round++) {
+      runMeasured(currentHelper, RANGE_OPERATION_COUNT, threadBean);
+      runMeasured(newApi, RANGE_OPERATION_COUNT, threadBean);
+    }
+
+    long[] currentCpuNanos = new long[MEASUREMENT_ROUNDS];
+    long[] currentAllocatedBytes = new long[MEASUREMENT_ROUNDS];
+    long[] apiCpuNanos = new long[MEASUREMENT_ROUNDS];
+    long[] apiAllocatedBytes = new long[MEASUREMENT_ROUNDS];
+    for (int round = 0; round < MEASUREMENT_ROUNDS; round++) {
+      MeasuredRun currentRun;
+      MeasuredRun apiRun;
+      if ((round & 1) == 0) {
+        currentRun = runMeasured(currentHelper, RANGE_OPERATION_COUNT, 
threadBean);
+        apiRun = runMeasured(newApi, RANGE_OPERATION_COUNT, threadBean);
+      } else {
+        apiRun = runMeasured(newApi, RANGE_OPERATION_COUNT, threadBean);
+        currentRun = runMeasured(currentHelper, RANGE_OPERATION_COUNT, 
threadBean);
+      }
+      assertEquals(currentRun.checksum, apiRun.checksum);
+      currentCpuNanos[round] = currentRun.cpuNanos;
+      currentAllocatedBytes[round] = currentRun.allocatedBytes;
+      apiCpuNanos[round] = apiRun.cpuNanos;
+      apiAllocatedBytes[round] = apiRun.allocatedBytes;
+    }
+
+    return new RangeBenchmarkResult(
+        name,
+        median(currentCpuNanos) / (double) RANGE_OPERATION_COUNT,
+        median(currentAllocatedBytes) / (double) RANGE_OPERATION_COUNT,
+        median(apiCpuNanos) / (double) RANGE_OPERATION_COUNT,
+        median(apiAllocatedBytes) / (double) RANGE_OPERATION_COUNT);
+  }
+
+  private static MeasuredRun runMeasured(
+      Workload workload, int operationCount, com.sun.management.ThreadMXBean 
threadBean) {
+    long threadId = Thread.currentThread().getId();
+    long allocatedBytesBefore = threadBean.getThreadAllocatedBytes(threadId);
+    long cpuNanosBefore = threadBean.getCurrentThreadCpuTime();
+    long checksum = workload.run(operationCount);
+    long cpuNanos = threadBean.getCurrentThreadCpuTime() - cpuNanosBefore;
+    long allocatedBytes = threadBean.getThreadAllocatedBytes(threadId) - 
allocatedBytesBefore;
+    blackHole = checksum;
+    return new MeasuredRun(cpuNanos, allocatedBytes, checksum);
+  }
+
   private static BenchmarkResult benchmark(
       String name, int operationCount, Workload arrayWorkload, Workload 
longWorkload) {
     for (int round = 0; round < WARMUP_ROUNDS; round++) {
@@ -437,6 +537,72 @@ public class BitMapPerformanceTest {
     };
   }
 
+  private static Workload currentIoTDBRangeAnyMarkedWorkload(
+      BitMap[] bitMaps, int start, int length) {
+    return operationCount -> {
+      long checksum = 0;
+      for (int i = 0; i < operationCount; i++) {
+        checksum += currentIoTDBRangeAnyMarked(bitMaps[i & 63], start, length) 
? 1 : 0;
+      }
+      return checksum;
+    };
+  }
+
+  private static Workload rangeAnyMarkedWorkload(BitMap[] bitMaps, int start, 
int length) {
+    return operationCount -> {
+      long checksum = 0;
+      for (int i = 0; i < operationCount; i++) {
+        checksum += bitMaps[i & 63].isRangeAnyMarked(start, length) ? 1 : 0;
+      }
+      return checksum;
+    };
+  }
+
+  private static boolean currentIoTDBRangeAnyMarked(BitMap bitMap, int start, 
int length) {
+    // Mirror IoTDB's prefix shortcut and its byte scan for ranges with a 
non-zero offset.
+    if (start == 0) {
+      return !bitMap.isAllUnmarked(length);
+    }
+
+    byte[] bytes = bitMap.getByteArray();
+    int end = start + length;
+    int firstByte = start >>> 3;
+    int lastByte = (end - 1) >>> 3;
+    if (firstByte == lastByte) {
+      int mask = ((1 << length) - 1) << (start & 7);
+      return (bytes[firstByte] & mask) != 0;
+    }
+
+    int firstMask = (0xFF << (start & 7)) & 0xFF;
+    if ((bytes[firstByte] & firstMask) != 0) {
+      return true;
+    }
+    for (int index = firstByte + 1; index < lastByte; index++) {
+      if (bytes[index] != 0) {
+        return true;
+      }
+    }
+    int lastBitCount = end & 7;
+    int lastMask = lastBitCount == 0 ? 0xFF : (1 << lastBitCount) - 1;
+    return (bytes[lastByte] & lastMask) != 0;
+  }
+
+  private static BitMap[] createRangeBitMaps(boolean longBacked, int size, int 
start, int length) {
+    BitMap[] bitMaps = new BitMap[RANGE_BITMAP_COUNT];
+    for (int index = 0; index < bitMaps.length; index++) {
+      bitMaps[index] =
+          new BitMap(longBacked ? new BitMapLongImpl(size) : new 
BitMapArrayImpl(size));
+      if ((index & 1) == 0) {
+        bitMaps[index].mark(start + index % length);
+      } else if (start > 0) {
+        bitMaps[index].mark(start - 1);
+      } else if (start + length < size) {
+        bitMaps[index].mark(start + length);
+      }
+    }
+    return bitMaps;
+  }
+
   private static BitMap arrayBitMap() {
     return new BitMap(new BitMapArrayImpl(BIT_MAP_SIZE));
   }
@@ -495,6 +661,22 @@ public class BitMapPerformanceTest {
         "Reduction: %.2f%% per object%n", (arrayBytes - longBytes) * 100.0 / 
arrayBytes);
   }
 
+  private static void printRangeResults(List<RangeBenchmarkResult> results) {
+    System.out.println("isRangeAnyMarked performance (current-thread median 
CPU/allocation)");
+    System.out.printf(
+        "%-28s %14s %12s %14s %12s%n",
+        "representation / range", "current ns/op", "current B/op", "new API 
ns/op", "new API B/op");
+    for (RangeBenchmarkResult result : results) {
+      System.out.printf(
+          "%-28s %14.3f %12.3f %14.3f %12.3f%n",
+          result.name,
+          result.currentCpuNanosPerOperation,
+          result.currentAllocatedBytesPerOperation,
+          result.apiCpuNanosPerOperation,
+          result.apiAllocatedBytesPerOperation);
+    }
+  }
+
   @FunctionalInterface
   private interface Workload {
     long run(int operationCount);
@@ -521,6 +703,19 @@ public class BitMapPerformanceTest {
     }
   }
 
+  private static class MeasuredRun {
+
+    private final long cpuNanos;
+    private final long allocatedBytes;
+    private final long checksum;
+
+    private MeasuredRun(long cpuNanos, long allocatedBytes, long checksum) {
+      this.cpuNanos = cpuNanos;
+      this.allocatedBytes = allocatedBytes;
+      this.checksum = checksum;
+    }
+  }
+
   private static class BenchmarkResult {
 
     private final String name;
@@ -534,4 +729,26 @@ public class BitMapPerformanceTest {
       this.longNanosPerOperation = longNanosPerOperation;
     }
   }
+
+  private static class RangeBenchmarkResult {
+
+    private final String name;
+    private final double currentCpuNanosPerOperation;
+    private final double currentAllocatedBytesPerOperation;
+    private final double apiCpuNanosPerOperation;
+    private final double apiAllocatedBytesPerOperation;
+
+    private RangeBenchmarkResult(
+        String name,
+        double currentCpuNanosPerOperation,
+        double currentAllocatedBytesPerOperation,
+        double apiCpuNanosPerOperation,
+        double apiAllocatedBytesPerOperation) {
+      this.name = name;
+      this.currentCpuNanosPerOperation = currentCpuNanosPerOperation;
+      this.currentAllocatedBytesPerOperation = 
currentAllocatedBytesPerOperation;
+      this.apiCpuNanosPerOperation = apiCpuNanosPerOperation;
+      this.apiAllocatedBytesPerOperation = apiAllocatedBytesPerOperation;
+    }
+  }
 }
diff --git a/java/tsfile/src/test/java/org/apache/tsfile/utils/BitMapTest.java 
b/java/tsfile/src/test/java/org/apache/tsfile/utils/BitMapTest.java
index e4b59e452..d92684555 100644
--- a/java/tsfile/src/test/java/org/apache/tsfile/utils/BitMapTest.java
+++ b/java/tsfile/src/test/java/org/apache/tsfile/utils/BitMapTest.java
@@ -27,6 +27,7 @@ import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 
 public class BitMapTest {
@@ -217,6 +218,54 @@ public class BitMapTest {
     assertFalse(bitMap.isAllUnmarked(10));
   }
 
+  @Test
+  public void testRangeMarkedQueries() {
+    assertRangeMarkedQueries(new BitMap(0));
+    assertRangeMarkedQueries(new BitMap(64));
+    assertRangeMarkedQueries(BitMap.createBitMapDynamically(64));
+    assertRangeMarkedQueries(new BitMap(100));
+
+    BitMap fullLongRange = BitMap.createBitMapDynamically(Long.SIZE);
+    fullLongRange.markAll();
+    assertTrue(fullLongRange.isRangeAllMarked(0, Long.SIZE));
+
+    BitMap crossingArrayRange = new BitMap(128);
+    crossingArrayRange.markRange(32, Long.SIZE);
+    assertTrue(crossingArrayRange.isRangeAllMarked(32, Long.SIZE));
+    assertFalse(crossingArrayRange.isRangeAllMarked(31, Long.SIZE));
+  }
+
+  private void assertRangeMarkedQueries(BitMap bitMap) {
+    for (int i = 0; i < bitMap.getSize(); i++) {
+      if (i % 4 != 2) {
+        bitMap.mark(i);
+      }
+    }
+
+    for (int start = 0; start <= bitMap.getSize(); start++) {
+      for (int length = 0; length <= bitMap.getSize() - start; length++) {
+        boolean anyMarked = false;
+        boolean allMarked = true;
+        for (int i = start; i < start + length; i++) {
+          anyMarked |= bitMap.isMarked(i);
+          allMarked &= bitMap.isMarked(i);
+        }
+        assertEquals(anyMarked, bitMap.isRangeAnyMarked(start, length));
+        assertEquals(allMarked, bitMap.isRangeAllMarked(start, length));
+        assertEquals(!anyMarked, bitMap.isRangeNoneMarked(start, length));
+      }
+    }
+
+    assertThrows(IndexOutOfBoundsException.class, () -> 
bitMap.isRangeAnyMarked(-1, 0));
+    assertThrows(IndexOutOfBoundsException.class, () -> 
bitMap.isRangeAllMarked(0, -1));
+    assertThrows(
+        IndexOutOfBoundsException.class, () -> 
bitMap.isRangeNoneMarked(bitMap.getSize() + 1, 0));
+    assertThrows(
+        IndexOutOfBoundsException.class, () -> 
bitMap.isRangeAnyMarked(bitMap.getSize(), 1));
+    assertThrows(
+        IndexOutOfBoundsException.class, () -> bitMap.isRangeAllMarked(1, 
Integer.MAX_VALUE));
+  }
+
   @Test
   public void testGetTruncatedByteArray() {
     BitMap bitMap = new BitMap(16);

Reply via email to