Implement String to/from CharArray conversion (KT-29265)

This commit is contained in:
Abduqodiri Qurbonzoda
2019-04-11 02:57:33 +03:00
parent a3247b1b92
commit 81d2d3cb6a
12 changed files with 169 additions and 14 deletions
+2 -1
View File
@@ -80,7 +80,8 @@ compileCoroutinesExperimentalKotlinCommon {
compileTestKotlinCommon {
kotlinOptions {
freeCompilerArgs += [
"-Xuse-experimental=kotlin.ExperimentalUnsignedTypes"
"-Xuse-experimental=kotlin.ExperimentalUnsignedTypes",
"-Xuse-experimental=kotlin.ExperimentalStdlibApi"
]
}
}
@@ -109,6 +109,31 @@ public expect fun String(chars: CharArray): String
@SinceKotlin("1.2")
public expect fun String(chars: CharArray, offset: Int, length: Int): String
/**
* Concatenates characters in this [CharArray] or its subrange into a String.
*
* @param startIndex the beginning (inclusive) of the subrange of characters, 0 by default.
* @param endIndex the end (exclusive) of the subrange of characters, size of this array by default.
*
* @throws IndexOutOfBoundsException if [startIndex] is less than zero or [endIndex] is greater than the size of this array.
* @throws IllegalArgumentException if [startIndex] is greater than [endIndex].
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
public expect fun CharArray.concatToString(startIndex: Int = 0, endIndex: Int = this.size): String
/**
* Returns a [CharArray] containing characters of this string or its substring.
*
* @param startIndex the beginning (inclusive) of the substring, 0 by default.
* @param endIndex the end (exclusive) of the substring, length of this string by default.
*
* @throws IndexOutOfBoundsException if [startIndex] is less than zero or [endIndex] is greater than the length of this string.
* @throws IllegalArgumentException if [startIndex] is greater than [endIndex].
*/
@SinceKotlin("1.3")
@ExperimentalStdlibApi
public expect fun String.toCharArray(startIndex: Int = 0, endIndex: Int = this.length): CharArray
internal expect fun String.nativeIndexOf(str: String, fromIndex: Int): Int
internal expect fun String.nativeLastIndexOf(str: String, fromIndex: Int): Int
+6 -1
View File
@@ -91,7 +91,12 @@ compileKotlin {
}
compileTestKotlin {
kotlinOptions.freeCompilerArgs = ["-Xallow-kotlin-package", "-Xmulti-platform", "-Xuse-experimental=kotlin.ExperimentalUnsignedTypes"]
kotlinOptions.freeCompilerArgs = [
"-Xallow-kotlin-package",
"-Xmulti-platform",
"-Xuse-experimental=kotlin.ExperimentalUnsignedTypes",
"-Xuse-experimental=kotlin.ExperimentalStdlibApi"
]
}
compileJava9Sources(project, 'kotlin.stdlib.jdk7')
+6 -1
View File
@@ -85,7 +85,12 @@ compileKotlin {
}
compileTestKotlin {
kotlinOptions.freeCompilerArgs = ["-Xallow-kotlin-package", "-Xmulti-platform", "-Xuse-experimental=kotlin.ExperimentalUnsignedTypes"]
kotlinOptions.freeCompilerArgs = [
"-Xallow-kotlin-package",
"-Xmulti-platform",
"-Xuse-experimental=kotlin.ExperimentalUnsignedTypes",
"-Xuse-experimental=kotlin.ExperimentalStdlibApi"
]
}
compileJava9Sources(project, 'kotlin.stdlib.jdk8')
+2 -1
View File
@@ -135,7 +135,8 @@ compileTestKotlin2Js {
"-Xir",
"-Xir-produce-only=js",
"-verbose",
"-Xuse-experimental=kotlin.ExperimentalUnsignedTypes"
"-Xuse-experimental=kotlin.ExperimentalUnsignedTypes",
"-Xuse-experimental=kotlin.ExperimentalStdlibApi"
]
}
}
+2 -1
View File
@@ -193,7 +193,8 @@ compileTestKotlin2Js {
kotlinOptions {
moduleKind = "umd"
freeCompilerArgs += [
"-Xuse-experimental=kotlin.ExperimentalUnsignedTypes"
"-Xuse-experimental=kotlin.ExperimentalUnsignedTypes",
"-Xuse-experimental=kotlin.ExperimentalStdlibApi"
]
}
}
@@ -36,6 +36,44 @@ public actual fun String(chars: CharArray, offset: Int, length: Int): String {
return result
}
/**
* Concatenates characters in this [CharArray] or its subrange into a String.
*
* @param startIndex the beginning (inclusive) of the subrange of characters, 0 by default.
* @param endIndex the end (exclusive) of the subrange of characters, size of this array by default.
*
* @throws IndexOutOfBoundsException if [startIndex] is less than zero or [endIndex] is greater than the size of this array.
* @throws IllegalArgumentException if [startIndex] is greater than [endIndex].
*/
@SinceKotlin("1.3")
@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
@ExperimentalStdlibApi
public actual fun CharArray.concatToString(startIndex: Int = 0, endIndex: Int = this.size): String {
AbstractList.checkBoundsIndexes(startIndex, endIndex, this.size)
var result = ""
for (index in startIndex until endIndex) {
result += this[index]
}
return result
}
/**
* Returns a [CharArray] containing characters of this string or its substring.
*
* @param startIndex the beginning (inclusive) of the substring, 0 by default.
* @param endIndex the end (exclusive) of the substring, length of this string by default.
*
* @throws IndexOutOfBoundsException if [startIndex] is less than zero or [endIndex] is greater than the length of this string.
* @throws IllegalArgumentException if [startIndex] is greater than [endIndex].
*/
@SinceKotlin("1.3")
@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
@ExperimentalStdlibApi
public actual fun String.toCharArray(startIndex: Int = 0, endIndex: Int = this.length): CharArray {
AbstractList.checkBoundsIndexes(startIndex, endIndex, length)
return CharArray(endIndex - startIndex) { get(startIndex + it) }
}
/**
* Returns a copy of this string converted to upper case using the rules of the default locale.
*
+2 -1
View File
@@ -249,7 +249,8 @@ check.dependsOn(coroutinesExperimentalMigrationTest)
compileTestKotlin {
kotlinOptions {
freeCompilerArgs += [
"-Xuse-experimental=kotlin.ExperimentalUnsignedTypes"
"-Xuse-experimental=kotlin.ExperimentalUnsignedTypes",
"-Xuse-experimental=kotlin.ExperimentalStdlibApi"
]
}
}
@@ -10,7 +10,7 @@
package kotlin.text
import java.nio.charset.Charset
import java.util.*
import java.util.Locale
import java.util.regex.Pattern
@@ -108,6 +108,40 @@ public actual inline fun String.toUpperCase(): String = (this as java.lang.Strin
@kotlin.internal.InlineOnly
public actual inline fun String.toLowerCase(): String = (this as java.lang.String).toLowerCase()
/**
* Concatenates characters in this [CharArray] or its subrange into a String.
*
* @param startIndex the beginning (inclusive) of the subrange of characters, 0 by default.
* @param endIndex the end (exclusive) of the subrange of characters, size of this array by default.
*
* @throws IndexOutOfBoundsException if [startIndex] is less than zero or [endIndex] is greater than the size of this array.
* @throws IllegalArgumentException if [startIndex] is greater than [endIndex].
*/
@SinceKotlin("1.3")
@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
@ExperimentalStdlibApi
public actual fun CharArray.concatToString(startIndex: Int = 0, endIndex: Int = this.size): String {
AbstractList.checkBoundsIndexes(startIndex, endIndex, this.size)
return String(this, startIndex, endIndex - startIndex)
}
/**
* Returns a [CharArray] containing characters of this string or its substring.
*
* @param startIndex the beginning (inclusive) of the substring, 0 by default.
* @param endIndex the end (exclusive) of the substring, length of this string by default.
*
* @throws IndexOutOfBoundsException if [startIndex] is less than zero or [endIndex] is greater than the length of this string.
* @throws IllegalArgumentException if [startIndex] is greater than [endIndex].
*/
@SinceKotlin("1.3")
@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
@ExperimentalStdlibApi
public actual fun String.toCharArray(startIndex: Int = 0, endIndex: Int = this.length): CharArray {
AbstractList.checkBoundsIndexes(startIndex, endIndex, length)
return toCharArray(CharArray(endIndex - startIndex), 0, startIndex, endIndex)
}
/**
* Returns a new character array containing the characters from this string.
*/
@@ -123,6 +123,15 @@ public abstract class AbstractList<out E> protected constructor() : AbstractColl
}
}
internal fun checkBoundsIndexes(startIndex: Int, endIndex: Int, size: Int) {
if (startIndex < 0 || endIndex > size) {
throw IndexOutOfBoundsException("startIndex: $startIndex, endIndex: $endIndex, size: $size")
}
if (startIndex > endIndex) {
throw IllegalArgumentException("startIndex: $startIndex > endIndex: $endIndex")
}
}
internal fun orderedHashCode(c: Collection<*>): Int {
var hashCode = 1
for (e in c) {
+38 -7
View File
@@ -45,36 +45,52 @@ fun Char.isAsciiUpperCase() = this in 'A'..'Z'
class StringTest {
private fun testStringFromChars(expected: String, chars: CharArray, offset: Int, length: Int) {
assertEquals(expected, String(chars, offset, length))
assertEquals(expected, chars.concatToString(startIndex = offset, endIndex = offset + length))
}
private fun testStringFromChars(expected: String, chars: CharArray) {
assertEquals(expected, String(chars))
assertEquals(expected, chars.concatToString())
}
@Test fun stringFromCharArrayFullSlice() {
val chars: CharArray = charArrayOf('K', 'o', 't', 'l', 'i', 'n')
assertEquals("Kotlin", String(chars, 0, chars.size))
testStringFromChars("Kotlin", chars, 0, chars.size)
}
@Test fun stringFromCharArraySlice() {
val chars: CharArray = charArrayOf('K', 'o', 't', 'l', 'i', 'n', ' ', 'r', 'u', 'l', 'e', 's')
assertEquals("rule", String(chars, 7, 4))
testStringFromChars("rule", chars, 7, 4)
val longChars = CharArray(200_000) { 'k' }
val longString = String(longChars, 1000, 190_000)
val longString = String(longChars, offset = 1000, length = 190_000)
val longConcatString = longChars.concatToString(startIndex = 1000, endIndex = 191_000)
assertEquals(190_000, longString.length)
assertEquals(190_000, longConcatString.length)
assertTrue(longString.all { it == 'k' })
assertTrue(longConcatString.all { it == 'k' })
}
@Test fun stringFromCharArray() {
val chars: CharArray = charArrayOf('K', 'o', 't', 'l', 'i', 'n')
assertEquals("Kotlin", String(chars))
testStringFromChars("Kotlin", chars)
val longChars = CharArray(200_000) { 'k' }
val longString = String(longChars)
val longConcatString = longChars.concatToString()
assertEquals(200_000, longString.length)
assertEquals(200_000, longConcatString.length)
assertTrue(longString.all { it == 'k' })
assertTrue(longConcatString.all { it == 'k' })
}
@Test fun stringFromCharArrayUnicodeSurrogatePairs() {
val chars: CharArray = charArrayOf('Ц', '月', '語', '\u016C', '\u138D', '\uD83C', '\uDC3A')
assertEquals("Ц月語Ŭᎍ🀺", String(chars))
assertEquals("", String(chars, 1, 1))
assertEquals("Ŭᎍ🀺", String(chars, 3, 4))
testStringFromChars("Ц月語Ŭᎍ🀺", chars)
testStringFromChars("", chars, 1, 1)
testStringFromChars("Ŭᎍ🀺", chars, 3, 4)
}
@Test fun stringFromCharArrayOutOfBounds() {
@@ -82,11 +98,26 @@ class StringTest {
assertFailsWith<IndexOutOfBoundsException> { String(chars, -1, 1) }
assertFailsWith<IndexOutOfBoundsException> { String(chars, 1, -1) }
assertFailsWith<IndexOutOfBoundsException> { String(chars, chars.size - 1, 2) }
assertFailsWith<IndexOutOfBoundsException> { chars.concatToString(-1, 1) }
assertFailsWith<IllegalArgumentException> { chars.concatToString(1, -1) }
assertFailsWith<IllegalArgumentException> { chars.concatToString(chars.size - 1, 2) }
}
test(CharArray(16) { 'k' })
test(CharArray(160_000) { 'k' })
}
@Test
fun toCharArray() {
val s = "hello"
assertArrayContentEquals(charArrayOf('h', 'e', 'l', 'l', 'o'), s.toCharArray())
assertArrayContentEquals(charArrayOf('e', 'l'), s.toCharArray(1, 3))
assertFailsWith<IndexOutOfBoundsException> { s.toCharArray(-1) }
assertFailsWith<IndexOutOfBoundsException> { s.toCharArray(0, 6) }
assertFailsWith<IllegalArgumentException> { s.toCharArray(3, 1) }
}
@Test fun isEmptyAndBlank() = withOneCharSequenceArg { arg1 ->
class Case(val value: String?, val isNull: Boolean = false, val isEmpty: Boolean = false, val isBlank: Boolean = false)
@@ -4906,6 +4906,8 @@ public final class kotlin/text/StringsKt {
public static synthetic fun commonSuffixWith$default (Ljava/lang/CharSequence;Ljava/lang/CharSequence;ZILjava/lang/Object;)Ljava/lang/String;
public static final fun compareTo (Ljava/lang/String;Ljava/lang/String;Z)I
public static synthetic fun compareTo$default (Ljava/lang/String;Ljava/lang/String;ZILjava/lang/Object;)I
public static final fun concatToString ([CII)Ljava/lang/String;
public static synthetic fun concatToString$default ([CIIILjava/lang/Object;)Ljava/lang/String;
public static final fun contains (Ljava/lang/CharSequence;CZ)Z
public static final fun contains (Ljava/lang/CharSequence;Ljava/lang/CharSequence;Z)Z
public static synthetic fun contains$default (Ljava/lang/CharSequence;CZILjava/lang/Object;)Z
@@ -5131,6 +5133,8 @@ public final class kotlin/text/StringsKt {
public static final fun toBigIntegerOrNull (Ljava/lang/String;I)Ljava/math/BigInteger;
public static final fun toByteOrNull (Ljava/lang/String;)Ljava/lang/Byte;
public static final fun toByteOrNull (Ljava/lang/String;I)Ljava/lang/Byte;
public static final fun toCharArray (Ljava/lang/String;II)[C
public static synthetic fun toCharArray$default (Ljava/lang/String;IIILjava/lang/Object;)[C
public static final fun toCollection (Ljava/lang/CharSequence;Ljava/util/Collection;)Ljava/util/Collection;
public static final fun toDoubleOrNull (Ljava/lang/String;)Ljava/lang/Double;
public static final fun toFloatOrNull (Ljava/lang/String;)Ljava/lang/Float;