Provide unsigned string to number conversion in arbitrary base
#KT-26161
This commit is contained in:
@@ -151,5 +151,3 @@ internal actual fun digitOf(char: Char, radix: Int): Int = when {
|
||||
char >= 'a' && char <= 'z' -> char - 'a' + 10
|
||||
else -> -1
|
||||
}.let { if (it >= radix) -1 else it }
|
||||
|
||||
private fun numberFormatError(input: String): Nothing = throw NumberFormatException("Invalid number format: '$input'")
|
||||
@@ -178,3 +178,6 @@ public fun String.toLongOrNull(radix: Int): Long? {
|
||||
|
||||
return if (isNegative) result else -result
|
||||
}
|
||||
|
||||
|
||||
internal fun numberFormatError(input: String): Nothing = throw NumberFormatException("Invalid number format: '$input'")
|
||||
@@ -154,6 +154,128 @@ class StringNumberConversionTest {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test fun toUByte() {
|
||||
compareConversion({it.toUByte()}, {it.toUByteOrNull()}) {
|
||||
assertProduces("255", UByte.MAX_VALUE)
|
||||
// assertProduces("+77", 77.toUByte())
|
||||
assertProduces("128", 128u.toUByte())
|
||||
assertFailsOrNull("-1")
|
||||
assertFailsOrNull("256")
|
||||
assertFailsOrNull("")
|
||||
assertFailsOrNull(" ")
|
||||
}
|
||||
|
||||
compareConversionWithRadix(String::toUByte, String::toUByteOrNull) {
|
||||
assertProduces(16, "7a", 0x7a.toUByte())
|
||||
// assertProduces(16, "+7F", 127.toByte())
|
||||
assertProduces(16, "80", 128u.toUByte())
|
||||
assertProduces(16, "Ff", 255u.toUByte())
|
||||
assertFailsOrNull(2, "100000000")
|
||||
assertFailsOrNull(8, "")
|
||||
assertFailsOrNull(8, " ")
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun toUShort() {
|
||||
compareConversion({it.toUShort()}, {it.toUShortOrNull()}) {
|
||||
// assertProduces("+77", 77.toUShort())
|
||||
assertProduces("65535", UShort.MAX_VALUE)
|
||||
assertFailsOrNull("+65536")
|
||||
assertFailsOrNull("-32768")
|
||||
assertFailsOrNull("")
|
||||
assertFailsOrNull(" ")
|
||||
}
|
||||
|
||||
compareConversionWithRadix(String::toUShort, String::toUShortOrNull) {
|
||||
assertProduces(16, "7FFF", 0x7FFF.toUShort())
|
||||
assertProduces(16, "FfFf", UShort.MAX_VALUE)
|
||||
assertFailsOrNull(16, "-8000")
|
||||
assertFailsOrNull(5, "10000000")
|
||||
assertFailsOrNull(2, "")
|
||||
assertFailsOrNull(2, " ")
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun toUInt() {
|
||||
compareConversion({it.toUInt()}, {it.toUIntOrNull()}) {
|
||||
assertProduces("77", 77u)
|
||||
assertProduces("4294967295", UInt.MAX_VALUE)
|
||||
|
||||
assertFailsOrNull("-1")
|
||||
assertFailsOrNull("4294967296")
|
||||
assertFailsOrNull("42949672940")
|
||||
assertFailsOrNull("-2147483649")
|
||||
assertFailsOrNull("239239kotlin")
|
||||
assertFailsOrNull("")
|
||||
assertFailsOrNull(" ")
|
||||
}
|
||||
|
||||
compareConversionWithRadix(String::toUInt, String::toUIntOrNull) {
|
||||
assertProduces(10, "0", 0u)
|
||||
assertProduces(10, "473", 473u)
|
||||
// assertProduces(10, "+42", 42u)
|
||||
assertProduces(10, "2147483647", 2147483647u)
|
||||
|
||||
assertProduces(16, "FF", 255)
|
||||
assertProduces(16, "ffFFff01", 0u - 255u)
|
||||
assertProduces(2, "1100110", 102)
|
||||
assertProduces(27, "Kona", 411787)
|
||||
|
||||
assertFailsOrNull(10, "-0")
|
||||
assertFailsOrNull(10, "42949672940")
|
||||
assertFailsOrNull(8, "99")
|
||||
assertFailsOrNull(10, "Kona")
|
||||
assertFailsOrNull(16, "")
|
||||
assertFailsOrNull(16, " ")
|
||||
}
|
||||
|
||||
assertFailsWith<IllegalArgumentException>("Expected to fail with radix 1") { "1".toUInt(radix = 1) }
|
||||
assertFailsWith<IllegalArgumentException>("Expected to fail with radix 37") { "37".toUIntOrNull(radix = 37) }
|
||||
}
|
||||
|
||||
|
||||
@Test fun toULong() {
|
||||
compareConversion({it.toULong()}, {it.toULongOrNull()}) {
|
||||
assertProduces("77", 77uL)
|
||||
assertProduces("18446744073709551615", ULong.MAX_VALUE)
|
||||
|
||||
assertFailsOrNull("-1")
|
||||
assertFailsOrNull("18446744073709551616")
|
||||
assertFailsOrNull("922337 75809")
|
||||
assertFailsOrNull("92233,75809")
|
||||
assertFailsOrNull("92233`75809")
|
||||
assertFailsOrNull("-922337KOTLIN775809")
|
||||
assertFailsOrNull("")
|
||||
assertFailsOrNull(" ")
|
||||
}
|
||||
|
||||
compareConversionWithRadix(String::toULong, String::toULongOrNull) {
|
||||
assertProduces(10, "0", 0uL)
|
||||
assertProduces(10, "473", 473uL)
|
||||
// assertProduces(10, "+42", 42uL)
|
||||
|
||||
assertProduces(16, "7F11223344556677", 0x7F11223344556677uL)
|
||||
// assertProduces(16, "+7faabbccddeeff00", 0x7faabbccddeeff00uL)
|
||||
assertProduces(16, "8000000000000000", Long.MIN_VALUE.toULong())
|
||||
assertProduces(16, "FFFFffffFFFFffff", ULong.MAX_VALUE)
|
||||
assertProduces(2, "1100110", 102uL)
|
||||
assertProduces(36, "Hazelnut", 1356099454469uL)
|
||||
|
||||
assertFailsOrNull(8, "-7")
|
||||
assertFailsOrNull(8, "99")
|
||||
assertFailsOrNull(10, "Hazelnut")
|
||||
assertFailsOrNull(4, "")
|
||||
assertFailsOrNull(4, " ")
|
||||
}
|
||||
|
||||
assertFailsWith<IllegalArgumentException>("Expected to fail with radix 37") { "37".toULong(radix = 37) }
|
||||
assertFailsWith<IllegalArgumentException>("Expected to fail with radix 1") { "1".toULongOrNull(radix = 1) }
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test fun byteToStringWithRadix() {
|
||||
assertEquals("7a", 0x7a.toByte().toString(16))
|
||||
assertEquals("-80", Byte.MIN_VALUE.toString(radix = 16))
|
||||
|
||||
@@ -46,3 +46,212 @@ public /*inline*/ fun UInt.toString(radix: Int): String = this.toLong().toString
|
||||
public fun ULong.toString(radix: Int): String = ulongToString(this.toLong(), checkRadix(radix))
|
||||
|
||||
|
||||
/**
|
||||
* Parses the string as a signed [UByte] number and returns the result.
|
||||
* @throws NumberFormatException if the string is not a valid representation of a number.
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
@ExperimentalUnsignedTypes
|
||||
public fun String.toUByte(): UByte = toUByteOrNull() ?: numberFormatError(this)
|
||||
|
||||
/**
|
||||
* Parses the string as a signed [UByte] number and returns the result.
|
||||
* @throws NumberFormatException if the string is not a valid representation of a number.
|
||||
* @throws IllegalArgumentException when [radix] is not a valid radix for string to number conversion.
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
@ExperimentalUnsignedTypes
|
||||
public fun String.toUByte(radix: Int): UByte = toUByteOrNull(radix) ?: numberFormatError(this)
|
||||
|
||||
|
||||
/**
|
||||
* Parses the string as a [UShort] number and returns the result.
|
||||
* @throws NumberFormatException if the string is not a valid representation of a number.
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
@ExperimentalUnsignedTypes
|
||||
public fun String.toUShort(): UShort = toUShortOrNull() ?: numberFormatError(this)
|
||||
|
||||
/**
|
||||
* Parses the string as a [UShort] number and returns the result.
|
||||
* @throws NumberFormatException if the string is not a valid representation of a number.
|
||||
* @throws IllegalArgumentException when [radix] is not a valid radix for string to number conversion.
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
@ExperimentalUnsignedTypes
|
||||
public fun String.toUShort(radix: Int): UShort = toUShortOrNull(radix) ?: numberFormatError(this)
|
||||
|
||||
/**
|
||||
* Parses the string as an [UInt] number and returns the result.
|
||||
* @throws NumberFormatException if the string is not a valid representation of a number.
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
@ExperimentalUnsignedTypes
|
||||
public fun String.toUInt(): UInt = toUIntOrNull() ?: numberFormatError(this)
|
||||
|
||||
/**
|
||||
* Parses the string as an [UInt] number and returns the result.
|
||||
* @throws NumberFormatException if the string is not a valid representation of a number.
|
||||
* @throws IllegalArgumentException when [radix] is not a valid radix for string to number conversion.
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
@ExperimentalUnsignedTypes
|
||||
public fun String.toUInt(radix: Int): UInt = toUIntOrNull(radix) ?: numberFormatError(this)
|
||||
|
||||
/**
|
||||
* Parses the string as a [ULong] number and returns the result.
|
||||
* @throws NumberFormatException if the string is not a valid representation of a number.
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
@ExperimentalUnsignedTypes
|
||||
public fun String.toULong(): ULong = toULongOrNull() ?: numberFormatError(this)
|
||||
|
||||
/**
|
||||
* Parses the string as a [ULong] number and returns the result.
|
||||
* @throws NumberFormatException if the string is not a valid representation of a number.
|
||||
* @throws IllegalArgumentException when [radix] is not a valid radix for string to number conversion.
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
@ExperimentalUnsignedTypes
|
||||
public fun String.toULong(radix: Int): ULong = toULongOrNull(radix) ?: numberFormatError(this)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Parses the string as an [UByte] number and returns the result
|
||||
* or `null` if the string is not a valid representation of a number.
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
@ExperimentalUnsignedTypes
|
||||
public fun String.toUByteOrNull(): UByte? = toUByteOrNull(radix = 10)
|
||||
|
||||
/**
|
||||
* Parses the string as an [UByte] number and returns the result
|
||||
* or `null` if the string is not a valid representation of a number.
|
||||
*
|
||||
* @throws IllegalArgumentException when [radix] is not a valid radix for string to number conversion.
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
@ExperimentalUnsignedTypes
|
||||
public fun String.toUByteOrNull(radix: Int): UByte? {
|
||||
val int = this.toUIntOrNull(radix) ?: return null
|
||||
if (int > UByte.MAX_VALUE) return null
|
||||
return int.toUByte()
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the string as an [UShort] number and returns the result
|
||||
* or `null` if the string is not a valid representation of a number.
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
@ExperimentalUnsignedTypes
|
||||
public fun String.toUShortOrNull(): UShort? = toUShortOrNull(radix = 10)
|
||||
|
||||
/**
|
||||
* Parses the string as an [UShort] number and returns the result
|
||||
* or `null` if the string is not a valid representation of a number.
|
||||
*
|
||||
* @throws IllegalArgumentException when [radix] is not a valid radix for string to number conversion.
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
@ExperimentalUnsignedTypes
|
||||
public fun String.toUShortOrNull(radix: Int): UShort? {
|
||||
val int = this.toUIntOrNull(radix) ?: return null
|
||||
if (int > UShort.MAX_VALUE) return null
|
||||
return int.toUShort()
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the string as an [UInt] number and returns the result
|
||||
* or `null` if the string is not a valid representation of a number.
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
@ExperimentalUnsignedTypes
|
||||
public fun String.toUIntOrNull(): UInt? = toUIntOrNull(radix = 10)
|
||||
|
||||
/**
|
||||
* Parses the string as an [UInt] number and returns the result
|
||||
* or `null` if the string is not a valid representation of a number.
|
||||
*
|
||||
* @throws IllegalArgumentException when [radix] is not a valid radix for string to number conversion.
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
@ExperimentalUnsignedTypes
|
||||
public fun String.toUIntOrNull(radix: Int): UInt? {
|
||||
checkRadix(radix)
|
||||
|
||||
val length = this.length
|
||||
if (length == 0) return null
|
||||
|
||||
val limit: UInt = UInt.MAX_VALUE
|
||||
|
||||
val firstChar = this[0]
|
||||
if (firstChar < '0') return null
|
||||
|
||||
val uradix = radix.toUInt()
|
||||
val limitBeforeMul = limit / uradix
|
||||
var result = 0u
|
||||
for (i in 0 until length) {
|
||||
val digit = digitOf(this[i], radix)
|
||||
|
||||
if (digit < 0) return null
|
||||
if (result > limitBeforeMul) return null
|
||||
|
||||
result *= uradix
|
||||
|
||||
if (result > limit - digit.toUInt()) return null
|
||||
|
||||
result += digit.toUInt()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the string as an [ULong] number and returns the result
|
||||
* or `null` if the string is not a valid representation of a number.
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
@ExperimentalUnsignedTypes
|
||||
public fun String.toULongOrNull(): ULong? = toULongOrNull(radix = 10)
|
||||
|
||||
/**
|
||||
* Parses the string as an [ULong] number and returns the result
|
||||
* or `null` if the string is not a valid representation of a number.
|
||||
*
|
||||
* @throws IllegalArgumentException when [radix] is not a valid radix for string to number conversion.
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
@ExperimentalUnsignedTypes
|
||||
public fun String.toULongOrNull(radix: Int): ULong? {
|
||||
checkRadix(radix)
|
||||
|
||||
val length = this.length
|
||||
if (length == 0) return null
|
||||
|
||||
val limit: ULong = ULong.MAX_VALUE
|
||||
|
||||
val firstChar = this[0]
|
||||
if (firstChar < '0') return null
|
||||
|
||||
|
||||
val uradix = radix.toUInt()
|
||||
val limitBeforeMul = limit / uradix
|
||||
var result = 0uL
|
||||
for (i in 0 until length) {
|
||||
val digit = digitOf(this[i], radix)
|
||||
|
||||
if (digit < 0) return null
|
||||
if (result > limitBeforeMul) return null
|
||||
|
||||
result *= uradix
|
||||
|
||||
if (result > limit - digit.toUInt()) return null
|
||||
|
||||
result += digit.toUInt()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
+16
@@ -5064,5 +5064,21 @@ public final class kotlin/text/UStringNumberConversionsKt {
|
||||
public static final fun toString (II)Ljava/lang/String;
|
||||
public static final fun toString (JI)Ljava/lang/String;
|
||||
public static final fun toString (SI)Ljava/lang/String;
|
||||
public static final fun toUByte (Ljava/lang/String;)B
|
||||
public static final fun toUByte (Ljava/lang/String;I)B
|
||||
public static final fun toUByteOrNull (Ljava/lang/String;)Lkotlin/UByte;
|
||||
public static final fun toUByteOrNull (Ljava/lang/String;I)Lkotlin/UByte;
|
||||
public static final fun toUInt (Ljava/lang/String;)I
|
||||
public static final fun toUInt (Ljava/lang/String;I)I
|
||||
public static final fun toUIntOrNull (Ljava/lang/String;)Lkotlin/UInt;
|
||||
public static final fun toUIntOrNull (Ljava/lang/String;I)Lkotlin/UInt;
|
||||
public static final fun toULong (Ljava/lang/String;)J
|
||||
public static final fun toULong (Ljava/lang/String;I)J
|
||||
public static final fun toULongOrNull (Ljava/lang/String;)Lkotlin/ULong;
|
||||
public static final fun toULongOrNull (Ljava/lang/String;I)Lkotlin/ULong;
|
||||
public static final fun toUShort (Ljava/lang/String;)S
|
||||
public static final fun toUShort (Ljava/lang/String;I)S
|
||||
public static final fun toUShortOrNull (Ljava/lang/String;)Lkotlin/UShort;
|
||||
public static final fun toUShortOrNull (Ljava/lang/String;I)Lkotlin/UShort;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user