[K/Wasm] Generate wasm-specific unsigned implementations ^KT-58039 Fixed

This commit is contained in:
Artem Kobzar
2024-01-23 18:49:06 +01:00
committed by Space Team
parent b59993d88a
commit 8c69ffe8c9
24 changed files with 666 additions and 541 deletions
+71 -37
View File
@@ -1,25 +1,25 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2023 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()
import kotlin.internal.InlineOnly
// CHANGES IN THIS FILE SHOULD BE SYNCED WITH THE SAME CHANGES IN: UnsignedJVM.kt and kotlin-native/Unsigned.kt
// Division and remainder are based on Guava's UnsignedLongs implementation
// Copyright 2011 The Guava Authors
@PublishedApi
internal fun ulongDivide(v1: ULong, v2: ULong): ULong {
internal actual fun uintRemainder(v1: UInt, v2: UInt): UInt = (v1.toLong() % v2.toLong()).toUInt()
@PublishedApi
internal actual fun uintDivide(v1: UInt, v2: UInt): UInt = (v1.toLong() / v2.toLong()).toUInt()
@PublishedApi
internal actual fun ulongDivide(v1: ULong, v2: ULong): ULong {
val dividend = v1.toLong()
val divisor = v2.toLong()
if (divisor < 0) { // i.e., divisor >= 2^63:
@@ -39,7 +39,7 @@ internal fun ulongDivide(v1: ULong, v2: ULong): ULong {
}
@PublishedApi
internal fun ulongRemainder(v1: ULong, v2: ULong): ULong {
internal actual fun ulongRemainder(v1: ULong, v2: ULong): ULong {
val dividend = v1.toLong()
val divisor = v2.toLong()
if (divisor < 0) { // i.e., divisor >= 2^63:
@@ -62,44 +62,78 @@ internal fun ulongRemainder(v1: ULong, v2: ULong): ULong {
}
@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
internal actual fun uintCompare(v1: Int, v2: Int): Int = (v1 xor Int.MIN_VALUE).compareTo(v2 xor Int.MIN_VALUE)
@PublishedApi
internal actual fun ulongCompare(v1: Long, v2: Long): Int = (v1 xor Long.MIN_VALUE).compareTo(v2 xor Long.MIN_VALUE)
@PublishedApi
@InlineOnly
internal actual inline fun uintToULong(value: Int): ULong = ULong(uintToLong(value))
@PublishedApi
@InlineOnly
internal actual inline fun uintToLong(value: Int): Long = value.toLong() and 0xFFFF_FFFF
@PublishedApi
@InlineOnly
internal actual inline fun uintToFloat(value: Int): Float = uintToDouble(value).toFloat()
@PublishedApi
@InlineOnly
internal actual inline fun floatToUInt(value: Float): UInt = doubleToUInt(value.toDouble())
@PublishedApi
internal actual fun uintToDouble(value: Int): Double = (value and Int.MAX_VALUE).toDouble() + (value ushr 31 shl 30).toDouble() * 2
@PublishedApi
internal actual fun doubleToUInt(value: Double): UInt = when {
value.isNaN() -> 0u
value <= UInt.MIN_VALUE.toDouble() -> UInt.MIN_VALUE
value >= UInt.MAX_VALUE.toDouble() -> UInt.MAX_VALUE
value <= Int.MAX_VALUE -> value.toInt().toUInt()
else -> (value - 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()
@InlineOnly
internal actual inline fun ulongToFloat(value: Long): Float = ulongToDouble(value).toFloat()
@PublishedApi
@InlineOnly
internal actual inline fun floatToULong(value: Float): ULong = doubleToULong(value.toDouble())
@PublishedApi
internal actual fun ulongToDouble(value: Long): Double = (value ushr 11).toDouble() * 2048 + (value and 2047)
@PublishedApi
internal actual fun doubleToULong(value: Double): ULong = when {
value.isNaN() -> 0u
value <= ULong.MIN_VALUE.toDouble() -> ULong.MIN_VALUE
value >= ULong.MAX_VALUE.toDouble() -> ULong.MAX_VALUE
value < Long.MAX_VALUE -> value.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
else -> (value - 9223372036854775808.0).toLong().toULong() + 9223372036854775808uL // Long.MAX_VALUE + 1 < v < ULong.MAX_VALUE
}
@InlineOnly
internal actual inline fun uintToString(value: Int): String = uintToLong(value).toString()
@PublishedApi
internal fun uintToDouble(v: Int): Double = (v and Int.MAX_VALUE).toDouble() + (v ushr 31 shl 30).toDouble() * 2
@InlineOnly
internal actual inline fun uintToString(value: Int, base: Int): String = ulongToString(uintToLong(value), base)
@PublishedApi
internal fun ulongToDouble(v: Long): Double = (v ushr 11).toDouble() * 2048 + (v and 2047)
@InlineOnly
internal actual inline fun ulongToString(value: Long): String = ulongToString(value, 10)
internal actual fun ulongToString(value: Long, base: Int): String {
if (value >= 0) return value.toString(base)
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
var quotient = ((value ushr 1) / base) shl 1
var rem = value - quotient * base
if (rem >= base) {
rem -= base
quotient += 1
}
return quotient.toString(base) + rem.toString(base)
}
@@ -1,25 +1,24 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2023 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()
import kotlin.internal.InlineOnly
// CHANGES IN THIS FILE SHOULD BE SYNCED WITH THE SAME CHANGES IN: UnsignedJs.kt and kotlin-native/Unsigned.kt
// Division and remainder are based on Guava's UnsignedLongs implementation
// Copyright 2011 The Guava Authors
@PublishedApi
internal fun ulongDivide(v1: ULong, v2: ULong): ULong {
internal actual fun uintRemainder(v1: UInt, v2: UInt): UInt = (v1.toLong() % v2.toLong()).toUInt()
@PublishedApi
internal actual fun uintDivide(v1: UInt, v2: UInt): UInt = (v1.toLong() / v2.toLong()).toUInt()
@PublishedApi
internal actual fun ulongDivide(v1: ULong, v2: ULong): ULong {
val dividend = v1.toLong()
val divisor = v2.toLong()
if (divisor < 0) { // i.e., divisor >= 2^63:
@@ -39,7 +38,7 @@ internal fun ulongDivide(v1: ULong, v2: ULong): ULong {
}
@PublishedApi
internal fun ulongRemainder(v1: ULong, v2: ULong): ULong {
internal actual fun ulongRemainder(v1: ULong, v2: ULong): ULong {
val dividend = v1.toLong()
val divisor = v2.toLong()
if (divisor < 0) { // i.e., divisor >= 2^63:
@@ -62,44 +61,78 @@ internal fun ulongRemainder(v1: ULong, v2: ULong): ULong {
}
@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
internal actual fun uintCompare(v1: Int, v2: Int): Int = (v1 xor Int.MIN_VALUE).compareTo(v2 xor Int.MIN_VALUE)
@PublishedApi
internal actual fun ulongCompare(v1: Long, v2: Long): Int = (v1 xor Long.MIN_VALUE).compareTo(v2 xor Long.MIN_VALUE)
@PublishedApi
@InlineOnly
internal actual inline fun uintToULong(value: Int): ULong = ULong(uintToLong(value))
@PublishedApi
@InlineOnly
internal actual inline fun uintToLong(value: Int): Long = value.toLong() and 0xFFFF_FFFF
@PublishedApi
@InlineOnly
internal actual inline fun uintToFloat(value: Int): Float = uintToDouble(value).toFloat()
@PublishedApi
@InlineOnly
internal actual inline fun floatToUInt(value: Float): UInt = doubleToUInt(value.toDouble())
@PublishedApi
internal actual fun uintToDouble(value: Int): Double = (value and Int.MAX_VALUE).toDouble() + (value ushr 31 shl 30).toDouble() * 2
@PublishedApi
internal actual fun doubleToUInt(value: Double): UInt = when {
value.isNaN() -> 0u
value <= UInt.MIN_VALUE.toDouble() -> UInt.MIN_VALUE
value >= UInt.MAX_VALUE.toDouble() -> UInt.MAX_VALUE
value <= Int.MAX_VALUE -> value.toInt().toUInt()
else -> (value - 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()
@InlineOnly
internal actual inline fun ulongToFloat(value: Long): Float = ulongToDouble(value).toFloat()
@PublishedApi
@InlineOnly
internal actual inline fun floatToULong(value: Float): ULong = doubleToULong(value.toDouble())
@PublishedApi
internal actual fun ulongToDouble(value: Long): Double = (value ushr 11).toDouble() * 2048 + (value and 2047)
@PublishedApi
internal actual fun doubleToULong(value: Double): ULong = when {
value.isNaN() -> 0u
value <= ULong.MIN_VALUE.toDouble() -> ULong.MIN_VALUE
value >= ULong.MAX_VALUE.toDouble() -> ULong.MAX_VALUE
value < Long.MAX_VALUE -> value.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
else -> (value - 9223372036854775808.0).toLong().toULong() + 9223372036854775808uL // Long.MAX_VALUE + 1 < v < ULong.MAX_VALUE
}
@InlineOnly
internal actual inline fun uintToString(value: Int): String = uintToLong(value).toString()
@PublishedApi
internal fun uintToDouble(v: Int): Double = (v and Int.MAX_VALUE).toDouble() + (v ushr 31 shl 30).toDouble() * 2
@InlineOnly
internal actual inline fun uintToString(value: Int, base: Int): String = ulongToString(uintToLong(value), base)
@PublishedApi
internal fun ulongToDouble(v: Long): Double = (v ushr 11).toDouble() * 2048 + (v and 2047)
@InlineOnly
internal actual inline fun ulongToString(value: Long): String = ulongToString(value, 10)
internal actual fun ulongToString(value: Long, base: Int): String {
if (value >= 0) return value.toString(base)
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
var quotient = ((value ushr 1) / base) shl 1
var rem = value - quotient * base
if (rem >= base) {
rem -= base
quotient += 1
}
return quotient.toString(base) + rem.toString(base)
}
@@ -342,14 +342,14 @@ public value class UByte @kotlin.internal.IntrinsicConstEvaluation @PublishedApi
* The resulting `Float` value represents the same numerical value as this `UByte`.
*/
@kotlin.internal.InlineOnly
public inline fun toFloat(): Float = this.toInt().toFloat()
public inline fun toFloat(): Float = uintToFloat(this.toInt())
/**
* Converts this [UByte] value to [Double].
*
* The resulting `Double` value represents the same numerical value as this `UByte`.
*/
@kotlin.internal.InlineOnly
public inline fun toDouble(): Double = this.toInt().toDouble()
public inline fun toDouble(): Double = uintToDouble(this.toInt())
public override fun toString(): String = toInt().toString()
+5 -5
View File
@@ -320,7 +320,7 @@ public value class UInt @kotlin.internal.IntrinsicConstEvaluation @PublishedApi
* whereas the most significant 32 bits are filled with zeros.
*/
@kotlin.internal.InlineOnly
public inline fun toLong(): Long = data.toLong() and 0xFFFF_FFFF
public inline fun toLong(): Long = uintToLong(data)
/**
* Converts this [UInt] value to [UByte].
@@ -354,7 +354,7 @@ public value class UInt @kotlin.internal.IntrinsicConstEvaluation @PublishedApi
* whereas the most significant 32 bits are filled with zeros.
*/
@kotlin.internal.InlineOnly
public inline fun toULong(): ULong = ULong(data.toLong() and 0xFFFF_FFFF)
public inline fun toULong(): ULong = uintToULong(data)
/**
* Converts this [UInt] value to [Float].
@@ -364,7 +364,7 @@ public value class UInt @kotlin.internal.IntrinsicConstEvaluation @PublishedApi
* the one with zero at least significant bit of mantissa is selected.
*/
@kotlin.internal.InlineOnly
public inline fun toFloat(): Float = this.toDouble().toFloat()
public inline fun toFloat(): Float = uintToFloat(data)
/**
* Converts this [UInt] value to [Double].
*
@@ -373,7 +373,7 @@ public value class UInt @kotlin.internal.IntrinsicConstEvaluation @PublishedApi
@kotlin.internal.InlineOnly
public inline fun toDouble(): Double = uintToDouble(data)
public override fun toString(): String = toLong().toString()
public override fun toString(): String = uintToString(data)
}
@@ -434,7 +434,7 @@ public inline fun Long.toUInt(): UInt = UInt(this.toInt())
@SinceKotlin("1.5")
@WasExperimental(ExperimentalUnsignedTypes::class)
@kotlin.internal.InlineOnly
public inline fun Float.toUInt(): UInt = doubleToUInt(this.toDouble())
public inline fun Float.toUInt(): UInt = floatToUInt(this)
/**
* Converts this [Double] value to [UInt].
*
@@ -365,7 +365,7 @@ public value class ULong @kotlin.internal.IntrinsicConstEvaluation @PublishedApi
* the one with zero at least significant bit of mantissa is selected.
*/
@kotlin.internal.InlineOnly
public inline fun toFloat(): Float = this.toDouble().toFloat()
public inline fun toFloat(): Float = ulongToFloat(data)
/**
* Converts this [ULong] value to [Double].
*
@@ -437,7 +437,7 @@ public inline fun Long.toULong(): ULong = ULong(this)
@SinceKotlin("1.5")
@WasExperimental(ExperimentalUnsignedTypes::class)
@kotlin.internal.InlineOnly
public inline fun Float.toULong(): ULong = doubleToULong(this.toDouble())
public inline fun Float.toULong(): ULong = floatToULong(this)
/**
* Converts this [Double] value to [ULong].
*
@@ -343,14 +343,14 @@ public value class UShort @kotlin.internal.IntrinsicConstEvaluation @PublishedAp
* The resulting `Float` value represents the same numerical value as this `UShort`.
*/
@kotlin.internal.InlineOnly
public inline fun toFloat(): Float = this.toInt().toFloat()
public inline fun toFloat(): Float = uintToFloat(this.toInt())
/**
* Converts this [UShort] value to [Double].
*
* The resulting `Double` value represents the same numerical value as this `UShort`.
*/
@kotlin.internal.InlineOnly
public inline fun toDouble(): Double = this.toInt().toDouble()
public inline fun toDouble(): Double = uintToDouble(this.toInt())
public override fun toString(): String = toInt().toString()
@@ -36,7 +36,7 @@ public /*inline*/ fun UShort.toString(radix: Int): String = this.toInt().toStrin
@SinceKotlin("1.5")
@WasExperimental(ExperimentalUnsignedTypes::class)
//@kotlin.internal.InlineOnly
public /*inline*/ fun UInt.toString(radix: Int): String = this.toLong().toString(radix)
public /*inline*/ fun UInt.toString(radix: Int): String = uintToString(this.toInt(), checkRadix(radix))
/**
* Returns a string representation of this [Long] value in the specified [radix].
@@ -1,105 +1,62 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2023 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)
internal expect fun uintRemainder(v1: UInt, v2: UInt): UInt
@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
internal expect fun ulongRemainder(v1: ULong, v2: ULong): ULong
@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)
}
internal expect fun uintDivide(v1: UInt, v2: UInt): UInt
@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)
}
internal expect fun ulongDivide(v1: ULong, v2: ULong): ULong
@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
}
internal expect fun uintCompare(v1: Int, v2: Int): Int
@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
}
internal expect fun ulongCompare(v1: Long, v2: Long): Int
@PublishedApi
internal fun uintToDouble(v: Int): Double = (v and Int.MAX_VALUE).toDouble() + (v ushr 31 shl 30).toDouble() * 2
internal expect fun uintToULong(value: Int): ULong
@PublishedApi
internal fun ulongToDouble(v: Long): Double = (v ushr 11).toDouble() * 2048 + (v and 2047)
internal expect fun uintToLong(value: Int): Long
@PublishedApi
internal expect fun uintToFloat(value: Int): Float
internal fun ulongToString(v: Long): String = ulongToString(v, 10)
@PublishedApi
internal expect fun floatToUInt(value: Float): UInt
internal fun ulongToString(v: Long, base: Int): String {
if (v >= 0) return v.toString(base)
@PublishedApi
internal expect fun uintToDouble(value: Int): Double
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)
}
@PublishedApi
internal expect fun doubleToUInt(value: Double): UInt
@PublishedApi
internal expect fun ulongToFloat(value: Long): Float
@PublishedApi
internal expect fun floatToULong(value: Float): ULong
@PublishedApi
internal expect fun ulongToDouble(value: Long): Double
@PublishedApi
internal expect fun doubleToULong(value: Double): ULong
internal expect fun uintToString(value: Int): String
internal expect fun uintToString(value: Int, base: Int): String
internal expect fun ulongToString(value: Long): String
internal expect fun ulongToString(value: Long, base: Int): String
@@ -1356,7 +1356,7 @@ public class Int private constructor(private val value: Int) : Number(), Compara
@kotlin.internal.IntrinsicConstEvaluation
public override fun toString(): String =
itoa32(this, 10)
itoa32(this)
@kotlin.internal.IntrinsicConstEvaluation
public override fun equals(other: Any?): Boolean =
@@ -1865,7 +1865,7 @@ public class Long private constructor(private val value: Long) : Number(), Compa
@kotlin.internal.IntrinsicConstEvaluation
public override fun toString(): String =
itoa64(this, 10)
itoa64(this)
@kotlin.internal.IntrinsicConstEvaluation
public override fun equals(other: Any?): Boolean =
@@ -4,6 +4,8 @@
*/
package kotlin.wasm.internal
// Based on the AssemblyScript implementation [https://github.com/AssemblyScript/assemblyscript/blob/1e0466ef94fa5cacd0984e4f31a0087de51538a8/std/assembly/util/number.ts]
private enum class CharCodes(val code: Int) {
// PERCENT(0x25),
PLUS(0x2B),
@@ -42,125 +44,115 @@ private fun digitToChar(input: Int): Char {
return (CharCodes._0.code + input).toChar()
}
// Inspired by the AssemblyScript implementation
internal fun itoa32(inputValue: Int, radix: Int): String {
if (radix < 2 || radix > 36)
throw IllegalArgumentException("Radix argument is unreasonable")
if (radix != 10)
TODO("When we need it")
internal fun itoa32(inputValue: Int): String {
if (inputValue == 0) return "0"
// We can't represent abs(Int.MIN_VALUE), so just hardcode it here
if (inputValue == Int.MIN_VALUE) return "-2147483648"
val sign = inputValue ushr 31
assert(sign == 1 || sign == 0)
val absValue = if (sign == 1) -inputValue else inputValue
val isNegative = inputValue < 0
val absValue = if (isNegative) -inputValue else inputValue
val absValueString = utoa32(absValue.toUInt())
val decimals = decimalCount32(absValue) + sign
return if (isNegative) "-$absValueString" else absValueString
}
internal fun utoa32(inputValue: UInt): String {
if (inputValue == 0U) return "0"
val decimals = decimalCount32(inputValue)
val buf = WasmCharArray(decimals)
utoaDecSimple(buf, absValue, decimals)
if (sign == 1)
buf.set(0, CharCodes.MINUS.code.toChar())
utoaDecSimple(buf, inputValue, decimals)
return buf.createString()
}
private fun utoaDecSimple(buffer: WasmCharArray, numInput: Int, offsetInput: Int) {
assert(numInput != 0)
private fun utoaDecSimple(buffer: WasmCharArray, numInput: UInt, offsetInput: Int) {
assert(numInput != 0U)
assert(buffer.len() > 0)
assert(offsetInput > 0 && offsetInput <= buffer.len())
var num = numInput
var offset = offsetInput
do {
val t = num / 10
val r = num % 10
val t = num / 10U
val r = num % 10U
num = t
offset--
buffer.set(offset, digitToChar(r))
} while (num > 0)
buffer.set(offset, digitToChar(r.toInt()))
} while (num > 0U)
}
private fun utoaDecSimple64(buffer: WasmCharArray, numInput: Long, offsetInput: Int) {
assert(numInput != 0L)
private fun utoaDecSimple64(buffer: WasmCharArray, numInput: ULong, offsetInput: Int) {
assert(numInput != 0UL)
assert(buffer.len() > 0)
assert(offsetInput > 0 && offsetInput <= buffer.len())
var num = numInput
var offset = offsetInput
do {
val t = num / 10
val r = (num % 10).toInt()
val t = num / 10U
val r = num % 10U
num = t
offset--
buffer.set(offset, digitToChar(r))
} while (num > 0)
buffer.set(offset, digitToChar(r.toInt()))
} while (num > 0U)
}
private fun Boolean.toInt() = if (this) 1 else 0
private fun Boolean.toLong() = if (this) 1L else 0L
private fun decimalCount32(value: Int): Int {
if (value < 100000) {
if (value < 100) {
return 1 + (value >= 10).toInt()
private fun decimalCount32(value: UInt): Int {
if (value < 100000u) {
if (value < 100u) {
return 1 + (value >= 10u).toInt()
} else {
return 3 + (value >= 10000).toInt() + (value >= 1000).toInt()
return 3 + (value >= 10000u).toInt() + (value >= 1000u).toInt()
}
} else {
if (value < 10000000) {
return 6 + (value >= 1000000).toInt()
if (value < 10000000u) {
return 6 + (value >= 1000000u).toInt()
} else {
return 8 + (value >= 1000000000).toInt() + (value >= 100000000).toInt()
return 8 + (value >= 1000000000u).toInt() + (value >= 100000000u).toInt()
}
}
}
internal fun itoa64(inputValue: Long, radix: Int): String {
internal fun itoa64(inputValue: Long): String {
if (inputValue in Int.MIN_VALUE..Int.MAX_VALUE)
return itoa32(inputValue.toInt(), radix)
return itoa32(inputValue.toInt())
if (radix < 2 || radix > 36)
throw IllegalArgumentException("Radix argument is unreasonable")
val isNegative = inputValue < 0
val absValue = if (isNegative) -inputValue else inputValue
val absValueString = utoa64(absValue.toULong())
if (inputValue == 0L) return "0"
// We can't represent abs(Long.MIN_VALUE), so just hardcode it here
if (inputValue == Long.MIN_VALUE) return "-9223372036854775808"
return if (isNegative) "-$absValueString" else absValueString
}
if (radix != 10) {
TODO("When we need it")
}
internal fun utoa64(inputValue: ULong): String {
if (inputValue <= UInt.MAX_VALUE) return utoa32(inputValue.toUInt())
val sign = (inputValue ushr 63).toInt()
assert(sign == 1 || sign == 0)
val absValue = if (sign == 1) -inputValue else inputValue
val decimals = decimalCount64High(absValue) + sign
val decimals = decimalCount64High(inputValue)
val buf = WasmCharArray(decimals)
utoaDecSimple64(buf, absValue, decimals)
if (sign == 1)
buf.set(0, CharCodes.MINUS.code.toChar())
utoaDecSimple64(buf, inputValue, decimals)
return buf.createString()
}
// Count number of decimals for u64 values
// In our case input value always greater than 2^32-1 so we can skip some parts
private fun decimalCount64High(value: Long): Int {
if (value < 1000000000000000) {
if (value < 1000000000000) {
return 10 + (value >= 100000000000).toInt() + (value >= 10000000000).toInt()
private fun decimalCount64High(value: ULong): Int {
if (value < 1000000000000000UL) {
if (value < 1000000000000UL) {
return 10 + (value >= 100000000000UL).toInt() + (value >= 10000000000UL).toInt()
} else {
return 13 + (value >= 100000000000000).toInt() + (value >= 10000000000000).toInt()
return 13 + (value >= 100000000000000UL).toInt() + (value >= 10000000000000UL).toInt()
}
} else {
if (value < 100000000000000000) {
return 16 + (value >= 10000000000000000).toInt()
if (value < 100000000000000000UL) {
return 16 + (value >= 10000000000000000UL).toInt()
} else {
return 18 + (value >= 1000000000000000000).toInt()
return 18 + (value >= 10000000000000000000UL).toInt() + (value >= 1000000000000000000UL).toInt()
}
}
}
@@ -331,7 +323,7 @@ private fun genDigits(buffer: WasmCharArray, w_frc: Long, mp_frc: Long, mp_exp:
var p1 = (mp_frc ushr one_exp).toInt()
var p2 = mp_frc and mask
var kappa = decimalCount32(p1)
var kappa = decimalCount32(p1.toUInt())
var len = sign
while (kappa > 0) {
@@ -11,6 +11,14 @@ package kotlin.wasm.internal
internal fun wasm_i32_compareTo(x: Int, y: Int): Int =
wasm_i32_ge_s(x, y).toInt() - wasm_i32_le_s(x, y).toInt()
@PublishedApi
internal fun wasm_u32_compareTo(x: Int, y: Int): Int =
wasm_i32_ge_u(x, y).toInt() - wasm_i32_le_u(x, y).toInt()
@PublishedApi
internal fun wasm_i64_compareTo(x: Long, y: Long): Int =
wasm_i64_ge_s(x, y).toInt() - wasm_i64_le_s(x, y).toInt()
@PublishedApi
internal fun wasm_u64_compareTo(x: Long, y: Long): Int =
wasm_i64_ge_u(x, y).toInt() - wasm_i64_le_u(x, y).toInt()
@@ -5,7 +5,6 @@
package kotlin.text
import kotlin.math.abs
import kotlin.wasm.internal.wasm_f32_demote_f64
/**
@@ -103,7 +102,7 @@ public actual fun String.toDoubleOrNull(): Double? {
* @throws IllegalArgumentException when [radix] is not a valid radix for number to string conversion.
*/
@SinceKotlin("1.2")
public actual fun Byte.toString(radix: Int): String = this.toLong().toString(radix)
public actual fun Byte.toString(radix: Int): String = this.toInt().toString(radix)
/**
* Returns a string representation of this [Short] value in the specified [radix].
@@ -111,7 +110,7 @@ public actual fun Byte.toString(radix: Int): String = this.toLong().toString(rad
* @throws IllegalArgumentException when [radix] is not a valid radix for number to string conversion.
*/
@SinceKotlin("1.2")
public actual fun Short.toString(radix: Int): String = this.toLong().toString(radix)
public actual fun Short.toString(radix: Int): String = this.toInt().toString(radix)
/**
* Returns a string representation of this [Int] value in the specified [radix].
@@ -119,7 +118,13 @@ public actual fun Short.toString(radix: Int): String = this.toLong().toString(ra
* @throws IllegalArgumentException when [radix] is not a valid radix for number to string conversion.
*/
@SinceKotlin("1.2")
public actual fun Int.toString(radix: Int): String = toLong().toString(radix)
public actual fun Int.toString(radix: Int): String {
val isNegative = this < 0
val absValue = if (isNegative) -this else this
val absValueString = uintToString(absValue, checkRadix(radix))
return if (isNegative) "-$absValueString" else absValueString
}
/**
* Returns a string representation of this [Long] value in the specified [radix].
@@ -128,28 +133,9 @@ public actual fun Int.toString(radix: Int): String = toLong().toString(radix)
*/
@SinceKotlin("1.2")
public actual fun Long.toString(radix: Int): String {
checkRadix(radix)
fun Long.getChar() = toInt().let { if (it < 10) '0' + it else 'a' + (it - 10) }
if (radix == 10) return toString()
if (this in 0 until radix) return getChar().toString()
val isNegative = this < 0
val buffer = CharArray(Long.SIZE_BITS + 1)
val absValue = if (isNegative) -this else this
val absValueString = ulongToString(absValue, checkRadix(radix))
var currentBufferIndex = buffer.lastIndex
var current: Long = this
while(current != 0L) {
buffer[currentBufferIndex] = abs(current % radix).getChar()
current /= radix
currentBufferIndex--
}
if (isNegative) {
buffer[currentBufferIndex] = '-'
currentBufferIndex--
}
return buffer.concatToString(currentBufferIndex + 1)
}
return if (isNegative) "-$absValueString" else absValueString
}
@@ -1,105 +1,134 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2023 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)
import kotlin.internal.InlineOnly
import kotlin.math.abs
import kotlin.wasm.internal.*
import kotlin.wasm.internal.WasmOp
import kotlin.wasm.internal.implementedAsIntrinsic
import kotlin.wasm.internal.wasm_u32_compareTo
@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
@WasmOp(WasmOp.I32_REM_U)
internal actual fun uintRemainder(v1: UInt, v2: UInt): UInt = implementedAsIntrinsic
@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)
@WasmOp(WasmOp.I32_DIV_U)
internal actual fun uintDivide(v1: UInt, v2: UInt): UInt = implementedAsIntrinsic
@PublishedApi
@WasmOp(WasmOp.I64_REM_U)
internal actual fun ulongRemainder(v1: ULong, v2: ULong): ULong = implementedAsIntrinsic
@PublishedApi
@WasmOp(WasmOp.I64_DIV_U)
internal actual fun ulongDivide(v1: ULong, v2: ULong): ULong = implementedAsIntrinsic
@PublishedApi
@InlineOnly
internal actual inline fun uintCompare(v1: Int, v2: Int): Int = wasm_u32_compareTo(v1, v2)
@PublishedApi
@InlineOnly
internal actual inline fun ulongCompare(v1: Long, v2: Long): Int = wasm_u64_compareTo(v1, v2)
@PublishedApi
@WasmOp(WasmOp.I64_EXTEND_I32_U)
internal actual fun uintToULong(value: Int): ULong = implementedAsIntrinsic
@PublishedApi
@WasmOp(WasmOp.I64_EXTEND_I32_U)
internal actual fun uintToLong(value: Int): Long = implementedAsIntrinsic
@PublishedApi
@WasmOp(WasmOp.F32_CONVERT_I32_U)
internal actual fun uintToFloat(value: Int): Float = implementedAsIntrinsic
@PublishedApi
@WasmOp(WasmOp.I32_TRUNC_SAT_F32_U)
internal actual fun floatToUInt(value: Float): UInt = implementedAsIntrinsic
@PublishedApi
@WasmOp(WasmOp.F64_CONVERT_I32_U)
internal actual fun uintToDouble(value: Int): Double = implementedAsIntrinsic
@PublishedApi
@WasmOp(WasmOp.I32_TRUNC_SAT_F64_U)
internal actual fun doubleToUInt(value: Double): UInt = implementedAsIntrinsic
@PublishedApi
@WasmOp(WasmOp.F32_CONVERT_I64_U)
internal actual fun ulongToFloat(value: Long): Float = implementedAsIntrinsic
@PublishedApi
@WasmOp(WasmOp.I64_TRUNC_SAT_F32_U)
internal actual fun floatToULong(value: Float): ULong = implementedAsIntrinsic
@PublishedApi
@WasmOp(WasmOp.F64_CONVERT_I64_U)
internal actual fun ulongToDouble(value: Long): Double = implementedAsIntrinsic
@PublishedApi
@WasmOp(WasmOp.I64_TRUNC_SAT_F64_U)
internal actual fun doubleToULong(value: Double): ULong = implementedAsIntrinsic
@InlineOnly
internal actual inline fun uintToString(value: Int): String = utoa32(value.toUInt())
internal actual fun uintToString(value: Int, base: Int): String {
var unsignedValue = value.toUInt()
if (base == 10) return unsignedValue.toString()
if (value in 0 until base) return value.getChar().toString()
val buffer = WasmCharArray(UInt.SIZE_BITS)
val ulongRadix = base.toUInt()
var currentBufferIndex = UInt.SIZE_BITS - 1
while (unsignedValue != 0U) {
buffer.set(currentBufferIndex, (unsignedValue % ulongRadix).toInt().getChar())
unsignedValue /= ulongRadix
currentBufferIndex--
}
// Optimization - use signed division if both dividend and divisor < 2^63
if (dividend >= 0) {
return ULong(dividend / divisor)
return buffer.createStringStartingFrom(currentBufferIndex + 1)
}
@InlineOnly
internal actual inline fun ulongToString(value: Long): String = utoa64(value.toULong())
internal actual fun ulongToString(value: Long, base: Int): String {
var unsignedValue = value.toULong()
if (base == 10) return unsignedValue.toString()
if (value in 0 until base) return value.toInt().getChar().toString()
val buffer = WasmCharArray(ULong.SIZE_BITS)
val ulongRadix = base.toULong()
var currentBufferIndex = ULong.SIZE_BITS - 1
while (unsignedValue != 0UL) {
buffer.set(currentBufferIndex, (unsignedValue % ulongRadix).toInt().getChar())
unsignedValue /= ulongRadix
currentBufferIndex--
}
// 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)
return buffer.createStringStartingFrom(currentBufferIndex + 1)
}
@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)
internal fun WasmCharArray.createStringStartingFrom(index: Int): String {
if (index == 0) return createString()
val newLength = this.len() - index
if (newLength == 0) return ""
val newChars = WasmCharArray(newLength)
copyWasmArray(this, newChars, index, 0, newLength)
return newChars.createString()
}
private fun Int.getChar() = if (this < 10) '0' + this else 'a' + (this - 10)