[WASM] Replace String storage type from CharArray to WasmCharArray
This commit is contained in:
@@ -93,7 +93,7 @@ public class Char private constructor(public val value: Char) : Comparable<Char>
|
||||
this.toInt().toDouble()
|
||||
|
||||
override fun toString(): String =
|
||||
String.unsafeFromCharArray(charArrayOf(this))
|
||||
String.unsafeFromCharArray(WasmCharArray(1).also { it.set(0, this) })
|
||||
|
||||
override fun hashCode(): Int =
|
||||
this.toInt().hashCode()
|
||||
|
||||
@@ -12,20 +12,27 @@ import kotlin.math.min
|
||||
* The `String` class represents character strings. All string literals in Kotlin programs, such as `"abc"`, are
|
||||
* implemented as instances of this class.
|
||||
*/
|
||||
public class String private constructor(internal val chars: CharArray) : Comparable<String>, CharSequence {
|
||||
public class String private constructor(internal val chars: WasmCharArray) : Comparable<String>, CharSequence {
|
||||
public companion object {
|
||||
// Note: doesn't copy the array, use with care.
|
||||
internal fun unsafeFromCharArray(chars: CharArray) = String(chars)
|
||||
internal fun unsafeFromCharArray(chars: WasmCharArray) = String(chars)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string obtained by concatenating this string with the string representation of the given [other] object.
|
||||
*/
|
||||
public operator fun plus(other: Any?): String =
|
||||
String(chars + other.toString().chars)
|
||||
public operator fun plus(other: Any?): String {
|
||||
val otherChars = if (other is String) other.chars else other.toString().chars
|
||||
val newCharsLen = chars.len() + otherChars.len()
|
||||
val newChars = WasmCharArray(newCharsLen)
|
||||
newChars.fill(newCharsLen) { i ->
|
||||
if (i < chars.len()) chars.get(i) else otherChars.get(i - chars.len())
|
||||
}
|
||||
return String(newChars)
|
||||
}
|
||||
|
||||
public override val length: Int
|
||||
get() = chars.size
|
||||
get() = chars.len()
|
||||
|
||||
/**
|
||||
* Returns the character of this string at the specified [index].
|
||||
@@ -33,10 +40,20 @@ public class String private constructor(internal val chars: CharArray) : Compara
|
||||
* If the [index] is out of bounds of this string, throws an [IndexOutOfBoundsException] except in Kotlin/JS
|
||||
* where the behavior is unspecified.
|
||||
*/
|
||||
public override fun get(index: Int): Char = chars[index]
|
||||
public override fun get(index: Int): Char {
|
||||
if (index < 0 || index >= chars.len()) throw IndexOutOfBoundsException()
|
||||
return chars.get(index)
|
||||
}
|
||||
|
||||
public override fun subSequence(startIndex: Int, endIndex: Int): CharSequence {
|
||||
return String(chars.sliceArray(startIndex until endIndex))
|
||||
val actualStartIndex = startIndex.coerceAtLeast(0)
|
||||
val actualEndIndex = endIndex.coerceAtMost(chars.len())
|
||||
val newCharsLen = actualEndIndex - actualStartIndex
|
||||
val newChars = WasmCharArray(newCharsLen)
|
||||
newChars.fill(newCharsLen) { i ->
|
||||
chars.get(actualStartIndex + i)
|
||||
}
|
||||
return String(newChars)
|
||||
}
|
||||
|
||||
public override fun compareTo(other: String): Int {
|
||||
@@ -62,12 +79,16 @@ public class String private constructor(internal val chars: CharArray) : Compara
|
||||
public override fun hashCode(): Int {
|
||||
if (_hashCode != 0 || this.isEmpty())
|
||||
return _hashCode
|
||||
|
||||
var hash = 0
|
||||
for (c in chars)
|
||||
hash = 31 * hash + c.toInt()
|
||||
var i = 0
|
||||
while (i < chars.len()) {
|
||||
hash = 31 * hash + chars.get(i).toInt()
|
||||
i++
|
||||
}
|
||||
_hashCode = hash
|
||||
return _hashCode
|
||||
}
|
||||
}
|
||||
|
||||
internal fun stringLiteral(startAddr: Int, length: Int) = String.unsafeFromCharArray(unsafeRawMemoryToCharArray(startAddr, length))
|
||||
internal fun stringLiteral(startAddr: Int, length: Int) = String.unsafeFromCharArray(unsafeRawMemoryToWasmCharArray(startAddr, length))
|
||||
|
||||
@@ -156,7 +156,7 @@ internal fun convertJsStringToKotlinString(x: ExternalInterfaceType): String {
|
||||
val length = stringLength(x)
|
||||
val addr = unsafeGetScratchRawMemory(INT_SIZE_BYTES + length * CHAR_SIZE_BYTES)
|
||||
jsWriteStringIntoMemory(x, addr)
|
||||
return String.unsafeFromCharArray(unsafeRawMemoryToCharArray(addr, length))
|
||||
return String.unsafeFromCharArray(unsafeRawMemoryToWasmCharArray(addr, length))
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ internal fun exportString(src: String?): Int {
|
||||
|
||||
val retAddr = unsafeGetScratchRawMemory(INT_SIZE_BYTES + src.length * CHAR_SIZE_BYTES)
|
||||
wasm_i32_store(retAddr, src.length)
|
||||
unsafeCharArrayToRawMemory(src.chars, retAddr + INT_SIZE_BYTES)
|
||||
unsafeWasmCharArrayToRawMemory(src.chars, retAddr + INT_SIZE_BYTES)
|
||||
return retAddr
|
||||
}
|
||||
|
||||
|
||||
@@ -59,18 +59,18 @@ internal fun itoa32(inputValue: Int, radix: Int): String {
|
||||
val absValue = if (sign == 1) -inputValue else inputValue
|
||||
|
||||
val decimals = decimalCount32(absValue) + sign
|
||||
val buf = CharArray(decimals)
|
||||
val buf = WasmCharArray(decimals)
|
||||
utoaDecSimple(buf, absValue, decimals)
|
||||
if (sign == 1)
|
||||
buf[0] = CharCodes.MINUS.code.toChar()
|
||||
buf.set(0, CharCodes.MINUS.code.toChar())
|
||||
|
||||
return String.unsafeFromCharArray(buf)
|
||||
}
|
||||
|
||||
private fun utoaDecSimple(buffer: CharArray, numInput: Int, offsetInput: Int) {
|
||||
private fun utoaDecSimple(buffer: WasmCharArray, numInput: Int, offsetInput: Int) {
|
||||
assert(numInput != 0)
|
||||
assert(buffer.isNotEmpty())
|
||||
assert(offsetInput > 0 && offsetInput <= buffer.size)
|
||||
assert(buffer.len() > 0)
|
||||
assert(offsetInput > 0 && offsetInput <= buffer.len())
|
||||
|
||||
var num = numInput
|
||||
var offset = offsetInput
|
||||
@@ -79,14 +79,14 @@ private fun utoaDecSimple(buffer: CharArray, numInput: Int, offsetInput: Int) {
|
||||
val r = num % 10
|
||||
num = t
|
||||
offset--
|
||||
buffer[offset] = digitToChar(r)
|
||||
buffer.set(offset, digitToChar(r))
|
||||
} while (num > 0)
|
||||
}
|
||||
|
||||
private fun utoaDecSimple64(buffer: CharArray, numInput: Long, offsetInput: Int) {
|
||||
private fun utoaDecSimple64(buffer: WasmCharArray, numInput: Long, offsetInput: Int) {
|
||||
assert(numInput != 0L)
|
||||
assert(buffer.isNotEmpty())
|
||||
assert(offsetInput > 0 && offsetInput <= buffer.size)
|
||||
assert(buffer.len() > 0)
|
||||
assert(offsetInput > 0 && offsetInput <= buffer.len())
|
||||
|
||||
var num = numInput
|
||||
var offset = offsetInput
|
||||
@@ -95,7 +95,7 @@ private fun utoaDecSimple64(buffer: CharArray, numInput: Long, offsetInput: Int)
|
||||
val r = (num % 10).toInt()
|
||||
num = t
|
||||
offset--
|
||||
buffer[offset] = digitToChar(r)
|
||||
buffer.set(offset, digitToChar(r))
|
||||
} while (num > 0)
|
||||
}
|
||||
|
||||
@@ -139,10 +139,10 @@ internal fun itoa64(inputValue: Long, radix: Int): String {
|
||||
val absValue = if (sign == 1) -inputValue else inputValue
|
||||
|
||||
val decimals = decimalCount64High(absValue) + sign
|
||||
val buf = CharArray(decimals)
|
||||
val buf = WasmCharArray(decimals)
|
||||
utoaDecSimple64(buf, absValue, decimals)
|
||||
if (sign == 1)
|
||||
buf[0] = CharCodes.MINUS.code.toChar()
|
||||
buf.set(0, CharCodes.MINUS.code.toChar())
|
||||
|
||||
return String.unsafeFromCharArray(buf)
|
||||
}
|
||||
@@ -174,22 +174,21 @@ internal fun dtoa(value: Double): String {
|
||||
return if (value < 0) "-Infinity" else "Infinity"
|
||||
}
|
||||
|
||||
val buf = CharArray(MAX_DOUBLE_LENGTH)
|
||||
val buf = WasmCharArray(MAX_DOUBLE_LENGTH)
|
||||
val size = dtoaCore(buf, value)
|
||||
val ret = CharArray(size)
|
||||
val ret = WasmCharArray(size)
|
||||
buf.copyInto(ret, 0, 0, size)
|
||||
return String.unsafeFromCharArray(ret)
|
||||
}
|
||||
|
||||
private fun dtoaCore(buffer: CharArray, valueInp: Double): Int {
|
||||
private fun dtoaCore(buffer: WasmCharArray, valueInp: Double): Int {
|
||||
var value = valueInp
|
||||
|
||||
val sign = (value < 0).toInt()
|
||||
if (sign == 1) {
|
||||
value = -value
|
||||
buffer[0] = CharCodes.MINUS.code.toChar()
|
||||
buffer.set(0, CharCodes.MINUS.code.toChar())
|
||||
}
|
||||
|
||||
var len = grisu2(value, buffer, sign)
|
||||
len = prettify(BufferWithOffset(buffer, sign), len - sign, _K)
|
||||
return len + sign
|
||||
@@ -242,7 +241,7 @@ private val FRC_POWERS = longArrayOf(
|
||||
0x9E19DB92B4E31BA9UL.toLong(), 0xEB96BF6EBADF77D9UL.toLong(), 0xAF87023B9BF0EE6BUL.toLong()
|
||||
)
|
||||
|
||||
private fun grisu2(value: Double, buffer: CharArray, sign: Int): Int {
|
||||
private fun grisu2(value: Double, buffer: WasmCharArray, sign: Int): Int {
|
||||
// frexp routine
|
||||
val uv = value.toBits()
|
||||
var exp = ((uv and 0x7FF0000000000000) ushr 52).toInt()
|
||||
@@ -322,7 +321,7 @@ private fun getCachedPower(minExp: Int) {
|
||||
_exp_pow = EXP_POWERS[index].toInt()
|
||||
}
|
||||
|
||||
private fun genDigits(buffer: CharArray, w_frc: Long, w_exp: Int, mp_frc: Long, mp_exp: Int, deltaInp: Long, sign: Int): Int {
|
||||
private fun genDigits(buffer: WasmCharArray, w_frc: Long, w_exp: Int, mp_frc: Long, mp_exp: Int, deltaInp: Long, sign: Int): Int {
|
||||
var delta = deltaInp
|
||||
val one_exp = -mp_exp
|
||||
val one_frc = 1L shl one_exp
|
||||
@@ -354,7 +353,7 @@ private fun genDigits(buffer: CharArray, w_frc: Long, w_exp: Int, mp_frc: Long,
|
||||
}
|
||||
|
||||
if (d or len != 0)
|
||||
buffer[len++] = digitToChar(d)
|
||||
buffer.set(len++, digitToChar(d))
|
||||
|
||||
--kappa
|
||||
val tmp = (p1.toLong() shl one_exp) + p2
|
||||
@@ -373,7 +372,7 @@ private fun genDigits(buffer: CharArray, w_frc: Long, w_exp: Int, mp_frc: Long,
|
||||
|
||||
val d = p2 ushr one_exp
|
||||
if (d or len.toLong() != 0L)
|
||||
buffer[len++] = digitToChar(d.toInt())
|
||||
buffer.set(len++, digitToChar(d.toInt()))
|
||||
|
||||
p2 = p2 and mask
|
||||
--kappa
|
||||
@@ -385,10 +384,10 @@ private fun genDigits(buffer: CharArray, w_frc: Long, w_exp: Int, mp_frc: Long,
|
||||
}
|
||||
}
|
||||
|
||||
private fun grisuRound(buffer: CharArray, len: Int, delta: Long, restInp: Long, ten_kappa: Long, wp_w: Long) {
|
||||
private fun grisuRound(buffer: WasmCharArray, len: Int, delta: Long, restInp: Long, ten_kappa: Long, wp_w: Long) {
|
||||
var rest = restInp
|
||||
val lastp = len - 1
|
||||
var digit = buffer[lastp]
|
||||
var digit = buffer.get(lastp)
|
||||
while (
|
||||
rest < wp_w &&
|
||||
delta - rest >= ten_kappa && (
|
||||
@@ -399,17 +398,38 @@ private fun grisuRound(buffer: CharArray, len: Int, delta: Long, restInp: Long,
|
||||
--digit
|
||||
rest += ten_kappa;
|
||||
}
|
||||
buffer[lastp] = digit
|
||||
buffer.set(lastp, digit)
|
||||
}
|
||||
|
||||
private class BufferWithOffset(val buf: CharArray, val off: Int) {
|
||||
private fun WasmCharArray.copyInto(destination: WasmCharArray, destinationOffset: Int, sourceOffset: Int, len: Int) {
|
||||
var srcIndex: Int
|
||||
var dstIndex: Int
|
||||
var increment: Int
|
||||
if (destinationOffset <= sourceOffset) {
|
||||
srcIndex = sourceOffset
|
||||
dstIndex = destinationOffset
|
||||
increment = 1
|
||||
} else {
|
||||
srcIndex = sourceOffset + len - 1
|
||||
dstIndex = destinationOffset + len - 1
|
||||
increment = -1
|
||||
}
|
||||
|
||||
repeat(len) {
|
||||
destination.set(dstIndex, this.get(srcIndex))
|
||||
srcIndex += increment
|
||||
dstIndex += increment
|
||||
}
|
||||
}
|
||||
|
||||
private class BufferWithOffset(val buf: WasmCharArray, val off: Int) {
|
||||
operator fun set(addr: Int, value: Char) {
|
||||
buf[off + addr] = value
|
||||
buf.set(off + addr, value)
|
||||
}
|
||||
|
||||
fun memoryCopy(destAddr: Int, srcAddr: Int, len: Int) {
|
||||
val startIdx = off + srcAddr
|
||||
buf.copyInto(buf, off + destAddr, startIdx, startIdx + len)
|
||||
buf.copyInto(buf, off + destAddr, startIdx, len)
|
||||
}
|
||||
|
||||
fun offsetABitMore(anotherOff: Int) = BufferWithOffset(buf, off + anotherOff)
|
||||
|
||||
@@ -10,12 +10,10 @@ internal const val INT_SIZE_BYTES = 4
|
||||
|
||||
internal fun unsafeRawMemoryToChar(addr: Int) = wasm_i32_load16_u(addr).toChar()
|
||||
|
||||
internal fun unsafeRawMemoryToCharArray(startAddr: Int, length: Int): CharArray {
|
||||
val ret = CharArray(length)
|
||||
for (i in 0 until length) {
|
||||
ret[i] = unsafeRawMemoryToChar(startAddr + i * CHAR_SIZE_BYTES)
|
||||
}
|
||||
return ret
|
||||
internal fun unsafeRawMemoryToWasmCharArray(startAddr: Int, length: Int): WasmCharArray {
|
||||
val result = WasmCharArray(length)
|
||||
result.fill(length) { unsafeRawMemoryToChar(startAddr + it * CHAR_SIZE_BYTES) }
|
||||
return result
|
||||
}
|
||||
|
||||
// Returns a pointer into a temporary scratch segment in the raw wasm memory. Aligned by 4.
|
||||
@@ -25,11 +23,13 @@ internal fun unsafeGetScratchRawMemory(sizeBytes: Int): Int =
|
||||
implementedAsIntrinsic
|
||||
|
||||
// Assumes there is enough space at the destination, fails with wasm trap otherwise.
|
||||
internal fun unsafeCharArrayToRawMemory(src: CharArray, dstAddr: Int) {
|
||||
internal fun unsafeWasmCharArrayToRawMemory(src: WasmCharArray, dstAddr: Int) {
|
||||
var curAddr = dstAddr
|
||||
for (i in src) {
|
||||
wasm_i32_store16(curAddr, i)
|
||||
var i = 0
|
||||
while (i < src.len()) {
|
||||
wasm_i32_store16(curAddr, src.get(i))
|
||||
curAddr += CHAR_SIZE_BYTES
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
package kotlin.text
|
||||
|
||||
import kotlin.wasm.internal.*
|
||||
|
||||
internal fun insertString(array: CharArray, distIndex: Int, value: String, sourceIndex: Int, count: Int): Int {
|
||||
var arrayIdx = distIndex
|
||||
var stringIdx = sourceIndex
|
||||
@@ -14,8 +16,11 @@ internal fun insertString(array: CharArray, distIndex: Int, value: String, sourc
|
||||
return count
|
||||
}
|
||||
|
||||
internal fun unsafeStringFromCharArray(array: CharArray, start: Int, size: Int): String =
|
||||
String.unsafeFromCharArray(array.copyOfRange(start, start + size))
|
||||
internal fun unsafeStringFromCharArray(array: CharArray, start: Int, size: Int): String {
|
||||
val copy = WasmCharArray(size)
|
||||
copy.fill(size) { array[it + start] }
|
||||
return String.unsafeFromCharArray(copy)
|
||||
}
|
||||
|
||||
internal fun insertInt(array: CharArray, start: Int, value: Int): Int {
|
||||
val valueString = value.toString()
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
package kotlin.text
|
||||
|
||||
import kotlin.wasm.internal.*
|
||||
|
||||
/**
|
||||
* Returns the index within this string of the first occurrence of the specified character, starting from the specified offset.
|
||||
*/
|
||||
@@ -70,7 +72,10 @@ public actual fun String(chars: CharArray): String =
|
||||
public actual fun String(chars: CharArray, offset: Int, length: Int): String {
|
||||
if (offset < 0 || length < 0 || offset + length > chars.size)
|
||||
throw IndexOutOfBoundsException()
|
||||
return String.unsafeFromCharArray(chars.copyOfRange(offset, offset + length))
|
||||
|
||||
val copy = WasmCharArray(length)
|
||||
copy.fill(length) { chars[it + offset] }
|
||||
return String.unsafeFromCharArray(copy)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,8 +83,11 @@ public actual fun String(chars: CharArray, offset: Int, length: Int): String {
|
||||
*/
|
||||
@SinceKotlin("1.4")
|
||||
@WasExperimental(ExperimentalStdlibApi::class)
|
||||
public actual fun CharArray.concatToString(): String =
|
||||
String.unsafeFromCharArray(this.copyOf())
|
||||
public actual fun CharArray.concatToString(): String {
|
||||
val copy = WasmCharArray(this.size)
|
||||
copy.fill(this.size) { this[it] }
|
||||
return String.unsafeFromCharArray(copy)
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenates characters in this [CharArray] or its subrange into a String.
|
||||
@@ -95,7 +103,11 @@ public actual fun CharArray.concatToString(): String =
|
||||
@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
|
||||
public actual fun CharArray.concatToString(startIndex: Int = 0, endIndex: Int = this.size): String {
|
||||
AbstractList.checkBoundsIndexes(startIndex, endIndex, this.size)
|
||||
return String.unsafeFromCharArray(this.copyOfRange(startIndex, endIndex))
|
||||
|
||||
val length = endIndex - startIndex
|
||||
val copy = WasmCharArray(length)
|
||||
copy.fill(length) { this[it + startIndex] }
|
||||
return String.unsafeFromCharArray(copy)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user