Optimize ProtoBuf encoding:

    * Leverage hotspot intrinsics for conversion to LE
    * Optimize LE loops
    * Optimize varint decoding, add fast-path for single byte and two byte values as the most common
    * Single copy String decoding
    * Make extract parameters loops EA and inliner independent
diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/ProtoBuf.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/ProtoBuf.kt
index c8b1a66..af6f88d 100644
--- a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/ProtoBuf.kt
+++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/ProtoBuf.kt
@@ -12,7 +12,6 @@
 import kotlinx.serialization.modules.*
 import kotlinx.serialization.protobuf.ProtoBuf.Varint.decodeSignedVarintInt
 import kotlinx.serialization.protobuf.ProtoBuf.Varint.decodeSignedVarintLong
-import kotlinx.serialization.protobuf.ProtoBuf.Varint.decodeVarint
 import kotlinx.serialization.protobuf.ProtoBuf.Varint.encodeVarint
 import kotlin.jvm.*
 
@@ -160,7 +159,7 @@
             ProtoNumberType.DEFAULT
         )
 
-        override fun SerialDescriptor.getTag(index: Int) = this.getProtoDesc(index)
+        override fun SerialDescriptor.getTag(index: Int) = extractParameters(index)
 
         @Suppress("UNCHECKED_CAST", "NAME_SHADOWING")
         override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) = when {
@@ -235,12 +234,12 @@
 
         fun writeDouble(value: Double, tag: Int) {
             out.encode32((tag shl 3) or i64)
-            value.toLittleEndian().writeTo(out)
+            value.reverseBytes().writeTo(out)
         }
 
         fun writeFloat(value: Float, tag: Int) {
             out.encode32((tag shl 3) or i32)
-            value.toLittleEndian().writeTo(out)
+            value.reverseBytes().writeTo(out)
         }
 
         private fun OutputStream.encode32(
@@ -248,7 +247,7 @@
             format: ProtoNumberType = ProtoNumberType.DEFAULT
         ) {
             when (format) {
-                ProtoNumberType.FIXED -> number.toLittleEndian().writeTo(this)
+                ProtoNumberType.FIXED -> number.reverseBytes().writeTo(this)
                 ProtoNumberType.DEFAULT -> encodeVarint(number.toLong())
                 ProtoNumberType.SIGNED -> encodeVarint(((number shl 1) xor (number shr 31)))
             }
@@ -257,7 +256,7 @@
 
         private fun OutputStream.encode64(number: Long, format: ProtoNumberType = ProtoNumberType.DEFAULT) {
             when (format) {
-                ProtoNumberType.FIXED -> number.toLittleEndian().writeTo(this)
+                ProtoNumberType.FIXED -> number.reverseBytes().writeTo(this)
                 ProtoNumberType.DEFAULT -> encodeVarint(number)
                 ProtoNumberType.SIGNED -> encodeVarint((number shl 1) xor (number shr 63))
             }
@@ -265,7 +264,7 @@
     }
 
 
-    private open inner class ProtobufReader(val decoder: ProtobufDecoder) : TaggedDecoder<ProtoDesc>() {
+    private open inner class ProtobufReader(@JvmField val decoder: ProtobufDecoder) : TaggedDecoder<ProtoDesc>() {
         override val context: SerialModule
             get() = this@ProtoBuf.context
 
@@ -338,7 +337,10 @@
 
         override fun decodeTaggedByte(tag: ProtoDesc): Byte = decoder.nextInt(tag.numberType).toByte()
         override fun decodeTaggedShort(tag: ProtoDesc): Short = decoder.nextInt(tag.numberType).toShort()
-        override fun decodeTaggedInt(tag: ProtoDesc): Int = decoder.nextInt(tag.numberType)
+
+        override fun decodeTaggedInt(tag: ProtoDesc): Int {
+            return decoder.nextInt(tag.numberType)
+        }
         override fun decodeTaggedLong(tag: ProtoDesc): Long = decoder.nextLong(tag.numberType)
         override fun decodeTaggedFloat(tag: ProtoDesc): Float = decoder.nextFloat()
         override fun decodeTaggedDouble(tag: ProtoDesc): Double = decoder.nextDouble()
@@ -365,7 +367,7 @@
             return setOfEntries.associateBy({ it.key }, { it.value }) as T
         }
 
-        override fun SerialDescriptor.getTag(index: Int) = this.getProtoDesc(index)
+        override fun SerialDescriptor.getTag(index: Int) = extractParameters(index)
 
         override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
             while (true) {
@@ -385,10 +387,10 @@
         decoder: ProtobufDecoder,
         private val targetTag: ProtoDesc
     ) : ProtobufReader(decoder) {
-        private var ind = -1
+        private var index = -1
 
         override fun decodeElementIndex(descriptor: SerialDescriptor) =
-            if (decoder.currentId == targetTag.protoId) ++ind else READ_DONE
+            if (decoder.currentId == targetTag.protoId) ++index else READ_DONE
 
         override fun SerialDescriptor.getTag(index: Int): ProtoDesc = targetTag
     }
@@ -399,7 +401,7 @@
                 else ProtoDesc(2, (parentTag?.numberType ?: ProtoNumberType.DEFAULT))
     }
 
-    internal class ProtobufDecoder(private val inp: ByteArrayInputStream) {
+    internal class ProtobufDecoder(private val input: ByteArrayInputStream) {
         @JvmField
         public var currentId = -1
         @JvmField
@@ -432,16 +434,16 @@
 
         @Suppress("NOTHING_TO_INLINE")
         private inline fun assertWireType(expected: Int) {
-            if (currentType != expected) throw ProtobufDecodingException("Expected wire type $expected, but found ${currentType}")
+            if (currentType != expected) throw ProtobufDecodingException("Expected wire type $expected, but found $currentType")
         }
 
         fun nextObject(): ByteArray {
             assertWireType(SIZE_DELIMITED)
-            val len = decode32()
-            check(len >= 0)
-            val ans = inp.readExactNBytes(len)
+            val length = decode32()
+            check(length >= 0)
+            val result = input.readExactNBytes(length)
             readTag()
-            return ans
+            return result
         }
 
         private fun InputStream.readExactNBytes(bytes: Int): ByteArray {
@@ -458,66 +460,69 @@
         fun nextInt(format: ProtoNumberType): Int {
             val wireType = if (format == ProtoNumberType.FIXED) i32 else VARINT
             assertWireType(wireType)
-            val ans = decode32(format)
+            val result = decode32(format)
             readTag()
-            return ans
+            return result
         }
 
         fun nextLong(format: ProtoNumberType): Long {
             val wireType = if (format == ProtoNumberType.FIXED) i64 else VARINT
             assertWireType(wireType)
-            val ans = decode64(format)
+            val result = decode64(format)
             readTag()
-            return ans
+            return result
         }
 
         fun nextFloat(): Float {
             assertWireType(i32)
-            val ans = readIntLittleEndian()
+            val result = Float.fromBits(readIntLittleEndian())
             readTag()
-            return Float.fromBits(ans)
+            return result
         }
 
         private fun readIntLittleEndian(): Int {
             var result = 0
             for (i in 0..3) {
-                val byte = inp.read()
-                result = (result shl 8) or byte
+                val byte = input.read() and 0x000000FF
+                result = result or (byte shl (i * 8))
             }
-            return result.toLittleEndian()
+            return result
         }
 
         private fun readLongLittleEndian(): Long {
             var result = 0L
             for (i in 0..7) {
-                val byte = inp.read()
-                result = (result shl 8) or byte.toLong()
+                val byte = (input.read() and 0x000000FF).toLong()
+                result = result or (byte shl (i * 8))
             }
-            return result.toLittleEndian()
+            return result
         }
 
         fun nextDouble(): Double {
             assertWireType(i64)
-            val ans = readLongLittleEndian()
+            val result = Double.fromBits(readLongLittleEndian())
             readTag()
-            return Double.fromBits(ans)
+            return result
         }
 
         fun nextString(): String {
-            val bytes = this.nextObject()
-            return bytes.decodeToString()
+            assertWireType(SIZE_DELIMITED)
+            val length = decode32()
+            check(length >= 0)
+            val result = input.readString(length)
+            readTag()
+            return result
         }
 
-        private fun decode32(format: ProtoNumberType = ProtoNumberType.DEFAULT, eofAllowed: Boolean = false): Int =
-            when (format) {
-                ProtoNumberType.DEFAULT -> decodeVarint(inp, 64, eofAllowed).toInt()
-                ProtoNumberType.SIGNED -> decodeSignedVarintInt(inp)
-                ProtoNumberType.FIXED -> readIntLittleEndian()
-            }
+        private fun decode32(format: ProtoNumberType = ProtoNumberType.DEFAULT, eofAllowed: Boolean = false): Int = when (format) {
+            ProtoNumberType.DEFAULT -> input.readVarint64(eofAllowed).toInt()
+            ProtoNumberType.SIGNED -> decodeSignedVarintInt(input)
+            ProtoNumberType.FIXED -> readIntLittleEndian()
+        }
 
         private fun decode64(format: ProtoNumberType = ProtoNumberType.DEFAULT): Long = when (format) {
-            ProtoNumberType.DEFAULT -> decodeVarint(inp, 64)
-            ProtoNumberType.SIGNED -> decodeSignedVarintLong(inp)
+            ProtoNumberType.DEFAULT -> input.readVarint64(false)
+            ProtoNumberType.SIGNED -> decodeSignedVarintLong(input)
             ProtoNumberType.FIXED -> readLongLittleEndian()
         }
     }
@@ -545,29 +550,8 @@
             write((value and 0x7F).toInt())
         }
 
-        internal fun decodeVarint(inp: InputStream, bitLimit: Int = 32, eofOnStartAllowed: Boolean = false): Long {
-            var result = 0L
-            var shift = 0
-            var b: Int
-            do {
-                if (shift >= bitLimit) {
-                    // Out of range
-                    throw ProtobufDecodingException("Varint too long: exceeded $bitLimit bits")
-                }
-                // Get 7 bits from next byte
-                b = inp.read()
-                if (b == -1) {
-                    if (eofOnStartAllowed && shift == 0) return -1
-                    else error("Unexpected EOF")
-                }
-                result = result or (b.toLong() and 0x7FL shl shift)
-                shift += 7
-            } while (b and 0x80 != 0)
-            return result
-        }
-
-        internal fun decodeSignedVarintInt(inp: InputStream): Int {
-            val raw = decodeVarint(inp, 32).toInt()
+        internal fun decodeSignedVarintInt(input: InputStream): Int {
+            val raw = input.readVarint32()
             val temp = raw shl 31 shr 31 xor raw shr 1
             // This extra step lets us deal with the largest signed values by treating
             // negative results from read unsigned methods as like unsigned values.
@@ -575,8 +559,8 @@
             return temp xor (raw and (1 shl 31))
         }
 
-        internal fun decodeSignedVarintLong(inp: InputStream): Long {
-            val raw = decodeVarint(inp, 64)
+        internal fun decodeSignedVarintLong(input: InputStream): Long {
+            val raw = input.readVarint64(false)
             val temp = raw shl 63 shr 63 xor raw shr 1
             // This extra step lets us deal with the largest signed values by treating
             // negative results from read unsigned methods as like unsigned values
@@ -594,10 +578,6 @@
             return ProtobufDecoder(ByteArrayInputStream(bytes))
         }
 
-        private fun SerialDescriptor.getProtoDesc(index: Int): ProtoDesc {
-            return extractParameters(this, index)
-        }
-
         internal const val VARINT = 0
         internal const val i64 = 1
         internal const val SIZE_DELIMITED = 2
@@ -618,19 +598,9 @@
     }
 }
 
-private fun Short.toLittleEndian(): Short =
-    (((this.toInt() and 0xff) shl 8) or ((this.toInt() and 0xffff) ushr 8)).toShort()
+private fun Float.reverseBytes(): Int = toRawBits().reverseBytes()
 
-private fun Int.toLittleEndian(): Int =
-    ((this and 0xffff).toShort().toLittleEndian().toInt() shl 16) or ((this ushr 16).toShort().toLittleEndian().toInt() and 0xffff)
-
-private fun Long.toLittleEndian(): Long =
-    ((this and 0xffffffff).toInt().toLittleEndian().toLong() shl 32) or ((this ushr 32).toInt()
-        .toLittleEndian().toLong() and 0xffffffff)
-
-private fun Float.toLittleEndian(): Int = toRawBits().toLittleEndian()
-
-private fun Double.toLittleEndian(): Long = toRawBits().toLittleEndian()
+private fun Double.reverseBytes(): Long = toRawBits().reverseBytes()
 
 private fun Int.writeTo(out: OutputStream) {
     for (i in 3 downTo 0) {
diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/ProtoTypes.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/ProtoTypes.kt
index de2c950..08f784f 100644
--- a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/ProtoTypes.kt
+++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/ProtoTypes.kt
@@ -24,7 +24,6 @@
     SIGNED(2L shl 32),
     FIXED(3L shl 32);
 
-
     internal fun equalTo(descriptor: ProtoDesc): Boolean {
         return descriptor and MASK == signature
     }
@@ -53,11 +52,12 @@
     return ProtoNumberType.FIXED
 }
 
-internal fun extractParameters(descriptor: SerialDescriptor, index: Int): ProtoDesc {
-    val annotations = descriptor.getElementAnnotations(index)
+internal fun SerialDescriptor.extractParameters(index: Int): ProtoDesc {
+    val annotations = getElementAnnotations(index)
     var protoId: Int = index + 1
     var format: ProtoNumberType = ProtoNumberType.DEFAULT
-    for (annotation in annotations) {
+    for (i in annotations.indices) { // Allocation-friendly loop
+        val annotation = annotations[i]
         if (annotation is ProtoId) {
             protoId = annotation.id
         } else if (annotation is ProtoType) {
@@ -68,12 +68,17 @@
 }
 
 internal fun extractProtoId(descriptor: SerialDescriptor, index: Int, zeroBasedDefault: Boolean = false): Int {
-    val protoId = descriptor.findAnnotation<ProtoId>(index)
-    return protoId?.id ?: (if (zeroBasedDefault) index else index + 1)
+    val annotations = descriptor.getElementAnnotations(index)
+    for (i in annotations.indices) { // Allocation-friendly loop
+        val annotation = annotations[i]
+        if (annotation is ProtoId) {
+            return annotation.id
+        }
+    }
+    return if (zeroBasedDefault) index else index + 1
 }
 
 public class ProtobufDecodingException(message: String) : SerializationException(message)
 
-internal inline fun <reified A: Annotation> SerialDescriptor.findAnnotation(elementIndex: Int): A? {
-    return getElementAnnotations(elementIndex).find { it is A } as A?
-}
+internal expect fun Int.reverseBytes(): Int
+internal expect fun Long.reverseBytes(): Long
diff --git a/formats/protobuf/jsMain/src/kotlinx/serialization/protobuf/Bytes.kt b/formats/protobuf/jsMain/src/kotlinx/serialization/protobuf/Bytes.kt
new file mode 100644
index 0000000..288a4a0
--- /dev/null
+++ b/formats/protobuf/jsMain/src/kotlinx/serialization/protobuf/Bytes.kt
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf
+
+private fun Short.reverseBytes(): Short = (((this.toInt() and 0xff) shl 8) or ((this.toInt() and 0xffff) ushr 8)).toShort()
+
+internal actual fun Int.reverseBytes(): Int =
+    ((this and 0xffff).toShort().reverseBytes().toInt() shl 16) or ((this ushr 16).toShort().reverseBytes().toInt() and 0xffff)
+
+internal actual  fun Long.reverseBytes(): Long =
+    ((this and 0xffffffff).toInt().reverseBytes().toLong() shl 32) or ((this ushr 32).toInt()
+        .reverseBytes().toLong() and 0xffffffff)
diff --git a/formats/protobuf/jvmMain/src/kotlinx/serialization/protobuf/Bytes.kt b/formats/protobuf/jvmMain/src/kotlinx/serialization/protobuf/Bytes.kt
new file mode 100644
index 0000000..bfb6fdd
--- /dev/null
+++ b/formats/protobuf/jvmMain/src/kotlinx/serialization/protobuf/Bytes.kt
@@ -0,0 +1,9 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf
+
+internal actual fun Int.reverseBytes(): Int = Integer.reverseBytes(this)
+
+internal actual  fun Long.reverseBytes(): Long = java.lang.Long.reverseBytes(this)
diff --git a/formats/protobuf/nativeMain/src/kotlinx/serialization/protobuf/Bytes.kt b/formats/protobuf/nativeMain/src/kotlinx/serialization/protobuf/Bytes.kt
new file mode 100644
index 0000000..288a4a0
--- /dev/null
+++ b/formats/protobuf/nativeMain/src/kotlinx/serialization/protobuf/Bytes.kt
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf
+
+private fun Short.reverseBytes(): Short = (((this.toInt() and 0xff) shl 8) or ((this.toInt() and 0xffff) ushr 8)).toShort()
+
+internal actual fun Int.reverseBytes(): Int =
+    ((this and 0xffff).toShort().reverseBytes().toInt() shl 16) or ((this ushr 16).toShort().reverseBytes().toInt() and 0xffff)
+
+internal actual  fun Long.reverseBytes(): Long =
+    ((this and 0xffffffff).toInt().reverseBytes().toLong() shl 32) or ((this ushr 32).toInt()
+        .reverseBytes().toLong() and 0xffffffff)
diff --git a/formats/protobuf/nativeTest/src/kotlinx/serialization/NativeTest.kt b/formats/protobuf/nativeTest/src/kotlinx/serialization/NativeTest.kt
index eea0315..5880d94 100644
--- a/formats/protobuf/nativeTest/src/kotlinx/serialization/NativeTest.kt
+++ b/formats/protobuf/nativeTest/src/kotlinx/serialization/NativeTest.kt
@@ -27,4 +27,9 @@
         val id1 = country.descriptor.findAnnotation<ProtoId>(0)?.id ?: 0
         assertEquals(10, id1)
     }
+
+    private inline fun <reified A: Annotation> SerialDescriptor.findAnnotation(elementIndex: Int): A? {
+        return getElementAnnotations(elementIndex).find { it is A } as A?
+    }
+
 }
diff --git a/runtime/commonMain/src/kotlinx/io/Streams.common.kt b/runtime/commonMain/src/kotlinx/io/Streams.common.kt
new file mode 100644
index 0000000..1e6a088
--- /dev/null
+++ b/runtime/commonMain/src/kotlinx/io/Streams.common.kt
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.io
+
+import kotlinx.serialization.*
+
+@Deprecated(message = message, level = DeprecationLevel.ERROR)
+expect open class IOException : Exception {
+    constructor()
+    constructor(message: String)
+}
+
+@InternalSerializationApi
+abstract class InputStream {
+    abstract fun read(): Int
+    abstract fun read(b: ByteArray, offset: Int, length: Int): Int
+    abstract fun readString(length: Int): String
+    abstract fun readVarint32(): Int
+    abstract fun readVarint64(eofAllowed: Boolean): Long
+}
+
+@InternalSerializationApi
+class ByteArrayInputStream(private var array: ByteArray) : InputStream() {
+
+    private var position: Int = 0
+
+    override fun read(): Int {
+        return if (position < array.size) array[position++].toInt() and 0xFF else -1
+    }
+
+    override fun read(b: ByteArray, offset: Int, length: Int): Int {
+        // avoid int overflow
+        if (offset < 0 || offset > b.size || length < 0
+            || length > b.size - offset
+        ) {
+            throw IndexOutOfBoundsException()
+        }
+        // Are there any bytes available?
+        if (this.position >= array.size) {
+            return -1
+        }
+        if (length == 0) {
+            return 0
+        }
+
+        val copied = if (this.array.size - position < length) this.array.size - position else length
+        array.copyInto(destination = b, destinationOffset = offset, startIndex = position, endIndex = position + copied)
+        position += copied
+        return copied
+    }
+
+    override fun readString(length: Int): String {
+        val result = array.decodeToString(position, position + length)
+        position += length
+        return result
+    }
+
+    override fun readVarint32(): Int {
+        if (position == array.size) {
+            error("Unexpected EOF")
+        }
+
+        // Fast-path: single and two byte values
+        var currentPosition = position
+        var result = array[currentPosition++].toInt()
+        if (result >= 0) {
+            position  = currentPosition
+            return result
+        } else if (array.size - position > 1) {
+            result = result xor (array[currentPosition++].toInt() shl 7)
+            if (result < 0) {
+                position = currentPosition
+                return result xor (0.inv() shl 7)
+            }
+        }
+
+        return readVarint32SlowPath()
+    }
+
+    override fun readVarint64(eofAllowed: Boolean): Long {
+        if (position == array.size) {
+            if (eofAllowed) return -1
+            else error("Unexpected EOF")
+        }
+
+        // Fast-path: single and two byte values
+        var currentPosition = position
+        var result = array[currentPosition++].toLong()
+        if (result >= 0) {
+            position  = currentPosition
+            return result
+        } else if (array.size - position > 1) {
+            result = result xor (array[currentPosition++].toLong() shl 7)
+            if (result < 0) {
+                position = currentPosition
+                return result xor (0L.inv() shl 7)
+            }
+        }
+
+        return readVarint64SlowPath()
+    }
+
+    private fun readVarint64SlowPath(): Long {
+        var result = 0L
+        var shift = 0
+        while (shift != 64) {
+            val byte = read()
+            result = result or ((byte and 0x7F).toLong() shl shift)
+            if (byte and 0x80 == 0) {
+                return result
+            }
+            shift += 7
+        }
+        throw SerializationException("Varint too long: exceeded 64 bits")
+    }
+
+    private fun readVarint32SlowPath(): Int {
+        var result = 0
+        var shift = 0
+        while (shift != 32) {
+            val byte = read()
+            result = result or ((byte and 0x7F) shl shift)
+            if (byte and 0x80 == 0) {
+                return result
+            }
+            shift += 7
+        }
+        throw SerializationException("Varint too long: exceeded 32 bits")
+    }
+}
+
+@InternalSerializationApi
+expect abstract class OutputStream {
+    open fun close()
+    open fun flush()
+    open fun write(buffer: ByteArray, offset: Int, count: Int)
+    open fun write(buffer: ByteArray)
+    abstract fun write(oneByte: Int)
+
+}
+
+@Suppress("NON_FINAL_MEMBER_IN_FINAL_CLASS") // KT-17944
+@InternalSerializationApi
+expect class ByteArrayOutputStream() : OutputStream {
+    override fun write(oneByte: Int)
+    fun toByteArray(): ByteArray
+    fun size(): Int
+}
diff --git a/runtime/commonMain/src/kotlinx/io/Streams.kt b/runtime/commonMain/src/kotlinx/io/Streams.kt
index 0785d8f..e69de29 100644
--- a/runtime/commonMain/src/kotlinx/io/Streams.kt
+++ b/runtime/commonMain/src/kotlinx/io/Streams.kt
@@ -1,49 +0,0 @@
-/*
- * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-@file:Suppress(
-    "DEPRECATION_ERROR", "NO_EXPLICIT_VISIBILITY_IN_API_MODE_WARNING",
-    "NO_EXPLICIT_RETURN_TYPE_IN_API_MODE_WARNING"
-)
-package kotlinx.io
-
-import kotlinx.serialization.*
-
-@Deprecated(message = message, level = DeprecationLevel.ERROR)
-expect open class IOException: Exception {
-    constructor()
-    constructor(message: String)
-}
-
-@InternalSerializationApi
-expect abstract class InputStream {
-    open fun available(): Int
-    open fun close()
-    abstract fun read(): Int
-    open fun read(b: ByteArray): Int
-    open fun read(b: ByteArray, offset: Int, len: Int): Int
-    open fun skip(n: Long): Long
-}
-
-@InternalSerializationApi
-expect class ByteArrayInputStream(buf: ByteArray): InputStream {
-    override fun read(): Int
-}
-
-@InternalSerializationApi
-expect abstract class OutputStream {
-    open fun close()
-    open fun flush()
-    open fun write(buffer: ByteArray, offset: Int, count: Int)
-    open fun write(buffer: ByteArray)
-    abstract fun write(oneByte: Int)
-
-}
-
-@Suppress("NON_FINAL_MEMBER_IN_FINAL_CLASS") // KT-17944
-@InternalSerializationApi
-expect class ByteArrayOutputStream(): OutputStream {
-    override fun write(oneByte: Int)
-    fun toByteArray(): ByteArray
-    fun size(): Int
-}
diff --git a/runtime/jsMain/src/kotlinx/io/Streams.kt b/runtime/jsMain/src/kotlinx/io/Streams.kt
index 73331bc..e5bebc7 100644
--- a/runtime/jsMain/src/kotlinx/io/Streams.kt
+++ b/runtime/jsMain/src/kotlinx/io/Streams.kt
@@ -15,141 +15,6 @@
     actual constructor() : this("IO Exception")
 }
 
-@InternalSerializationApi
-actual abstract class InputStream {
-
-    actual open fun available(): Int {
-        return 0
-    }
-
-    actual open fun close() {
-        /* empty */
-    }
-
-    actual abstract fun read(): Int
-
-    actual open fun read(b: ByteArray) = read(b, 0, b.size)
-
-    actual open fun read(b: ByteArray, offset: Int, len: Int): Int {
-        // Force null check for b first!
-        if (offset > b.size || offset < 0) {
-            throw IndexOutOfBoundsException()
-        }
-        if (len < 0 || len > b.size - offset) {
-            throw IndexOutOfBoundsException()
-        }
-        for (i in 0 until len) {
-            val c: Int = read()
-            if (c == -1) {
-                return if (i == 0) -1 else i
-            }
-            b[offset + i] = c.toByte()
-        }
-        return len
-    }
-
-
-    actual open fun skip(n: Long): Long {
-        if (n <= 0) {
-            return 0
-        }
-        var skipped: Long = 0
-        var toRead = if (n < 4096) n.toInt() else 4096
-        // We are unsynchronized, so take a local copy of the skipBuf at some
-        // point in time.
-        var localBuf = skipBuf
-        if (localBuf == null || localBuf.size < toRead) {
-            // May be lazily written back to the static. No matter if it
-            // overwrites somebody else's store.
-            localBuf = ByteArray(toRead)
-            skipBuf = localBuf
-        }
-        while (skipped < n) {
-            val read = read(localBuf, 0, toRead)
-            if (read == -1) {
-                return skipped
-            }
-            skipped += read.toLong()
-            if (read < toRead) {
-                return skipped
-            }
-            if (n - skipped < toRead) {
-                toRead = (n - skipped).toInt()
-            }
-        }
-        return skipped
-    }
-
-    companion object {
-
-        private var skipBuf: ByteArray? = null
-    }
-}
-
-@InternalSerializationApi
-actual class ByteArrayInputStream : InputStream {
-
-    protected var buf: ByteArray
-    protected var pos: Int = 0
-    protected var mark: Int = 0
-    protected var count: Int = 0
-
-
-    actual constructor(buf: ByteArray) {
-        this.mark = 0
-        this.buf = buf
-        this.count = buf.size
-    }
-
-    constructor(buf: ByteArray, offset: Int, length: Int) {
-        this.buf = buf
-        pos = offset
-        mark = offset
-        count = if (offset + length > buf.size) buf.size else offset + length
-    }
-
-    override fun available(): Int {
-        return count - pos
-    }
-
-
-    actual override fun read(): Int {
-        return if (pos < count) buf[pos++].toInt() and 0xFF else -1
-    }
-
-    fun read(b: ByteArray?, offset: Int, len: Int): Int {
-        if (b == null) {
-            throw NullPointerException()
-        }
-        // avoid int overflow
-        if (offset < 0 || offset > b.size || len < 0
-                || len > b.size - offset) {
-            throw IndexOutOfBoundsException()
-        }
-        // Are there any bytes available?
-        if (this.pos >= this.count) {
-            return -1
-        }
-        if (len == 0) {
-            return 0
-        }
-
-        val copylen = if (this.count - pos < len) this.count - pos else len
-        arraycopy(buf, pos, b, offset, copylen)
-        pos += copylen
-        return copylen
-    }
-
-    override fun skip(n: Long): Long {
-        if (n <= 0) {
-            return 0
-        }
-        val temp = pos
-        pos = if (this.count - pos < n) this.count else (pos + n).toInt()
-        return (pos - temp).toLong()
-    }
-}
-
 
 @InternalSerializationApi
 actual abstract class OutputStream {
diff --git a/runtime/jvmMain/src/kotlinx/io/Streams.kt b/runtime/jvmMain/src/kotlinx/io/Streams.kt
index c0249f4..ab09625 100644
--- a/runtime/jvmMain/src/kotlinx/io/Streams.kt
+++ b/runtime/jvmMain/src/kotlinx/io/Streams.kt
@@ -10,10 +10,6 @@
 
 @Deprecated(message = message, level = DeprecationLevel.ERROR)
 actual typealias IOException = java.io.IOException
-@InternalSerializationApi
-actual typealias InputStream = java.io.InputStream
-@InternalSerializationApi
-actual typealias ByteArrayInputStream = java.io.ByteArrayInputStream
 
 @InternalSerializationApi
 actual typealias OutputStream = java.io.OutputStream
diff --git a/runtime/nativeMain/src/kotlinx/io/Streams.kt b/runtime/nativeMain/src/kotlinx/io/Streams.kt
index a484723..b8fe947 100644
--- a/runtime/nativeMain/src/kotlinx/io/Streams.kt
+++ b/runtime/nativeMain/src/kotlinx/io/Streams.kt
@@ -17,144 +17,6 @@
 }
 
 @InternalSerializationApi
-actual abstract class InputStream {
-
-    actual open fun available(): Int {
-        return 0
-    }
-
-    actual open fun close() {
-        /* empty */
-    }
-
-    actual abstract fun read(): Int
-
-    actual open fun read(b: ByteArray) = read(b, 0, b.size)
-
-    actual open fun read(b: ByteArray, offset: Int, len: Int): Int {
-        // Force null check for b first!
-        if (offset > b.size || offset < 0) {
-            throw IndexOutOfBoundsException()
-        }
-        if (len < 0 || len > b.size - offset) {
-            throw IndexOutOfBoundsException()
-        }
-        for (i in 0 until len) {
-            val c: Int = read()
-            if (c == -1) {
-                return if (i == 0) -1 else i
-            }
-            b[offset + i] = c.toByte()
-        }
-        return len
-    }
-
-
-    actual open fun skip(n: Long): Long {
-        if (n <= 0) {
-            return 0
-        }
-        var skipped: Long = 0
-        var toRead = if (n < 4096) n.toInt() else 4096
-        // We are unsynchronized, so take a local copy of the skipBuf at some
-        // point in time.
-        var localBuf = skipBuf
-        if (localBuf == null || localBuf.size < toRead) {
-            // May be lazily written back to the static. No matter if it
-            // overwrites somebody else's store.
-            localBuf = ByteArray(toRead)
-            skipBuf = localBuf
-        }
-        while (skipped < n) {
-            val read = read(localBuf, 0, toRead)
-            if (read == -1) {
-                return skipped
-            }
-            skipped += read.toLong()
-            if (read < toRead) {
-                return skipped
-            }
-            if (n - skipped < toRead) {
-                toRead = (n - skipped).toInt()
-            }
-        }
-        return skipped
-    }
-
-    companion object {
-
-        private var skipBuf: ByteArray? = null
-    }
-}
-
-@InternalSerializationApi
-actual class ByteArrayInputStream : InputStream {
-
-    protected var buf: ByteArray
-    protected var pos: Int = 0
-    protected var mark: Int = 0
-    protected var count: Int = 0
-
-
-    actual constructor(buf: ByteArray) {
-        this.mark = 0
-        this.buf = buf
-        this.count = buf.size
-    }
-
-    constructor(buf: ByteArray, offset: Int, length: Int) {
-        this.buf = buf
-        pos = offset
-        mark = offset
-        count = if (offset + length > buf.size) buf.size else offset + length
-    }
-
-    @Suppress("ACTUAL_MISSING") // https://youtrack.jetbrains.com/issue/KT-27390#focus=streamItem-27-3749478.0-0
-    override fun available(): Int {
-        return count - pos
-    }
-
-
-    actual override fun read(): Int {
-        return if (pos < count) buf[pos++].toInt() and 0xFF else -1
-    }
-
-    fun read(b: ByteArray?, offset: Int, len: Int): Int {
-        if (b == null) {
-            throw NullPointerException()
-        }
-        // avoid int overflow
-        if (offset < 0 || offset > b.size || len < 0
-                || len > b.size - offset) {
-            throw IndexOutOfBoundsException()
-        }
-        // Are there any bytes available?
-        if (this.pos >= this.count) {
-            return -1
-        }
-        if (len == 0) {
-            return 0
-        }
-
-        val copylen = if (this.count - pos < len) this.count - pos else len
-        arraycopy(buf, pos, b, offset, copylen)
-        pos += copylen
-        return copylen
-    }
-
-    @Suppress("ACTUAL_MISSING") // https://youtrack.jetbrains.com/issue/KT-27390#focus=streamItem-27-3749478.0-0
-    override fun skip(n: Long): Long {
-        if (n <= 0) {
-            return 0
-        }
-        val temp = pos
-        pos = if (this.count - pos < n) this.count else (pos + n).toInt()
-        return (pos - temp).toLong()
-    }
-}
-
-
-@InternalSerializationApi
 actual abstract class OutputStream {
     actual open fun close() {
         /* empty */
@@ -172,7 +34,7 @@
                 || count > buffer.size - offset) {
             throw IndexOutOfBoundsException()
         }
-        for (i in offset..offset + count - 1) {
+        for (i in offset until offset + count) {
             write(buffer[i].toInt())
         }
     }