Skip to content

Commit 640b712

Browse files
authored
Merge pull request scala#86 from linasm/fix-aioobe-in-mutable-bitset-shift-left
Fix ArrayIndexOutOfBoundsException in mutable.BitSet.<<=
2 parents f8c98ec + 2cbdde4 commit 640b712

File tree

2 files changed

+39
-26
lines changed

2 files changed

+39
-26
lines changed

src/main/scala/scala/collection/decorators/MutableBitSetDecorator.scala

+29-25
Original file line numberDiff line numberDiff line change
@@ -39,38 +39,42 @@ class MutableBitSetDecorator(protected val bs: mutable.BitSet) {
3939

4040
private def shiftLeftInPlace(shiftBy: Int): Unit = {
4141

42-
val bitOffset = shiftBy & WordMask
43-
val wordOffset = shiftBy >>> LogWL
44-
4542
var significantWordCount = bs.nwords
4643
while (significantWordCount > 0 && bs.word(significantWordCount - 1) == 0) {
4744
significantWordCount -= 1
4845
}
4946

50-
if (bitOffset == 0) {
51-
val newSize = significantWordCount + wordOffset
52-
require(newSize <= MaxSize)
53-
ensureCapacity(newSize)
54-
System.arraycopy(bs.elems, 0, bs.elems, wordOffset, significantWordCount)
55-
} else {
56-
val revBitOffset = WordLength - bitOffset
57-
val extraBits = bs.elems(significantWordCount - 1) >>> revBitOffset
58-
val extraWordCount = if (extraBits == 0) 0 else 1
59-
val newSize = significantWordCount + wordOffset + extraWordCount
60-
require(newSize <= MaxSize)
61-
ensureCapacity(newSize)
62-
var i = significantWordCount - 1
63-
var previous = bs.elems(i)
64-
while (i > 0) {
65-
val current = bs.elems(i - 1)
66-
bs.elems(i + wordOffset) = (current >>> revBitOffset) | (previous << bitOffset)
67-
previous = current
68-
i -= 1
47+
if (significantWordCount > 0) {
48+
49+
val bitOffset = shiftBy & WordMask
50+
val wordOffset = shiftBy >>> LogWL
51+
52+
if (bitOffset == 0) {
53+
val newSize = significantWordCount + wordOffset
54+
require(newSize <= MaxSize)
55+
ensureCapacity(newSize)
56+
System.arraycopy(bs.elems, 0, bs.elems, wordOffset, significantWordCount)
57+
} else {
58+
val revBitOffset = WordLength - bitOffset
59+
val extraBits = bs.elems(significantWordCount - 1) >>> revBitOffset
60+
val extraWordCount = if (extraBits == 0) 0 else 1
61+
val newSize = significantWordCount + wordOffset + extraWordCount
62+
require(newSize <= MaxSize)
63+
ensureCapacity(newSize)
64+
var i = significantWordCount - 1
65+
var previous = bs.elems(i)
66+
while (i > 0) {
67+
val current = bs.elems(i - 1)
68+
bs.elems(i + wordOffset) = (current >>> revBitOffset) | (previous << bitOffset)
69+
previous = current
70+
i -= 1
71+
}
72+
bs.elems(wordOffset) = previous << bitOffset
73+
if (extraWordCount != 0) bs.elems(newSize - 1) = extraBits
6974
}
70-
bs.elems(wordOffset) = previous << bitOffset
71-
if (extraWordCount != 0) bs.elems(newSize - 1) = extraBits
75+
76+
java.util.Arrays.fill(bs.elems, 0, wordOffset, 0)
7277
}
73-
java.util.Arrays.fill(bs.elems, 0, wordOffset, 0)
7478
}
7579

7680
private def shiftRightInPlace(shiftBy: Int): Unit = {

src/test/scala/scala/collection/decorators/MutableBitSetDecoratorTest.scala

+10-1
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ package scala.collection.decorators
22

33
import org.junit.{Assert, Test}
44

5+
import scala.collection.BitSetOps
56
import scala.collection.mutable.BitSet
67

78
class MutableBitSetDecoratorTest {
89

9-
import Assert.{assertEquals, assertSame}
10+
import Assert.{assertEquals, assertSame, assertTrue}
1011
import BitSet.empty
1112

1213
@Test
@@ -104,4 +105,12 @@ class MutableBitSetDecoratorTest {
104105
}
105106
}
106107

108+
@Test
109+
def shiftLeftTwoEmptyWords(): Unit = {
110+
val twoWords = BitSet(BitSetOps.WordLength + 1)
111+
twoWords ^= twoWords
112+
twoWords <<= 1
113+
assertTrue(twoWords.isEmpty)
114+
}
115+
107116
}

0 commit comments

Comments
 (0)