Merge pull request #2109 from Kotlin/fix-incompatible-targets

Fix incompatible targets in guide
diff --git a/.gitignore b/.gitignore
index c56b7b9..a88aa87 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,3 +15,6 @@
 
 # Modules for JS projects build
 node_modules
+
+# benchmarks.jar
+/benchmarks.jar
diff --git a/README.md b/README.md
index 6841e13..9863841 100644
--- a/README.md
+++ b/README.md
@@ -35,6 +35,8 @@
 * **Additional links**
   * [Kotlin Serialization Guide](docs/serialization-guide.md)
   * [Full API reference](https://kotlinlang.org/api/kotlinx.serialization/)
+  * [Submitting issues and PRs](CONTRIBUTING.md)
+  * [Building this library](docs/building.md)
 
 ## Introduction and references
 
diff --git a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/JacksonComparisonBenchmark.kt b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/JacksonComparisonBenchmark.kt
index c192fc0..d162418 100644
--- a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/JacksonComparisonBenchmark.kt
+++ b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/JacksonComparisonBenchmark.kt
@@ -8,6 +8,7 @@
 import okio.blackholeSink
 import okio.buffer
 import org.openjdk.jmh.annotations.*
+import java.io.ByteArrayInputStream
 import java.io.OutputStream
 import java.util.concurrent.*
 
@@ -75,6 +76,7 @@
     }
 
     private val stringData = Json.encodeToString(DefaultPixelEvent.serializer(), data)
+    private val utf8BytesData = stringData.toByteArray()
 
     @Serializable
     private class SmallDataClass(val id: Int, val name: String)
@@ -97,6 +99,9 @@
     fun kotlinToStream() = Json.encodeToStream(DefaultPixelEvent.serializer(), data, devNullStream)
 
     @Benchmark
+    fun kotlinFromStream() = Json.decodeFromStream(DefaultPixelEvent.serializer(), ByteArrayInputStream(utf8BytesData))
+
+    @Benchmark
     fun kotlinToOkio() = Json.encodeToBufferedSink(DefaultPixelEvent.serializer(), data, devNullSink)
 
     @Benchmark
diff --git a/build.gradle b/build.gradle
index 170a9df..56f3f0a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -147,6 +147,8 @@
 }
 
 def unpublishedProjects = ["benchmark", "guide", "kotlinx-serialization-json-tests"] as Set
+def excludedFromBomProjects = unpublishedProjects + "kotlinx-serialization-bom" as Set
+def uncoveredProjects = ["kotlinx-serialization-bom", "benchmark", "guide"] as Set
 
 subprojects {
     tasks.withType(org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile).all { task ->
@@ -162,12 +164,11 @@
     if (!unpublishedProjects.contains(project.name)) {
         apply from: rootProject.file('gradle/publishing.gradle')
     }
-
 }
 
 subprojects {
     // Can't be applied to BOM
-    if (project.name == "kotlinx-serialization-bom" || project.name == "benchmark" || project.name == "guide") return
+    if (excludedFromBomProjects.contains(project.name)) return
 
     // Animalsniffer setup
     apply plugin: 'ru.vyarus.animalsniffer'
@@ -190,9 +191,16 @@
             signature 'net.sf.androidscents.signature:android-api-level-14:4.0_r4@signature'
             signature 'org.codehaus.mojo.signature:java18:1.0@signature'
         }
-    }
 
-    // Kover setup
+        // Add dependency on kotlinx-serialization-bom inside other kotlinx-serialization modules themselves, so they have same versions
+        BomKt.addBomApiDependency(project, ":kotlinx-serialization-bom")
+    }
+}
+
+// Kover setup
+subprojects {
+    if (uncoveredProjects.contains(project.name)) return
+
     apply from: rootProject.file("gradle/kover.gradle")
 }
 
diff --git a/buildSrc/src/main/kotlin/Bom.kt b/buildSrc/src/main/kotlin/Bom.kt
new file mode 100644
index 0000000..7f93ed3
--- /dev/null
+++ b/buildSrc/src/main/kotlin/Bom.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.*
+import org.jetbrains.kotlin.gradle.dsl.*
+
+fun Project.addBomApiDependency(bomProjectPath: String) {
+    val isMultiplatform = plugins.hasPlugin("kotlin-multiplatform")
+
+    if (isMultiplatform) {
+        kotlinExtension.sourceSets.getByName("jvmMain").dependencies {
+            api(project.dependencies.platform(project(bomProjectPath)))
+        }
+    } else {
+        dependencies {
+            "api"(platform(project(bomProjectPath)))
+        }
+    }
+}
+
diff --git a/buildSrc/src/main/kotlin/Java9Modularity.kt b/buildSrc/src/main/kotlin/Java9Modularity.kt
index d33e11b..b5a345f 100644
--- a/buildSrc/src/main/kotlin/Java9Modularity.kt
+++ b/buildSrc/src/main/kotlin/Java9Modularity.kt
@@ -19,6 +19,9 @@
     @JvmStatic
     @JvmOverloads
     fun Project.configureJava9ModuleInfo(multiRelease: Boolean = true) {
+        val disableJPMS = this.rootProject.extra.has("disableJPMS")
+        val ideaActive = System.getProperty("idea.active") == "true"
+        if (disableJPMS || ideaActive) return
         val kotlin = extensions.findByType<KotlinProjectExtension>() ?: return
         val jvmTargets = kotlin.targets.filter { it is KotlinJvmTarget || it is KotlinWithJavaTarget<*> }
         if (jvmTargets.isEmpty()) {
diff --git a/docs/building.md b/docs/building.md
index 533cdcc..840fae0 100644
--- a/docs/building.md
+++ b/docs/building.md
@@ -2,15 +2,22 @@
 
 ## JDK version
 
-To build Kotlin Serialization JDK version 9 or higher is required.
+To build Kotlin Serialization JDK version 11 or higher is required. Make sure this is your default JDK (`JAVA_HOME` is set accordingly).
 This is needed to compile the `module-info` file included for JPMS support.
 
+In case you are determined to use different JDK version, or experience problems with JPMS you can turn off compilation of modules
+completely with `disableJPMS` property: add `disableJPMS=true` to gradle.properties or `-PdisableJPMS` to Gradle CLI invocation.
+
 ## Runtime library
 
 Kotlin Serialization runtime library itself is a [multiplatform](http://kotlinlang.org/docs/reference/multiplatform.html) project.
-To build library from the source and run all tests, use `./gradlew build`. Corresponding platform tasks like `jvmTest`, `jsTest` and so on are also available.
+To build library from the source and run all tests, use `./gradlew build`. Corresponding platform tasks like `jvmTest`, `jsIrTest`, `nativeTest` and so on are also available.
 
-To install it into the local Maven repository, run `./gradlew publishToMavenLocal`. 
+Project can be opened in in Intellij IDEA without additional prerequisites.
+In case you want to work with Protobuf tests, you may need to run `./gradlew generateTestProto` beforehand.
+
+
+To install runtime library into the local Maven repository, run `./gradlew publishToMavenLocal`. 
 After that, you can include this library in arbitrary projects like usual gradle dependency:
 
 ```gradle
@@ -23,17 +30,19 @@
 }
 ```
 
-To open project in Intellij IDEA, first run `./gradlew generateTestProto` from console.
-Make sure you've set an option 'Use Gradle wrapper' on import to use a correct version of Gradle.
-You may also need to mark `runtime/build/generated/source/proto/test/java` as 'Generated source root' to build project/run tests in IDE without delegating a build to Gradle.
-This requires Kotlin 1.3.11 and higher.
+Note that by default, only one Native target is built (the one that is the current host, e.g. `macosX64` on Intel Mac machines, `linuxX64` on linux machines, etc).
+To compile and publish all Native artifacts, not only the host one, use Gradle property `native.deploy=true`.
 
-To use snapshot version of compiler (if you have built it from sources), use flag `-Pbootstrap`. To compile and publish all Native artifacts, not only the host one, use `-Pnative.deploy=true`.
+To use snapshot version of compiler (if you have built and install it from sources), use flag `-Pbootstrap`.
+If you have built both Kotlin and Kotlin/Native compilers, set `KONAN_LOCAL_DIST` environment property to the path with Kotlin/Native distribution
+(usually `kotlin-native/dist` folder inside Kotlin project).
 
-`master` branch of library should be binary compatible with latest released compiler plugin. In case you want to test some new features from other branches, which are still in development and may not be compatible in terms of bytecode produced by plugin, you'll need to build the plugin by yourself.
+`master` and `dev` branches of library should be binary compatible with latest released compiler plugin. In case you want to test some new features from other branches,
+which are still in development and may not be compatible in terms of bytecode produced by plugin, you'll need to build the plugin by yourself.
 
 ## Compiler plugin
 
 Compiler plugin for Gradle/Maven and IntelliJ plugin, starting from Kotlin 1.3, are embedded into the Kotlin compiler. 
 
-Sources and steps to build it are located [here](https://github.com/JetBrains/kotlin/blob/master/plugins/kotlin-serialization/kotlin-serialization-compiler/). In general, you'll just need to run `./gradlew dist install` to get `1.x.255` versions of Kotlin compiler, stdlib and serialization plugins in the Maven local repository.
+Sources and steps to build it are located [here](https://github.com/JetBrains/kotlin/tree/master/plugins/kotlinx-serialization).
+In short, you'll just need to run `./gradlew dist install` to get `1.x.255-SNAPSHOT` versions of Kotlin compiler, stdlib and serialization plugins in the Maven local repository.
diff --git a/formats/hocon/build.gradle b/formats/hocon/build.gradle
index 0dadca3..4e2def9 100644
--- a/formats/hocon/build.gradle
+++ b/formats/hocon/build.gradle
@@ -21,6 +21,7 @@
 dependencies {
     api project(':kotlinx-serialization-core')
     api 'org.jetbrains.kotlin:kotlin-stdlib'
+    api 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
 
     api 'com.typesafe:config:1.4.1'
 
diff --git a/formats/json-tests/jvmTest/src/kotlinx/serialization/json/JsonConcurrentStressTest.kt b/formats/json-tests/jvmTest/src/kotlinx/serialization/json/JsonConcurrentStressTest.kt
new file mode 100644
index 0000000..cdcbab7
--- /dev/null
+++ b/formats/json-tests/jvmTest/src/kotlinx/serialization/json/JsonConcurrentStressTest.kt
@@ -0,0 +1,78 @@
+package kotlinx.serialization.json
+
+import kotlinx.coroutines.*
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.builtins.*
+import org.junit.Test
+import java.io.ByteArrayInputStream
+import java.io.ByteArrayOutputStream
+import kotlin.random.*
+import kotlin.test.*
+
+// Stresses out that JSON decoded in parallel does not interfere (mostly via caching of various buffers)
+class JsonConcurrentStressTest : JsonTestBase() {
+    private val charset = "ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz0123456789"
+
+    @Test
+    fun testDecodeInParallelSimpleList() = doTest(100) { mode ->
+        val value = (1..10000).map { Random.nextDouble() }
+        val string = Json.encodeToString(ListSerializer(Double.serializer()), value, mode)
+        assertEquals(value, Json.decodeFromString(ListSerializer(Double.serializer()), string, mode))
+    }
+
+    @Serializable
+    data class Foo(val s: String, val f: Foo?)
+
+    @Test
+    fun testDecodeInParallelListOfPojo() = doTest(1_000) { mode ->
+        val value = (1..100).map {
+            val randomString = getRandomString()
+            val nestedFoo = Foo("null抢\u000E鋽윝䑜厼\uF70A紲ᢨ䣠null⛾䉻嘖緝ᯧnull쎶\u0005null" + randomString, null)
+            Foo(getRandomString(), nestedFoo)
+        }
+        val string = Json.encodeToString(ListSerializer(Foo.serializer()), value, mode)
+        assertEquals(value, Json.decodeFromString(ListSerializer(Foo.serializer()), string, mode))
+    }
+
+    @Test
+    fun testDecodeInParallelPojo() = doTest(100_000) { mode ->
+        val randomString = getRandomString()
+        val nestedFoo = Foo("null抢\u000E鋽윝䑜厼\uF70A紲ᢨ䣠null⛾䉻嘖緝ᯧnull쎶\u0005null" + randomString, null)
+        val randomFoo = Foo(getRandomString(), nestedFoo)
+        val string = Json.encodeToString(Foo.serializer(), randomFoo, mode)
+        assertEquals(randomFoo, Json.decodeFromString(Foo.serializer(), string, mode))
+    }
+
+    @Test
+    fun testDecodeInParallelSequencePojo() =  runBlocking<Unit> {
+        for (i in 1 until 1_000) {
+            launch(Dispatchers.Default) {
+                val values = (1..100).map {
+                    val randomString = getRandomString()
+                    val nestedFoo = Foo("null抢\u000E鋽윝䑜厼\uF70A紲ᢨ䣠null⛾䉻嘖緝ᯧnull쎶\u0005null" + randomString, null)
+                    Foo(getRandomString(), nestedFoo)
+                }
+                val baos = ByteArrayOutputStream()
+                for (value in values) {
+                    Json.encodeToStream(Foo.serializer(), value, baos)
+                }
+                val bais = ByteArrayInputStream(baos.toByteArray())
+                assertEquals(values, Json.decodeToSequence(bais, Foo.serializer()).toList())
+            }
+        }
+    }
+
+    private fun getRandomString() = (1..Random.nextInt(0, charset.length)).map { charset[it] }.joinToString(separator = "")
+
+    private fun doTest(iterations: Int, block: (JsonTestingMode) -> Unit) {
+        runBlocking<Unit> {
+            for (i in 1 until iterations) {
+                launch(Dispatchers.Default) {
+                    parametrizedTest {
+                        block(it)
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/formats/json-tests/jvmTest/src/kotlinx/serialization/json/ParallelJsonStressTest.kt b/formats/json-tests/jvmTest/src/kotlinx/serialization/json/ParallelJsonStressTest.kt
deleted file mode 100644
index 3901aab..0000000
--- a/formats/json-tests/jvmTest/src/kotlinx/serialization/json/ParallelJsonStressTest.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package kotlinx.serialization.json
-
-import kotlinx.coroutines.*
-import kotlinx.serialization.builtins.*
-import kotlinx.serialization.test.*
-import org.junit.*
-import kotlin.concurrent.*
-import kotlin.random.*
-
-class ParallelJsonStressTest : JsonTestBase() {
-    private val iterations = 1_000_000
-
-    @Test
-    fun testDecodeInParallel() = runBlocking<Unit> {
-        for (i in 1..1000) {
-            launch(Dispatchers.Default) {
-                val value = (1..10000).map { Random.nextDouble() }
-                assertSerializedAndRestored(value, ListSerializer(Double.serializer()))
-            }
-        }
-    }
-}
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/CharArrayPool.common.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/CharArrayPool.common.kt
new file mode 100644
index 0000000..920b65b
--- /dev/null
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/CharArrayPool.common.kt
@@ -0,0 +1,9 @@
+/*
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+package kotlinx.serialization.json.internal
+
+internal expect object CharArrayPoolBatchSize {
+    fun take(): CharArray
+    fun release(array: CharArray)
+}
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonStreams.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonStreams.kt
index c641f4f..74dbe45 100644
--- a/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonStreams.kt
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonStreams.kt
@@ -34,10 +34,14 @@
     reader: SerialReader
 ): T {
     val lexer = ReaderJsonLexer(reader)
-    val input = StreamingJsonDecoder(this, WriteMode.OBJ, lexer, deserializer.descriptor, null)
-    val result = input.decodeSerializableValue(deserializer)
-    lexer.expectEof()
-    return result
+    try {
+        val input = StreamingJsonDecoder(this, WriteMode.OBJ, lexer, deserializer.descriptor, null)
+        val result = input.decodeSerializableValue(deserializer)
+        lexer.expectEof()
+        return result
+    } finally {
+        lexer.release()
+    }
 }
 
 @PublishedApi
@@ -47,7 +51,7 @@
     deserializer: DeserializationStrategy<T>,
     format: DecodeSequenceMode = DecodeSequenceMode.AUTO_DETECT
 ): Sequence<T> {
-    val lexer = ReaderJsonLexer(reader)
+    val lexer = ReaderJsonLexer(reader, CharArray(BATCH_SIZE)) // Unpooled buffer due to lazy nature of sequence
     val iter = JsonIterator(format, this, lexer, deserializer)
     return Sequence { iter }.constrainOnce()
 }
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/JsonLexer.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/JsonLexer.kt
index 83483ea..fa4772c 100644
--- a/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/JsonLexer.kt
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/JsonLexer.kt
@@ -14,7 +14,7 @@
  * For some reason this hand-rolled implementation is faster than
  * fun ArrayAsSequence(s: CharArray): CharSequence = java.nio.CharBuffer.wrap(s, 0, length)
  */
-internal class ArrayAsSequence(val buffer: CharArray) : CharSequence {
+internal class ArrayAsSequence(private val buffer: CharArray) : CharSequence {
     override var length: Int = buffer.size
 
     override fun get(index: Int): Char = buffer[index]
@@ -34,11 +34,11 @@
 
 internal class ReaderJsonLexer(
     private val reader: SerialReader,
-    charsBuffer: CharArray = CharArray(BATCH_SIZE)
+    private val buffer: CharArray = CharArrayPoolBatchSize.take()
 ) : AbstractJsonLexer() {
     private var threshold: Int = DEFAULT_THRESHOLD // chars
 
-    override val source: ArrayAsSequence = ArrayAsSequence(charsBuffer)
+    override val source: ArrayAsSequence = ArrayAsSequence(buffer)
 
     init {
         preload(0)
@@ -177,4 +177,8 @@
 
     // Can be carefully implemented but postponed for now
     override fun consumeLeadingMatchingValue(keyToMatch: String, isLenient: Boolean): String? = null
+
+    fun release() {
+        CharArrayPoolBatchSize.release(buffer)
+    }
 }
diff --git a/formats/json/jsMain/src/kotlinx/serialization/json/internal/CharArrayPool.kt b/formats/json/jsMain/src/kotlinx/serialization/json/internal/CharArrayPool.kt
new file mode 100644
index 0000000..3f27896
--- /dev/null
+++ b/formats/json/jsMain/src/kotlinx/serialization/json/internal/CharArrayPool.kt
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+package kotlinx.serialization.json.internal
+
+
+internal actual object CharArrayPoolBatchSize {
+
+    actual fun take(): CharArray = CharArray(BATCH_SIZE)
+
+    actual fun release(array: CharArray) = Unit
+}
diff --git a/formats/json/jvmMain/src/kotlinx/serialization/json/internal/CharArrayPool.kt b/formats/json/jvmMain/src/kotlinx/serialization/json/internal/CharArrayPool.kt
index e51b3de..08d22ec 100644
--- a/formats/json/jvmMain/src/kotlinx/serialization/json/internal/CharArrayPool.kt
+++ b/formats/json/jvmMain/src/kotlinx/serialization/json/internal/CharArrayPool.kt
@@ -1,10 +1,14 @@
+/*
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
 package kotlinx.serialization.json.internal
 
 import java.util.concurrent.*
 
-internal object CharArrayPool {
+internal open class CharArrayPoolBase {
     private val arrays = ArrayDeque<CharArray>()
     private var charsTotal = 0
+
     /*
      * Not really documented kill switch as a workaround for potential
      * (unlikely) problems with memory consumptions.
@@ -13,7 +17,7 @@
         System.getProperty("kotlinx.serialization.json.pool.size").toIntOrNull()
     }.getOrNull() ?: 1024 * 1024 // 2 MB seems to be a reasonable constraint, (1M of chars)
 
-    fun take(): CharArray {
+    protected fun take(size: Int): CharArray {
         /*
          * Initially the pool is empty, so an instance will be allocated
          * and the pool will be populated in the 'release'
@@ -21,12 +25,30 @@
         val candidate = synchronized(this) {
             arrays.removeLastOrNull()?.also { charsTotal -= it.size }
         }
-        return candidate ?: CharArray(128)
+        return candidate ?: CharArray(size)
     }
 
-    fun release(array: CharArray) = synchronized(this) {
+    protected fun releaseImpl(array: CharArray): Unit = synchronized(this) {
         if (charsTotal + array.size >= MAX_CHARS_IN_POOL) return@synchronized
         charsTotal += array.size
         arrays.addLast(array)
     }
 }
+
+internal object CharArrayPool : CharArrayPoolBase() {
+    fun take(): CharArray = super.take(128)
+
+    // Can release array of an arbitrary size
+    fun release(array: CharArray) = releaseImpl(array)
+}
+
+// Pools char arrays of size 16K
+internal actual object CharArrayPoolBatchSize : CharArrayPoolBase() {
+
+    actual fun take(): CharArray = super.take(BATCH_SIZE)
+
+    actual fun release(array: CharArray) {
+        require(array.size == BATCH_SIZE) { "Inconsistent internal invariant: unexpected array size ${array.size}" }
+        releaseImpl(array)
+    }
+}
diff --git a/formats/json/jvmMain/src/kotlinx/serialization/json/internal/JvmJsonStreams.kt b/formats/json/jvmMain/src/kotlinx/serialization/json/internal/JvmJsonStreams.kt
index 17eac0a..746c044 100644
--- a/formats/json/jvmMain/src/kotlinx/serialization/json/internal/JvmJsonStreams.kt
+++ b/formats/json/jvmMain/src/kotlinx/serialization/json/internal/JvmJsonStreams.kt
@@ -68,12 +68,14 @@
                     0.toByte() -> {
                         charArray[sz++] = ch.toChar()
                     }
+
                     1.toByte() -> {
                         val escapedString = ESCAPE_STRINGS[ch]!!
                         sz = ensureTotalCapacity(sz, escapedString.length)
                         escapedString.toCharArray(charArray, sz, 0, escapedString.length)
                         sz += escapedString.length
                     }
+
                     else -> {
                         charArray[sz] = '\\'
                         charArray[sz + 1] = marker.toInt().toChar()
@@ -204,9 +206,9 @@
         }
     }
 
-    /*
-    Sources taken from okio library with minor changes, see https://github.com/square/okio
-    */
+    /**
+     * Sources taken from okio library with minor changes, see https://github.com/square/okio
+     */
     private fun writeUtf8CodePoint(codePoint: Int) {
         when {
             codePoint < 0x80 -> {
@@ -214,17 +216,20 @@
                 ensure(1)
                 write(codePoint)
             }
+
             codePoint < 0x800 -> {
                 // Emit a 11-bit code point with 2 bytes.
                 ensure(2)
                 write(codePoint shr 6 or 0xc0) // 110xxxxx
                 write(codePoint and 0x3f or 0x80) // 10xxxxxx
             }
+
             codePoint in 0xd800..0xdfff -> {
                 // Emit a replacement character for a partial surrogate.
                 ensure(1)
                 write('?'.code)
             }
+
             codePoint < 0x10000 -> {
                 // Emit a 16-bit code point with 3 bytes.
                 ensure(3)
@@ -232,6 +237,7 @@
                 write(codePoint shr 6 and 0x3f or 0x80) // 10xxxxxx
                 write(codePoint and 0x3f or 0x80) // 10xxxxxx
             }
+
             codePoint <= 0x10ffff -> {
                 // Emit a 21-bit code point with 4 bytes.
                 ensure(4)
@@ -240,6 +246,7 @@
                 write(codePoint shr 6 and 0x3f or 0x80) // 10xxyyyy
                 write(codePoint and 0x3f or 0x80) // 10yyyyyy
             }
+
             else -> {
                 throw JsonEncodingException("Unexpected code point: $codePoint")
             }
@@ -247,11 +254,8 @@
     }
 }
 
-internal class JavaStreamSerialReader(
-    stream: InputStream,
-    charset: Charset = Charsets.UTF_8
-) : SerialReader {
-    private val reader = stream.reader(charset)
+internal class JavaStreamSerialReader(stream: InputStream) : SerialReader {
+    private val reader = stream.reader(Charsets.UTF_8)
 
     override fun read(buffer: CharArray, bufferOffset: Int, count: Int): Int {
         return reader.read(buffer, bufferOffset, count)
diff --git a/formats/json/nativeMain/src/kotlinx/serialization/json/internal/CharArrayPool.kt b/formats/json/nativeMain/src/kotlinx/serialization/json/internal/CharArrayPool.kt
new file mode 100644
index 0000000..03c9926
--- /dev/null
+++ b/formats/json/nativeMain/src/kotlinx/serialization/json/internal/CharArrayPool.kt
@@ -0,0 +1,9 @@
+/*
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+package kotlinx.serialization.json.internal
+
+internal actual object CharArrayPoolBatchSize {
+    actual fun take(): CharArray = CharArray(BATCH_SIZE)
+    actual fun release(array: CharArray) = Unit
+}
diff --git a/gradle.properties b/gradle.properties
index f1ea1fb..03d7428 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -17,7 +17,8 @@
 native.deploy=
 validator_version=0.11.0
 knit_version=0.4.0
-coroutines_version=1.3.9
+# Only for tests
+coroutines_version=1.6.4
 kover_version=0.4.2
 okio_version=3.1.0