From 23040fe8da1dfd0595f7904211b8e391dceb78a6 Mon Sep 17 00:00:00 2001 From: Masaru Nomura Date: Sun, 25 Dec 2016 02:06:25 +0900 Subject: [Backport] Modify ArrayBuilder and WrappedArrayBuilder to be reusable As they're reusable in 2.12.x with this change[1], it'd be useful to make them reusable in 2.11.x. [1] https://github.com/scala/scala/commit/6eaae1b969b68ed3dc65a40613a8168b09246256 With this change, not only are they reusable but also we can avoid mutation of previously created arrays. Behaviour(Problem): Actual behaviour before this modification is as follows; ``` scala> import scala.collection.mutable.ArrayBuilder import scala.collection.mutable.ArrayBuilder scala> val builder = new ArrayBuilder.ofInt builder: scala.collection.mutable.ArrayBuilder.ofInt = ArrayBuilder.ofInt scala> builder ++= Vector.range(1, 17) res0: builder.type = ArrayBuilder.ofInt scala> val arr = builder.result() arr: Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16) scala> builder.clear() scala> builder += 100 res2: builder.type = ArrayBuilder.ofInt scala> val arr2 = builder.result() arr2: Array[Int] = Array(100) scala> arr res3: Array[Int] = Array(100, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16) // arr should be Array(1, .., 16) but was unexpectedly mutated by `+=` operation ``` `arr` was mutated as follows; 1. `result` & `clear` - `arr = elems` - `size = 0` 2. `+=(100)` - `ensureSize(0 + 1)` => `capacity < size || capacity == 0` is `false` as `capacity == 16` and `size == 1` - `elems(0) = 100` this is where `arr(0) = 100` was done because we did not reallocate a new array for `elems` when calling `ensureSize`, which should have happened. - `size = 1` 3. `result` - `mkArray(1)` gives us `arr2 = Array(100)` We can observe almost the same mutation behaviour of ArrayBuilder. ``` scala> import scala.collection.mutable.WrappedArray import scala.collection.mutable.WrappedArray scala> import scala.collection.mutable.WrappedArrayBuilder import scala.collection.mutable.WrappedArrayBuilder scala> import scala.reflect.ClassTag import scala.reflect.ClassTag scala> val builder = new WrappedArrayBuilder(ClassTag.Int) builder: scala.collection.mutable.WrappedArrayBuilder[Int] = scala.collection.mutable.WrappedArrayBuilder@56cbfb61 scala> builder ++= Vector.range(1, 17) res0: builder.type = scala.collection.mutable.WrappedArrayBuilder@56cbfb61 scala> builder.result() res1: scala.collection.mutable.WrappedArray[Int] = WrappedArray(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16) scala> builder.clear() scala> builder += 100 res3: builder.type = scala.collection.mutable.WrappedArrayBuilder@56cbfb61 scala> res1 res4: scala.collection.mutable.WrappedArray[Int] = WrappedArray(100, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16) scala> builder.result() res5: scala.collection.mutable.WrappedArray[Int] = WrappedArray(100) ``` Solution: We should reset `capacity` to `0` when calling `result` so that `ensureSize(1)` calls `resize(16)`, which satisfies the property of Builder reusability. Besides mutation of previously created arrays does not happen. --- .../scala/collection/mutable/ArrayBuilder.scala | 50 +++++++++++++++++----- .../collection/mutable/WrappedArrayBuilder.scala | 5 ++- 2 files changed, 44 insertions(+), 11 deletions(-) (limited to 'src/library/scala') diff --git a/src/library/scala/collection/mutable/ArrayBuilder.scala b/src/library/scala/collection/mutable/ArrayBuilder.scala index 6e53824cbe..f4ca27dcba 100644 --- a/src/library/scala/collection/mutable/ArrayBuilder.scala +++ b/src/library/scala/collection/mutable/ArrayBuilder.scala @@ -104,7 +104,10 @@ object ArrayBuilder { } def result() = { - if (capacity != 0 && capacity == size) elems + if (capacity != 0 && capacity == size) { + capacity = 0 + elems + } else mkArray(size) } @@ -169,7 +172,10 @@ object ArrayBuilder { } def result() = { - if (capacity != 0 && capacity == size) elems + if (capacity != 0 && capacity == size) { + capacity = 0 + elems + } else mkArray(size) } @@ -234,7 +240,10 @@ object ArrayBuilder { } def result() = { - if (capacity != 0 && capacity == size) elems + if (capacity != 0 && capacity == size) { + capacity = 0 + elems + } else mkArray(size) } @@ -299,7 +308,10 @@ object ArrayBuilder { } def result() = { - if (capacity != 0 && capacity == size) elems + if (capacity != 0 && capacity == size) { + capacity = 0 + elems + } else mkArray(size) } @@ -364,7 +376,10 @@ object ArrayBuilder { } def result() = { - if (capacity != 0 && capacity == size) elems + if (capacity != 0 && capacity == size) { + capacity = 0 + elems + } else mkArray(size) } @@ -429,7 +444,10 @@ object ArrayBuilder { } def result() = { - if (capacity != 0 && capacity == size) elems + if (capacity != 0 && capacity == size) { + capacity = 0 + elems + } else mkArray(size) } @@ -494,7 +512,10 @@ object ArrayBuilder { } def result() = { - if (capacity != 0 && capacity == size) elems + if (capacity != 0 && capacity == size) { + capacity = 0 + elems + } else mkArray(size) } @@ -559,7 +580,10 @@ object ArrayBuilder { } def result() = { - if (capacity != 0 && capacity == size) elems + if (capacity != 0 && capacity == size) { + capacity = 0 + elems + } else mkArray(size) } @@ -623,7 +647,10 @@ object ArrayBuilder { } def result() = { - if (capacity != 0 && capacity == size) elems + if (capacity != 0 && capacity == size) { + capacity = 0 + elems + } else mkArray(size) } @@ -688,7 +715,10 @@ object ArrayBuilder { } def result() = { - if (capacity != 0 && capacity == size) elems + if (capacity != 0 && capacity == size) { + capacity = 0 + elems + } else mkArray(size) } diff --git a/src/library/scala/collection/mutable/WrappedArrayBuilder.scala b/src/library/scala/collection/mutable/WrappedArrayBuilder.scala index bfe95a11ab..5781ec91be 100644 --- a/src/library/scala/collection/mutable/WrappedArrayBuilder.scala +++ b/src/library/scala/collection/mutable/WrappedArrayBuilder.scala @@ -78,7 +78,10 @@ class WrappedArrayBuilder[A](tag: ClassTag[A]) extends Builder[A, WrappedArray[A } def result() = { - if (capacity != 0 && capacity == size) elems + if (capacity != 0 && capacity == size) { + capacity = 0 + elems + } else mkArray(size) } -- cgit v1.2.3