Skip to content

Commit e102805

Browse files
authored
Merge pull request scala/scala#6926 from joshlemer/bug_8469
IterableOnce#copyToArray returns number of elements copied. Fixes scala/bug#8469 scala/bug#11048
2 parents 59f1fec + b12a490 commit e102805

File tree

7 files changed

+93
-45
lines changed

7 files changed

+93
-45
lines changed

library/src/scala/collection/ArrayOps.scala

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1263,13 +1263,20 @@ final class ArrayOps[A](val xs: Array[A]) extends AnyVal {
12631263
* @param len the maximal number of elements to copy.
12641264
* @tparam B the type of the elements of the array.
12651265
*/
1266-
def copyToArray[B >: A](dest: Array[B], start: Int, len: Int = Int.MaxValue): dest.type = {
1267-
Array.copy(xs, 0, dest, start, min(xs.length, min(dest.length-start, len)))
1268-
dest
1266+
def copyToArray[B >: A](dest: Array[B], start: Int, len: Int = Int.MaxValue): Int = {
1267+
val copied = IterableOnce.elemsToCopyToArray(xs.length, dest.length, start, len)
1268+
if (copied > 0) {
1269+
Array.copy(xs, 0, dest, start, copied)
1270+
}
1271+
copied
12691272
}
12701273

12711274
/** Create a copy of this array with the specified element type. */
1272-
def toArray[B >: A: ClassTag]: Array[B] = copyToArray(new Array[B](xs.length), 0)
1275+
def toArray[B >: A: ClassTag]: Array[B] = {
1276+
val destination = new Array[B](xs.length)
1277+
copyToArray(destination, 0)
1278+
destination
1279+
}
12731280

12741281
/** Counts the number of elements in this array which satisfy a predicate */
12751282
def count(p: A => Boolean): Int = {

library/src/scala/collection/IterableOnce.scala

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,17 @@ final class IterableOnceExtensionMethods[A](private val it: IterableOnce[A]) ext
215215
object IterableOnce {
216216
@`inline` implicit def iterableOnceExtensionMethods[A](it: IterableOnce[A]): IterableOnceExtensionMethods[A] =
217217
new IterableOnceExtensionMethods[A](it)
218+
219+
/** Computes the number of elements to copy to an array from a source IterableOnce
220+
*
221+
* @param srcLen the length of the source collection
222+
* @param destLen the length of the destination array
223+
* @param start the index in the destination array at which to start copying elements to
224+
* @param len the requested number of elements to copy (we may only be able to copy less than this)
225+
* @return the number of elements that will be copied to the destination array
226+
*/
227+
@inline private[collection] def elemsToCopyToArray(srcLen: Int, destLen: Int, start: Int, len: Int): Int =
228+
math.max(math.min(math.min(len, srcLen), destLen - start), 0)
218229
}
219230

220231
/** This implementation trait can be mixed into an `IterableOnce` to get the basic methods that are shared between
@@ -702,7 +713,7 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] =>
702713
@deprecated("Use `dest ++= coll` instead", "2.13.0")
703714
@inline final def copyToBuffer[B >: A](dest: mutable.Buffer[B]): Unit = dest ++= this
704715

705-
/** Copy elements to an array.
716+
/** Copy elements to an array, returning the number of elements written.
706717
*
707718
* Fills the given array `xs` starting at index `start` with values of this $coll.
708719
*
@@ -711,14 +722,15 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] =>
711722
*
712723
* @param xs the array to fill.
713724
* @tparam B the type of the elements of the array.
725+
* @return the number of elements written to the array
714726
*
715-
* @usecase def copyToArray(xs: Array[A]): Unit
727+
* @usecase def copyToArray(xs: Array[A]): Int
716728
*
717729
* $willNotTerminateInf
718730
*/
719-
def copyToArray[B >: A](xs: Array[B]): xs.type = copyToArray(xs, 0)
731+
def copyToArray[B >: A](xs: Array[B]): Int = copyToArray(xs, 0)
720732

721-
/** Copy elements to an array.
733+
/** Copy elements to an array, returning the number of elements written.
722734
*
723735
* Fills the given array `xs` starting at index `start` with values of this $coll.
724736
*
@@ -728,24 +740,25 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] =>
728740
* @param xs the array to fill.
729741
* @param start the starting index of xs.
730742
* @tparam B the type of the elements of the array.
743+
* @return the number of elements written to the array
731744
*
732-
* @usecase def copyToArray(xs: Array[A], start: Int): Unit
745+
* @usecase def copyToArray(xs: Array[A], start: Int): Int
733746
*
734747
* $willNotTerminateInf
735748
*/
736749

737-
def copyToArray[B >: A](xs: Array[B], start: Int): xs.type = {
750+
def copyToArray[B >: A](xs: Array[B], start: Int): Int = {
738751
val xsLen = xs.length
739752
val it = iterator
740753
var i = start
741754
while (i < xsLen && it.hasNext) {
742755
xs(i) = it.next()
743756
i += 1
744757
}
745-
xs
758+
i - start
746759
}
747760

748-
/** Copy elements to an array.
761+
/** Copy elements to an array, returning the number of elements written.
749762
*
750763
* Fills the given array `xs` starting at index `start` with at most `len` elements of this $coll.
751764
*
@@ -756,22 +769,23 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] =>
756769
* @param start the starting index of xs.
757770
* @param len the maximal number of elements to copy.
758771
* @tparam B the type of the elements of the array.
772+
* @return the number of elements written to the array
759773
*
760774
* @note Reuse: $consumesIterator
761775
*
762-
* @usecase def copyToArray(xs: Array[A], start: Int, len: Int): Unit
776+
* @usecase def copyToArray(xs: Array[A], start: Int, len: Int): Int
763777
*
764778
* $willNotTerminateInf
765779
*/
766-
def copyToArray[B >: A](xs: Array[B], start: Int, len: Int): xs.type = {
780+
def copyToArray[B >: A](xs: Array[B], start: Int, len: Int): Int = {
767781
val it = iterator
768782
var i = start
769783
val end = start + math.min(len, xs.length - start)
770784
while (i < end && it.hasNext) {
771785
xs(i) = it.next()
772786
i += 1
773787
}
774-
xs
788+
i - start
775789
}
776790

777791
/** Sums up the elements of this collection.
@@ -1204,7 +1218,11 @@ trait IterableOnceOps[+A, +CC[_], +C] extends Any { this: IterableOnce[A] =>
12041218

12051219
/** Convert collection to array. */
12061220
def toArray[B >: A: ClassTag]: Array[B] =
1207-
if (knownSize >= 0) copyToArray(new Array[B](knownSize), 0)
1221+
if (knownSize >= 0) {
1222+
val destination = new Array[B](knownSize)
1223+
copyToArray(destination, 0)
1224+
destination
1225+
}
12081226
else mutable.ArrayBuffer.from(this).toArray[B]
12091227

12101228
// For internal use

library/src/scala/collection/StringOps.scala

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1141,16 +1141,14 @@ final class StringOps(private val s: String) extends AnyVal {
11411141
@`inline` def filterNot(pred: Char => Boolean): String = filter(c => !pred(c))
11421142

11431143
/** Copy chars of this string to an array.
1144-
* Fills the given array `xs` starting at index `start` with at most `len` chars.
1145-
* Copying will stop once either the entire string has been copied,
1146-
* or the end of the array is reached or `len` chars have been copied.
1144+
* Fills the given array `xs` starting at index 0.
1145+
* Copying will stop once either the entire string has been copied
1146+
* or the end of the array is reached
11471147
*
11481148
* @param xs the array to fill.
1149-
* @param start the starting index.
1150-
* @param len the maximal number of elements to copy.
11511149
*/
1152-
def copyToArray(xs: Array[Char], start: Int, len: Int): Unit =
1153-
s.getChars(0, min(min(s.length, len), xs.length-start), xs, start)
1150+
@`inline` def copyToArray(xs: Array[Char]): Int =
1151+
copyToArray(xs, 0, Int.MaxValue)
11541152

11551153
/** Copy chars of this string to an array.
11561154
* Fills the given array `xs` starting at index `start`.
@@ -1159,11 +1157,27 @@ final class StringOps(private val s: String) extends AnyVal {
11591157
*
11601158
* @param xs the array to fill.
11611159
* @param start the starting index.
1162-
* @param len the maximal number of elements to copy.
11631160
*/
1164-
@`inline` def copyToArray(xs: Array[Char], start: Int): Unit =
1161+
@`inline` def copyToArray(xs: Array[Char], start: Int): Int =
11651162
copyToArray(xs, start, Int.MaxValue)
11661163

1164+
/** Copy chars of this string to an array.
1165+
* Fills the given array `xs` starting at index `start` with at most `len` chars.
1166+
* Copying will stop once either the entire string has been copied,
1167+
* or the end of the array is reached or `len` chars have been copied.
1168+
*
1169+
* @param xs the array to fill.
1170+
* @param start the starting index.
1171+
* @param len the maximal number of elements to copy.
1172+
*/
1173+
def copyToArray(xs: Array[Char], start: Int, len: Int): Int = {
1174+
val copied = IterableOnce.elemsToCopyToArray(s.length, xs.length, start, len)
1175+
if (copied > 0) {
1176+
s.getChars(0, copied, xs, start)
1177+
}
1178+
copied
1179+
}
1180+
11671181
/** Finds index of the first char satisfying some predicate after or at some start index.
11681182
*
11691183
* @param p the predicate used to test elements.

library/src/scala/collection/immutable/ArraySeq.scala

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import java.io.{ObjectInputStream, ObjectOutputStream}
55

66
import scala.collection.mutable.{ArrayBuffer, ArrayBuilder, Builder, ArraySeq => MutableArraySeq}
77
import scala.collection.{ArrayOps, ClassTagSeqFactory, SeqFactory, StrictOptimizedClassTagSeqFactory, View}
8+
import scala.collection.IterableOnce
89
import scala.annotation.unchecked.uncheckedVariance
910
import scala.util.hashing.MurmurHash3
1011
import scala.reflect.ClassTag
@@ -133,12 +134,14 @@ sealed abstract class ArraySeq[+A]
133134

134135
override protected[this] def className = "ArraySeq"
135136

136-
override def copyToArray[B >: A](xs: Array[B], start: Int = 0): xs.type = copyToArray[B](xs, start, length)
137+
override def copyToArray[B >: A](xs: Array[B], start: Int = 0): Int = copyToArray[B](xs, start, length)
137138

138-
override def copyToArray[B >: A](xs: Array[B], start: Int, len: Int): xs.type = {
139-
val l = scala.math.min(scala.math.min(len, length), xs.length-start)
140-
if(l > 0) Array.copy(unsafeArray, 0, xs, start, l)
141-
xs
139+
override def copyToArray[B >: A](xs: Array[B], start: Int, len: Int): Int = {
140+
val copied = IterableOnce.elemsToCopyToArray(length, xs.length, start, len)
141+
if(copied > 0) {
142+
Array.copy(unsafeArray, 0, xs, start, copied)
143+
}
144+
copied
142145
}
143146

144147
override protected[this] def writeReplace(): AnyRef = this

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

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@ package scala
22
package collection
33
package mutable
44

5-
import java.lang.{IndexOutOfBoundsException, IllegalArgumentException}
6-
7-
85
/** An implementation of the `Buffer` class using an array to
96
* represent the assembled sequence internally. Append, update and random
107
* access take constant time (amortized time). Prepends and removes are
@@ -159,12 +156,14 @@ class ArrayBuffer[A] private (initialElements: Array[AnyRef], initialSize: Int)
159156

160157
override protected[this] def stringPrefix = "ArrayBuffer"
161158

162-
override def copyToArray[B >: A](xs: Array[B], start: Int): xs.type = copyToArray[B](xs, start, length)
159+
override def copyToArray[B >: A](xs: Array[B], start: Int): Int = copyToArray[B](xs, start, length)
163160

164-
override def copyToArray[B >: A](xs: Array[B], start: Int, len: Int): xs.type = {
165-
val l = scala.math.min(scala.math.min(len, length), xs.length-start)
166-
if(l > 0) Array.copy(array, 0, xs, start, l)
167-
xs
161+
override def copyToArray[B >: A](xs: Array[B], start: Int, len: Int): Int = {
162+
val copied = IterableOnce.elemsToCopyToArray(length, xs.length, start, len)
163+
if(copied > 0) {
164+
Array.copy(array, 0, xs, start, copied)
165+
}
166+
copied
168167
}
169168

170169
/** Sorts this $coll in place according to an Ordering.

library/src/scala/collection/mutable/ArrayDeque.scala

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -434,8 +434,13 @@ class ArrayDeque[A] protected (
434434

435435
override def grouped(n: Int): Iterator[IterableCC[A]] = sliding(n, n)
436436

437-
override def copyToArray[B >: A](dest: Array[B], destStart: Int, len: Int): dest.type =
438-
copySliceToArray(srcStart = 0, dest = dest, destStart = destStart, maxItems = len)
437+
override def copyToArray[B >: A](dest: Array[B], destStart: Int, len: Int): Int = {
438+
val copied = IterableOnce.elemsToCopyToArray(length, dest.length, destStart, len)
439+
if (copied > 0) {
440+
copySliceToArray(srcStart = 0, dest = dest, destStart = destStart, maxItems = len)
441+
}
442+
copied
443+
}
439444

440445
override def toArray[B >: A: ClassTag]: Array[B] =
441446
copySliceToArray(srcStart = 0, dest = new Array[B](length), destStart = 0, maxItems = length)

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,14 @@ sealed abstract class ArraySeq[T]
5959
/** Clones this object, including the underlying Array. */
6060
override def clone(): ArraySeq[T] = ArraySeq.make(array.clone()).asInstanceOf[ArraySeq[T]]
6161

62-
override def copyToArray[B >: T](xs: Array[B], start: Int): xs.type = copyToArray[B](xs, start, length)
62+
override def copyToArray[B >: T](xs: Array[B], start: Int): Int = copyToArray[B](xs, start, length)
6363

64-
override def copyToArray[B >: T](xs: Array[B], start: Int, len: Int): xs.type = {
65-
val l = scala.math.min(scala.math.min(len, length), xs.length-start)
66-
if(l > 0) Array.copy(array, 0, xs, start, l)
67-
xs
64+
override def copyToArray[B >: T](xs: Array[B], start: Int, len: Int): Int = {
65+
val copied = IterableOnce.elemsToCopyToArray(length, xs.length, start, len)
66+
if(copied > 0) {
67+
Array.copy(array, 0, xs, start, copied)
68+
}
69+
copied
6870
}
6971

7072
override protected[this] def writeReplace(): AnyRef = this

0 commit comments

Comments
 (0)