@@ -15,11 +15,10 @@ package collection
15
15
package mutable
16
16
17
17
import java .util .Arrays
18
-
19
- import scala .annotation .nowarn
20
- import scala .annotation .tailrec
18
+ import scala .annotation .{nowarn , tailrec }
21
19
import scala .collection .Stepper .EfficientSplit
22
20
import scala .collection .generic .DefaultSerializable
21
+ import scala .runtime .PStatics .VM_MaxArraySize
23
22
24
23
/** An implementation of the `Buffer` class using an array to
25
24
* represent the assembled sequence internally. Append, update and random
@@ -70,13 +69,6 @@ class ArrayBuffer[A] private (initialElements: Array[AnyRef], initialSize: Int)
70
69
array = ArrayBuffer .ensureSize(array, size0, n)
71
70
}
72
71
73
- // TODO 3.T: should be `protected`, perhaps `protected[this]`
74
- /** Ensure that the internal array has at least `n` additional cells more than `size0`. */
75
- private [mutable] def ensureAdditionalSize (n : Int ): Unit = {
76
- // `.toLong` to ensure `Long` arithmetic is used and prevent `Int` overflow
77
- array = ArrayBuffer .ensureSize(array, size0, size0.toLong + n)
78
- }
79
-
80
72
/** Uses the given size to resize internal storage, if necessary.
81
73
*
82
74
* @param size Expected maximum number of elements.
@@ -147,10 +139,10 @@ class ArrayBuffer[A] private (initialElements: Array[AnyRef], initialSize: Int)
147
139
148
140
def addOne (elem : A ): this .type = {
149
141
mutationCount += 1
150
- ensureAdditionalSize( 1 )
151
- val oldSize = size0
152
- size0 = oldSize + 1
153
- this (oldSize ) = elem
142
+ val newSize = size0 + 1
143
+ ensureSize(newSize)
144
+ size0 = newSize
145
+ this (size0 - 1 ) = elem
154
146
this
155
147
}
156
148
@@ -161,7 +153,7 @@ class ArrayBuffer[A] private (initialElements: Array[AnyRef], initialSize: Int)
161
153
val elemsLength = elems.size0
162
154
if (elemsLength > 0 ) {
163
155
mutationCount += 1
164
- ensureAdditionalSize( elemsLength)
156
+ ensureSize(size0 + elemsLength)
165
157
Array .copy(elems.array, 0 , array, length, elemsLength)
166
158
size0 = length + elemsLength
167
159
}
@@ -173,7 +165,7 @@ class ArrayBuffer[A] private (initialElements: Array[AnyRef], initialSize: Int)
173
165
def insert (@ deprecatedName(" n" , " 2.13.0" ) index : Int , elem : A ): Unit = {
174
166
checkWithinBounds(index, index)
175
167
mutationCount += 1
176
- ensureAdditionalSize( 1 )
168
+ ensureSize(size0 + 1 )
177
169
Array .copy(array, index, array, index + 1 , size0 - index)
178
170
size0 += 1
179
171
this (index) = elem
@@ -191,7 +183,7 @@ class ArrayBuffer[A] private (initialElements: Array[AnyRef], initialSize: Int)
191
183
val elemsLength = elems.size
192
184
if (elemsLength > 0 ) {
193
185
mutationCount += 1
194
- ensureAdditionalSize( elemsLength)
186
+ ensureSize(size0 + elemsLength)
195
187
val len = size0
196
188
Array .copy(array, index, array, index + elemsLength, len - index)
197
189
// if `elems eq this`, this copy is safe because
@@ -314,24 +306,35 @@ object ArrayBuffer extends StrictOptimizedSeqFactory[ArrayBuffer] {
314
306
315
307
def empty [A ]: ArrayBuffer [A ] = new ArrayBuffer [A ]()
316
308
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
+
317
315
/**
316
+ * The increased size for an array-backed collection.
317
+ *
318
318
* @param arrayLen the length of the backing array
319
319
* @param targetLen the minimum length to resize up to
320
- * @return -1 if no resizing is needed, or the size for the new array otherwise
320
+ * @return
321
+ * - `-1` if no resizing is needed, else
322
+ * - `VM_MaxArraySize` if `arrayLen` is too large to be doubled, else
323
+ * - `max(targetLen, arrayLen * 2, , DefaultInitialSize)`.
324
+ * - Throws an exception if `targetLen` exceeds `VM_MaxArraySize` or is negative (overflow).
321
325
*/
322
- private def resizeUp (arrayLen : Long , targetLen : Long ): Int = {
323
- if (targetLen <= arrayLen) - 1
326
+ private [mutable] def resizeUp (arrayLen : Int , targetLen : Int ): Int = {
327
+ if (targetLen > 0 && targetLen <= arrayLen) - 1
324
328
else {
325
- if (targetLen > Int .MaxValue ) throw new Exception (s " Collections cannot have more than ${Int .MaxValue } elements " )
326
- IterableOnce .checkArraySizeWithinVMLimit(targetLen.toInt) // safe because `targetSize <= Int.MaxValue`
327
-
328
- val newLen = math.max(targetLen, math.max(arrayLen * 2 , DefaultInitialSize ))
329
- math.min(newLen, scala.runtime.PStatics .VM_MaxArraySize ).toInt
329
+ checkArrayLengthLimit(targetLen, arrayLen)
330
+ if (arrayLen > VM_MaxArraySize / 2 ) VM_MaxArraySize
331
+ else math.max(targetLen, math.max(arrayLen * 2 , DefaultInitialSize ))
330
332
}
331
333
}
334
+
332
335
// if necessary, copy (curSize elements of) the array to a new array of capacity n.
333
336
// Should use Array.copyOf(array, resizeEnsuring(array.length))?
334
- private def ensureSize (array : Array [AnyRef ], curSize : Int , targetSize : Long ): Array [AnyRef ] = {
337
+ private def ensureSize (array : Array [AnyRef ], curSize : Int , targetSize : Int ): Array [AnyRef ] = {
335
338
val newLen = resizeUp(array.length, targetSize)
336
339
if (newLen < 0 ) array
337
340
else {
0 commit comments