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(", ") + "]";
|
return "[" + Array.prototype.map.call(a, function(e) { return toString(e); }).join(", ") + "]";
|
||||||
};
|
};
|
||||||
|
|
||||||
Kotlin.arrayDeepToString = function (a, visited) {
|
Kotlin.arrayDeepToString = function (arr) {
|
||||||
visited = visited || [a];
|
return Kotlin.kotlin.collections.contentDeepToStringImpl(arr);
|
||||||
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.arrayEquals = function (a, b) {
|
Kotlin.arrayEquals = function (a, b) {
|
||||||
@@ -81,24 +69,7 @@ Kotlin.arrayEquals = function (a, b) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Kotlin.arrayDeepEquals = function (a, b) {
|
Kotlin.arrayDeepEquals = function (a, b) {
|
||||||
if (a === b) {
|
return Kotlin.kotlin.collections.contentDeepEqualsImpl(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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Kotlin.arrayHashCode = function (arr) {
|
Kotlin.arrayHashCode = function (arr) {
|
||||||
@@ -110,12 +81,7 @@ Kotlin.arrayHashCode = function (arr) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Kotlin.arrayDeepHashCode = function (arr) {
|
Kotlin.arrayDeepHashCode = function (arr) {
|
||||||
var result = 1;
|
return Kotlin.kotlin.collections.contentDeepHashCodeImpl(arr);
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Kotlin.primitiveArraySort = function (array) {
|
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.
|
* If any of arrays contains itself on any nesting level the behavior is undefined.
|
||||||
*/
|
*/
|
||||||
@SinceKotlin("1.1")
|
@SinceKotlin("1.1")
|
||||||
|
@JvmName("contentDeepEqualsInline")
|
||||||
@kotlin.internal.InlineOnly
|
@kotlin.internal.InlineOnly
|
||||||
public actual inline infix fun <T> Array<out T>.contentDeepEquals(other: Array<out T>): Boolean {
|
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.
|
* If any of arrays contains itself on any nesting level the behavior is undefined.
|
||||||
*/
|
*/
|
||||||
@SinceKotlin("1.1")
|
@SinceKotlin("1.1")
|
||||||
|
@JvmName("contentDeepHashCodeInline")
|
||||||
@kotlin.internal.InlineOnly
|
@kotlin.internal.InlineOnly
|
||||||
public actual inline fun <T> Array<out T>.contentDeepHashCode(): Int {
|
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
|
* @sample samples.collections.Arrays.ContentOperations.contentDeepToString
|
||||||
*/
|
*/
|
||||||
@SinceKotlin("1.1")
|
@SinceKotlin("1.1")
|
||||||
|
@JvmName("contentDeepToStringInline")
|
||||||
@kotlin.internal.InlineOnly
|
@kotlin.internal.InlineOnly
|
||||||
public actual inline fun <T> Array<out T>.contentDeepToString(): String {
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -48,3 +48,13 @@ internal actual fun <T> arrayOfNulls(reference: Array<T>, size: Int): Array<T> {
|
|||||||
internal fun copyOfRangeToIndexCheck(toIndex: Int, size: Int) {
|
internal fun copyOfRangeToIndexCheck(toIndex: Int, size: Int) {
|
||||||
if (toIndex > size) throw IndexOutOfBoundsException("toIndex ($toIndex) is greater than size ($size).")
|
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")
|
@Suppress("UPPER_BOUND_CANNOT_BE_ARRAY")
|
||||||
public inline fun <C, R> C.ifEmpty(defaultValue: () -> R): R where C : Array<*>, C : R =
|
public inline fun <C, R> C.ifEmpty(defaultValue: () -> R): R where C : Array<*>, C : R =
|
||||||
if (isEmpty()) defaultValue() else this
|
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() {
|
@Test fun contentDeepEquals() {
|
||||||
val arr1 = arrayOf("a", 1, intArrayOf(2))
|
val arr1 = arrayOf("a", 1, intArrayOf(2))
|
||||||
val arr2 = 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)
|
assertFalse(arr1 contentEquals arr2)
|
||||||
assertTrue(arr1 contentDeepEquals arr2)
|
assertTrue(arr1 contentDeepEquals arr2)
|
||||||
|
|
||||||
|
assertFalse(arr1 contentDeepEquals arr3)
|
||||||
|
assertTrue(arr3 contentDeepEquals arr4)
|
||||||
|
|
||||||
arr2[2] = arr1
|
arr2[2] = arr1
|
||||||
assertFalse(arr1 contentDeepEquals arr2)
|
assertFalse(arr1 contentDeepEquals arr2)
|
||||||
|
|
||||||
|
arr4[2] = arr3
|
||||||
|
assertFalse(arr3 contentDeepEquals arr4)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test fun contentToString() {
|
@Test fun contentToString() {
|
||||||
@@ -230,8 +239,13 @@ class ArraysTest {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val arr = arrayOf("aa", 1, null, charArrayOf('d'))
|
val arr = arrayOf(
|
||||||
assertEquals("[aa, 1, null, [d]]", arr.contentDeepToString())
|
"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() {
|
@Test fun contentDeepToStringNoRecursion() {
|
||||||
@@ -262,6 +276,16 @@ class ArraysTest {
|
|||||||
@Test fun contentDeepHashCode() {
|
@Test fun contentDeepHashCode() {
|
||||||
val arr = arrayOf(null, Value(2), arrayOf(Value(3)))
|
val arr = arrayOf(null, Value(2), arrayOf(Value(3)))
|
||||||
assertEquals(((1*31 + 0)*31 + 2) * 31 + (1 * 31 + 3), arr.contentDeepHashCode())
|
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())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+3
@@ -993,6 +993,9 @@ public final class kotlin/collections/ArraysKt {
|
|||||||
public static final fun contains ([Ljava/lang/Object;Ljava/lang/Object;)Z
|
public static final fun contains ([Ljava/lang/Object;Ljava/lang/Object;)Z
|
||||||
public static final fun contains ([SS)Z
|
public static final fun contains ([SS)Z
|
||||||
public static final fun contains ([ZZ)Z
|
public static final fun contains ([ZZ)Z
|
||||||
|
public static final fun contentDeepEquals ([Ljava/lang/Object;[Ljava/lang/Object;)Z
|
||||||
|
public static final fun contentDeepHashCode ([Ljava/lang/Object;)I
|
||||||
|
public static final fun contentDeepToString ([Ljava/lang/Object;)Ljava/lang/String;
|
||||||
public static final fun copyInto ([B[BIII)[B
|
public static final fun copyInto ([B[BIII)[B
|
||||||
public static final fun copyInto ([C[CIII)[C
|
public static final fun copyInto ([C[CIII)[C
|
||||||
public static final fun copyInto ([D[DIII)[D
|
public static final fun copyInto ([D[DIII)[D
|
||||||
|
|||||||
@@ -105,7 +105,15 @@ object ArrayOps : TemplateGroupBase() {
|
|||||||
returns("Boolean")
|
returns("Boolean")
|
||||||
on(Platform.JVM) {
|
on(Platform.JVM) {
|
||||||
inlineOnly()
|
inlineOnly()
|
||||||
body { "return java.util.Arrays.deepEquals(this, other)" }
|
annotation("""@JvmName("contentDeepEqualsInline")""")
|
||||||
|
body {
|
||||||
|
"""
|
||||||
|
if (kotlin.internal.apiVersionIsAtLeast(1, 3, 0))
|
||||||
|
return contentDeepEqualsImpl(other)
|
||||||
|
else
|
||||||
|
return java.util.Arrays.deepEquals(this, other)
|
||||||
|
"""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
on(Platform.JS) {
|
on(Platform.JS) {
|
||||||
annotation("""@library("arrayDeepEquals")""")
|
annotation("""@library("arrayDeepEquals")""")
|
||||||
@@ -155,7 +163,15 @@ object ArrayOps : TemplateGroupBase() {
|
|||||||
returns("String")
|
returns("String")
|
||||||
on(Platform.JVM) {
|
on(Platform.JVM) {
|
||||||
inlineOnly()
|
inlineOnly()
|
||||||
body { "return java.util.Arrays.deepToString(this)" }
|
annotation("""@JvmName("contentDeepToStringInline")""")
|
||||||
|
body {
|
||||||
|
"""
|
||||||
|
if (kotlin.internal.apiVersionIsAtLeast(1, 3, 0))
|
||||||
|
return contentDeepToStringImpl()
|
||||||
|
else
|
||||||
|
return java.util.Arrays.deepToString(this)
|
||||||
|
"""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
on(Platform.JS) {
|
on(Platform.JS) {
|
||||||
annotation("""@library("arrayDeepToString")""")
|
annotation("""@library("arrayDeepToString")""")
|
||||||
@@ -200,7 +216,15 @@ object ArrayOps : TemplateGroupBase() {
|
|||||||
returns("Int")
|
returns("Int")
|
||||||
on(Platform.JVM) {
|
on(Platform.JVM) {
|
||||||
inlineOnly()
|
inlineOnly()
|
||||||
body { "return java.util.Arrays.deepHashCode(this)" }
|
annotation("""@JvmName("contentDeepHashCodeInline")""")
|
||||||
|
body {
|
||||||
|
"""
|
||||||
|
if (kotlin.internal.apiVersionIsAtLeast(1, 3, 0))
|
||||||
|
return contentDeepHashCodeImpl()
|
||||||
|
else
|
||||||
|
return java.util.Arrays.deepHashCode(this)
|
||||||
|
"""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
on(Platform.JS) {
|
on(Platform.JS) {
|
||||||
annotation("""@library("arrayDeepHashCode")""")
|
annotation("""@library("arrayDeepHashCode")""")
|
||||||
|
|||||||
Reference in New Issue
Block a user