Provide stable sorting when JS engine sorting doesn't look stable
#KT-12473
This commit is contained in:
@@ -1128,8 +1128,7 @@ public actual fun IntArray.sort(): Unit {
|
||||
* Sorts the array in-place.
|
||||
*/
|
||||
public actual fun LongArray.sort(): Unit {
|
||||
if (size > 1)
|
||||
sort { a: Long, b: Long -> a.compareTo(b) }
|
||||
if (size > 1) sort { a: Long, b: Long -> a.compareTo(b) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1171,16 +1170,14 @@ public actual fun CharArray.sort(): Unit {
|
||||
* Sorts the array in-place according to the natural order of its elements.
|
||||
*/
|
||||
public actual fun <T : Comparable<T>> Array<out T>.sort(): Unit {
|
||||
if (size > 1)
|
||||
sort { a: T, b: T -> a.compareTo(b) }
|
||||
if (size > 1) sortArray(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the array in-place according to the order specified by the given [comparison] function.
|
||||
*/
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun <T> Array<out T>.sort(noinline comparison: (a: T, b: T) -> Int): Unit {
|
||||
asDynamic().sort(comparison)
|
||||
public fun <T> Array<out T>.sort(comparison: (a: T, b: T) -> Int): Unit {
|
||||
if (size > 1) sortArrayWith(this, comparison)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1243,8 +1240,7 @@ public inline fun CharArray.sort(noinline comparison: (a: Char, b: Char) -> Int)
|
||||
* Sorts the array in-place according to the order specified by the given [comparator].
|
||||
*/
|
||||
public actual fun <T> Array<out T>.sortWith(comparator: Comparator<in T>): Unit {
|
||||
if (size > 1)
|
||||
sort { a, b -> comparator.compare(a, b) }
|
||||
if (size > 1) sortArrayWith(this, comparator)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1159,8 +1159,7 @@ public actual fun IntArray.sort(): Unit {
|
||||
* Sorts the array in-place.
|
||||
*/
|
||||
public actual fun LongArray.sort(): Unit {
|
||||
if (size > 1)
|
||||
sort { a: Long, b: Long -> a.compareTo(b) }
|
||||
if (size > 1) sort { a: Long, b: Long -> a.compareTo(b) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1207,16 +1206,14 @@ public actual fun CharArray.sort(): Unit {
|
||||
* Sorts the array in-place according to the natural order of its elements.
|
||||
*/
|
||||
public actual fun <T : Comparable<T>> Array<out T>.sort(): Unit {
|
||||
if (size > 1)
|
||||
sort { a: T, b: T -> a.compareTo(b) }
|
||||
if (size > 1) sortArray(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the array in-place according to the order specified by the given [comparison] function.
|
||||
*/
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun <T> Array<out T>.sort(noinline comparison: (a: T, b: T) -> Int): Unit {
|
||||
asDynamic().sort(comparison)
|
||||
public fun <T> Array<out T>.sort(comparison: (a: T, b: T) -> Int): Unit {
|
||||
if (size > 1) sortArrayWith(this, comparison)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1279,8 +1276,7 @@ public inline fun CharArray.sort(noinline comparison: (a: Char, b: Char) -> Int)
|
||||
* Sorts the array in-place according to the order specified by the given [comparator].
|
||||
*/
|
||||
public actual fun <T> Array<out T>.sortWith(comparator: Comparator<in T>): Unit {
|
||||
if (size > 1)
|
||||
sort { a, b -> comparator.compare(a, b) }
|
||||
if (size > 1) sortArrayWith(this, comparator)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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.
|
||||
*/
|
||||
|
||||
package kotlin.collections
|
||||
|
||||
internal fun <T> sortArrayWith(array: Array<out T>, comparison: (T, T) -> Int) {
|
||||
if (getStableSortingIsSupported()) {
|
||||
array.asDynamic().sort(comparison)
|
||||
} else {
|
||||
mergeSort(array.unsafeCast<Array<T>>(), 0, array.lastIndex, Comparator(comparison))
|
||||
}
|
||||
}
|
||||
|
||||
internal fun <T> sortArrayWith(array: Array<out T>, comparator: Comparator<in T>) {
|
||||
if (getStableSortingIsSupported()) {
|
||||
val comparison = { a: T, b: T -> comparator.compare(a, b) }
|
||||
array.asDynamic().sort(comparison)
|
||||
} else {
|
||||
mergeSort(array.unsafeCast<Array<T>>(), 0, array.lastIndex, comparator)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun <T : Comparable<T>> sortArray(array: Array<out T>) {
|
||||
if (getStableSortingIsSupported()) {
|
||||
val comparison = { a: T, b: T -> a.compareTo(b) }
|
||||
array.asDynamic().sort(comparison)
|
||||
} else {
|
||||
mergeSort(array.unsafeCast<Array<T>>(), 0, array.lastIndex, naturalOrder())
|
||||
}
|
||||
}
|
||||
|
||||
private var _stableSortingIsSupported: Boolean? = null
|
||||
private fun getStableSortingIsSupported(): Boolean {
|
||||
_stableSortingIsSupported?.let { return it }
|
||||
_stableSortingIsSupported = false
|
||||
|
||||
val array = js("[]").unsafeCast<Array<Int>>()
|
||||
// known implementations may use stable sort for arrays of up to 512 elements
|
||||
// so we create slightly more elements to test stability
|
||||
for (index in 0 until 600) array.asDynamic().push(index)
|
||||
val comparison = { a: Int, b: Int -> (a and 3) - (b and 3) }
|
||||
array.asDynamic().sort(comparison)
|
||||
for (index in 1 until array.size) {
|
||||
val a = array[index - 1]
|
||||
val b = array[index]
|
||||
if ((a and 3) == (b and 3) && a >= b) return false
|
||||
}
|
||||
_stableSortingIsSupported = true
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
private fun <T> mergeSort(array: Array<T>, start: Int, endInclusive: Int, comparator: Comparator<in T>) {
|
||||
val buffer = arrayOfNulls<Any?>(array.size).unsafeCast<Array<T>>()
|
||||
val result = mergeSort(array, buffer, start, endInclusive, comparator)
|
||||
if (result !== array) {
|
||||
result.forEachIndexed { i, v -> array[i] = v }
|
||||
}
|
||||
}
|
||||
|
||||
// Both start and end are inclusive indices.
|
||||
private fun <T> mergeSort(array: Array<T>, buffer: Array<T>, start: Int, end: Int, comparator: Comparator<in T>): Array<T> {
|
||||
if (start == end) {
|
||||
return array
|
||||
}
|
||||
|
||||
val median = (start + end) / 2
|
||||
val left = mergeSort(array, buffer, start, median, comparator)
|
||||
val right = mergeSort(array, buffer, median + 1, end, comparator)
|
||||
|
||||
val target = if (left === buffer) array else buffer
|
||||
|
||||
// Merge.
|
||||
var leftIndex = start
|
||||
var rightIndex = median + 1
|
||||
for (i in start..end) {
|
||||
when {
|
||||
leftIndex <= median && rightIndex <= end -> {
|
||||
val leftValue = left[leftIndex]
|
||||
val rightValue = right[rightIndex]
|
||||
|
||||
if (comparator.compare(leftValue, rightValue) <= 0) {
|
||||
target[i] = leftValue
|
||||
leftIndex++
|
||||
} else {
|
||||
target[i] = rightValue
|
||||
rightIndex++
|
||||
}
|
||||
}
|
||||
leftIndex <= median -> {
|
||||
target[i] = left[leftIndex]
|
||||
leftIndex++
|
||||
}
|
||||
else /* rightIndex <= end */ -> {
|
||||
target[i] = right[rightIndex]
|
||||
rightIndex++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return target
|
||||
}
|
||||
Reference in New Issue
Block a user