Skip to content

Commit 50995d9

Browse files
authored
Merge pull request scala/scala#10642 from som-snytt/followup/array-builder
Revert maladapted ArrayBuffer builder [ci: last-only]
2 parents 735ba03 + c54a92c commit 50995d9

14 files changed

+63
-61
lines changed

library/src/scala/collection/ArrayOps.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1174,8 +1174,7 @@ final class ArrayOps[A](private val xs: Array[A]) extends AnyVal {
11741174
/** A copy of this array with all elements of a collection appended. */
11751175
def appendedAll[B >: A : ClassTag](suffix: IterableOnce[B]): Array[B] = {
11761176
val b = ArrayBuilder.make[B]
1177-
val k = suffix.knownSize
1178-
if(k >= 0) b.sizeHint(k + xs.length)
1177+
b.sizeHint(suffix, delta = xs.length)
11791178
b.addAll(xs)
11801179
b.addAll(suffix)
11811180
b.result()

library/src/scala/collection/Factory.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ object Factory {
6161
private class ArrayFactory[A: ClassTag] extends Factory[A, Array[A]] with Serializable {
6262
def fromSpecific(it: IterableOnce[A]): Array[A] = {
6363
val b = newBuilder
64-
b.sizeHint(scala.math.max(0, it.knownSize))
64+
b.sizeHint(it, delta = 0)
6565
b ++= it
6666
b.result()
6767
}

library/src/scala/collection/StrictOptimizedSeqOps.scala

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,15 @@ trait StrictOptimizedSeqOps [+A, +CC[_], +C]
3434

3535
override def prepended[B >: A](elem: B): CC[B] = {
3636
val b = iterableFactory.newBuilder[B]
37-
if (knownSize >= 0) {
38-
b.sizeHint(size + 1)
39-
}
37+
b.sizeHint(this, delta = 1)
4038
b += elem
4139
b ++= this
4240
b.result()
4341
}
4442

4543
override def appended[B >: A](elem: B): CC[B] = {
4644
val b = iterableFactory.newBuilder[B]
47-
if (knownSize >= 0) {
48-
b.sizeHint(size + 1)
49-
}
45+
b.sizeHint(this, delta = 1)
5046
b ++= this
5147
b += elem
5248
b.result()

library/src/scala/collection/immutable/StrictOptimizedSeqOps.scala

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,8 @@ package scala
1414
package collection
1515
package immutable
1616

17-
/**
18-
* Trait that overrides operations to take advantage of strict builders.
19-
*/
17+
/** Trait that overrides operations to take advantage of strict builders.
18+
*/
2019
trait StrictOptimizedSeqOps[+A, +CC[_], +C]
2120
extends Any
2221
with SeqOps[A, CC, C]
@@ -41,9 +40,7 @@ trait StrictOptimizedSeqOps[+A, +CC[_], +C]
4140
override def updated[B >: A](index: Int, elem: B): CC[B] = {
4241
if (index < 0) throw new IndexOutOfBoundsException(s"$index is out of bounds (min 0, max ${if (knownSize>=0) knownSize else "unknown"})")
4342
val b = iterableFactory.newBuilder[B]
44-
if (knownSize >= 0) {
45-
b.sizeHint(size)
46-
}
43+
b.sizeHint(this)
4744
var i = 0
4845
val it = iterator
4946
while (i < index && it.hasNext) {

library/src/scala/collection/immutable/WrappedString.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,7 @@ final class WrappedString(private val self: String) extends AbstractSeq[Char] wi
125125
object WrappedString extends SpecificIterableFactory[Char, WrappedString] {
126126
def fromSpecific(it: IterableOnce[Char]): WrappedString = {
127127
val b = newBuilder
128-
val s = it.knownSize
129-
if(s >= 0) b.sizeHint(s)
128+
b.sizeHint(it)
130129
b ++= it
131130
b.result()
132131
}

library/src/scala/collection/mutable/ArrayBuffer.scala

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -299,19 +299,12 @@ object ArrayBuffer extends StrictOptimizedSeqFactory[ArrayBuffer] {
299299
else new ArrayBuffer[B] ++= coll
300300
}
301301

302-
def newBuilder[A]: Builder[A, ArrayBuffer[A]] =
303-
new GrowableBuilder[A, ArrayBuffer[A]](empty) {
304-
override def sizeHint(size: Int): Unit = elems.ensureSize(size)
305-
}
302+
def newBuilder[A]: Builder[A, ArrayBuffer[A]] = new GrowableBuilder[A, ArrayBuffer[A]](empty[A]) {
303+
override def sizeHint(size: Int): Unit = elems.sizeHint(size)
304+
}
306305

307306
def empty[A]: ArrayBuffer[A] = new ArrayBuffer[A]()
308307

309-
@inline private def checkArrayLengthLimit(length: Int, currentLength: Int): Unit =
310-
if (length > VM_MaxArraySize)
311-
throw new Exception(s"Array of array-backed collection exceeds VM length limit of $VM_MaxArraySize. Requested length: $length; current length: $currentLength")
312-
else if (length < 0)
313-
throw new Exception(s"Overflow while resizing array of array-backed collection. Requested length: $length; current length: $currentLength; increase: ${length - currentLength}")
314-
315308
/**
316309
* The increased size for an array-backed collection.
317310
*
@@ -320,13 +313,19 @@ object ArrayBuffer extends StrictOptimizedSeqFactory[ArrayBuffer] {
320313
* @return
321314
* - `-1` if no resizing is needed, else
322315
* - `VM_MaxArraySize` if `arrayLen` is too large to be doubled, else
323-
* - `max(targetLen, arrayLen * 2, , DefaultInitialSize)`.
316+
* - `max(targetLen, arrayLen * 2, DefaultInitialSize)`.
324317
* - Throws an exception if `targetLen` exceeds `VM_MaxArraySize` or is negative (overflow).
325318
*/
326319
private[mutable] def resizeUp(arrayLen: Int, targetLen: Int): Int = {
320+
def checkArrayLengthLimit(): Unit =
321+
if (targetLen > VM_MaxArraySize)
322+
throw new Exception(s"Array of array-backed collection exceeds VM length limit of $VM_MaxArraySize. Requested length: $targetLen; current length: $arrayLen")
323+
else if (targetLen < 0)
324+
throw new Exception(s"Overflow while resizing array of array-backed collection. Requested length: $targetLen; current length: $arrayLen; increase: ${targetLen - arrayLen}")
325+
327326
if (targetLen > 0 && targetLen <= arrayLen) -1
328327
else {
329-
checkArrayLengthLimit(targetLen, arrayLen)
328+
checkArrayLengthLimit()
330329
if (arrayLen > VM_MaxArraySize / 2) VM_MaxArraySize
331330
else math.max(targetLen, math.max(arrayLen * 2, DefaultInitialSize))
332331
}

library/src/scala/collection/mutable/ArraySeq.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,7 @@ sealed abstract class ArraySeq[T]
4343

4444
override protected def fromSpecific(coll: scala.collection.IterableOnce[T]): ArraySeq[T] = {
4545
val b = ArrayBuilder.make(elemTag).asInstanceOf[ArrayBuilder[T]]
46-
val s = coll.knownSize
47-
if(s > 0) b.sizeHint(s)
46+
b.sizeHint(coll, delta = 0)
4847
b ++= coll
4948
ArraySeq.make(b.result())
5049
}

library/src/scala/collection/mutable/Builder.scala

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -30,31 +30,45 @@ trait Builder[-A, +To] extends Growable[A] { self =>
3030
/** Result collection consisting of all elements appended so far. */
3131
def result(): To
3232

33-
/** Gives a hint how many elements are expected to be added
34-
* when the next `result` is called. Some builder classes
35-
* will optimize their representation based on the hint. However,
36-
* builder implementations are still required to work correctly even if the hint is
37-
* wrong, i.e. a different number of elements is added.
38-
*
39-
* @param size the hint how many elements will be added.
40-
*/
33+
/** Gives a hint how many elements are expected to be added in total
34+
* by the time `result` is called.
35+
*
36+
* Some builder classes will optimize their representation based on the hint.
37+
* However, builder implementations are required to work correctly even if the hint is
38+
* wrong, i.e., if a different number of elements is added.
39+
*
40+
* The semantics of supplying a hint out of range, such as a size that is negative
41+
* or unreasonably large, is not specified but is implementation-dependent.
42+
*
43+
* The default implementation simply ignores the hint.
44+
*
45+
* @param size the hint how many elements will be added.
46+
*/
4147
def sizeHint(size: Int): Unit = ()
4248

43-
/** Gives a hint that one expects the `result` of this builder
44-
* to have the same size as the given collection, plus some delta. This will
45-
* provide a hint only if the collection has a known size
46-
* Some builder classes
47-
* will optimize their representation based on the hint. However,
48-
* builder implementations are still required to work correctly even if the hint is
49-
* wrong, i.e. a different number of elements is added.
50-
*
51-
* @param coll the collection which serves as a hint for the result's size.
52-
* @param delta a correction to add to the `coll.size` to produce the size hint.
53-
*/
54-
final def sizeHint(coll: scala.collection.IterableOnce[_], delta: Int = 0): Unit = {
55-
val s = coll.knownSize
56-
if (s != -1) sizeHint(s + delta)
57-
}
49+
/** Gives a hint that the `result` of this builder is expected
50+
* to have the same size as the given collection, plus some delta.
51+
*
52+
* This method provides a hint only if the collection has a known size,
53+
* as specified by the following pseudocode:
54+
*
55+
* {{{
56+
* if (coll.knownSize != -1)
57+
* sizeHint(coll.knownSize + delta)
58+
* }}}
59+
*
60+
* Some builder classes will optimize their representation based on the hint.
61+
* However, builder implementations are required to work correctly even if the hint is
62+
* wrong, i.e., if a different number of elements is added.
63+
*
64+
* @param coll the collection which serves as a hint for the result's size.
65+
* @param delta a correction to add to the `coll.size` to produce the size hint (zero if omitted).
66+
*/
67+
final def sizeHint(coll: scala.collection.IterableOnce[_], delta: Int = 0): Unit =
68+
coll.knownSize match {
69+
case -1 =>
70+
case sz => sizeHint(sz + delta)
71+
}
5872

5973
/** Gives a hint how many elements are expected to be added
6074
* when the next `result` is called, together with an upper bound

library/src/scala/collection/mutable/CollisionProofHashMap.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,7 @@ final class CollisionProofHashMap[K, V](initialCapacity: Int, loadFactor: Double
174174
}
175175

176176
override def addAll(xs: IterableOnce[(K, V)]): this.type = {
177-
val k = xs.knownSize
178-
if(k > 0) sizeHint(contentSize + k)
177+
sizeHint(xs, delta = contentSize)
179178
super.addAll(xs)
180179
}
181180

library/src/scala/collection/mutable/HashMap.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ class HashMap[K, V](initialCapacity: Int, loadFactor: Double)
9595
}
9696

9797
override def addAll(xs: IterableOnce[(K, V)]): this.type = {
98-
sizeHint(xs.knownSize)
98+
sizeHint(xs)
9999

100100
xs match {
101101
case hm: immutable.HashMap[K, V] =>

0 commit comments

Comments
 (0)