From 89270c8108c5e16736de6008caf39ae348be4cc5 Mon Sep 17 00:00:00 2001 From: Ilya Matveev Date: Thu, 20 Apr 2017 15:57:32 +0700 Subject: [PATCH] stdlib: Implement toString with radix in accordance with Kotlin JVM Old Int.toString(radix: Int) and Long.toString(radix: Long) implementations didn't add sign for negative values. E.g. -1.toString(16) == "0xff..ff" while JVM returns "-1" in such case. This patch fixes this behaviour and allow us to use any radix between 2 and 36 as JVM does. --- backend.native/tests/build.gradle | 5 ++ .../tests/runtime/text/to_string0.kt | 41 +++++++++ runtime/src/main/cpp/ToString.cpp | 89 +++++++++++++------ 3 files changed, 109 insertions(+), 26 deletions(-) create mode 100644 backend.native/tests/runtime/text/to_string0.kt diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index a1277755093..f994a253d1c 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -1098,6 +1098,11 @@ task parse0(type: RunKonanTest) { source = "runtime/text/parse0.kt" } +task to_string0(type: RunKonanTest) { + goldValue = "OK\n" + source = "runtime/text/to_string0.kt" +} + task catch1(type: RunKonanTest) { goldValue = "Before\nCaught Throwable\nDone\n" source = "runtime/exceptions/catch1.kt" diff --git a/backend.native/tests/runtime/text/to_string0.kt b/backend.native/tests/runtime/text/to_string0.kt new file mode 100644 index 00000000000..0490f6a7ae0 --- /dev/null +++ b/backend.native/tests/runtime/text/to_string0.kt @@ -0,0 +1,41 @@ +// Based on Apache Harmony tests. + +fun assertEquals(actual: String, expected: String, msg: String) { + if (actual != expected) throw AssertionError("$msg. Actual: $actual. Expected: $expected") +} + +fun testIntToStringWithRadix() { + assertEquals(2147483647.toString(8), "17777777777", "Octal string") + assertEquals(2147483647.toString(16), "7fffffff", "Hex string") + assertEquals(2147483647.toString(2), "1111111111111111111111111111111", "Binary string") + assertEquals(2147483647.toString(10), "2147483647", "Decimal string") + + assertEquals((-2147483647).toString(8), "-17777777777", "Octal string") + assertEquals((-2147483647).toString(16), "-7fffffff", "Hex string") + assertEquals((-2147483647).toString(2), "-1111111111111111111111111111111", "Binary string") + assertEquals((-2147483647).toString(10), "-2147483647", "Decimal string") + + assertEquals((-2147483648).toString(8), "-20000000000", "Octal string") + assertEquals((-2147483648).toString(16), "-80000000", "Hex string") + assertEquals((-2147483648).toString(2), "-10000000000000000000000000000000", "Binary string") + assertEquals((-2147483648).toString(10), "-2147483648", "Decimal string") +} + +fun testLongToStringWithRadix() { + assertEquals(100000000L.toString(10), "100000000", "Decimal string") + assertEquals(68719476735L.toString(16), "fffffffff", "Hex string") + assertEquals(8589934591L.toString(8), "77777777777", "Octal string") + assertEquals(8796093022207L.toString(2), "1111111111111111111111111111111111111111111", "Binary string") + + assertEquals((-0x7fffffffffffffffL - 1).toString(10), "-9223372036854775808", "Min decimal string") + assertEquals(0x7fffffffffffffffL.toString(10), "9223372036854775807", "Max decimal string") + assertEquals((-0x7fffffffffffffffL - 1).toString(16), "-8000000000000000", "Min hex string") + assertEquals(0x7fffffffffffffffL.toString(16), "7fffffffffffffff", "Max hex string") + +} + +fun main(args: Array) { + testIntToStringWithRadix() + testLongToStringWithRadix() + println("OK") +} \ No newline at end of file diff --git a/runtime/src/main/cpp/ToString.cpp b/runtime/src/main/cpp/ToString.cpp index 013e6e5bd0f..b0e7ec9ba9c 100644 --- a/runtime/src/main/cpp/ToString.cpp +++ b/runtime/src/main/cpp/ToString.cpp @@ -24,6 +24,17 @@ #include "KString.h" #include "Types.h" +constexpr int MIN_RADIX = 2; +constexpr int MAX_RADIX = 36; + +char int_to_digit(uint32_t value) { + if (value < 10) { + return '0' + value; + } else { + return 'a' + (value - 10); + } +} + extern "C" { OBJ_GETTER(Kotlin_Any_toString, KConstRef thiz) { @@ -60,21 +71,34 @@ OBJ_GETTER(Kotlin_Int_toString, KInt value) { } OBJ_GETTER(Kotlin_Int_toStringRadix, KInt value, KInt radix) { + if (value == 0) { + RETURN_RESULT_OF(CreateStringFromCString, "0"); + } + if (radix < MIN_RADIX || radix > MAX_RADIX) { + radix = 10; + } // TODO: maye not fit for smaller radices. char cstring[32]; - switch (radix) { - case 8: - snprintf(cstring, sizeof(cstring), "%o", value); - break; - case 10: - snprintf(cstring, sizeof(cstring), "%d", value); - break; - case 16: - snprintf(cstring, sizeof(cstring), "%x", value); - break; - default: - RuntimeAssert(false, "Unsupported radix"); + bool negative = (value < 0); + // Calculate in negative values because MIN_VALUE * -1 == MIN_VALUE + if (!negative) { + value = -value; } + + int32_t length = 0; + while (value < 0) { + cstring[length++] = int_to_digit(-(value % radix)); + value /= radix; + } + if (negative) { + cstring[length++] = '-'; + } + for (int i = 0, j = length - 1; i < j; i++, j--) { + char tmp = cstring[i]; + cstring[i] = cstring[j]; + cstring[j] = tmp; + } + cstring[length] = '\0'; RETURN_RESULT_OF(CreateStringFromCString, cstring); } @@ -85,21 +109,34 @@ OBJ_GETTER(Kotlin_Long_toString, KLong value) { } OBJ_GETTER(Kotlin_Long_toStringRadix, KLong value, KInt radix) { - // TODO: may not fit for smaller radices. - char cstring[64]; - switch (radix) { - case 8: - snprintf(cstring, sizeof(cstring), "%llo", value); - break; - case 10: - snprintf(cstring, sizeof(cstring), "%lld", value); - break; - case 16: - snprintf(cstring, sizeof(cstring), "%llx", value); - break; - default: - RuntimeAssert(false, "Unsupported radix"); + if (value == 0) { + RETURN_RESULT_OF(CreateStringFromCString, "0"); } + if (radix < MIN_RADIX || radix > MAX_RADIX) { + radix = 10; + } + // TODO: maye not fit for smaller radices. + char cstring[64]; + bool negative = (value < 0); + // Calculate in negative values because MIN_VALUE * -1 == MIN_VALUE + if (!negative) { + value = -value; + } + + int32_t length = 0; + while (value < 0) { + cstring[length++] = int_to_digit(-(value % radix)); + value /= radix; + } + if (negative) { + cstring[length++] = '-'; + } + for (int i = 0, j = length - 1; i < j; i++, j--) { + char tmp = cstring[i]; + cstring[i] = cstring[j]; + cstring[j] = tmp; + } + cstring[length] = '\0'; RETURN_RESULT_OF(CreateStringFromCString, cstring); }