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);