Common StringBuilder

(cherry picked from commit e465b75f252c4423b5c253060986e0646a7c6609)
This commit is contained in:
Abduqodiri Qurbonzoda
2019-11-18 22:25:59 +03:00
committed by Vasily Levchenko
parent ecd63a8dd9
commit 4d560d42f0
4 changed files with 782 additions and 139 deletions
@@ -35,7 +35,8 @@ fun assertException(body: () -> Unit) {
try {
body()
throw AssertionError ("Test failed: no IndexOutOfBoundsException on wrong indices")
} catch (e: IndexOutOfBoundsException) {}
} catch (e: IndexOutOfBoundsException) {
} catch (e: IllegalArgumentException) {}
}
// Insert ===================================================================================================
@@ -5,8 +5,33 @@
package kotlin.text
/**
* An object to which char sequences and values can be appended.
*/
public actual interface Appendable {
/**
* Appends the specified character [c] to this Appendable and returns this instance.
*
* @param c the character to append.
*/
actual fun append(c: Char): Appendable
/**
* Appends the specified character sequence [csq] to this Appendable and returns this instance.
*
* @param csq the character sequence to append. If [csq] is `null`, then the four characters `"null"` are appended to this Appendable.
*/
actual fun append(csq: CharSequence?): Appendable
/**
* Appends a subsequence of the specified character sequence [csq] to this Appendable and returns this instance.
*
* @param csq the character sequence from which a subsequence is appended. If [csq] is `null`,
* then characters are appended as if [csq] contained the four characters `"null"`.
* @param start the beginning (inclusive) of the subsequence to append.
* @param end the end (exclusive) of the subsequence to append.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [start] or [end] is out of range of the [csq] character sequence indices or when `start > end`.
*/
actual fun append(csq: CharSequence?, start: Int, end: Int): Appendable
}
@@ -6,34 +6,30 @@
package kotlin.text
/**
* Clears the content of this string builder making it empty.
* A mutable sequence of characters.
*
* @sample samples.text.Strings.clearStringBuilder
* String builder can be used to efficiently perform multiple string manipulation operations.
*/
@SinceKotlin("1.3")
public actual fun StringBuilder.clear(): StringBuilder = apply { setLength(0) }
/**
* 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)
actual class StringBuilder private constructor (
private var array: CharArray) : CharSequence, Appendable {
/** Constructs an empty string builder. */
actual constructor() : this(10)
/** Constructs an empty string builder with the specified initial [capacity]. */
actual constructor(capacity: Int) : this(CharArray(capacity))
constructor(string: String) : this(string.toCharArray()) {
/** Constructs a string builder that contains the same characters as the specified [content] string. */
actual constructor(content: String) : this(content.toCharArray()) {
_length = array.size
}
/** Constructs a string builder that contains the same characters as the specified [content] char sequence. */
actual constructor(content: CharSequence): this(content.length) {
append(content)
}
// Of CharSequence.
private var _length: Int = 0
set(capacity) {
ensureCapacity(capacity)
@@ -47,34 +43,33 @@ actual class StringBuilder private constructor (
return array[index]
}
fun setLength(l: Int) {
_length = l
}
actual override fun subSequence(startIndex: Int, endIndex: Int): CharSequence = substring(startIndex, endIndex)
override fun toString(): String = unsafeStringFromCharArray(array, 0, _length)
fun substring(startIndex: Int, endIndex: Int): String {
checkInsertIndex(startIndex)
checkInsertIndexFrom(endIndex, startIndex)
return unsafeStringFromCharArray(array, startIndex, endIndex - startIndex)
// Of Appenable.
actual override fun append(c: Char) : StringBuilder {
ensureExtraCapacity(1)
array[_length++] = c
return this
}
fun trimToSize() {
if (_length < array.size)
array = array.copyOf(_length)
actual override fun append(csq: CharSequence?): StringBuilder {
// Kotlin/JVM processes null as if the argument was "null" char sequence.
val toAppend = csq ?: "null"
return append(toAppend, 0, toAppend.length)
}
fun ensureCapacity(capacity: Int) {
if (capacity > array.size) {
var newSize = array.size * 2 + 2
if (capacity > newSize)
newSize = capacity
array = array.copyOf(newSize)
}
}
@UseExperimental(ExperimentalStdlibApi::class)
actual override fun append(csq: CharSequence?, start: Int, end: Int): StringBuilder = this.appendRange(csq, start, end)
/**
* Reverses the contents of this string builder and returns this instance.
*
* Surrogate pairs included in this string builder are treated as single characters.
* Therefore, the order of the high-low surrogates is never reversed.
*
* Note that the reverse operation may produce new surrogate pairs that were unpaired low-surrogates and high-surrogates before the operation.
* For example, reversing `"\uDC00\uD800"` produces `"\uD800\uDC00"` which is a valid surrogate pair.
*/
// Based on Apache Harmony implementation.
actual fun reverse(): StringBuilder {
if (this.length < 2) {
@@ -142,36 +137,446 @@ actual class StringBuilder private constructor (
return this
}
fun insert(index: Int, c: Char): StringBuilder {
/**
* Appends the string representation of the specified object [value] to this string builder and returns this instance.
*
* The overall effect is exactly as if the [value] were converted to a string by the `value.toString()` method,
* and then that string was appended to this string builder.
*/
actual fun append(value: Any?): StringBuilder = append(value.toString())
/**
* Appends the string representation of the specified boolean [value] to this string builder and returns this instance.
*
* The overall effect is exactly as if the [value] were converted to a string by the `value.toString()` method,
* and then that string was appended to this string builder.
*/
// TODO: optimize those!
actual fun append(value: Boolean): StringBuilder = append(value.toString())
fun append(value: Byte): StringBuilder = append(value.toString())
fun append(value: Short): StringBuilder = append(value.toString())
fun append(value: Int): StringBuilder {
ensureExtraCapacity(11)
_length += insertInt(array, _length, value)
return this
}
fun append(value: Long): StringBuilder = append(value.toString())
fun append(value: Float): StringBuilder = append(value.toString())
fun append(value: Double): StringBuilder = append(value.toString())
/**
* Appends characters in the specified character array [value] to this string builder and returns this instance.
*
* Characters are appended in order, starting at the index 0.
*/
actual fun append(value: CharArray): StringBuilder {
ensureExtraCapacity(value.size)
value.copyInto(array, _length)
_length += value.size
return this
}
/**
* Appends the specified string [value] to this string builder and returns this instance.
*/
actual fun append(value: String): StringBuilder {
ensureExtraCapacity(value.length)
_length += insertString(array, _length, value)
return this
}
/**
* Returns the current capacity of this string builder.
*
* The capacity is the maximum length this string builder can have before an allocation occurs.
*/
actual fun capacity(): Int = array.size
/**
* Ensures that the capacity of this string builder is at least equal to the specified [minimumCapacity].
*
* If the current capacity is less than the [minimumCapacity], a new backing storage is allocated with greater capacity.
* Otherwise, this method takes no action and simply returns.
*/
actual fun ensureCapacity(minimumCapacity: Int) {
if (minimumCapacity > array.size) {
var newSize = array.size * 2 + 2
if (minimumCapacity > newSize)
newSize = minimumCapacity
array = array.copyOf(newSize)
}
}
/**
* Returns the index within this string builder of the first occurrence of the specified [string].
*
* Returns `-1` if the specified [string] does not occur in this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
actual fun indexOf(string: String): Int {
return (this as CharSequence).indexOf(string, startIndex = 0, ignoreCase = false)
}
/**
* Returns the index within this string builder of the first occurrence of the specified [string],
* starting at the specified [startIndex].
*
* Returns `-1` if the specified [string] does not occur in this string builder starting at the specified [startIndex].
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
actual fun indexOf(string: String, startIndex: Int): Int {
if (string.isEmpty() && startIndex >= _length) return _length
return (this as CharSequence).indexOf(string, startIndex, ignoreCase = false)
}
/**
* Returns the index within this string builder of the last occurrence of the specified [string].
* The last occurrence of empty string `""` is considered to be at the index equal to `this.length`.
*
* Returns `-1` if the specified [string] does not occur in this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
actual fun lastIndexOf(string: String): Int {
if (string.isEmpty()) return _length
return (this as CharSequence).lastIndexOf(string, startIndex = lastIndex, ignoreCase = false)
}
/**
* Returns the index within this string builder of the last occurrence of the specified [string],
* starting from the specified [startIndex] toward the beginning.
*
* Returns `-1` if the specified [string] does not occur in this string builder starting at the specified [startIndex].
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
actual fun lastIndexOf(string: String, startIndex: Int): Int {
if (string.isEmpty() && startIndex >= _length) return _length
return (this as CharSequence).lastIndexOf(string, startIndex, ignoreCase = false)
}
/**
* Inserts the string representation of the specified boolean [value] into this string builder at the specified [index] and returns this instance.
*
* The overall effect is exactly as if the [value] were converted to a string by the `value.toString()` method,
* and then that string was inserted into this string builder at the specified [index].
*
* @throws IndexOutOfBoundsException if [index] is less than zero or greater than the length of this string builder.
*/
// TODO: optimize those!
actual fun insert(index: Int, value: Boolean): StringBuilder = 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())
/**
* Inserts the specified character [value] into this string builder at the specified [index] and returns this instance.
*
* @throws IndexOutOfBoundsException if [index] is less than zero or greater than the length of this string builder.
*/
actual fun insert(index: Int, value: 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
array[index] = value
_length++
return this
}
fun insert(index: Int, csq: CharSequence?): StringBuilder {
// Kotlin/JVM inserts the "null" string if the argument is null.
val toInsert = csq ?: "null"
return insert(index, toInsert, 0, toInsert.length)
/**
* Inserts characters in the specified character array [value] into this string builder at the specified [index] and returns this instance.
*
* The inserted characters go in same order as in the [value] character array, starting at [index].
*
* @throws IndexOutOfBoundsException if [index] is less than zero or greater than the length of this string builder.
*/
actual fun insert(index: Int, value: CharArray): StringBuilder {
checkInsertIndex(index)
ensureExtraCapacity(value.size)
array.copyInto(array, startIndex = index, endIndex = _length, destinationOffset = index + value.size)
value.copyInto(array, destinationOffset = index)
_length += value.size
return this
}
fun insert(index: Int, csq: CharSequence?, start: Int, end: Int): StringBuilder {
// Kotlin/JVM processes null as if the argument was "null" char sequence.
val toInsert = csq ?: "null"
if (start < 0 || end < start || start > toInsert.length) throw IndexOutOfBoundsException()
/**
* Inserts characters in the specified character sequence [value] into this string builder at the specified [index] and returns this instance.
*
* The inserted characters go in the same order as in the [value] character sequence, starting at [index].
*
* @param index the position in this string builder to insert at.
* @param value the character sequence from which characters are inserted. If [value] is `null`, then the four characters `"null"` are inserted.
*
* @throws IndexOutOfBoundsException if [index] is less than zero or greater than the length of this string builder.
*/
@UseExperimental(ExperimentalStdlibApi::class)
actual fun insert(index: Int, value: CharSequence?): StringBuilder {
// Kotlin/JVM inserts the "null" string if the argument is null.
val toInsert = value ?: "null"
return insertRange(index, toInsert, 0, toInsert.length)
}
/**
* Inserts the string representation of the specified object [value] into this string builder at the specified [index] and returns this instance.
*
* The overall effect is exactly as if the [value] were converted to a string by the `value.toString()` method,
* and then that string was inserted into this string builder at the specified [index].
*
* @throws IndexOutOfBoundsException if [index] is less than zero or greater than the length of this string builder.
*/
actual fun insert(index: Int, value: Any?): StringBuilder = insert(index, value.toString())
/**
* Inserts the string [value] into this string builder at the specified [index] and returns this instance.
*
* @throws IndexOutOfBoundsException if [index] is less than zero or greater than the length of this string builder.
*/
actual fun insert(index: Int, value: String): StringBuilder {
checkInsertIndex(index)
val extraLength = end - start
ensureExtraCapacity(value.length)
array.copyInto(array, startIndex = index, endIndex = _length, destinationOffset = index + value.length)
_length += insertString(array, index, value)
return this
}
/**
* Sets the length of this string builder to the specified [newLength].
*
* If the [newLength] is less than the current length, it is changed to the specified [newLength].
* Otherwise, null characters '\u0000' are appended to this string builder until its length is less than the [newLength].
*
* Note that in Kotlin/JS [set] operator function has non-constant execution time complexity.
* Therefore, increasing length of this string builder and then updating each character by index may slow down your program.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] if [newLength] is less than zero.
*/
actual fun setLength(newLength: Int) {
if (newLength < 0) {
throw IllegalArgumentException("Negative new length: $newLength.")
}
if (newLength > _length) {
array.fill('\u0000', _length, newLength.coerceAtMost(array.size))
}
_length = newLength
}
/**
* Returns a new [String] that contains characters in this string builder at [startIndex] (inclusive) and up to the [endIndex] (exclusive).
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] or [endIndex] is out of range of this string builder indices or when `startIndex > endIndex`.
*/
actual fun substring(startIndex: Int, endIndex: Int): String {
checkBoundsIndexes(startIndex, endIndex, _length)
return unsafeStringFromCharArray(array, startIndex, endIndex - startIndex)
}
/**
* Returns a new [String] that contains characters in this string builder at [startIndex] (inclusive) and up to the [length] (exclusive).
*
* @throws IndexOutOfBoundsException if [startIndex] is less than zero or greater than the length of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
actual fun substring(startIndex: Int): String {
return substring(startIndex, _length)
}
/**
* Attempts to reduce storage used for this string builder.
*
* If the backing storage of this string builder is larger than necessary to hold its current contents,
* then it may be resized to become more space efficient.
* Calling this method may, but is not required to, affect the value of the [capacity] property.
*/
actual fun trimToSize() {
if (_length < array.size)
array = array.copyOf(_length)
}
override fun toString(): String = unsafeStringFromCharArray(array, 0, _length)
/**
* Sets the character at the specified [index] to the specified [value].
*
* @throws IndexOutOfBoundsException if [index] is out of bounds of this string builder.
*/
operator fun set(index: Int, value: Char) {
checkIndex(index)
array[index] = value
}
/**
* Replaces characters in the specified range of this string builder with characters in the specified string [value] and returns this instance.
*
* @param startIndex the beginning (inclusive) of the range to replace.
* @param endIndex the end (exclusive) of the range to replace.
* @param value the string to replace with.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] if [startIndex] is less than zero, greater than the length of this string builder, or `startIndex > endIndex`.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
fun setRange(startIndex: Int, endIndex: Int, value: String): StringBuilder {
checkReplaceRange(startIndex, endIndex, _length)
val coercedEndIndex = endIndex.coerceAtMost(_length)
val lengthDiff = value.length - (coercedEndIndex - startIndex)
ensureExtraCapacity(_length + lengthDiff)
array.copyInto(array, startIndex = coercedEndIndex, endIndex = _length, destinationOffset = startIndex + value.length)
var replaceIndex = startIndex
for (index in 0 until value.length) array[replaceIndex++] = value[index] // optimize
_length += lengthDiff
return this
}
/**
* Removes the character at the specified [index] from this string builder and returns this instance.
*
* If the `Char` at the specified [index] is part of a supplementary code point, this method does not remove the entire supplementary character.
*
* @param index the index of `Char` to remove.
*
* @throws IndexOutOfBoundsException if [index] is out of bounds of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
fun deleteAt(index: Int): StringBuilder {
checkIndex(index)
array.copyInto(array, startIndex = index + 1, endIndex = _length, destinationOffset = index)
--_length
return this
}
/**
* Removes characters in the specified range from this string builder and returns this instance.
*
* @param startIndex the beginning (inclusive) of the range to remove.
* @param endIndex the end (exclusive) of the range to remove.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] is out of range of this string builder indices or when `startIndex > endIndex`.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
fun deleteRange(startIndex: Int, endIndex: Int): StringBuilder {
checkReplaceRange(startIndex, endIndex, _length)
val coercedEndIndex = endIndex.coerceAtMost(_length)
array.copyInto(array, startIndex = coercedEndIndex, endIndex = _length, destinationOffset = startIndex)
_length -= coercedEndIndex - startIndex
return this
}
/**
* Copies characters from this string builder into the [destination] character array.
*
* @param destination the array to copy to.
* @param destinationOffset the position in the array to copy to, 0 by default.
* @param startIndex the beginning (inclusive) of the range to copy, 0 by default.
* @param endIndex the end (exclusive) of the range to copy, length of this string builder by default.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] or [endIndex] is out of range of this string builder indices or when `startIndex > endIndex`.
* @throws IndexOutOfBoundsException when the subrange doesn't fit into the [destination] array starting at the specified [destinationOffset],
* or when that index is out of the [destination] array indices range.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
fun toCharArray(destination: CharArray, destinationOffset: Int = 0, startIndex: Int = 0, endIndex: Int = this.length) {
checkBoundsIndexes(startIndex, endIndex, _length)
checkBoundsIndexes(destinationOffset, destinationOffset + endIndex - startIndex, destination.size)
array.copyInto(destination, destinationOffset, startIndex, endIndex)
}
/**
* Appends characters in a subarray of the specified character array [value] to this string builder and returns this instance.
*
* Characters are appended in order, starting at specified [startIndex].
*
* @param value the array from which characters are appended.
* @param startIndex the beginning (inclusive) of the subarray to append.
* @param endIndex the end (exclusive) of the subarray to append.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] or [endIndex] is out of range of the [value] array indices or when `startIndex > endIndex`.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
fun appendRange(value: CharArray, startIndex: Int, endIndex: Int): StringBuilder {
checkBoundsIndexes(startIndex, endIndex, value.size)
val extraLength = endIndex - startIndex
ensureExtraCapacity(extraLength)
value.copyInto(array, _length, startIndex, endIndex)
_length += extraLength
return this
}
/**
* Appends a subsequence of the specified character sequence [value] to this string builder and returns this instance.
*
* @param value the character sequence from which a subsequence is appended. If [value] is `null`,
* then characters are appended as if [value] contained the four characters `"null"`.
* @param startIndex the beginning (inclusive) of the subsequence to append.
* @param endIndex the end (exclusive) of the subsequence to append.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] or [endIndex] is out of range of the [value] character sequence indices or when `startIndex > endIndex`.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
fun appendRange(value: CharSequence?, startIndex: Int, endIndex: Int): StringBuilder {
// Kotlin/JVM processes null as if the argument was "null" char sequence.
val toAppend = value ?: "null"
checkBoundsIndexes(startIndex, endIndex, toAppend.length)
val extraLength = endIndex - startIndex
ensureExtraCapacity(extraLength)
(toAppend as? String)?.let {
_length += insertString(array, _length, it, startIndex, extraLength)
return this
}
var index = startIndex
while (index < endIndex)
array[_length++] = toAppend[index++]
return this
}
/**
* Inserts characters in a subsequence of the specified character sequence [value] into this string builder at the specified [index] and returns this instance.
*
* The inserted characters go in the same order as in the [value] character sequence, starting at [index].
*
* @param index the position in this string builder to insert at.
* @param value the character sequence from which a subsequence is inserted. If [value] is `null`,
* then characters will be inserted as if [value] contained the four characters `"null"`.
* @param startIndex the beginning (inclusive) of the subsequence to insert.
* @param endIndex the end (exclusive) of the subsequence to insert.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] or [endIndex] is out of range of the [value] character sequence indices or when `startIndex > endIndex`.
* @throws IndexOutOfBoundsException if [index] is less than zero or greater than the length of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
fun insertRange(index: Int, value: CharSequence?, startIndex: Int, endIndex: Int): StringBuilder {
// Kotlin/JVM processes null as if the argument was "null" char sequence.
val toInsert = value ?: "null"
checkBoundsIndexes(startIndex, endIndex, toInsert.length)
checkInsertIndex(index)
val extraLength = endIndex - startIndex
ensureExtraCapacity(extraLength)
array.copyInto(array, startIndex = index, endIndex = _length, destinationOffset = index + extraLength)
var from = start
var from = startIndex
var to = index
while (from < end) {
while (from < endIndex) {
array[to++] = toInsert[from++]
}
@@ -179,102 +584,33 @@ actual class StringBuilder private constructor (
return this
}
fun insert(index: Int, chars: CharArray): StringBuilder {
/**
* Inserts characters in a subarray of the specified character array [value] into this string builder at the specified [index] and returns this instance.
*
* The inserted characters go in same order as in the [value] array, starting at [index].
*
* @param index the position in this string builder to insert at.
* @param value the array from which characters are inserted.
* @param startIndex the beginning (inclusive) of the subarray to insert.
* @param endIndex the end (exclusive) of the subarray to insert.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] or [endIndex] is out of range of the [value] array indices or when `startIndex > endIndex`.
* @throws IndexOutOfBoundsException if [index] is less than zero or greater than the length of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
fun insertRange(index: Int, value: CharArray, startIndex: Int, endIndex: Int): StringBuilder {
checkInsertIndex(index)
ensureExtraCapacity(chars.size)
checkBoundsIndexes(startIndex, endIndex, value.size)
array.copyInto(array, startIndex = index, endIndex = _length, destinationOffset = index + chars.size)
chars.copyInto(array, destinationOffset = index)
val extraLength = endIndex - startIndex
array.copyInto(array, startIndex = index, endIndex = _length, destinationOffset = index + extraLength)
value.copyInto(array, startIndex = startIndex, endIndex = endIndex, destinationOffset = index)
_length += chars.size
_length += extraLength
return this
}
fun insert(index: Int, string: String): StringBuilder {
checkInsertIndex(index)
ensureExtraCapacity(string.length)
array.copyInto(array, startIndex = index, endIndex = _length, destinationOffset = 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())
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.
actual override fun append(c: Char) : StringBuilder {
ensureExtraCapacity(1)
array[_length++] = c
return this
}
actual override fun append(csq: CharSequence?): StringBuilder {
// Kotlin/JVM processes null as if the argument was "null" char sequence.
val toAppend = csq ?: "null"
return append(toAppend, 0, toAppend.length)
}
actual override fun append(csq: CharSequence?, start: Int, end: Int): StringBuilder {
// Kotlin/JVM processes null as if the argument was "null" char sequence.
val toAppend = csq ?: "null"
if (start < 0 || end < start || start > toAppend.length) throw IndexOutOfBoundsException()
ensureExtraCapacity(end - start)
(toAppend as? String)?.let {
_length += insertString(array, _length, it, start, end - start)
return this
}
var index = start
while (index < end)
array[_length++] = toAppend[index++]
return this
}
fun append(it: CharArray): StringBuilder {
ensureExtraCapacity(it.size)
it.copyInto(array, _length)
_length += it.size
return this
}
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): 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())
actual fun append(obj: Any?): StringBuilder = append(obj.toString())
fun deleteCharAt(index: Int) {
checkIndex(index)
array.copyInto(array, startIndex = index + 1, endIndex = _length, destinationOffset = index)
--_length
}
fun setCharAt(index: Int, value: Char) {
checkIndex(index)
array[index] = value
}
// ---------------------------- private ----------------------------
private fun ensureExtraCapacity(n: Int) {
@@ -292,4 +628,283 @@ actual class StringBuilder private constructor (
private fun checkInsertIndexFrom(index: Int, fromIndex: Int) {
if (index < fromIndex || index > _length) throw IndexOutOfBoundsException()
}
private fun checkReplaceRange(startIndex: Int, endIndex: Int, length: Int) {
if (startIndex < 0 || startIndex > length) {
throw IndexOutOfBoundsException("startIndex: $startIndex, length: $length")
}
if (startIndex > endIndex) {
throw IllegalArgumentException("startIndex($startIndex) > endIndex($endIndex)")
}
}
}
/**
* Clears the content of this string builder making it empty.
*
* @sample samples.text.Strings.clearStringBuilder
*/
@SinceKotlin("1.3")
public actual fun StringBuilder.clear(): StringBuilder = apply { setLength(0) }
/**
* Sets the character at the specified [index] to the specified [value].
*
* @throws IndexOutOfBoundsException if [index] is out of bounds of this string builder.
*/
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
@kotlin.internal.InlineOnly
public actual inline operator fun StringBuilder.set(index: Int, value: Char): Unit = this.set(index, value)
/**
* Replaces characters in the specified range of this string builder with characters in the specified string [value] and returns this instance.
*
* @param startIndex the beginning (inclusive) of the range to replace.
* @param endIndex the end (exclusive) of the range to replace.
* @param value the string to replace with.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] if [startIndex] is less than zero, greater than the length of this string builder, or `startIndex > endIndex`.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
@kotlin.internal.InlineOnly
public actual inline fun StringBuilder.setRange(startIndex: Int, endIndex: Int, value: String): StringBuilder =
this.setRange(startIndex, endIndex, value)
/**
* Removes the character at the specified [index] from this string builder and returns this instance.
*
* If the `Char` at the specified [index] is part of a supplementary code point, this method does not remove the entire supplementary character.
*
* @param index the index of `Char` to remove.
*
* @throws IndexOutOfBoundsException if [index] is out of bounds of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
@kotlin.internal.InlineOnly
public actual inline fun StringBuilder.deleteAt(index: Int): StringBuilder = this.deleteAt(index)
/**
* Removes characters in the specified range from this string builder and returns this instance.
*
* @param startIndex the beginning (inclusive) of the range to remove.
* @param endIndex the end (exclusive) of the range to remove.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] or [endIndex] is out of range of this string builder indices or when `startIndex > endIndex`.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
@kotlin.internal.InlineOnly
public actual inline fun StringBuilder.deleteRange(startIndex: Int, endIndex: Int): StringBuilder = this.deleteRange(startIndex, endIndex)
/**
* Copies characters from this string builder into the [destination] character array.
*
* @param destination the array to copy to.
* @param destinationOffset the position in the array to copy to, 0 by default.
* @param startIndex the beginning (inclusive) of the range to copy, 0 by default.
* @param endIndex the end (exclusive) of the range to copy, length of this string builder by default.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] or [endIndex] is out of range of this string builder indices or when `startIndex > endIndex`.
* @throws IndexOutOfBoundsException when the subrange doesn't fit into the [destination] array starting at the specified [destinationOffset],
* or when that index is out of the [destination] array indices range.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
@kotlin.internal.InlineOnly
public actual inline fun StringBuilder.toCharArray(destination: CharArray, destinationOffset: Int = 0, startIndex: Int = 0, endIndex: Int = this.length) =
this.toCharArray(destination, destinationOffset, startIndex, endIndex)
/**
* Appends characters in a subarray of the specified character array [value] to this string builder and returns this instance.
*
* Characters are appended in order, starting at specified [startIndex].
*
* @param value the array from which characters are appended.
* @param startIndex the beginning (inclusive) of the subarray to append.
* @param endIndex the end (exclusive) of the subarray to append.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] or [endIndex] is out of range of the [value] array indices or when `startIndex > endIndex`.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
@kotlin.internal.InlineOnly
public actual inline fun StringBuilder.appendRange(value: CharArray, startIndex: Int, endIndex: Int): StringBuilder =
this.appendRange(value, startIndex, endIndex)
/**
* Appends a subsequence of the specified character sequence [value] to this string builder and returns this instance.
*
* @param value the character sequence from which a subsequence is appended. If [value] is `null`,
* then characters are appended as if [value] contained the four characters `"null"`.
* @param startIndex the beginning (inclusive) of the subsequence to append.
* @param endIndex the end (exclusive) of the subsequence to append.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] or [endIndex] is out of range of the [value] character sequence indices or when `startIndex > endIndex`.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
@kotlin.internal.InlineOnly
public actual inline fun StringBuilder.appendRange(value: CharSequence?, startIndex: Int, endIndex: Int): StringBuilder =
this.appendRange(value, startIndex, endIndex)
/**
* Inserts characters in a subarray of the specified character array [value] into this string builder at the specified [index] and returns this instance.
*
* The inserted characters go in same order as in the [value] array, starting at [index].
*
* @param index the position in this string builder to insert at.
* @param value the array from which characters are inserted.
* @param startIndex the beginning (inclusive) of the subarray to insert.
* @param endIndex the end (exclusive) of the subarray to insert.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] or [endIndex] is out of range of the [value] array indices or when `startIndex > endIndex`.
* @throws IndexOutOfBoundsException if [index] is less than zero or greater than the length of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
@kotlin.internal.InlineOnly
public actual inline fun StringBuilder.insertRange(index: Int, value: CharArray, startIndex: Int, endIndex: Int): StringBuilder =
this.insertRange(index, value, startIndex, endIndex)
/**
* Inserts characters in a subsequence of the specified character sequence [value] into this string builder at the specified [index] and returns this instance.
*
* The inserted characters go in the same order as in the [value] character sequence, starting at [index].
*
* @param index the position in this string builder to insert at.
* @param value the character sequence from which a subsequence is inserted. If [value] is `null`,
* then characters will be inserted as if [value] contained the four characters `"null"`.
* @param startIndex the beginning (inclusive) of the subsequence to insert.
* @param endIndex the end (exclusive) of the subsequence to insert.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [startIndex] or [endIndex] is out of range of the [value] character sequence indices or when `startIndex > endIndex`.
* @throws IndexOutOfBoundsException if [index] is less than zero or greater than the length of this string builder.
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
@kotlin.internal.InlineOnly
public actual inline fun StringBuilder.insertRange(index: Int, value: CharSequence?, startIndex: Int, endIndex: Int): StringBuilder =
this.insertRange(index, value, startIndex, endIndex)
// Method parameters renamings
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
@Deprecated("Use append(value: Boolean) instead", ReplaceWith("append(value = it)"), DeprecationLevel.WARNING)
@kotlin.internal.InlineOnly
public inline fun StringBuilder.append(it: Boolean): StringBuilder = this.append(value = it)
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
@Deprecated("Use append(value: Byte) instead", ReplaceWith("append(value = it)"), DeprecationLevel.WARNING)
@kotlin.internal.InlineOnly
public inline fun StringBuilder.append(it: Byte): StringBuilder = this.append(value = it)
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
@Deprecated("Use append(value: Short) instead", ReplaceWith("append(value = it)"), DeprecationLevel.WARNING)
@kotlin.internal.InlineOnly
public inline fun StringBuilder.append(it: Short): StringBuilder = this.append(value = it)
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
@Deprecated("Use append(value: Int) instead", ReplaceWith("append(value = it)"), DeprecationLevel.WARNING)
@kotlin.internal.InlineOnly
public inline fun StringBuilder.append(it: Int): StringBuilder = this.append(value = it)
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
@Deprecated("Use append(value: Long) instead", ReplaceWith("append(value = it)"), DeprecationLevel.WARNING)
@kotlin.internal.InlineOnly
public inline fun StringBuilder.append(it: Long): StringBuilder = this.append(value = it)
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
@Deprecated("Use append(value: Float) instead", ReplaceWith("append(value = it)"), DeprecationLevel.WARNING)
@kotlin.internal.InlineOnly
public inline fun StringBuilder.append(it: Float): StringBuilder = this.append(value = it)
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
@Deprecated("Use append(value: Double) instead", ReplaceWith("append(value = it)"), DeprecationLevel.WARNING)
@kotlin.internal.InlineOnly
public inline fun StringBuilder.append(it: Double): StringBuilder = this.append(value = it)
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
@Deprecated("Use append(value: String) instead", ReplaceWith("append(value = it)"), DeprecationLevel.WARNING)
@kotlin.internal.InlineOnly
public inline fun StringBuilder.append(it: String): StringBuilder = this.append(value = it)
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
@Deprecated("Use append(value: CharArray) instead", ReplaceWith("append(value = it)"), DeprecationLevel.WARNING)
@kotlin.internal.InlineOnly
public inline fun StringBuilder.append(it: CharArray): StringBuilder = this.append(value = it)
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
@Deprecated("Use ensureCapacity(minimumCapacity: Int) instead", ReplaceWith("ensureCapacity(minimumCapacity = capacity)"), DeprecationLevel.WARNING)
@kotlin.internal.InlineOnly
public inline fun StringBuilder.ensureCapacity(capacity: Int): Unit = this.ensureCapacity(minimumCapacity = capacity)
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
@Deprecated("Use insert(index: Int, value: Char) instead", ReplaceWith("insert(index, value = c)"), DeprecationLevel.WARNING)
@kotlin.internal.InlineOnly
public inline fun StringBuilder.insert(index: Int, c: Char): StringBuilder = this.insert(index, value = c)
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
@Deprecated("Use insert(index: Int, value: CharArray) instead", ReplaceWith("insert(index, value = chars)"), DeprecationLevel.WARNING)
@kotlin.internal.InlineOnly
public inline fun StringBuilder.insert(index: Int, chars: CharArray): StringBuilder = this.insert(index, value = chars)
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
@Deprecated("Use insert(index: Int, value: CharSequence?) instead", ReplaceWith("insert(index, value = csq)"), DeprecationLevel.WARNING)
@kotlin.internal.InlineOnly
public inline fun StringBuilder.insert(index: Int, csq: CharSequence?): StringBuilder = this.insert(index, value = csq)
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
@Deprecated("Use insert(index: Int, value: String) instead", ReplaceWith("insert(index, value = string)"), DeprecationLevel.WARNING)
@kotlin.internal.InlineOnly
public inline fun StringBuilder.insert(index: Int, string: String): StringBuilder = this.insert(index, value = string)
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
@Deprecated("Use setLength(newLength: Int) instead", ReplaceWith("setLength(newLength = l)"), DeprecationLevel.WARNING)
@kotlin.internal.InlineOnly
public inline fun StringBuilder.setLength(l: Int) = this.setLength(newLength = l)
// Method renamings
/**
* Inserts characters in a subsequence of the specified character sequence [csq] into this string builder at the specified [index] and returns this instance.
*
* The inserted characters go in the same order as in the [csq] character sequence, starting at [index].
*
* @param index the position in this string builder to insert at.
* @param csq the character sequence from which a subsequence is inserted. If [csq] is `null`,
* then characters will be inserted as if [csq] contained the four characters `"null"`.
* @param start the beginning (inclusive) of the subsequence to insert.
* @param end the end (exclusive) of the subsequence to insert.
*
* @throws IndexOutOfBoundsException or [IllegalArgumentException] when [start] or [end] is out of range of the [csq] character sequence indices or when `start > end`.
* @throws IndexOutOfBoundsException if [index] is less than zero or greater than the length of this string builder.
*/
@UseExperimental(ExperimentalStdlibApi::class)
@kotlin.internal.InlineOnly
public inline fun StringBuilder.insert(index: Int, csq: CharSequence?, start: Int, end: Int): StringBuilder =
this.insertRange(index, csq, start, end)
@Deprecated("Use set(index: Int, value: Char) instead", ReplaceWith("set(index, value)"), DeprecationLevel.WARNING)
@kotlin.internal.InlineOnly
public inline fun StringBuilder.setCharAt(index: Int, value: Char) = this.set(index, value)
/**
* Removes the character at the specified [index] from this string builder and returns this instance.
*
* If the `Char` at the specified [index] is part of a supplementary code point, this method does not remove the entire supplementary character.
*
* @param index the index of `Char` to remove.
*
* @throws IndexOutOfBoundsException if [index] is out of bounds of this string builder.
*/
@UseExperimental(ExperimentalStdlibApi::class)
@kotlin.internal.InlineOnly
public inline fun StringBuilder.deleteCharAt(index: Int) = this.deleteAt(index)
@@ -485,6 +485,7 @@ internal class CharClass(val ignoreCase: Boolean = false, negative: Boolean = fa
}
}
@UseExperimental(ExperimentalStdlibApi::class)
override val instance: AbstractCharClass
get() {
@@ -506,7 +507,7 @@ internal class CharClass(val ignoreCase: Boolean = false, negative: Boolean = fa
}
if (temp.length > 0)
temp.deleteCharAt(temp.length - 1)
temp.deleteAt(temp.length - 1)
return temp.toString()
}
@@ -518,6 +519,7 @@ internal class CharClass(val ignoreCase: Boolean = false, negative: Boolean = fa
}
}
@UseExperimental(ExperimentalStdlibApi::class)
//for debugging purposes only
override fun toString(): String {
val temp = StringBuilder()
@@ -529,7 +531,7 @@ internal class CharClass(val ignoreCase: Boolean = false, negative: Boolean = fa
}
if (temp.length > 0)
temp.deleteCharAt(temp.length - 1)
temp.deleteAt(temp.length - 1)
return temp.toString()
}