diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index 8738a95f5e3..5d6004195f1 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -526,6 +526,11 @@ task array2(type: RunKonanTest) { source = "runtime/collections/array2.kt" } +task sort0(type: RunKonanTest) { + goldValue = "[a, b, x]\n[-1, 0, 42, 239, 100500]\n" + source = "runtime/collections/sort0.kt" +} + task if_else(type: RunKonanTest) { source = "codegen/branching/if_else.kt" } diff --git a/backend.native/tests/runtime/collections/sort0.kt b/backend.native/tests/runtime/collections/sort0.kt new file mode 100644 index 00000000000..18d0faa9d27 --- /dev/null +++ b/backend.native/tests/runtime/collections/sort0.kt @@ -0,0 +1,4 @@ +fun main(a: Array) { + println(arrayOf("x", "a", "b").sorted().toString()) + println(intArrayOf(239, 42, -1, 100500, 0).sorted().toString()); +} diff --git a/runtime/src/main/cpp/Arrays.cpp b/runtime/src/main/cpp/Arrays.cpp index 47a2c4fed7b..4a78807412b 100644 --- a/runtime/src/main/cpp/Arrays.cpp +++ b/runtime/src/main/cpp/Arrays.cpp @@ -45,8 +45,8 @@ void Kotlin_Array_set(KRef thiz, KInt index, KConstRef value) { OBJ_GETTER(Kotlin_Array_clone, KConstRef thiz) { const ArrayHeader* array = thiz->array(); - ArrayHeader* result = ArrayContainer( - array->type_info(), array->count_).GetPlace(); + ArrayHeader* result = AllocArrayInstance( + array->type_info(), array->count_, OBJ_RESULT)->array(); for (int index = 0; index < array->count_; index++) { SetGlobalRef( ArrayAddressOfElementAt(result, index), *ArrayAddressOfElementAt(array, index)); @@ -109,8 +109,8 @@ void Kotlin_ByteArray_set(KRef thiz, KInt index, KByte value) { OBJ_GETTER(Kotlin_ByteArray_clone, KConstRef thiz) { const ArrayHeader* array = thiz->array(); - ArrayHeader* result = ArrayContainer( - theByteArrayTypeInfo, array->count_).GetPlace(); + ArrayHeader* result = AllocArrayInstance( + array->type_info(), array->count_, OBJ_RESULT)->array(); memcpy( ByteArrayAddressOfElementAt(result, 0), ByteArrayAddressOfElementAt(array, 0), @@ -141,8 +141,8 @@ void Kotlin_CharArray_set(KRef thiz, KInt index, KChar value) { OBJ_GETTER(Kotlin_CharArray_clone, KConstRef thiz) { const ArrayHeader* array = thiz->array(); - ArrayHeader* result = ArrayContainer( - theCharArrayTypeInfo, array->count_).GetPlace(); + ArrayHeader* result = AllocArrayInstance( + array->type_info(), array->count_, OBJ_RESULT)->array(); memcpy( PrimitiveArrayAddressOfElementAt(result, 0), PrimitiveArrayAddressOfElementAt(array, 0), @@ -185,8 +185,8 @@ void Kotlin_ShortArray_set(KRef thiz, KInt index, KShort value) { OBJ_GETTER(Kotlin_ShortArray_clone, KConstRef thiz) { const ArrayHeader* array = thiz->array(); - ArrayHeader* result = ArrayContainer( - theShortArrayTypeInfo, array->count_).GetPlace(); + ArrayHeader* result = AllocArrayInstance( + array->type_info(), array->count_, OBJ_RESULT)->array(); memcpy( PrimitiveArrayAddressOfElementAt(result, 0), PrimitiveArrayAddressOfElementAt(array, 0), @@ -217,8 +217,8 @@ void Kotlin_IntArray_set(KRef thiz, KInt index, KInt value) { OBJ_GETTER(Kotlin_IntArray_clone, KConstRef thiz) { const ArrayHeader* array = thiz->array(); - ArrayHeader* result = ArrayContainer( - theIntArrayTypeInfo, array->count_).GetPlace(); + ArrayHeader* result = AllocArrayInstance( + array->type_info(), array->count_, OBJ_RESULT)->array(); memcpy( PrimitiveArrayAddressOfElementAt(result, 0), PrimitiveArrayAddressOfElementAt(array, 0), @@ -300,8 +300,8 @@ void Kotlin_LongArray_set(KRef thiz, KInt index, KLong value) { OBJ_GETTER(Kotlin_LongArray_clone, KConstRef thiz) { const ArrayHeader* array = thiz->array(); - ArrayHeader* result = ArrayContainer( - theLongArrayTypeInfo, array->count_).GetPlace(); + ArrayHeader* result = AllocArrayInstance( + array->type_info(), array->count_, OBJ_RESULT)->array(); memcpy( PrimitiveArrayAddressOfElementAt(result, 0), PrimitiveArrayAddressOfElementAt(array, 0), @@ -332,8 +332,8 @@ void Kotlin_FloatArray_set(KRef thiz, KInt index, KFloat value) { OBJ_GETTER(Kotlin_FloatArray_clone, KConstRef thiz) { const ArrayHeader* array = thiz->array(); - ArrayHeader* result = ArrayContainer( - theFloatArrayTypeInfo, array->count_).GetPlace(); + ArrayHeader* result = AllocArrayInstance( + array->type_info(), array->count_, OBJ_RESULT)->array(); memcpy( PrimitiveArrayAddressOfElementAt(result, 0), PrimitiveArrayAddressOfElementAt(array, 0), @@ -364,8 +364,8 @@ void Kotlin_DoubleArray_set(KRef thiz, KInt index, KDouble value) { OBJ_GETTER(Kotlin_DoubleArray_clone, KConstRef thiz) { const ArrayHeader* array = thiz->array(); - ArrayHeader* result = ArrayContainer( - theDoubleArrayTypeInfo, array->count_).GetPlace(); + ArrayHeader* result = AllocArrayInstance( + array->type_info(), array->count_, OBJ_RESULT)->array(); memcpy( PrimitiveArrayAddressOfElementAt(result, 0), PrimitiveArrayAddressOfElementAt(array, 0), @@ -396,8 +396,8 @@ void Kotlin_BooleanArray_set(KRef thiz, KInt index, KBoolean value) { OBJ_GETTER(Kotlin_BooleanArray_clone, KConstRef thiz) { const ArrayHeader* array = thiz->array(); - ArrayHeader* result = ArrayContainer( - theBooleanArrayTypeInfo, array->count_).GetPlace(); + ArrayHeader* result = AllocArrayInstance( + array->type_info(), array->count_, OBJ_RESULT)->array(); memcpy( PrimitiveArrayAddressOfElementAt(result, 0), PrimitiveArrayAddressOfElementAt(array, 0), diff --git a/runtime/src/main/kotlin/kotlin/Arrays.kt b/runtime/src/main/kotlin/kotlin/Arrays.kt index 6af4743b39f..946d73128d7 100644 --- a/runtime/src/main/kotlin/kotlin/Arrays.kt +++ b/runtime/src/main/kotlin/kotlin/Arrays.kt @@ -2,7 +2,8 @@ package kotlin import kotlin.collections.* import kotlin.internal.PureReifiable - +import kotlin.util.sortArrayComparable +import kotlin.util.sortArrayWith // TODO: make all iterator() methods inline. /** @@ -782,6 +783,25 @@ public inline fun CharArray.isNotEmpty(): Boolean { public val Array.lastIndex: Int get() = size - 1 +/** + * Returns last index of [element], or -1 if the array does not contain element. + */ +public fun <@kotlin.internal.OnlyInputTypes T> Array.lastIndexOf(element: T): Int { + if (element == null) { + for (index in indices.reversed()) { + if (this[index] == null) { + return index + } + } + } else { + for (index in indices.reversed()) { + if (element == this[index]) { + return index + } + } + } + return -1 +} /** * Returns the last valid index for the array. @@ -921,6 +941,24 @@ public fun > CharArray.toCollection(destination: return destination } +/** + * Returns a [List] containing all elements. + */ +public fun Array.toList(): List { + return when (size) { + 0 -> emptyList() + 1 -> listOf(this[0]) + else -> this.toMutableList() + } +} + +/** + * Returns a [MutableList] filled with all elements of this array. + */ +public fun Array.toMutableList(): MutableList { + return ArrayList(this.asCollection()) +} + /** * Returns a [HashSet] of all elements. */ @@ -984,6 +1022,135 @@ public fun CharArray.toHashSet(): HashSet { return toCollection(HashSet(mapCapacity(size))) } +/** + * Returns new array which is a copy of the original array. + */ +@kotlin.internal.InlineOnly +public inline fun Array.copyOf(): Array { + return this.copyOfUninitializedElements(size) +} + +/** + * Sorts the array in-place according to the order specified by the given [comparator]. + */ +public fun Array.sortWith(comparator: Comparator): Unit { + if (size > 1) { + sortArrayWith(this, 0, size - 1, comparator) + } +} + +/** + * Sorts elements in the array in-place according to natural sort order of the value returned by specified [selector] function. + */ +public inline fun > Array.sortBy(crossinline selector: (T) -> R?): Unit { + if (size > 1) sortWith(compareBy(selector)) +} + +/** + * Sorts elements in the array in-place descending according to natural sort order of the value returned by specified [selector] function. + */ +public inline fun > Array.sortByDescending(crossinline selector: (T) -> R?): Unit { + if (size > 1) sortWith(compareByDescending(selector)) +} + +/** + * Sorts elements in the array in-place descending according to their natural sort order. + */ +public fun > Array.sortDescending(): Unit { + sortWith(reverseOrder()) +} + +/** + * Returns a list of all elements sorted according to their natural sort order. + */ +public fun > Array.sorted(): List { + return sortedArray().asList() +} + +/** + * Returns a list of all elements sorted according to their natural sort order. + */ +public fun IntArray.sorted(): List { + return toTypedArray().apply { sort() }.asList() +} + +/** + * Returns an array with all elements of this array sorted according to their natural sort order. + */ +public fun > Array.sortedArray(): Array { + if (isEmpty()) return this + return this.copyOf().apply { sort() } +} + +/** + * Returns an array with all elements of this array sorted descending according to their natural sort order. + */ +public fun > Array.sortedArrayDescending(): Array { + if (isEmpty()) return this + return this.copyOf().apply { sortWith(reverseOrder()) } +} + +/** + * Returns a list of all elements sorted according to natural sort order of the value returned by specified [selector] function. + */ +public inline fun > Array.sortedBy(crossinline selector: (T) -> R?): List { + return sortedWith(compareBy(selector)) +} + +/** + * Returns a list of all elements sorted descending according to natural sort order of the value returned by specified [selector] function. + */ +public inline fun > Array.sortedByDescending(crossinline selector: (T) -> R?): List { + return sortedWith(compareByDescending(selector)) +} + +/** + * Returns a list of all elements sorted descending according to their natural sort order. + */ +public fun > Array.sortedDescending(): List { + return sortedWith(reverseOrder()) +} + +/** + * Sorts the array in-place according to the natural order of its elements. + * + * @throws ClassCastException if any element of the array is not [Comparable]. + */ +public fun Array.sort(): Unit { + if (size > 1) sortArrayComparable(this) +} + + +public fun Array.sortWith(comparator: Comparator, fromIndex: Int = 0, toIndex: Int = size): Unit { + sortArrayWith(this, fromIndex, toIndex, comparator) +} + +/** + * Returns a *typed* object array containing all of the elements of this primitive array. + */ +public fun IntArray.toTypedArray(): Array { + val result = arrayOfNulls(size) + for (index in indices) + result[index] = this[index] + @Suppress("UNCHECKED_CAST") + return result as Array +} + +/** + * Returns a list of all elements sorted according to the specified [comparator]. + */ +public fun Array.sortedWith(comparator: Comparator): List { + return sortedArrayWith(comparator).asList() +} + +/** + * Returns an array with all elements of this array sorted according the specified [comparator]. + */ +public fun Array.sortedArrayWith(comparator: Comparator): Array { + if (isEmpty()) return this + return this.copyOf().apply { sortWith(comparator) } +} + // From Library.kt. /** * Returns an array of objects of the given type with the given [size], initialized with null values. @@ -992,6 +1159,19 @@ public inline fun arrayOfNulls(size: Int): Array @Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE") arrayOfUninitializedElements(size) +/** + * Returns a *typed* array containing all of the elements of this collection. + * + * Allocates an array of runtime type `T` having its size equal to the size of this collection + * and populates the array with the elements of this collection. + */ +public inline fun Collection.toTypedArray(): Array { + val result = arrayOfNulls(size) + var index = 0 + for (element in this) result[index++] = element + return result as Array +} + /** * Returns an array containing the specified elements. */ @@ -1000,7 +1180,6 @@ public inline fun arrayOf(vararg elements: T): Array< /** * Returns an array containing the specified [Double] numbers. */ - public inline fun doubleArrayOf(vararg elements: Double) = elements /** diff --git a/runtime/src/main/kotlin/kotlin/collections/AbstractCollection.kt b/runtime/src/main/kotlin/kotlin/collections/AbstractCollection.kt index 9bee9013fb2..06f08215415 100644 --- a/runtime/src/main/kotlin/kotlin/collections/AbstractCollection.kt +++ b/runtime/src/main/kotlin/kotlin/collections/AbstractCollection.kt @@ -12,8 +12,7 @@ public abstract class AbstractCollection protected constructor() : Collec override fun isEmpty(): Boolean = size == 0 - @Fixme - override fun toString(): String = TODO() // joinToString(", ", "[", "]") { - // if (it === this) "(this Collection)" else it.toString() - // } + override fun toString(): String = joinToString(", ", "[", "]") { + if (it === this) "(this Collection)" else it.toString() + } } diff --git a/runtime/src/main/kotlin/kotlin/collections/Collections.kt b/runtime/src/main/kotlin/kotlin/collections/Collections.kt index 99740b8cd25..8a2c2200e05 100644 --- a/runtime/src/main/kotlin/kotlin/collections/Collections.kt +++ b/runtime/src/main/kotlin/kotlin/collections/Collections.kt @@ -149,17 +149,6 @@ public interface MutableIterable : Iterable { override fun iterator(): MutableIterator } - -@Fixme -fun Array.asList(): List { - // TODO: consider making lighter list over an array. - val result = ArrayList(this.size) - for (e in this) { - result.add(e) - } - return result -} -/* TODO: use this one! public fun Array.asList(): List { return object : AbstractList() { override val size: Int get() = this@asList.size @@ -169,7 +158,7 @@ public fun Array.asList(): List { override fun indexOf(element: T): Int = this@asList.indexOf(element) override fun lastIndexOf(element: T): Int = this@asList.lastIndexOf(element) } -} */ +} fun Array.toSet(): Set { val result = HashSet(this.size) @@ -1009,15 +998,13 @@ public fun > MutableList.sortDescending(): Unit { /** * Returns a list of all elements sorted according to their natural sort order. */ -@FixmeSorting public fun > Iterable.sorted(): List { - TODO() - //if (this is Collection) { - // if (size <= 1) return this.toList() - // @Suppress("UNCHECKED_CAST") - // return (toTypedArray>() as Array).apply { sort() }.asList() - //} - //return toMutableList().apply { sort() } + if (this is Collection) { + if (size <= 1) return this.toList() + @Suppress("UNCHECKED_CAST") + return (toTypedArray>() as Array).apply { sort() }.asList() + } + return toMutableList().apply { sort() } } /** @@ -1044,15 +1031,13 @@ public fun > Iterable.sortedDescending(): List { /** * Returns a list of all elements sorted according to the specified [comparator]. */ -@FixmeSorting public fun Iterable.sortedWith(comparator: Comparator): List { - TODO() - //if (this is Collection) { - // if (size <= 1) return this.toList() - // @Suppress("UNCHECKED_CAST") - // return (toTypedArray() as Array).apply { sortWith(comparator) }.asList() - //} - //return toMutableList().apply { sortWith(comparator) } + if (this is Collection) { + if (size <= 1) return this.toList() + @Suppress("UNCHECKED_CAST") + return (toTypedArray() as Array).apply { sortWith(comparator) }.asList() + } + return toMutableList().apply { sortWith(comparator) } } /** @@ -1568,6 +1553,7 @@ public inline fun Iterable.any(predicate: (T) -> Boolean): Boolean { } /** + * Returns the number of elements in this collection. * Returns the number of elements in this collection. */ public fun Iterable.count(): Int { diff --git a/runtime/src/main/kotlin/kotlin/text/StringBuilder.kt b/runtime/src/main/kotlin/kotlin/text/StringBuilder.kt index cd31b4d4d82..9382aa69bfa 100644 --- a/runtime/src/main/kotlin/kotlin/text/StringBuilder.kt +++ b/runtime/src/main/kotlin/kotlin/text/StringBuilder.kt @@ -64,9 +64,21 @@ class StringBuilder private constructor ( return this } - override fun append(csq: CharSequence?): Appendable = TODO() + override fun append(csq: CharSequence?): Appendable { + // TODO: how to treat nulls properly? + if (csq == null) return this + return append(csq, 0, csq.length) + } - override fun append(csq: CharSequence?, start: Int, end: Int): Appendable = TODO() + override fun append(csq: CharSequence?, start: Int, end: Int): Appendable { + // TODO: how to treat nulls properly? + if (csq == null) return this + ensureExtraCapacity(end - start) + var index = start + while (index < end) + array[length++] = csq[index++] + return this + } fun append(it: CharArray) { ensureExtraCapacity(it.size) diff --git a/runtime/src/main/kotlin/kotlin/util/Sort.kt b/runtime/src/main/kotlin/kotlin/util/Sort.kt new file mode 100644 index 00000000000..009fd5342c9 --- /dev/null +++ b/runtime/src/main/kotlin/kotlin/util/Sort.kt @@ -0,0 +1,73 @@ +package kotlin.util + +import kotlin.comparisons.* + +// Yes, rather naive qsort for now. +private fun partition(array: Array, left: Int, right: Int): Int { + var i = left + var j = right + val pivot = array[(left + right) / 2] as Comparable + while (i <= j) { + while (pivot.compareTo(array[i]) > 0) { + i++ + } + while (pivot.compareTo(array[j]) < 0) { + j-- + } + if (i <= j) { + val tmp = array[i] + array[i] = array[j] + array[j] = tmp + i++ + j-- + } + } + return i +} + +private fun quickSort(array: Array, left: Int, right: Int) { + val index = partition(array, left, right) + if (left < index - 1) + quickSort(array, left, index - 1) + if (index < right) + quickSort(array, index, right) +} + +private fun partition( + array: Array, left: Int, right: Int, comparator: Comparator): Int { + var i = left + var j = right + val pivot = array[(left + right) / 2] + while (i <= j) { + while (comparator.compare(array[i], pivot) < 0) + i++ + while (comparator.compare(array[j], pivot) > 0) + j-- + if (i <= j) { + val tmp = array[i] + array[i] = array[j] + array[j] = tmp + i++ + j-- + } + } + return i +} + +private fun quickSort( + array: Array, left: Int, right: Int, comparator: Comparator) { + val index = partition(array, left, right, comparator) + if (left < index - 1) + quickSort(array, left, index - 1, comparator) + if (index < right) + quickSort(array, index, right, comparator) +} + +internal fun sortArrayWith( + array: Array, fromIndex: Int, toIndex: Int, comparator: Comparator) { + quickSort(array as Array, fromIndex, toIndex, comparator) +} + +internal fun sortArrayComparable(array: Array) { + quickSort(array as Array, 0, array.size - 1) +} \ No newline at end of file