diff --git a/src/main/java/com/zaxxer/sparsebits/SparseBitSet.java b/src/main/java/com/zaxxer/sparsebits/SparseBitSet.java index 3d86170..60d06d6 100644 --- a/src/main/java/com/zaxxer/sparsebits/SparseBitSet.java +++ b/src/main/java/com/zaxxer/sparsebits/SparseBitSet.java @@ -943,8 +943,15 @@ a null area or block, or a clear bit (a set bit in the if ((a3 = a2[w2]) == null) break loop; for (; w3 != LENGTH3; ++w3) - if ((nword = ~a3[w3]) != 0) + { + final long current; + if ((current = ~a3[w3]) != 0) + { + // Only set nword if there was a clear bit in the current word + nword = current; break loop; + } + } w3 = 0; } w2 = w3 = 0; diff --git a/src/test/java/com/zaxxer/sparsebits/NextClearBitTest.java b/src/test/java/com/zaxxer/sparsebits/NextClearBitTest.java new file mode 100644 index 0000000..8ea17eb --- /dev/null +++ b/src/test/java/com/zaxxer/sparsebits/NextClearBitTest.java @@ -0,0 +1,142 @@ +package com.zaxxer.sparsebits; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.Rule; +import org.junit.rules.ExpectedException; + +public class NextClearBitTest { + + SparseBitSet set; + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Before + public void setUp() { + set = new SparseBitSet(); + } + + @Test + public void testNextClearBitAfterFullLastWordInFirstLevel3Entry() { + // The last word in the first level3 block covers bits 1984-2047, set those + the last bit from second + // to last word + for (int i = 1983; i < 2047; i++) { + set.set(i); + } + + // Test a known clear bit + Assert.assertEquals(1982, set.nextClearBit(1982)); + + // Test nextClearBit from the beginning of the last word + Assert.assertEquals(2047, set.nextClearBit(1984)); + + // Test nextClearBit from the middle of the last word + Assert.assertEquals(2047, set.nextClearBit(2020)); + + // Test nextClearBit from the end of the last word + Assert.assertEquals(2047, set.nextClearBit(2047)); + + // Test nextClearBit from the end of the previous word + Assert.assertEquals(2047, set.nextClearBit(1983)); + + // set the last bit + set.set(2047); + // Test a known clear bit + Assert.assertEquals(1982, set.nextClearBit(1982)); + + // Test nextClearBit from the beginning of the last word + Assert.assertEquals(2048, set.nextClearBit(1984)); + + // Test nextClearBit from the middle of the last word + Assert.assertEquals(2048, set.nextClearBit(2020)); + + // Test nextClearBit from the end of the last word + Assert.assertEquals(2048, set.nextClearBit(2047)); + + // Test nextClearBit from the end of the previous word + Assert.assertEquals(2048, set.nextClearBit(1983)); + } + + @Test + public void testNextClearBitAfterFullLastWordInFirstLevel2() { + // The last word in the first level2 covers bits 65472-65535, set those + the last bit from second + // to last word + for (int i = 65471; i < 65535; i++) { + set.set(i); + } + + // Set a bit in the third level2 block leaving the second level2 block empty / null. + set.set(65536 * 3 + 1); + + // Test a known clear bit + Assert.assertEquals(65470, set.nextClearBit(65470)); + + // Test nextClearBit from the beginning of the last word + Assert.assertEquals(65535, set.nextClearBit(65472)); + + // Test nextClearBit from the middle of the last word + Assert.assertEquals(65535, set.nextClearBit(65504)); + + // Test nextClearBit from the end of the last word + Assert.assertEquals(65535, set.nextClearBit(65535)); + + // Test nextClearBit from the end of the previous word + Assert.assertEquals(65535, set.nextClearBit(65471)); + + // set the last bit + set.set(65535); + + // Test a known clear bit + Assert.assertEquals(65470, set.nextClearBit(65470)); + + // Test nextClearBit from the beginning of the last word + Assert.assertEquals(65536, set.nextClearBit(65472)); + + // Test nextClearBit from the middle of the last word + Assert.assertEquals(65536, set.nextClearBit(65504)); + + // Test nextClearBit from the end of the last word + Assert.assertEquals(65536, set.nextClearBit(65535)); + + // Test nextClearBit from the end of the previous word + Assert.assertEquals(65536, set.nextClearBit(65471)); + } + + @Test + public void testNextClearBitForEmptySet() { + Assert.assertEquals(0, set.nextClearBit(0)); + final int x = 65536 * 4 + set.length(); + Assert.assertEquals(x, set.nextClearBit(x)); + } + + @Test + public void testNextClearBitForSingleEntry() { + set.set(50); + Assert.assertEquals(51, set.nextClearBit(50)); + } + + @Test + public void testNoNextClearBit() { + // Test that nextClearBit returns -1 when there are no clear bits + set.set(Integer.MAX_VALUE - 1); + Assert.assertEquals(-1, set.nextClearBit(Integer.MAX_VALUE - 1)); + } + + @Test + public void testNegativeIndex() { + // Test that nextClearBit throws an IndexOutOfBoundsException for negative indexes + thrown.expect(IndexOutOfBoundsException.class); + thrown.expectMessage(String.valueOf(-1)); + set.nextClearBit(-1); + } + + @Test + public void testNegativeIndexLarge() { + // Test with another negative index + thrown.expect(IndexOutOfBoundsException.class); + thrown.expectMessage(String.valueOf(-10000)); + set.nextClearBit(-10000); + } +}