diff --git a/src/main/java/com/zaxxer/sparsebits/SparseBitSet.java b/src/main/java/com/zaxxer/sparsebits/SparseBitSet.java index 81e31eb..4efbc90 100644 --- a/src/main/java/com/zaxxer/sparsebits/SparseBitSet.java +++ b/src/main/java/com/zaxxer/sparsebits/SparseBitSet.java @@ -230,6 +230,21 @@ when choosing to increase the size (see resize()). The only place where */ protected static final int SHIFT1 = LEVEL2 + LEVEL3; + /** + * LENGTH2_SIZE is maximum index of a LEVEL2 page. + */ + protected static final int LENGTH2_SIZE = LENGTH2 - 1; + + /** + * LENGTH3_SIZE is maximum index of a LEVEL3 page. + */ + protected static final int LENGTH3_SIZE = LENGTH3 - 1; + + /** + * LENGTH4_SIZE is maximum index of a bit in a LEVEL4 word. + */ + protected static final int LENGTH4_SIZE = LENGTH4 - 1; + /** * Holds reference to the cache of statistics values computed by the * UpdateStrategy @@ -1035,50 +1050,43 @@ public int previousClearBit(int i) } final long[][][] bits = this.bits; - final int aLength = bits.length; + final int aSize = bits.length - 1; int w = i >> SHIFT3; int w3 = w & MASK3; int w2 = (w >> SHIFT2) & MASK2; int w1 = w >> SHIFT1; - if (w1 > aLength - 1) + if (w1 > aSize) return i; - w1 = Math.min(w1, aLength - 1); - final int w4 = i % LENGTH4; + w1 = Math.min(w1, aSize); + int w4 = i % LENGTH4; long word; long[][] a2; long[] a3; - final int f3 = w3; - final int f2 = w2; - final int f1 = w1; - for (; w1 >= 0; --w1) { if ((a2 = bits[w1]) == null) - return (((w1 << SHIFT1) + (w2 << SHIFT2) + w3) << SHIFT3) - + (f1 == w1 ? w4 : LENGTH4 - 1); + return (((w1 << SHIFT1) + (w2 << SHIFT2) + w3) << SHIFT3) + w4; for (; w2 >= 0; --w2) { if ((a3 = a2[w2]) == null) - return (((w1 << SHIFT1) + (w2 << SHIFT2) + w3) << SHIFT3) - + (f2 == w2 ? w4 : LENGTH4 - 1); + return (((w1 << SHIFT1) + (w2 << SHIFT2) + w3) << SHIFT3) + w4; for (; w3 >= 0; --w3) { if ((word = a3[w3]) == 0) - return (((w1 << SHIFT1) + (w2 << SHIFT2) + w3) << SHIFT3) - + (f3 == w3 ? w4 : LENGTH4 - 1); + return (((w1 << SHIFT1) + (w2 << SHIFT2) + w3) << SHIFT3) + w4; for (int bitIdx = w4; bitIdx >= 0; --bitIdx) { if ((word & (1L << bitIdx)) == 0) return (((w1 << SHIFT1) + (w2 << SHIFT2) + w3) << SHIFT3) + bitIdx; } + w4 = LENGTH4_SIZE; } - w3 = LENGTH3 - 1; + w3 = LENGTH3_SIZE; } - w2 = LENGTH2 - 1; - w3 = LENGTH3 - 1; + w2 = LENGTH2_SIZE; } return -1; } @@ -1107,19 +1115,19 @@ public int previousSetBit(int i) } final long[][][] bits = this.bits; - final int aLength = bits.length; + final int aSize = bits.length - 1; /* This is the word from which the search begins. */ final int w = i >> SHIFT3; int w1 = w >> SHIFT1; int w2, w3, w4; /* But if its off the end of the array, start from the very end. */ - if (w1 > aLength - 1) + if (w1 > aSize) { - w1 = aLength - 1; - w2 = LENGTH2 - 1; - w3 = LENGTH3 - 1; - w4 = LENGTH4 - 1; + w1 = aSize; + w2 = LENGTH2_SIZE; + w3 = LENGTH3_SIZE; + w4 = LENGTH4_SIZE; } else { @@ -1127,30 +1135,31 @@ public int previousSetBit(int i) w3 = w & MASK3; w4 = i % LENGTH4; } - boolean initialWord = true; - long word; long[][] a2; long[] a3; - for (; w1 >= 0; --w1, initialWord = false) + for (; w1 >= 0; --w1) { if ((a2 = bits[w1]) != null) - for (; w2 >= 0; --w2, initialWord = false) + for (; w2 >= 0; --w2) { if ((a3 = a2[w2]) != null) - for (; w3 >= 0; --w3, initialWord = false) + for (; w3 >= 0; --w3) { if ((word = a3[w3]) != 0) - for (int bitIdx = (initialWord ? w4 : LENGTH4 - 1); bitIdx >= 0; --bitIdx) + for (int bitIdx = w4; bitIdx >= 0; --bitIdx) { if ((word & (1L << bitIdx)) != 0) return (((w1 << SHIFT1) + (w2 << SHIFT2) + w3) << SHIFT3) + bitIdx; } + w4 = LENGTH4_SIZE; } - w3 = LENGTH3 - 1; + w3 = LENGTH3_SIZE; + w4 = LENGTH4_SIZE; } - w2 = LENGTH2 - 1; - w3 = LENGTH3 - 1; + w2 = LENGTH2_SIZE; + w3 = LENGTH3_SIZE; + w4 = LENGTH4_SIZE; } return -1; } diff --git a/src/test/java/com/zaxxer/sparsebits/PreviousClearBitTest.java b/src/test/java/com/zaxxer/sparsebits/PreviousClearBitTest.java index 730dbcc..d7b639b 100644 --- a/src/test/java/com/zaxxer/sparsebits/PreviousClearBitTest.java +++ b/src/test/java/com/zaxxer/sparsebits/PreviousClearBitTest.java @@ -206,4 +206,14 @@ public void randomMultiEntry() throws Exception { values.clear(); } } + + @Test + public void bug15() throws Exception { + set.set(1); + set.set(64); + assertEquals(63, set.previousClearBit(64)); + set.clear(0); + set.set(1); + assertEquals(63, set.previousClearBit(64)); + } } \ No newline at end of file