[WASM] Wasm string.plus optimisations

This commit is contained in:
Igor Yakovlev
2022-07-11 15:49:58 +02:00
committed by teamcity
parent fe187b609f
commit fff6b16483
6 changed files with 65 additions and 35 deletions
@@ -103,7 +103,7 @@ public class Char private constructor(public val value: Char) : Comparable<Char>
this.toInt().toDouble() this.toInt().toDouble()
override fun toString(): String = override fun toString(): String =
String.unsafeFromCharArray(WasmCharArray(1).also { it.set(0, this) }) String(WasmCharArray(1).also { it.set(0, this) })
override fun hashCode(): Int = override fun hashCode(): Int =
this.toInt().hashCode() this.toInt().hashCode()
+55 -25
View File
@@ -12,30 +12,31 @@ import kotlin.math.min
* The `String` class represents character strings. All string literals in Kotlin programs, such as `"abc"`, are * The `String` class represents character strings. All string literals in Kotlin programs, such as `"abc"`, are
* implemented as instances of this class. * implemented as instances of this class.
*/ */
public class String private constructor(internal val chars: WasmCharArray) : Comparable<String>, CharSequence { public class String private constructor(
public companion object { private var leftIfInSum: String?,
// Note: doesn't copy the array, use with care. private var _chars: WasmCharArray,
internal fun unsafeFromCharArray(chars: WasmCharArray) = String(chars) ) : Comparable<String>, CharSequence {
} public companion object {}
internal constructor(chars: WasmCharArray) : this(null, chars) { }
/** /**
* Returns a string obtained by concatenating this string with the string representation of the given [other] object. * Returns a string obtained by concatenating this string with the string representation of the given [other] object.
*/ */
public operator fun plus(other: Any?): String { public operator fun plus(other: Any?): String {
val thisChars = chars val right = if (other is String) other else other.toString()
val otherChars = if (other is String) other.chars else other.toString().chars return String(this, right.chars)
val thisLen = thisChars.len()
val otherLen = otherChars.len()
if (otherLen == 0) return String(thisChars)
val newChars = WasmCharArray(thisLen + otherLen)
copyWasmArray(thisChars, newChars, 0, 0, thisLen)
copyWasmArray(otherChars, newChars, 0, thisLen, otherLen)
return String(newChars)
} }
public override val length: Int public override val length: Int get() {
get() = chars.len() var currentLeftString = leftIfInSum
var currentLength = _chars.len()
while (currentLeftString != null) {
currentLength += currentLeftString._chars.len()
currentLeftString = currentLeftString.leftIfInSum
}
return currentLength
}
/** /**
* Returns the character of this string at the specified [index]. * Returns the character of this string at the specified [index].
@@ -44,8 +45,36 @@ public class String private constructor(internal val chars: WasmCharArray) : Com
* where the behavior is unspecified. * where the behavior is unspecified.
*/ */
public override fun get(index: Int): Char { public override fun get(index: Int): Char {
if (index < 0 || index >= chars.len()) throw IndexOutOfBoundsException() if (index < 0) throw IndexOutOfBoundsException()
return chars.get(index) val folded = chars
val length = folded.len()
if (index >= length) throw IndexOutOfBoundsException()
return folded.get(index)
}
internal fun foldChars() {
val stringLength = this.length
val newArray = WasmCharArray(stringLength)
var currentStartIndex = stringLength
var currentLeftString: String? = this
while (currentLeftString != null) {
val currentLeftStringChars = currentLeftString._chars
val currentLeftStringLen = currentLeftStringChars.len()
currentStartIndex -= currentLeftStringLen
copyWasmArray(currentLeftStringChars, newArray, 0, currentStartIndex, currentLeftStringLen)
currentLeftString = currentLeftString.leftIfInSum
}
check(currentStartIndex == 0)
_chars = newArray
leftIfInSum = null
}
internal inline val chars: WasmCharArray get() {
if (leftIfInSum != null) {
foldChars()
}
return _chars
} }
public override fun subSequence(startIndex: Int, endIndex: Int): CharSequence { public override fun subSequence(startIndex: Int, endIndex: Int): CharSequence {
@@ -84,17 +113,18 @@ public class String private constructor(internal val chars: WasmCharArray) : Com
public override fun toString(): String = this public override fun toString(): String = this
public override fun hashCode(): Int { public override fun hashCode(): Int {
val thisLength = length if (_hashCode != 0) return _hashCode
val thisChars = chars
if (_hashCode != 0 || thisLength == null) val thisLength = thisChars.len()
return _hashCode if (thisLength == 0) return 0
var hash = 0 var hash = 0
var i = 0 var i = 0
while (i < thisLength) { while (i < thisLength) {
hash = 31 * hash + chars.get(i).toInt() hash = 31 * hash + thisChars.get(i).toInt()
i++ i++
} }
_hashCode = hash _hashCode = hash
return _hashCode return _hashCode
} }
@@ -103,5 +133,5 @@ public class String private constructor(internal val chars: WasmCharArray) : Com
internal fun stringLiteral(startAddr: Int, length: Int): String { internal fun stringLiteral(startAddr: Int, length: Int): String {
val array = WasmCharArray(length) val array = WasmCharArray(length)
unsafeRawMemoryToWasmCharArray(startAddr, 0, length, array) unsafeRawMemoryToWasmCharArray(startAddr, 0, length, array)
return String.unsafeFromCharArray(array) return String(array)
} }
@@ -197,7 +197,7 @@ internal fun jsToKotlinStringAdapter(x: ExternalInterfaceType): String {
val stringLength = stringLength(x) val stringLength = stringLength(x)
val dstArray = WasmCharArray(stringLength) val dstArray = WasmCharArray(stringLength)
if (stringLength == 0) { if (stringLength == 0) {
return String.unsafeFromCharArray(dstArray) return String(dstArray)
} }
val maxStringLength = unsafeGetScratchRawMemorySize() / CHAR_SIZE_BYTES val maxStringLength = unsafeGetScratchRawMemorySize() / CHAR_SIZE_BYTES
@@ -212,7 +212,7 @@ internal fun jsToKotlinStringAdapter(x: ExternalInterfaceType): String {
jsExportStringToWasm(x, srcStartIndex, stringLength - srcStartIndex, memBuffer) jsExportStringToWasm(x, srcStartIndex, stringLength - srcStartIndex, memBuffer)
unsafeRawMemoryToWasmCharArray(memBuffer, srcStartIndex, stringLength - srcStartIndex, dstArray) unsafeRawMemoryToWasmCharArray(memBuffer, srcStartIndex, stringLength - srcStartIndex, dstArray)
return String.unsafeFromCharArray(dstArray) return String(dstArray)
} }
@@ -64,7 +64,7 @@ internal fun itoa32(inputValue: Int, radix: Int): String {
if (sign == 1) if (sign == 1)
buf.set(0, CharCodes.MINUS.code.toChar()) buf.set(0, CharCodes.MINUS.code.toChar())
return String.unsafeFromCharArray(buf) return String(buf)
} }
private fun utoaDecSimple(buffer: WasmCharArray, numInput: Int, offsetInput: Int) { private fun utoaDecSimple(buffer: WasmCharArray, numInput: Int, offsetInput: Int) {
@@ -144,7 +144,7 @@ internal fun itoa64(inputValue: Long, radix: Int): String {
if (sign == 1) if (sign == 1)
buf.set(0, CharCodes.MINUS.code.toChar()) buf.set(0, CharCodes.MINUS.code.toChar())
return String.unsafeFromCharArray(buf) return String(buf)
} }
// Count number of decimals for u64 values // Count number of decimals for u64 values
@@ -178,7 +178,7 @@ internal fun dtoa(value: Double): String {
val size = dtoaCore(buf, value) val size = dtoaCore(buf, value)
val ret = WasmCharArray(size) val ret = WasmCharArray(size)
buf.copyInto(ret, 0, 0, size) buf.copyInto(ret, 0, 0, size)
return String.unsafeFromCharArray(ret) return String(ret)
} }
private fun dtoaCore(buffer: WasmCharArray, valueInp: Double): Int { private fun dtoaCore(buffer: WasmCharArray, valueInp: Double): Int {
@@ -15,7 +15,7 @@ internal fun insertString(array: CharArray, destinationIndex: Int, value: String
internal fun unsafeStringFromCharArray(array: CharArray, start: Int, size: Int): String { internal fun unsafeStringFromCharArray(array: CharArray, start: Int, size: Int): String {
val copy = WasmCharArray(size) val copy = WasmCharArray(size)
copyWasmArray(array.storage, copy, start, 0, size) copyWasmArray(array.storage, copy, start, 0, size)
return String.unsafeFromCharArray(copy) return String(copy)
} }
internal fun insertInt(array: CharArray, start: Int, value: Int): Int { internal fun insertInt(array: CharArray, start: Int, value: Int): Int {
@@ -75,7 +75,7 @@ public actual fun String(chars: CharArray, offset: Int, length: Int): String {
val copy = WasmCharArray(length) val copy = WasmCharArray(length)
copyWasmArray(chars.storage, copy, offset, 0, length) copyWasmArray(chars.storage, copy, offset, 0, length)
return String.unsafeFromCharArray(copy) return String(copy)
} }
/** /**
@@ -88,7 +88,7 @@ public actual fun CharArray.concatToString(): String {
val thisLength = thisStorage.len() val thisLength = thisStorage.len()
val copy = WasmCharArray(thisLength) val copy = WasmCharArray(thisLength)
copyWasmArray(this.storage, copy, 0, 0, thisLength) copyWasmArray(this.storage, copy, 0, 0, thisLength)
return String.unsafeFromCharArray(copy) return String(copy)
} }
/** /**
@@ -109,7 +109,7 @@ public actual fun CharArray.concatToString(startIndex: Int = 0, endIndex: Int =
val length = endIndex - startIndex val length = endIndex - startIndex
val copy = WasmCharArray(length) val copy = WasmCharArray(length)
copyWasmArray(this.storage, copy, startIndex, 0, length) copyWasmArray(this.storage, copy, startIndex, 0, length)
return String.unsafeFromCharArray(copy) return String(copy)
} }
/** /**