Null-terminate Collection.toArray destination only in JVM
In other platforms the elements following the collection elements should not be changed. As a part of efforts to stabilize Native stdlib.
This commit is contained in:
committed by
Space Team
parent
60455daa9d
commit
6fdfd4e8dd
+1
-1
@@ -24,7 +24,7 @@ fun abstractCollectionToArray() {
|
||||
assertTrue("toArray1" in coll.invocations || "toArray2" in coll.invocations)
|
||||
|
||||
val arr2: Array<String> = coll.toArray(Array(coll.size + 1) { "" })
|
||||
assertEquals(data + listOf(null), arr2.asList())
|
||||
assertEquals(data + listOf(""), arr2.asList())
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
|
||||
@@ -30,8 +30,5 @@ internal actual fun <T> copyToArrayImpl(collection: Collection<*>, array: Array<
|
||||
while (iterator.hasNext()) {
|
||||
array[index++] = iterator.next() as T
|
||||
}
|
||||
if (index < array.size) {
|
||||
return array.copyOf(index) as Array<T>
|
||||
}
|
||||
return array
|
||||
return terminateCollectionToArray(collection.size, array)
|
||||
}
|
||||
@@ -54,12 +54,11 @@ internal actual fun <T> copyToArrayImpl(collection: Collection<*>, array: Array<
|
||||
while (iterator.hasNext()) {
|
||||
array[index++] = iterator.next().unsafeCast<T>()
|
||||
}
|
||||
if (index < array.size) {
|
||||
array[index] = null.unsafeCast<T>()
|
||||
}
|
||||
return array
|
||||
return terminateCollectionToArray(collection.size, array)
|
||||
}
|
||||
|
||||
internal actual fun <T> terminateCollectionToArray(collectionSize: Int, array: Array<T>): Array<T> = array
|
||||
|
||||
/**
|
||||
* Returns a new read-only list containing only the specified object [element].
|
||||
*
|
||||
|
||||
@@ -171,11 +171,7 @@ public actual open class ArrayList<E> internal constructor(private var array: Ar
|
||||
|
||||
(this.array as Array<T>).copyInto(array)
|
||||
|
||||
if (array.size > size) {
|
||||
array[size] = null as T // null-terminate
|
||||
}
|
||||
|
||||
return array
|
||||
return terminateCollectionToArray(size, array)
|
||||
}
|
||||
|
||||
override fun toArray(): Array<Any?> {
|
||||
|
||||
@@ -85,6 +85,14 @@ internal actual inline fun copyToArrayImpl(collection: Collection<*>): Array<Any
|
||||
internal actual inline fun <T> copyToArrayImpl(collection: Collection<*>, array: Array<T>): Array<T> =
|
||||
kotlin.jvm.internal.collectionToArray(collection, array as Array<Any?>) as Array<T>
|
||||
|
||||
internal actual fun <T> terminateCollectionToArray(collectionSize: Int, array: Array<T>): Array<T> {
|
||||
if (collectionSize < array.size) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
array[collectionSize] = null as T // null-terminate
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// copies typed varargs array to array of objects
|
||||
internal actual fun <T> Array<out T>.copyToArrayOfAny(isVarargs: Boolean): Array<out Any?> =
|
||||
if (isVarargs && this.javaClass == Array<Any?>::class.java)
|
||||
|
||||
@@ -176,12 +176,7 @@ internal class ListBuilder<E> private constructor(
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
(array as Array<T>).copyInto(destination, 0, startIndex = offset, endIndex = offset + length)
|
||||
|
||||
if (destination.size > length) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
destination[length] = null as T // null-terminate
|
||||
}
|
||||
|
||||
return destination
|
||||
return terminateCollectionToArray(length, destination)
|
||||
}
|
||||
|
||||
override fun toArray(): Array<Any?> {
|
||||
|
||||
@@ -221,11 +221,7 @@ actual class ArrayList<E> private constructor(
|
||||
|
||||
(backingArray as Array<T>).copyInto(array, 0, startIndex = offset, endIndex = offset + length)
|
||||
|
||||
if (array.size > length) {
|
||||
array[length] = null as T // null-terminate
|
||||
}
|
||||
|
||||
return array
|
||||
return terminateCollectionToArray(length, array)
|
||||
}
|
||||
|
||||
override fun toArray(): Array<Any?> {
|
||||
|
||||
@@ -93,6 +93,8 @@ internal actual fun copyToArrayImpl(collection: Collection<*>): Array<Any?> {
|
||||
return array
|
||||
}
|
||||
|
||||
internal actual fun <T> terminateCollectionToArray(collectionSize: Int, array: Array<T>): Array<T> = array
|
||||
|
||||
/**
|
||||
* Returns a new array which is a copy of the original array with new elements filled with null values.
|
||||
*/
|
||||
|
||||
@@ -541,12 +541,9 @@ public class ArrayDeque<E> : AbstractMutableList<E> {
|
||||
elementData.copyInto(dest, destinationOffset = 0, startIndex = head, endIndex = elementData.size)
|
||||
elementData.copyInto(dest, destinationOffset = elementData.size - head, startIndex = 0, endIndex = tail)
|
||||
}
|
||||
if (dest.size > size) {
|
||||
dest[size] = null // null-terminate
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return dest as Array<T>
|
||||
return terminateCollectionToArray(size, dest) as Array<T>
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_OVERRIDE")
|
||||
|
||||
@@ -479,3 +479,9 @@ internal fun throwIndexOverflow() { throw ArithmeticException("Index overflow ha
|
||||
@SinceKotlin("1.3")
|
||||
internal fun throwCountOverflow() { throw ArithmeticException("Count overflow has happened.") }
|
||||
|
||||
/**
|
||||
* In JVM if the size of [array] is bigger than [collectionSize], sets `array[collectionSize] = null`.
|
||||
* In other platforms does nothing.
|
||||
* Returns the given [array].
|
||||
*/
|
||||
internal expect fun <T> terminateCollectionToArray(collectionSize: Int, array: Array<T>): Array<T>
|
||||
|
||||
@@ -146,9 +146,8 @@ private class RingBuffer<T>(private val buffer: Array<Any?>, filledSize: Int) :
|
||||
widx++
|
||||
idx++
|
||||
}
|
||||
if (result.size > this.size) result[this.size] = null
|
||||
|
||||
return result as Array<T>
|
||||
return terminateCollectionToArray(size, result) as Array<T>
|
||||
}
|
||||
|
||||
override fun toArray(): Array<Any?> {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
package test.collections
|
||||
|
||||
import test.*
|
||||
import test.collections.behaviors.iteratorBehavior
|
||||
import test.collections.behaviors.listIteratorBehavior
|
||||
import test.collections.behaviors.listIteratorProperties
|
||||
@@ -622,12 +623,20 @@ class ArrayDequeTest {
|
||||
|
||||
val dest = Array(expected.size + 2) { it + 100 }
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val nullTerminatedExpected = (expected as Array<Any?>) + null + (expected.size + 101)
|
||||
val expectedDest = buildList {
|
||||
addAll(expected)
|
||||
if (TestPlatform.current == TestPlatform.Jvm) {
|
||||
add(null)
|
||||
} else {
|
||||
add(expected.size + 100)
|
||||
}
|
||||
add(expected.size + 101)
|
||||
}.toTypedArray()
|
||||
|
||||
val actual = deque.testToArray(dest)
|
||||
assertTrue(
|
||||
nullTerminatedExpected contentEquals actual,
|
||||
message = "Expected: ${nullTerminatedExpected.contentToString()}, Actual: ${actual.contentToString()}"
|
||||
expectedDest contentEquals actual,
|
||||
message = "Expected: ${expectedDest.contentToString()}, Actual: ${actual.contentToString()}"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1311,7 +1311,12 @@ class CollectionTest {
|
||||
assertTrue("toArray1" in coll.invocations || "toArray2" in coll.invocations)
|
||||
|
||||
val arr2: Array<String> = coll.toArray(Array(coll.size + 1) { "" })
|
||||
assertEquals(data + listOf(null), arr2.asList())
|
||||
testOnlyOn(TestPlatform.Jvm) {
|
||||
assertEquals(data + listOf(null), arr2.asList())
|
||||
}
|
||||
testExceptOn(TestPlatform.Jvm) {
|
||||
assertEquals(data + listOf(""), arr2.asList())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -34,8 +34,5 @@ internal actual fun <T> copyToArrayImpl(collection: Collection<*>, array: Array<
|
||||
while (iterator.hasNext()) {
|
||||
array[index++] = iterator.next() as T
|
||||
}
|
||||
if (index < array.size) {
|
||||
(array as Array<T?>).fill(null, index)
|
||||
}
|
||||
return array
|
||||
return terminateCollectionToArray(collection.size, array)
|
||||
}
|
||||
Reference in New Issue
Block a user