From b59993d88aa3c2837201fa19d2f2f8879131de04 Mon Sep 17 00:00:00 2001 From: Artem Kobzar Date: Tue, 23 Jan 2024 18:48:53 +0100 Subject: [PATCH] [K/Wasm] Prepare for splitting unsigned implementation helpers into expect/actuals KT-58039 --- .../src/main/kotlin/kotlin/Unsigned.kt | 0 libraries/stdlib/js/src/kotlin/UnsignedJs.kt | 105 ++++++++++++++++++ .../stdlib/jvm/src/kotlin/util/UnsignedJVM.kt | 105 ++++++++++++++++++ .../unsigned/src/kotlin/UnsignedCommon.kt | 105 ++++++++++++++++++ .../wasm/src/kotlin/util/UnsignedWasm.kt | 105 ++++++++++++++++++ 5 files changed, 420 insertions(+) rename libraries/stdlib/unsigned/src/kotlin/UnsignedUtils.kt => kotlin-native/runtime/src/main/kotlin/kotlin/Unsigned.kt (100%) create mode 100644 libraries/stdlib/js/src/kotlin/UnsignedJs.kt create mode 100644 libraries/stdlib/jvm/src/kotlin/util/UnsignedJVM.kt create mode 100644 libraries/stdlib/unsigned/src/kotlin/UnsignedCommon.kt create mode 100644 libraries/stdlib/wasm/src/kotlin/util/UnsignedWasm.kt diff --git a/libraries/stdlib/unsigned/src/kotlin/UnsignedUtils.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/Unsigned.kt similarity index 100% rename from libraries/stdlib/unsigned/src/kotlin/UnsignedUtils.kt rename to kotlin-native/runtime/src/main/kotlin/kotlin/Unsigned.kt diff --git a/libraries/stdlib/js/src/kotlin/UnsignedJs.kt b/libraries/stdlib/js/src/kotlin/UnsignedJs.kt new file mode 100644 index 00000000000..e075329df16 --- /dev/null +++ b/libraries/stdlib/js/src/kotlin/UnsignedJs.kt @@ -0,0 +1,105 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ +@file:kotlin.jvm.JvmName("UnsignedKt") +package kotlin + +@PublishedApi +internal fun uintCompare(v1: Int, v2: Int): Int = (v1 xor Int.MIN_VALUE).compareTo(v2 xor Int.MIN_VALUE) +@PublishedApi +internal fun ulongCompare(v1: Long, v2: Long): Int = (v1 xor Long.MIN_VALUE).compareTo(v2 xor Long.MIN_VALUE) + +@PublishedApi +internal fun uintDivide(v1: UInt, v2: UInt): UInt = (v1.toLong() / v2.toLong()).toUInt() +@PublishedApi +internal fun uintRemainder(v1: UInt, v2: UInt): UInt = (v1.toLong() % v2.toLong()).toUInt() + +// Division and remainder are based on Guava's UnsignedLongs implementation +// Copyright 2011 The Guava Authors + +@PublishedApi +internal fun ulongDivide(v1: ULong, v2: ULong): ULong { + val dividend = v1.toLong() + val divisor = v2.toLong() + if (divisor < 0) { // i.e., divisor >= 2^63: + return if (v1 < v2) ULong(0) else ULong(1) + } + + // Optimization - use signed division if both dividend and divisor < 2^63 + if (dividend >= 0) { + return ULong(dividend / divisor) + } + + // Otherwise, approximate the quotient, check, and correct if necessary. + val quotient = ((dividend ushr 1) / divisor) shl 1 + val rem = dividend - quotient * divisor + return ULong(quotient + if (ULong(rem) >= ULong(divisor)) 1 else 0) + +} + +@PublishedApi +internal fun ulongRemainder(v1: ULong, v2: ULong): ULong { + val dividend = v1.toLong() + val divisor = v2.toLong() + if (divisor < 0) { // i.e., divisor >= 2^63: + return if (v1 < v2) { + v1 // dividend < divisor + } else { + v1 - v2 // dividend >= divisor + } + } + + // Optimization - use signed modulus if both dividend and divisor < 2^63 + if (dividend >= 0) { + return ULong(dividend % divisor) + } + + // Otherwise, approximate the quotient, check, and correct if necessary. + val quotient = ((dividend ushr 1) / divisor) shl 1 + val rem = dividend - quotient * divisor + return ULong(rem - if (ULong(rem) >= ULong(divisor)) divisor else 0) +} + +@PublishedApi +internal fun doubleToUInt(v: Double): UInt = when { + v.isNaN() -> 0u + v <= UInt.MIN_VALUE.toDouble() -> UInt.MIN_VALUE + v >= UInt.MAX_VALUE.toDouble() -> UInt.MAX_VALUE + v <= Int.MAX_VALUE -> v.toInt().toUInt() + else -> (v - Int.MAX_VALUE).toInt().toUInt() + Int.MAX_VALUE.toUInt() // Int.MAX_VALUE < v < UInt.MAX_VALUE +} + +@PublishedApi +internal fun doubleToULong(v: Double): ULong = when { + v.isNaN() -> 0u + v <= ULong.MIN_VALUE.toDouble() -> ULong.MIN_VALUE + v >= ULong.MAX_VALUE.toDouble() -> ULong.MAX_VALUE + v < Long.MAX_VALUE -> v.toLong().toULong() + + // Real values from Long.MAX_VALUE to (Long.MAX_VALUE + 1) are not representable in Double, so don't handle them. + else -> (v - 9223372036854775808.0).toLong().toULong() + 9223372036854775808uL // Long.MAX_VALUE + 1 < v < ULong.MAX_VALUE +} + + +@PublishedApi +internal fun uintToDouble(v: Int): Double = (v and Int.MAX_VALUE).toDouble() + (v ushr 31 shl 30).toDouble() * 2 + +@PublishedApi +internal fun ulongToDouble(v: Long): Double = (v ushr 11).toDouble() * 2048 + (v and 2047) + + +internal fun ulongToString(v: Long): String = ulongToString(v, 10) + +internal fun ulongToString(v: Long, base: Int): String { + if (v >= 0) return v.toString(base) + + var quotient = ((v ushr 1) / base) shl 1 + var rem = v - quotient * base + if (rem >= base) { + rem -= base + quotient += 1 + } + return quotient.toString(base) + rem.toString(base) +} + diff --git a/libraries/stdlib/jvm/src/kotlin/util/UnsignedJVM.kt b/libraries/stdlib/jvm/src/kotlin/util/UnsignedJVM.kt new file mode 100644 index 00000000000..e075329df16 --- /dev/null +++ b/libraries/stdlib/jvm/src/kotlin/util/UnsignedJVM.kt @@ -0,0 +1,105 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ +@file:kotlin.jvm.JvmName("UnsignedKt") +package kotlin + +@PublishedApi +internal fun uintCompare(v1: Int, v2: Int): Int = (v1 xor Int.MIN_VALUE).compareTo(v2 xor Int.MIN_VALUE) +@PublishedApi +internal fun ulongCompare(v1: Long, v2: Long): Int = (v1 xor Long.MIN_VALUE).compareTo(v2 xor Long.MIN_VALUE) + +@PublishedApi +internal fun uintDivide(v1: UInt, v2: UInt): UInt = (v1.toLong() / v2.toLong()).toUInt() +@PublishedApi +internal fun uintRemainder(v1: UInt, v2: UInt): UInt = (v1.toLong() % v2.toLong()).toUInt() + +// Division and remainder are based on Guava's UnsignedLongs implementation +// Copyright 2011 The Guava Authors + +@PublishedApi +internal fun ulongDivide(v1: ULong, v2: ULong): ULong { + val dividend = v1.toLong() + val divisor = v2.toLong() + if (divisor < 0) { // i.e., divisor >= 2^63: + return if (v1 < v2) ULong(0) else ULong(1) + } + + // Optimization - use signed division if both dividend and divisor < 2^63 + if (dividend >= 0) { + return ULong(dividend / divisor) + } + + // Otherwise, approximate the quotient, check, and correct if necessary. + val quotient = ((dividend ushr 1) / divisor) shl 1 + val rem = dividend - quotient * divisor + return ULong(quotient + if (ULong(rem) >= ULong(divisor)) 1 else 0) + +} + +@PublishedApi +internal fun ulongRemainder(v1: ULong, v2: ULong): ULong { + val dividend = v1.toLong() + val divisor = v2.toLong() + if (divisor < 0) { // i.e., divisor >= 2^63: + return if (v1 < v2) { + v1 // dividend < divisor + } else { + v1 - v2 // dividend >= divisor + } + } + + // Optimization - use signed modulus if both dividend and divisor < 2^63 + if (dividend >= 0) { + return ULong(dividend % divisor) + } + + // Otherwise, approximate the quotient, check, and correct if necessary. + val quotient = ((dividend ushr 1) / divisor) shl 1 + val rem = dividend - quotient * divisor + return ULong(rem - if (ULong(rem) >= ULong(divisor)) divisor else 0) +} + +@PublishedApi +internal fun doubleToUInt(v: Double): UInt = when { + v.isNaN() -> 0u + v <= UInt.MIN_VALUE.toDouble() -> UInt.MIN_VALUE + v >= UInt.MAX_VALUE.toDouble() -> UInt.MAX_VALUE + v <= Int.MAX_VALUE -> v.toInt().toUInt() + else -> (v - Int.MAX_VALUE).toInt().toUInt() + Int.MAX_VALUE.toUInt() // Int.MAX_VALUE < v < UInt.MAX_VALUE +} + +@PublishedApi +internal fun doubleToULong(v: Double): ULong = when { + v.isNaN() -> 0u + v <= ULong.MIN_VALUE.toDouble() -> ULong.MIN_VALUE + v >= ULong.MAX_VALUE.toDouble() -> ULong.MAX_VALUE + v < Long.MAX_VALUE -> v.toLong().toULong() + + // Real values from Long.MAX_VALUE to (Long.MAX_VALUE + 1) are not representable in Double, so don't handle them. + else -> (v - 9223372036854775808.0).toLong().toULong() + 9223372036854775808uL // Long.MAX_VALUE + 1 < v < ULong.MAX_VALUE +} + + +@PublishedApi +internal fun uintToDouble(v: Int): Double = (v and Int.MAX_VALUE).toDouble() + (v ushr 31 shl 30).toDouble() * 2 + +@PublishedApi +internal fun ulongToDouble(v: Long): Double = (v ushr 11).toDouble() * 2048 + (v and 2047) + + +internal fun ulongToString(v: Long): String = ulongToString(v, 10) + +internal fun ulongToString(v: Long, base: Int): String { + if (v >= 0) return v.toString(base) + + var quotient = ((v ushr 1) / base) shl 1 + var rem = v - quotient * base + if (rem >= base) { + rem -= base + quotient += 1 + } + return quotient.toString(base) + rem.toString(base) +} + diff --git a/libraries/stdlib/unsigned/src/kotlin/UnsignedCommon.kt b/libraries/stdlib/unsigned/src/kotlin/UnsignedCommon.kt new file mode 100644 index 00000000000..e075329df16 --- /dev/null +++ b/libraries/stdlib/unsigned/src/kotlin/UnsignedCommon.kt @@ -0,0 +1,105 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ +@file:kotlin.jvm.JvmName("UnsignedKt") +package kotlin + +@PublishedApi +internal fun uintCompare(v1: Int, v2: Int): Int = (v1 xor Int.MIN_VALUE).compareTo(v2 xor Int.MIN_VALUE) +@PublishedApi +internal fun ulongCompare(v1: Long, v2: Long): Int = (v1 xor Long.MIN_VALUE).compareTo(v2 xor Long.MIN_VALUE) + +@PublishedApi +internal fun uintDivide(v1: UInt, v2: UInt): UInt = (v1.toLong() / v2.toLong()).toUInt() +@PublishedApi +internal fun uintRemainder(v1: UInt, v2: UInt): UInt = (v1.toLong() % v2.toLong()).toUInt() + +// Division and remainder are based on Guava's UnsignedLongs implementation +// Copyright 2011 The Guava Authors + +@PublishedApi +internal fun ulongDivide(v1: ULong, v2: ULong): ULong { + val dividend = v1.toLong() + val divisor = v2.toLong() + if (divisor < 0) { // i.e., divisor >= 2^63: + return if (v1 < v2) ULong(0) else ULong(1) + } + + // Optimization - use signed division if both dividend and divisor < 2^63 + if (dividend >= 0) { + return ULong(dividend / divisor) + } + + // Otherwise, approximate the quotient, check, and correct if necessary. + val quotient = ((dividend ushr 1) / divisor) shl 1 + val rem = dividend - quotient * divisor + return ULong(quotient + if (ULong(rem) >= ULong(divisor)) 1 else 0) + +} + +@PublishedApi +internal fun ulongRemainder(v1: ULong, v2: ULong): ULong { + val dividend = v1.toLong() + val divisor = v2.toLong() + if (divisor < 0) { // i.e., divisor >= 2^63: + return if (v1 < v2) { + v1 // dividend < divisor + } else { + v1 - v2 // dividend >= divisor + } + } + + // Optimization - use signed modulus if both dividend and divisor < 2^63 + if (dividend >= 0) { + return ULong(dividend % divisor) + } + + // Otherwise, approximate the quotient, check, and correct if necessary. + val quotient = ((dividend ushr 1) / divisor) shl 1 + val rem = dividend - quotient * divisor + return ULong(rem - if (ULong(rem) >= ULong(divisor)) divisor else 0) +} + +@PublishedApi +internal fun doubleToUInt(v: Double): UInt = when { + v.isNaN() -> 0u + v <= UInt.MIN_VALUE.toDouble() -> UInt.MIN_VALUE + v >= UInt.MAX_VALUE.toDouble() -> UInt.MAX_VALUE + v <= Int.MAX_VALUE -> v.toInt().toUInt() + else -> (v - Int.MAX_VALUE).toInt().toUInt() + Int.MAX_VALUE.toUInt() // Int.MAX_VALUE < v < UInt.MAX_VALUE +} + +@PublishedApi +internal fun doubleToULong(v: Double): ULong = when { + v.isNaN() -> 0u + v <= ULong.MIN_VALUE.toDouble() -> ULong.MIN_VALUE + v >= ULong.MAX_VALUE.toDouble() -> ULong.MAX_VALUE + v < Long.MAX_VALUE -> v.toLong().toULong() + + // Real values from Long.MAX_VALUE to (Long.MAX_VALUE + 1) are not representable in Double, so don't handle them. + else -> (v - 9223372036854775808.0).toLong().toULong() + 9223372036854775808uL // Long.MAX_VALUE + 1 < v < ULong.MAX_VALUE +} + + +@PublishedApi +internal fun uintToDouble(v: Int): Double = (v and Int.MAX_VALUE).toDouble() + (v ushr 31 shl 30).toDouble() * 2 + +@PublishedApi +internal fun ulongToDouble(v: Long): Double = (v ushr 11).toDouble() * 2048 + (v and 2047) + + +internal fun ulongToString(v: Long): String = ulongToString(v, 10) + +internal fun ulongToString(v: Long, base: Int): String { + if (v >= 0) return v.toString(base) + + var quotient = ((v ushr 1) / base) shl 1 + var rem = v - quotient * base + if (rem >= base) { + rem -= base + quotient += 1 + } + return quotient.toString(base) + rem.toString(base) +} + diff --git a/libraries/stdlib/wasm/src/kotlin/util/UnsignedWasm.kt b/libraries/stdlib/wasm/src/kotlin/util/UnsignedWasm.kt new file mode 100644 index 00000000000..e075329df16 --- /dev/null +++ b/libraries/stdlib/wasm/src/kotlin/util/UnsignedWasm.kt @@ -0,0 +1,105 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ +@file:kotlin.jvm.JvmName("UnsignedKt") +package kotlin + +@PublishedApi +internal fun uintCompare(v1: Int, v2: Int): Int = (v1 xor Int.MIN_VALUE).compareTo(v2 xor Int.MIN_VALUE) +@PublishedApi +internal fun ulongCompare(v1: Long, v2: Long): Int = (v1 xor Long.MIN_VALUE).compareTo(v2 xor Long.MIN_VALUE) + +@PublishedApi +internal fun uintDivide(v1: UInt, v2: UInt): UInt = (v1.toLong() / v2.toLong()).toUInt() +@PublishedApi +internal fun uintRemainder(v1: UInt, v2: UInt): UInt = (v1.toLong() % v2.toLong()).toUInt() + +// Division and remainder are based on Guava's UnsignedLongs implementation +// Copyright 2011 The Guava Authors + +@PublishedApi +internal fun ulongDivide(v1: ULong, v2: ULong): ULong { + val dividend = v1.toLong() + val divisor = v2.toLong() + if (divisor < 0) { // i.e., divisor >= 2^63: + return if (v1 < v2) ULong(0) else ULong(1) + } + + // Optimization - use signed division if both dividend and divisor < 2^63 + if (dividend >= 0) { + return ULong(dividend / divisor) + } + + // Otherwise, approximate the quotient, check, and correct if necessary. + val quotient = ((dividend ushr 1) / divisor) shl 1 + val rem = dividend - quotient * divisor + return ULong(quotient + if (ULong(rem) >= ULong(divisor)) 1 else 0) + +} + +@PublishedApi +internal fun ulongRemainder(v1: ULong, v2: ULong): ULong { + val dividend = v1.toLong() + val divisor = v2.toLong() + if (divisor < 0) { // i.e., divisor >= 2^63: + return if (v1 < v2) { + v1 // dividend < divisor + } else { + v1 - v2 // dividend >= divisor + } + } + + // Optimization - use signed modulus if both dividend and divisor < 2^63 + if (dividend >= 0) { + return ULong(dividend % divisor) + } + + // Otherwise, approximate the quotient, check, and correct if necessary. + val quotient = ((dividend ushr 1) / divisor) shl 1 + val rem = dividend - quotient * divisor + return ULong(rem - if (ULong(rem) >= ULong(divisor)) divisor else 0) +} + +@PublishedApi +internal fun doubleToUInt(v: Double): UInt = when { + v.isNaN() -> 0u + v <= UInt.MIN_VALUE.toDouble() -> UInt.MIN_VALUE + v >= UInt.MAX_VALUE.toDouble() -> UInt.MAX_VALUE + v <= Int.MAX_VALUE -> v.toInt().toUInt() + else -> (v - Int.MAX_VALUE).toInt().toUInt() + Int.MAX_VALUE.toUInt() // Int.MAX_VALUE < v < UInt.MAX_VALUE +} + +@PublishedApi +internal fun doubleToULong(v: Double): ULong = when { + v.isNaN() -> 0u + v <= ULong.MIN_VALUE.toDouble() -> ULong.MIN_VALUE + v >= ULong.MAX_VALUE.toDouble() -> ULong.MAX_VALUE + v < Long.MAX_VALUE -> v.toLong().toULong() + + // Real values from Long.MAX_VALUE to (Long.MAX_VALUE + 1) are not representable in Double, so don't handle them. + else -> (v - 9223372036854775808.0).toLong().toULong() + 9223372036854775808uL // Long.MAX_VALUE + 1 < v < ULong.MAX_VALUE +} + + +@PublishedApi +internal fun uintToDouble(v: Int): Double = (v and Int.MAX_VALUE).toDouble() + (v ushr 31 shl 30).toDouble() * 2 + +@PublishedApi +internal fun ulongToDouble(v: Long): Double = (v ushr 11).toDouble() * 2048 + (v and 2047) + + +internal fun ulongToString(v: Long): String = ulongToString(v, 10) + +internal fun ulongToString(v: Long, base: Int): String { + if (v >= 0) return v.toString(base) + + var quotient = ((v ushr 1) / base) shl 1 + var rem = v - quotient * base + if (rem >= base) { + rem -= base + quotient += 1 + } + return quotient.toString(base) + rem.toString(base) +} +