summaryrefslogtreecommitdiff
path: root/runtime/commonMain/src/kotlinx/serialization/json/internal/StringOps.kt
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/commonMain/src/kotlinx/serialization/json/internal/StringOps.kt')
-rw-r--r--runtime/commonMain/src/kotlinx/serialization/json/internal/StringOps.kt90
1 files changed, 90 insertions, 0 deletions
diff --git a/runtime/commonMain/src/kotlinx/serialization/json/internal/StringOps.kt b/runtime/commonMain/src/kotlinx/serialization/json/internal/StringOps.kt
new file mode 100644
index 00000000..40e4cef8
--- /dev/null
+++ b/runtime/commonMain/src/kotlinx/serialization/json/internal/StringOps.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2018 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package kotlinx.serialization.json.internal
+
+import kotlinx.serialization.*
+
+private fun toHexChar(i: Int) : Char {
+ val d = i and 0xf
+ return if (d < 10) (d + '0'.toInt()).toChar()
+ else (d - 10 + 'a'.toInt()).toChar()
+}
+
+/*
+ * Even though the actual size of this array is 92, it has to be the power of two, otherwise
+ * JVM cannot perform advanced range-check elimination and vectorization in printQuoted
+ */
+@SharedImmutable
+private val ESCAPE_CHARS: Array<String?> = arrayOfNulls<String>(128).apply {
+ for (c in 0..0x1f) {
+ val c1 = toHexChar(c shr 12)
+ val c2 = toHexChar(c shr 8)
+ val c3 = toHexChar(c shr 4)
+ val c4 = toHexChar(c)
+ this[c] = "\\u$c1$c2$c3$c4"
+ }
+ this['"'.toInt()] = "\\\""
+ this['\\'.toInt()] = "\\\\"
+ this['\t'.toInt()] = "\\t"
+ this['\b'.toInt()] = "\\b"
+ this['\n'.toInt()] = "\\n"
+ this['\r'.toInt()] = "\\r"
+ this[0x0c] = "\\f"
+}
+
+internal fun StringBuilder.printQuoted(value: String) {
+ append(STRING)
+ var lastPos = 0
+ val length = value.length
+ for (i in 0 until length) {
+ val c = value[i].toInt()
+ // Do not replace this constant with C2ESC_MAX (which is smaller than ESCAPE_CHARS size),
+ // otherwise JIT won't eliminate range check and won't vectorize this loop
+ if (c >= ESCAPE_CHARS.size) continue // no need to escape
+ val esc = ESCAPE_CHARS[c] ?: continue
+ append(value, lastPos, i) // flush prev
+ append(esc)
+ lastPos = i + 1
+ }
+ append(value, lastPos, length)
+ append(STRING)
+}
+
+/**
+ * Returns `true` if the contents of this string is equal to the word "true", ignoring case, `false` if content equals "false",
+ * and throws [IllegalStateException] otherwise.
+ */
+internal fun String.toBooleanStrict(): Boolean = toBooleanStrictOrNull() ?: throw IllegalStateException("$this does not represent a Boolean")
+
+/**
+ * Returns `true` if the contents of this string is equal to the word "true", ignoring case, `false` if content equals "false",
+ * and returns `null` otherwise.
+ */
+internal fun String.toBooleanStrictOrNull(): Boolean? = when {
+ this.equals("true", ignoreCase = true) -> true
+ this.equals("false", ignoreCase = true) -> false
+ else -> null
+}
+
+internal fun shouldBeQuoted(str: String): Boolean {
+ if (str == NULL) return true
+ for (ch in str) {
+ if (charToTokenClass(ch) != TC_OTHER) return true
+ }
+
+ return false
+}