Specialize contentDeepEquals/HashCode/ToString for arrays of unsigned types
#KT-26388
This commit is contained in:
@@ -48,20 +48,8 @@ Kotlin.arrayToString = function (a) {
|
||||
return "[" + Array.prototype.map.call(a, function(e) { return toString(e); }).join(", ") + "]";
|
||||
};
|
||||
|
||||
Kotlin.arrayDeepToString = function (a, visited) {
|
||||
visited = visited || [a];
|
||||
var toString = Kotlin.isCharArray(a) ? String.fromCharCode : Kotlin.toString;
|
||||
return "[" + Array.prototype.map.call(a, function (e) {
|
||||
if (Kotlin.isArrayish(e) && visited.indexOf(e) < 0) {
|
||||
visited.push(e);
|
||||
var result = Kotlin.arrayDeepToString(e, visited);
|
||||
visited.pop();
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
return toString(e);
|
||||
}
|
||||
}).join(", ") + "]";
|
||||
Kotlin.arrayDeepToString = function (arr) {
|
||||
return Kotlin.kotlin.collections.contentDeepToStringImpl(arr);
|
||||
};
|
||||
|
||||
Kotlin.arrayEquals = function (a, b) {
|
||||
@@ -81,24 +69,7 @@ Kotlin.arrayEquals = function (a, b) {
|
||||
};
|
||||
|
||||
Kotlin.arrayDeepEquals = function (a, b) {
|
||||
if (a === b) {
|
||||
return true;
|
||||
}
|
||||
if (!Kotlin.isArrayish(b) || a.length !== b.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0, n = a.length; i < n; i++) {
|
||||
if (Kotlin.isArrayish(a[i])) {
|
||||
if (!Kotlin.arrayDeepEquals(a[i], b[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!Kotlin.equals(a[i], b[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return Kotlin.kotlin.collections.contentDeepEqualsImpl(a, b);
|
||||
};
|
||||
|
||||
Kotlin.arrayHashCode = function (arr) {
|
||||
@@ -110,12 +81,7 @@ Kotlin.arrayHashCode = function (arr) {
|
||||
};
|
||||
|
||||
Kotlin.arrayDeepHashCode = function (arr) {
|
||||
var result = 1;
|
||||
for (var i = 0, n = arr.length; i < n; i++) {
|
||||
var e = arr[i];
|
||||
result = ((31 * result | 0) + (Kotlin.isArrayish(e) ? Kotlin.arrayDeepHashCode(e) : Kotlin.hashCode(e))) | 0;
|
||||
}
|
||||
return result;
|
||||
return Kotlin.kotlin.collections.contentDeepHashCodeImpl(arr);
|
||||
};
|
||||
|
||||
Kotlin.primitiveArraySort = function (array) {
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2010-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.
|
||||
*/
|
||||
|
||||
package kotlin.collections
|
||||
|
||||
|
||||
@UseExperimental(ExperimentalUnsignedTypes::class)
|
||||
@SinceKotlin("1.3")
|
||||
@kotlin.js.JsName("contentDeepHashCodeImpl")
|
||||
internal fun <T> Array<out T>.contentDeepHashCodeImpl(): Int {
|
||||
var result = 1
|
||||
for (element in this) {
|
||||
val elementHash = when {
|
||||
element == null -> 0
|
||||
js("Kotlin").isArrayish(element) -> (element.unsafeCast<Array<*>>()).contentDeepHashCodeImpl()
|
||||
|
||||
element is UByteArray -> element.contentHashCode()
|
||||
element is UShortArray -> element.contentHashCode()
|
||||
element is UIntArray -> element.contentHashCode()
|
||||
element is ULongArray -> element.contentHashCode()
|
||||
|
||||
else -> element.hashCode()
|
||||
}
|
||||
|
||||
result = 31 * result + elementHash
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -295,9 +295,13 @@ public fun CharArray.binarySearch(element: Char, fromIndex: Int = 0, toIndex: In
|
||||
* If any of arrays contains itself on any nesting level the behavior is undefined.
|
||||
*/
|
||||
@SinceKotlin("1.1")
|
||||
@JvmName("contentDeepEqualsInline")
|
||||
@kotlin.internal.InlineOnly
|
||||
public actual inline infix fun <T> Array<out T>.contentDeepEquals(other: Array<out T>): Boolean {
|
||||
return java.util.Arrays.deepEquals(this, other)
|
||||
if (kotlin.internal.apiVersionIsAtLeast(1, 3, 0))
|
||||
return contentDeepEqualsImpl(other)
|
||||
else
|
||||
return java.util.Arrays.deepEquals(this, other)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -307,9 +311,13 @@ public actual inline infix fun <T> Array<out T>.contentDeepEquals(other: Array<o
|
||||
* If any of arrays contains itself on any nesting level the behavior is undefined.
|
||||
*/
|
||||
@SinceKotlin("1.1")
|
||||
@JvmName("contentDeepHashCodeInline")
|
||||
@kotlin.internal.InlineOnly
|
||||
public actual inline fun <T> Array<out T>.contentDeepHashCode(): Int {
|
||||
return java.util.Arrays.deepHashCode(this)
|
||||
if (kotlin.internal.apiVersionIsAtLeast(1, 3, 0))
|
||||
return contentDeepHashCodeImpl()
|
||||
else
|
||||
return java.util.Arrays.deepHashCode(this)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -322,9 +330,13 @@ public actual inline fun <T> Array<out T>.contentDeepHashCode(): Int {
|
||||
* @sample samples.collections.Arrays.ContentOperations.contentDeepToString
|
||||
*/
|
||||
@SinceKotlin("1.1")
|
||||
@JvmName("contentDeepToStringInline")
|
||||
@kotlin.internal.InlineOnly
|
||||
public actual inline fun <T> Array<out T>.contentDeepToString(): String {
|
||||
return java.util.Arrays.deepToString(this)
|
||||
if (kotlin.internal.apiVersionIsAtLeast(1, 3, 0))
|
||||
return contentDeepToStringImpl()
|
||||
else
|
||||
return java.util.Arrays.deepToString(this)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -47,4 +47,14 @@ internal actual fun <T> arrayOfNulls(reference: Array<T>, size: Int): Array<T> {
|
||||
@SinceKotlin("1.3")
|
||||
internal fun copyOfRangeToIndexCheck(toIndex: Int, size: Int) {
|
||||
if (toIndex > size) throw IndexOutOfBoundsException("toIndex ($toIndex) is greater than size ($size).")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SinceKotlin("1.3")
|
||||
@PublishedApi
|
||||
@kotlin.jvm.JvmName("contentDeepHashCode")
|
||||
internal fun <T> Array<out T>.contentDeepHashCodeImpl(): Int =
|
||||
// returns valid result for unsigned arrays by accident:
|
||||
// hash code of an inline class, which an unsigned array is,
|
||||
// is calculated structurally as in a data class
|
||||
java.util.Arrays.deepHashCode(this)
|
||||
|
||||
@@ -65,3 +65,95 @@ public inline fun Array<*>?.isNullOrEmpty(): Boolean {
|
||||
@Suppress("UPPER_BOUND_CANNOT_BE_ARRAY")
|
||||
public inline fun <C, R> C.ifEmpty(defaultValue: () -> R): R where C : Array<*>, C : R =
|
||||
if (isEmpty()) defaultValue() else this
|
||||
|
||||
|
||||
@SinceKotlin("1.3")
|
||||
@PublishedApi
|
||||
@kotlin.jvm.JvmName("contentDeepEquals")
|
||||
@kotlin.js.JsName("contentDeepEqualsImpl")
|
||||
internal fun <T> Array<out T>.contentDeepEqualsImpl(other: Array<out T>): Boolean {
|
||||
if (this === other) return true
|
||||
if (this.size != other.size) return false
|
||||
|
||||
for (i in indices) {
|
||||
val v1 = this[i]
|
||||
val v2 = other[i]
|
||||
|
||||
if (v1 === v2) {
|
||||
continue
|
||||
} else if (v1 == null || v2 == null) {
|
||||
return false
|
||||
}
|
||||
|
||||
when {
|
||||
v1 is Array<*> && v2 is Array<*> -> if (!v1.contentDeepEquals(v2)) return false
|
||||
v1 is ByteArray && v2 is ByteArray -> if (!v1.contentEquals(v2)) return false
|
||||
v1 is ShortArray && v2 is ShortArray -> if (!v1.contentEquals(v2)) return false
|
||||
v1 is IntArray && v2 is IntArray -> if (!v1.contentEquals(v2)) return false
|
||||
v1 is LongArray && v2 is LongArray -> if (!v1.contentEquals(v2)) return false
|
||||
v1 is FloatArray && v2 is FloatArray -> if (!v1.contentEquals(v2)) return false
|
||||
v1 is DoubleArray && v2 is DoubleArray -> if (!v1.contentEquals(v2)) return false
|
||||
v1 is CharArray && v2 is CharArray -> if (!v1.contentEquals(v2)) return false
|
||||
v1 is BooleanArray && v2 is BooleanArray -> if (!v1.contentEquals(v2)) return false
|
||||
|
||||
v1 is UByteArray && v2 is UByteArray -> if (!v1.contentEquals(v2)) return false
|
||||
v1 is UShortArray && v2 is UShortArray -> if (!v1.contentEquals(v2)) return false
|
||||
v1 is UIntArray && v2 is UIntArray -> if (!v1.contentEquals(v2)) return false
|
||||
v1 is ULongArray && v2 is ULongArray -> if (!v1.contentEquals(v2)) return false
|
||||
|
||||
else -> if (v1 != v2) return false
|
||||
}
|
||||
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@SinceKotlin("1.3")
|
||||
@PublishedApi
|
||||
@kotlin.jvm.JvmName("contentDeepToString")
|
||||
@kotlin.js.JsName("contentDeepToStringImpl")
|
||||
internal fun <T> Array<out T>.contentDeepToStringImpl(): String {
|
||||
val length = size.coerceAtMost((Int.MAX_VALUE - 2) / 5) * 5 + 2 // in order not to overflow Int.MAX_VALUE
|
||||
return buildString(length) {
|
||||
contentDeepToStringInternal(this, mutableSetOf())
|
||||
}
|
||||
}
|
||||
|
||||
@UseExperimental(ExperimentalUnsignedTypes::class)
|
||||
private fun <T> Array<out T>.contentDeepToStringInternal(result: StringBuilder, processed: MutableSet<Array<*>>) {
|
||||
if (this in processed) {
|
||||
result.append("[...]")
|
||||
return
|
||||
}
|
||||
processed.add(this)
|
||||
result.append('[')
|
||||
|
||||
for (i in indices) {
|
||||
if (i != 0) {
|
||||
result.append(", ")
|
||||
}
|
||||
val element = this[i]
|
||||
when (element) {
|
||||
null -> result.append("null")
|
||||
is Array<*> -> element.contentDeepToStringInternal(result, processed)
|
||||
is ByteArray -> result.append(element.contentToString())
|
||||
is ShortArray -> result.append(element.contentToString())
|
||||
is IntArray -> result.append(element.contentToString())
|
||||
is LongArray -> result.append(element.contentToString())
|
||||
is FloatArray -> result.append(element.contentToString())
|
||||
is DoubleArray -> result.append(element.contentToString())
|
||||
is CharArray -> result.append(element.contentToString())
|
||||
is BooleanArray -> result.append(element.contentToString())
|
||||
|
||||
is UByteArray -> result.append(element.contentToString())
|
||||
is UShortArray -> result.append(element.contentToString())
|
||||
is UIntArray -> result.append(element.contentToString())
|
||||
is ULongArray -> result.append(element.contentToString())
|
||||
|
||||
else -> result.append(element.toString())
|
||||
}
|
||||
}
|
||||
|
||||
result.append(']')
|
||||
processed.remove(this)
|
||||
}
|
||||
@@ -209,10 +209,19 @@ class ArraysTest {
|
||||
@Test fun contentDeepEquals() {
|
||||
val arr1 = arrayOf("a", 1, intArrayOf(2))
|
||||
val arr2 = arrayOf("a", 1, intArrayOf(2))
|
||||
val arr3 = arrayOf("a", 1, uintArrayOf(2u))
|
||||
val arr4 = arrayOf("a", 1, uintArrayOf(2u))
|
||||
assertFalse(arr1 contentEquals arr2)
|
||||
assertTrue(arr1 contentDeepEquals arr2)
|
||||
|
||||
assertFalse(arr1 contentDeepEquals arr3)
|
||||
assertTrue(arr3 contentDeepEquals arr4)
|
||||
|
||||
arr2[2] = arr1
|
||||
assertFalse(arr1 contentDeepEquals arr2)
|
||||
|
||||
arr4[2] = arr3
|
||||
assertFalse(arr3 contentDeepEquals arr4)
|
||||
}
|
||||
|
||||
@Test fun contentToString() {
|
||||
@@ -230,8 +239,13 @@ class ArraysTest {
|
||||
return
|
||||
}
|
||||
|
||||
val arr = arrayOf("aa", 1, null, charArrayOf('d'))
|
||||
assertEquals("[aa, 1, null, [d]]", arr.contentDeepToString())
|
||||
val arr = arrayOf(
|
||||
"aa", 1, null, arrayOf(arrayOf("foo")), charArrayOf('d'), booleanArrayOf(false),
|
||||
intArrayOf(-1), longArrayOf(-1), shortArrayOf(-1), byteArrayOf(-1),
|
||||
uintArrayOf(UInt.MAX_VALUE), ulongArrayOf(ULong.MAX_VALUE), ushortArrayOf(UShort.MAX_VALUE), ubyteArrayOf(UByte.MAX_VALUE),
|
||||
doubleArrayOf(3.14), floatArrayOf(1.25f)
|
||||
)
|
||||
assertEquals("[aa, 1, null, [[foo]], [d], [false], [-1], [-1], [-1], [-1], [4294967295], [18446744073709551615], [65535], [255], [3.14], [1.25]]", arr.contentDeepToString())
|
||||
}
|
||||
|
||||
@Test fun contentDeepToStringNoRecursion() {
|
||||
@@ -262,6 +276,16 @@ class ArraysTest {
|
||||
@Test fun contentDeepHashCode() {
|
||||
val arr = arrayOf(null, Value(2), arrayOf(Value(3)))
|
||||
assertEquals(((1*31 + 0)*31 + 2) * 31 + (1 * 31 + 3), arr.contentDeepHashCode())
|
||||
|
||||
val intArray2 = arrayOf(intArrayOf(1, 2), intArrayOf(3, 4))
|
||||
val intList2 = listOf(listOf(1, 2), listOf(3, 4))
|
||||
|
||||
assertEquals(intList2.hashCode(), intArray2.contentDeepHashCode())
|
||||
|
||||
val uintArray2 = arrayOf(uintArrayOf(1u, 2u), uintArrayOf(3u, 4u))
|
||||
val uintList2 = listOf(listOf(1u, 2u), listOf(3u, 4u))
|
||||
|
||||
assertEquals(uintList2.hashCode(), uintArray2.contentDeepHashCode())
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user