summaryrefslogtreecommitdiff
path: root/formats/json-okio/commonMain/src/kotlinx/serialization/json/okio/internal/OkioJsonStreams.kt
diff options
context:
space:
mode:
Diffstat (limited to 'formats/json-okio/commonMain/src/kotlinx/serialization/json/okio/internal/OkioJsonStreams.kt')
-rw-r--r--formats/json-okio/commonMain/src/kotlinx/serialization/json/okio/internal/OkioJsonStreams.kt124
1 files changed, 124 insertions, 0 deletions
diff --git a/formats/json-okio/commonMain/src/kotlinx/serialization/json/okio/internal/OkioJsonStreams.kt b/formats/json-okio/commonMain/src/kotlinx/serialization/json/okio/internal/OkioJsonStreams.kt
new file mode 100644
index 00000000..1de89713
--- /dev/null
+++ b/formats/json-okio/commonMain/src/kotlinx/serialization/json/okio/internal/OkioJsonStreams.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.okio.internal
+
+import kotlinx.serialization.json.internal.*
+import okio.*
+
+// Copied from kotlinx/serialization/json/internal/StringOps.kt
+private fun toHexChar(i: Int) : Char {
+ val d = i and 0xf
+ return if (d < 10) (d + '0'.code).toChar()
+ else (d - 10 + 'a'.code).toChar()
+}
+
+// Copied from kotlinx/serialization/json/internal/StringOps.kt
+private val ESCAPE_STRINGS: Array<String?> = arrayOfNulls<String>(93).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['"'.code] = "\\\""
+ this['\\'.code] = "\\\\"
+ this['\t'.code] = "\\t"
+ this['\b'.code] = "\\b"
+ this['\n'.code] = "\\n"
+ this['\r'.code] = "\\r"
+ this[0x0c] = "\\f"
+}
+
+
+
+internal class JsonToOkioStreamWriter(private val sink: BufferedSink) : InternalJsonWriter {
+ override fun writeLong(value: Long) {
+ write(value.toString())
+ }
+
+ override fun writeChar(char: Char) {
+ sink.writeUtf8CodePoint(char.code)
+ }
+
+ override fun write(text: String) {
+ sink.writeUtf8(text)
+ }
+
+ override fun writeQuoted(text: String) {
+ sink.writeUtf8CodePoint('"'.code)
+ var lastPos = 0
+ for (i in text.indices) {
+ val c = text[i].code
+ if (c < ESCAPE_STRINGS.size && ESCAPE_STRINGS[c] != null) {
+ sink.writeUtf8(text, lastPos, i) // flush prev
+ sink.writeUtf8(ESCAPE_STRINGS[c]!!)
+ lastPos = i + 1
+ }
+ }
+
+ if (lastPos != 0) sink.writeUtf8(text, lastPos, text.length)
+ else sink.writeUtf8(text)
+ sink.writeUtf8CodePoint('"'.code)
+ }
+
+ override fun release() {
+ // no-op, see https://github.com/Kotlin/kotlinx.serialization/pull/1982#discussion_r915043700
+ }
+}
+
+// Max value for a code point placed in one Char
+private const val SINGLE_CHAR_MAX_CODEPOINT = Char.MAX_VALUE.code
+// Value added to the high UTF-16 surrogate after shifting
+private const val HIGH_SURROGATE_HEADER = 0xd800 - (0x010000 ushr 10)
+// Value added to the low UTF-16 surrogate after masking
+private const val LOW_SURROGATE_HEADER = 0xdc00
+
+
+internal class OkioSerialReader(private val source: BufferedSource): InternalJsonReader {
+ /*
+ A sequence of code points is read from UTF-8, some of it can take 2 characters.
+ In case the last code point requires 2 characters, and the array is already full, we buffer the second character
+ */
+ private var bufferedChar: Char? = null
+
+ override fun read(buffer: CharArray, bufferOffset: Int, count: Int): Int {
+ var i = 0
+
+ if (bufferedChar != null) {
+ buffer[bufferOffset + i] = bufferedChar!!
+ i++
+ bufferedChar = null
+ }
+
+ while (i < count && !source.exhausted()) {
+ val codePoint = source.readUtf8CodePoint()
+ if (codePoint <= SINGLE_CHAR_MAX_CODEPOINT) {
+ buffer[bufferOffset + i] = codePoint.toChar()
+ i++
+ } else {
+ // an example of working with surrogates is taken from okio library with minor changes, see https://github.com/square/okio
+ // UTF-16 high surrogate: 110110xxxxxxxxxx (10 bits)
+ // UTF-16 low surrogate: 110111yyyyyyyyyy (10 bits)
+ // Unicode code point: 00010000000000000000 + xxxxxxxxxxyyyyyyyyyy (21 bits)
+ val upChar = ((codePoint ushr 10) + HIGH_SURROGATE_HEADER).toChar()
+ val lowChar = ((codePoint and 0x03ff) + LOW_SURROGATE_HEADER).toChar()
+
+ buffer[bufferOffset + i] = upChar
+ i++
+
+ if (i < count) {
+ buffer[bufferOffset + i] = lowChar
+ i++
+ } else {
+ // if char array is full - buffer lower surrogate
+ bufferedChar = lowChar
+ }
+ }
+ }
+ return if (i > 0) i else -1
+ }
+}
+