From dff4ce98e928fe36a1e6db75b133f662b0842296 Mon Sep 17 00:00:00 2001 From: Nikolay Igotti Date: Tue, 15 May 2018 16:00:36 +0300 Subject: [PATCH] Optimize important case of string appending to the builder. (#1591) --- runtime/src/main/cpp/KString.cpp | 24 ++++++++++++++++ .../main/kotlin/kotlin/text/StringBuilder.kt | 28 +++++++++++++++++-- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/runtime/src/main/cpp/KString.cpp b/runtime/src/main/cpp/KString.cpp index e00e690798a..29222616e2d 100644 --- a/runtime/src/main/cpp/KString.cpp +++ b/runtime/src/main/cpp/KString.cpp @@ -840,6 +840,30 @@ OBJ_GETTER(Kotlin_String_plusImpl, KString thiz, KString other) { RETURN_OBJ(result->obj()); } +KInt Kotlin_StringBuilder_insertString(KRef builder, KInt position, KString fromString) { + auto toArray = builder->array(); + RuntimeAssert(toArray->count_ >= fromString->count_ + position, "must be true"); + memcpy(CharArrayAddressOfElementAt(toArray, position), + CharArrayAddressOfElementAt(fromString, 0), + fromString->count_ * sizeof(KChar)); + return fromString->count_; +} + +KInt Kotlin_StringBuilder_insertInt(KRef builder, KInt position, KInt value) { + auto toArray = builder->array(); + RuntimeAssert(toArray->count_ >= 11 + position, "must be true"); + char cstring[12]; + auto length = konan::snprintf(cstring, sizeof(cstring), "%d", value); + auto* from = &cstring[0]; + auto* to = CharArrayAddressOfElementAt(toArray, position); + auto* end = from + length; + while (from != end) { + *to++ = *from++; + } + return length; +} + + KBoolean Kotlin_String_equals(KString thiz, KConstRef other) { if (other == nullptr || other->type_info() != theStringTypeInfo) return false; // Important, due to literal internalization. diff --git a/runtime/src/main/kotlin/kotlin/text/StringBuilder.kt b/runtime/src/main/kotlin/kotlin/text/StringBuilder.kt index e3741496e06..ccb1d623fbe 100644 --- a/runtime/src/main/kotlin/kotlin/text/StringBuilder.kt +++ b/runtime/src/main/kotlin/kotlin/text/StringBuilder.kt @@ -65,6 +65,12 @@ private external fun String.toUtf8OrThrowImpl(start: Int, size: Int) : ByteArray @SymbolName("Kotlin_String_fromCharArray") external fun fromCharArray(array: CharArray, start: Int, size: Int) : String +@SymbolName("Kotlin_StringBuilder_insertString") +private external fun insertString(array: CharArray, start: Int, value: String): Int + +@SymbolName("Kotlin_StringBuilder_insertInt") +private external fun insertInt(array: CharArray, start: Int, value: Int): Int + /** * Builds new string by populating newly created [StringBuilder] using provided [builderAction] * and then converting it to [String]. @@ -253,8 +259,15 @@ class StringBuilder private constructor ( return this } - fun insert(index: Int, string: String): StringBuilder = insert(index, string.toCharArray()) + fun insert(index: Int, string: String): StringBuilder { + checkInsertIndex(index) + ensureExtraCapacity(string.length) + array.copyRangeTo(array, index, length, index + string.length) + length += insertString(array, index, string) + return this + } + // TODO: optimize those! fun insert(index: Int, value: Boolean) = insert(index, value.toString()) fun insert(index: Int, value: Byte) = insert(index, value.toString()) fun insert(index: Int, value: Short) = insert(index, value.toString()) @@ -296,12 +309,21 @@ class StringBuilder private constructor ( return this } - fun append(it: String): StringBuilder = append(it.toCharArray()) + fun append(it: String): StringBuilder { + ensureExtraCapacity(it.length) + length += insertString(array, length, it) + return this + } + // TODO: optimize those! fun append(it: Boolean) = append(it.toString()) fun append(it: Byte) = append(it.toString()) fun append(it: Short) = append(it.toString()) - fun append(it: Int) = append(it.toString()) + fun append(it: Int): StringBuilder { + ensureExtraCapacity(11) + length += insertInt(array, length, it) + return this + } fun append(it: Long) = append(it.toString()) fun append(it: Float) = append(it.toString()) fun append(it: Double) = append(it.toString())