Skip to content

Commit

Permalink
[bug#12284] Ensure ArrayBufferView is consistent with buffer
Browse files Browse the repository at this point in the history
  • Loading branch information
NthPortal committed Sep 2, 2021
1 parent 39c43ea commit 75a3f19
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 4 deletions.
21 changes: 17 additions & 4 deletions src/library/scala/collection/mutable/ArrayBuffer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ class ArrayBuffer[A] private (initialElements: Array[AnyRef], initialSize: Int)

def length = size0

override def view: ArrayBufferView[A] = new ArrayBufferView(array, size0, () => mutationCount)
// TODO: return `IndexedSeqView` rather than `ArrayBufferView`
override def view: ArrayBufferView[A] = new ArrayBufferView(this, () => mutationCount)

override def iterableFactory: SeqFactory[ArrayBuffer] = ArrayBuffer

Expand Down Expand Up @@ -317,18 +318,30 @@ object ArrayBuffer extends StrictOptimizedSeqFactory[ArrayBuffer] {
}
}

final class ArrayBufferView[A] private[mutable](val array: Array[AnyRef], val length: Int, mutationCount: () => Int)
// TODO: use `CheckedIndexedSeqView.Id` once we can change the return type of `ArrayBuffer#view`
final class ArrayBufferView[A] private[mutable](underlying: ArrayBuffer[A], mutationCount: () => Int)
extends AbstractIndexedSeqView[A] {
@deprecated("never intended to be public; call ArrayBuffer#view instead", since = "2.13.6")
def this(array: Array[AnyRef], length: Int) = {
// this won't actually track mutation, but it would be a pain to have the implementation
// check if we have a method to get the current mutation count or not on every method and
// change what it does based on that. hopefully no one ever calls this.
this(array, length, () => 0)
this({
val _array = array
val _length = length
new ArrayBuffer[A](0) {
this.array = _array
this.size0 = _length
}
}, () => 0)
}

@deprecated("never intended to be public", since = "2.13.6")
def array: Array[AnyRef] = underlying.toArray[Any].asInstanceOf[Array[AnyRef]]

@throws[IndexOutOfBoundsException]
def apply(n: Int): A = if (n < length) array(n).asInstanceOf[A] else throw new IndexOutOfBoundsException(s"$n is out of bounds (min 0, max ${length - 1})")
def apply(n: Int): A = underlying(n)
def length: Int = underlying.length
override protected[this] def className = "ArrayBufferView"

// we could inherit all these from `CheckedIndexedSeqView`, except this class is public
Expand Down
17 changes: 17 additions & 0 deletions test/junit/scala/collection/mutable/ArrayBufferTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -455,4 +455,21 @@ class ArrayBufferTest {
buf.insertAll(1, buf)
assertSameElements(List(1, 1, 2, 3, 2, 3), buf)
}

// scala/bug#12284
@Test
def viewConsistency(): Unit = {
def check[U](op: ArrayBuffer[Int] => U): Unit = {
val buf = ArrayBuffer(1, 2, 3)
val view = buf.view
op(buf)
assertSameElements(buf, view)
}

check(_.clear())
check(_.dropRightInPlace(1))
check(_.dropInPlace(1))
check(_ ++= (1 to 100))
check(_.insertAll(1, 1 to 100))
}
}

0 comments on commit 75a3f19

Please sign in to comment.