stdlib: Add insert methods in StringBuilder
This commit is contained in:
@@ -1085,6 +1085,11 @@ task string_builder_reverse(type: RunKonanTest) {
|
|||||||
source = "runtime/text/string_builder_reverse.kt"
|
source = "runtime/text/string_builder_reverse.kt"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task string_builder_insert(type: RunKonanTest) {
|
||||||
|
goldValue = "OK\n"
|
||||||
|
source = "runtime/text/string_builder_insert.kt"
|
||||||
|
}
|
||||||
|
|
||||||
task string0(type: RunKonanTest) {
|
task string0(type: RunKonanTest) {
|
||||||
goldValue = "true\ntrue\nПРИВЕТ\nпривет\nПока\ntrue\n"
|
goldValue = "true\ntrue\nПРИВЕТ\nпривет\nПока\ntrue\n"
|
||||||
source = "runtime/text/string0.kt"
|
source = "runtime/text/string0.kt"
|
||||||
|
|||||||
@@ -0,0 +1,136 @@
|
|||||||
|
fun assertEquals(builder: StringBuilder, content: String) {
|
||||||
|
if (builder.toString() != content) throw AssertionError("Test failed: " + builder.toString() + " != " + content)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun assertTrue(condition: Boolean) {
|
||||||
|
if (!condition) throw AssertionError("The condition is false")
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexOutOfBoundsException
|
||||||
|
fun assertException(body: () -> Unit) {
|
||||||
|
try {
|
||||||
|
body()
|
||||||
|
throw AssertionError ("Test failed: no IndexOutOfBoundsException on wrong indices")
|
||||||
|
} catch (e: IndexOutOfBoundsException) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun String.toCharArray(): CharArray = CharArray(length) { i -> this[i] }
|
||||||
|
|
||||||
|
fun testInsertString(initial: String, index: Int, toInsert: String, expected: String) {
|
||||||
|
assertEquals(StringBuilder(initial).insert(index, toInsert), expected)
|
||||||
|
assertEquals(StringBuilder(initial).insert(index, toInsert.toCharArray()), expected)
|
||||||
|
assertEquals(StringBuilder(initial).insert(index, toInsert as CharSequence), expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testInsertStringException(initial: String, index: Int, toInsert: String) {
|
||||||
|
assertException { StringBuilder(initial).insert(index, toInsert) }
|
||||||
|
assertException { StringBuilder(initial).insert(index, toInsert.toCharArray()) }
|
||||||
|
assertException { StringBuilder(initial).insert(index, toInsert as CharSequence) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testInsertSingle(value: Byte) {
|
||||||
|
assertEquals(StringBuilder("abcd").insert(0, value), value.toString() + "abcd")
|
||||||
|
assertEquals(StringBuilder("abcd").insert(4, value), "abcd" + value.toString())
|
||||||
|
assertEquals(StringBuilder("abcd").insert(2, value), "ab" + value.toString() + "cd")
|
||||||
|
assertEquals(StringBuilder("").insert(0, value), value.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testInsertSingle(value: Short) {
|
||||||
|
assertEquals(StringBuilder("abcd").insert(0, value), value.toString() + "abcd")
|
||||||
|
assertEquals(StringBuilder("abcd").insert(4, value), "abcd" + value.toString())
|
||||||
|
assertEquals(StringBuilder("abcd").insert(2, value), "ab" + value.toString() + "cd")
|
||||||
|
assertEquals(StringBuilder("").insert(0, value), value.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testInsertSingle(value: Int) {
|
||||||
|
assertEquals(StringBuilder("abcd").insert(0, value), value.toString() + "abcd")
|
||||||
|
assertEquals(StringBuilder("abcd").insert(4, value), "abcd" + value.toString())
|
||||||
|
assertEquals(StringBuilder("abcd").insert(2, value), "ab" + value.toString() + "cd")
|
||||||
|
assertEquals(StringBuilder("").insert(0, value), value.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testInsertSingle(value: Long) {
|
||||||
|
assertEquals(StringBuilder("abcd").insert(0, value), value.toString() + "abcd")
|
||||||
|
assertEquals(StringBuilder("abcd").insert(4, value), "abcd" + value.toString())
|
||||||
|
assertEquals(StringBuilder("abcd").insert(2, value), "ab" + value.toString() + "cd")
|
||||||
|
assertEquals(StringBuilder("").insert(0, value), value.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testInsertSingle(value: Float) {
|
||||||
|
assertEquals(StringBuilder("abcd").insert(0, value), value.toString() + "abcd")
|
||||||
|
assertEquals(StringBuilder("abcd").insert(4, value), "abcd" + value.toString())
|
||||||
|
assertEquals(StringBuilder("abcd").insert(2, value), "ab" + value.toString() + "cd")
|
||||||
|
assertEquals(StringBuilder("").insert(0, value), value.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testInsertSingle(value: Double) {
|
||||||
|
assertEquals(StringBuilder("abcd").insert(0, value), value.toString() + "abcd")
|
||||||
|
assertEquals(StringBuilder("abcd").insert(4, value), "abcd" + value.toString())
|
||||||
|
assertEquals(StringBuilder("abcd").insert(2, value), "ab" + value.toString() + "cd")
|
||||||
|
assertEquals(StringBuilder("").insert(0, value), value.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testInsertSingle(value: Any?) {
|
||||||
|
assertEquals(StringBuilder("abcd").insert(0, value), value.toString() + "abcd")
|
||||||
|
assertEquals(StringBuilder("abcd").insert(4, value), "abcd" + value.toString())
|
||||||
|
assertEquals(StringBuilder("abcd").insert(2, value), "ab" + value.toString() + "cd")
|
||||||
|
assertEquals(StringBuilder("").insert(0, value), value.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testInsertSingle(value: Char) {
|
||||||
|
assertEquals(StringBuilder("abcd").insert(0, value), value.toString() + "abcd")
|
||||||
|
assertEquals(StringBuilder("abcd").insert(4, value), "abcd" + value.toString())
|
||||||
|
assertEquals(StringBuilder("abcd").insert(2, value), "ab" + value.toString() + "cd")
|
||||||
|
assertEquals(StringBuilder("").insert(0, value), value.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
// String/CharSequence/CharArray
|
||||||
|
testInsertString("abcd", 0, "12", "12abcd")
|
||||||
|
testInsertString("abcd", 4, "12", "abcd12")
|
||||||
|
testInsertString("abcd", 2, "12", "ab12cd")
|
||||||
|
testInsertString("", 0, "12", "12")
|
||||||
|
testInsertStringException("a", -1, "1")
|
||||||
|
testInsertStringException("a", 2, "1")
|
||||||
|
|
||||||
|
// Subsequence of CharSequence
|
||||||
|
// Insert in the beginning
|
||||||
|
assertEquals(StringBuilder("abcd").insert(0, "1234", 0, 0), "abcd") // 0 symbols
|
||||||
|
assertEquals(StringBuilder("abcd").insert(0, "1234", 0, 1), "1abcd") // 1 symbol
|
||||||
|
assertEquals(StringBuilder("abcd").insert(0, "1234", 1, 3), "23abcd") // 2 symbols
|
||||||
|
|
||||||
|
// Insert in the end
|
||||||
|
assertEquals(StringBuilder("abcd").insert(4, "1234", 0, 0), "abcd")
|
||||||
|
assertEquals(StringBuilder("abcd").insert(4, "1234", 0, 1), "abcd1")
|
||||||
|
assertEquals(StringBuilder("abcd").insert(4, "1234", 1, 3), "abcd23")
|
||||||
|
|
||||||
|
// Insert in the middle
|
||||||
|
assertEquals(StringBuilder("abcd").insert(2, "1234", 0, 0), "abcd")
|
||||||
|
assertEquals(StringBuilder("abcd").insert(2, "1234", 0, 1), "ab1cd")
|
||||||
|
assertEquals(StringBuilder("abcd").insert(2, "1234", 1, 3), "ab23cd")
|
||||||
|
|
||||||
|
// Incorrect indices
|
||||||
|
assertException { StringBuilder("a").insert(-1, "1", 0, 0) }
|
||||||
|
assertException { StringBuilder("a").insert(2, "1", 0, 0) }
|
||||||
|
assertException { StringBuilder("a").insert(1, "1", -1, 0) }
|
||||||
|
assertException { StringBuilder("a").insert(1, "1", 0, 2) }
|
||||||
|
assertException { StringBuilder("a").insert(1, "123", 2, 0) }
|
||||||
|
|
||||||
|
|
||||||
|
// Other types
|
||||||
|
testInsertSingle(true)
|
||||||
|
testInsertSingle(42.toByte())
|
||||||
|
testInsertSingle(42.toShort())
|
||||||
|
testInsertSingle(42.toInt())
|
||||||
|
testInsertSingle(42.toLong())
|
||||||
|
testInsertSingle(42.2.toFloat())
|
||||||
|
testInsertSingle(42.2.toDouble())
|
||||||
|
testInsertSingle(object {
|
||||||
|
override fun toString(): String {
|
||||||
|
return "Object"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
testInsertSingle('a')
|
||||||
|
|
||||||
|
println("OK")
|
||||||
|
}
|
||||||
@@ -29,6 +29,29 @@ external fun fromCharArray(array: CharArray, start: Int, size: Int) : String
|
|||||||
@SymbolName("Kotlin_String_toCharArray")
|
@SymbolName("Kotlin_String_toCharArray")
|
||||||
external fun toCharArray(string: String) : CharArray
|
external fun toCharArray(string: String) : CharArray
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds new string by populating newly created [StringBuilder] using provided [builderAction]
|
||||||
|
* and then converting it to [String].
|
||||||
|
*/
|
||||||
|
@kotlin.internal.InlineOnly
|
||||||
|
public inline fun buildString(builderAction: StringBuilder.() -> Unit): String =
|
||||||
|
StringBuilder().apply(builderAction).toString()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds new string by populating newly created [StringBuilder] initialized with the given [capacity]
|
||||||
|
* using provided [builderAction] and then converting it to [String].
|
||||||
|
*/
|
||||||
|
@SinceKotlin("1.1")
|
||||||
|
@kotlin.internal.InlineOnly
|
||||||
|
public inline fun buildString(capacity: Int, builderAction: StringBuilder.() -> Unit): String =
|
||||||
|
StringBuilder(capacity).apply(builderAction).toString()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the character at the specified [index] to the specified [value].
|
||||||
|
*/
|
||||||
|
@kotlin.internal.InlineOnly
|
||||||
|
public inline operator fun StringBuilder.set(index: Int, value: Char): Unit = this.setCharAt(index, value)
|
||||||
|
|
||||||
class StringBuilder private constructor (
|
class StringBuilder private constructor (
|
||||||
private var array: CharArray
|
private var array: CharArray
|
||||||
) : CharSequence, Appendable {
|
) : CharSequence, Appendable {
|
||||||
@@ -110,6 +133,79 @@ class StringBuilder private constructor (
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun insert(index: Int, c: Char): StringBuilder {
|
||||||
|
checkInsertIndex(index)
|
||||||
|
ensureExtraCapacity(1)
|
||||||
|
val newLastIndex = lastIndex + 1
|
||||||
|
for (i in newLastIndex downTo index + 1) {
|
||||||
|
array[i] = array[i - 1]
|
||||||
|
}
|
||||||
|
array[index] = c
|
||||||
|
length++
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun insert(index: Int, csq: CharSequence?): StringBuilder {
|
||||||
|
// TODO: how to treat nulls properly?
|
||||||
|
if (csq == null) return this
|
||||||
|
return insert(index, csq, 0, csq.length)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun insert(index: Int, csq: CharSequence?, start: Int, end: Int): StringBuilder {
|
||||||
|
// TODO: how to treat nulls properly?
|
||||||
|
if (csq == null) return this
|
||||||
|
if (start < 0 || end < start || start > csq.length) throw IndexOutOfBoundsException()
|
||||||
|
checkInsertIndex(index)
|
||||||
|
|
||||||
|
val extraLength = end - start
|
||||||
|
ensureExtraCapacity(extraLength)
|
||||||
|
var from = lastIndex
|
||||||
|
var to = length + extraLength - 1
|
||||||
|
while (from >= index) {
|
||||||
|
array[to--] = array[from--]
|
||||||
|
}
|
||||||
|
|
||||||
|
from = start
|
||||||
|
to = index
|
||||||
|
while (from < end) {
|
||||||
|
array[to++] = csq[from++]
|
||||||
|
}
|
||||||
|
|
||||||
|
length += extraLength
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun insert(index: Int, chars: CharArray): StringBuilder {
|
||||||
|
checkInsertIndex(index)
|
||||||
|
ensureExtraCapacity(chars.size)
|
||||||
|
|
||||||
|
var from = lastIndex
|
||||||
|
var to = length + chars.size - 1
|
||||||
|
while (from >= index) {
|
||||||
|
array[to--] = array[from--]
|
||||||
|
}
|
||||||
|
|
||||||
|
var i = index
|
||||||
|
for (c in chars) {
|
||||||
|
array[i++] = c
|
||||||
|
}
|
||||||
|
|
||||||
|
length += chars.size
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun insert(index: Int, string: String): StringBuilder = insert(index, toCharArray(string))
|
||||||
|
|
||||||
|
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())
|
||||||
|
fun insert(index: Int, value: Int) = insert(index, value.toString())
|
||||||
|
fun insert(index: Int, value: Long) = insert(index, value.toString())
|
||||||
|
fun insert(index: Int, value: Float) = insert(index, value.toString())
|
||||||
|
fun insert(index: Int, value: Double) = insert(index, value.toString())
|
||||||
|
fun insert(index: Int, value: Any?) = insert(index, value.toString())
|
||||||
|
|
||||||
|
|
||||||
// Of Appenable.
|
// Of Appenable.
|
||||||
override fun append(c: Char) : StringBuilder {
|
override fun append(c: Char) : StringBuilder {
|
||||||
ensureExtraCapacity(1)
|
ensureExtraCapacity(1)
|
||||||
@@ -126,6 +222,7 @@ class StringBuilder private constructor (
|
|||||||
override fun append(csq: CharSequence?, start: Int, end: Int): StringBuilder {
|
override fun append(csq: CharSequence?, start: Int, end: Int): StringBuilder {
|
||||||
// TODO: how to treat nulls properly?
|
// TODO: how to treat nulls properly?
|
||||||
if (csq == null) return this
|
if (csq == null) return this
|
||||||
|
if (start < 0 || end < start || start > csq.length) throw IndexOutOfBoundsException()
|
||||||
ensureExtraCapacity(end - start)
|
ensureExtraCapacity(end - start)
|
||||||
var index = start
|
var index = start
|
||||||
while (index < end)
|
while (index < end)
|
||||||
@@ -162,6 +259,11 @@ class StringBuilder private constructor (
|
|||||||
--length
|
--length
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setCharAt(index: Int, value: Char) {
|
||||||
|
checkIndex(index)
|
||||||
|
array[index] = value
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------- private ----------------------------
|
// ---------------------------- private ----------------------------
|
||||||
|
|
||||||
private fun ensureExtraCapacity(n: Int) {
|
private fun ensureExtraCapacity(n: Int) {
|
||||||
|
|||||||
Reference in New Issue
Block a user