kotlin.jvm.internal.CollectionToArray.kt Maven / Gradle / Ivy
/*
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
@file:JvmName("CollectionToArray")
package kotlin.jvm.internal
import java.lang.reflect.Array as JavaArray
import java.lang.NullPointerException as JavaNPE
import java.util.Arrays
private val EMPTY = emptyArray() // shared empty array
private const val MAX_SIZE = Int.MAX_VALUE - 2 // empirically maximal array size that can be allocated without exceeding VM limits
@JvmName("toArray")
fun collectionToArray(collection: Collection<*>): Array =
toArrayImpl(
collection,
empty = { EMPTY },
alloc = { size -> arrayOfNulls(size) },
trim = { result, size -> Arrays.copyOf(result, size) }
)
// Note: Array here can have any reference array JVM type at run time
@JvmName("toArray")
fun collectionToArray(collection: Collection<*>, a: Array?): Array {
// Collection.toArray contract requires that NullPointerException is thrown when array is null
if (a == null) throw JavaNPE()
return toArrayImpl(
collection,
empty = {
if (a.size > 0) a[0] = null
a
},
alloc = { size ->
@Suppress("UNCHECKED_CAST")
if (size <= a.size) a else JavaArray.newInstance(a.javaClass.componentType, size) as Array
},
trim = { result, size ->
if (result === a) {
a[size] = null
a
} else
Arrays.copyOf(result, size)
}
)
}
private inline fun toArrayImpl(
collection: Collection<*>,
empty: () -> Array,
alloc: (Int) -> Array,
trim: (Array, Int) -> Array
): Array {
val size = collection.size
if (size == 0) return empty() // quick path on zero size
val iter = collection.iterator() // allocate iterator for non-empty collection
if (!iter.hasNext()) return empty() // size was > 0, but no actual elements
var result = alloc(size) // use size as a guess to allocate result array
var i = 0
// invariant: iter.hasNext is true && i < result.size
while (true) {
result[i++] = iter.next()
if (i >= result.size) {
if (!iter.hasNext()) return result // perfect match of array size
// array size was too small -- grow array (invariant: i == result.size > 0 here)
// now grow at a factor of 1.5 (we expect this to be extremely rare, but still use fast code)
// note that corner case here is when i == 1 (should get newSize == 2)
var newSize = (i * 3 + 1) ushr 1
if (newSize <= i) { // detect overflow in the above line
if (i >= MAX_SIZE) throw OutOfMemoryError() // exceeded max array that VM can allocate
newSize = MAX_SIZE // try max array size that VM can allocate
}
result = Arrays.copyOf(result, newSize)
} else {
if (!iter.hasNext()) return trim(result, i) // ended too early (allocated array too big)
}
}
}