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())
}
}