diff options
author | Vsevolod Tolstopyatov <qwwdfsad@gmail.com> | 2020-06-29 05:36:02 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-06-29 15:36:02 +0300 |
commit | 36014b7293994870a146dbe9a9c7e193747a90cd (patch) | |
tree | 368085b14a7470487b6c7663b12c543e179ff0fd /formats/protobuf | |
parent | d8c9fc704d4ca155eadd430f48bc8282168f47fb (diff) | |
download | kotlinx.serialization-36014b7293994870a146dbe9a9c7e193747a90cd.tar.gz |
[API stabilization] Hide public IO classes, copy them to ProtoBuf and CBOR, remove redundant symbols and make internal in order to reduce public API shape (#872)
Diffstat (limited to 'formats/protobuf')
5 files changed, 225 insertions, 9 deletions
diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/ProtoBuf.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/ProtoBuf.kt index 2d0ec746..e0c1ae31 100644 --- a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/ProtoBuf.kt +++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/ProtoBuf.kt @@ -4,7 +4,6 @@ package kotlinx.serialization.protobuf -import kotlinx.io.* import kotlinx.serialization.* import kotlinx.serialization.CompositeDecoder.Companion.READ_DONE import kotlinx.serialization.builtins.* diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufReader.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufReader.kt index 7151fdff..2f6c08d9 100644 --- a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufReader.kt +++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufReader.kt @@ -4,7 +4,6 @@ package kotlinx.serialization.protobuf.internal -import kotlinx.io.* import kotlinx.serialization.protobuf.* import kotlin.jvm.* @@ -65,7 +64,7 @@ internal class ProtobufReader(private val input: ByteArrayInput) { return input.readExactNBytes(length) } - private fun Input.readExactNBytes(bytesCount: Int): ByteArray { + private fun ByteArrayInput.readExactNBytes(bytesCount: Int): ByteArray { if (bytesCount > availableBytes) { error("Unexpected EOF, available $availableBytes bytes, requested: $bytesCount") } diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufWriter.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufWriter.kt index b843f1fe..24ab06f2 100644 --- a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufWriter.kt +++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufWriter.kt @@ -4,7 +4,6 @@ package kotlinx.serialization.protobuf.internal -import kotlinx.io.* import kotlinx.serialization.protobuf.* import kotlin.jvm.* @@ -68,7 +67,7 @@ internal class ProtobufWriter(@JvmField val out: ByteArrayOutput) { out.writeInt(value.reverseBytes()) } - private fun Output.encode32( + private fun ByteArrayOutput.encode32( number: Int, format: ProtoNumberType = ProtoNumberType.DEFAULT ) { @@ -79,7 +78,7 @@ internal class ProtobufWriter(@JvmField val out: ByteArrayOutput) { } } - private fun Output.encode64(number: Long, format: ProtoNumberType = ProtoNumberType.DEFAULT) { + private fun ByteArrayOutput.encode64(number: Long, format: ProtoNumberType = ProtoNumberType.DEFAULT) { when (format) { ProtoNumberType.FIXED -> out.writeLong(number.reverseBytes()) ProtoNumberType.DEFAULT -> encodeVarint64(number) diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/Streams.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/Streams.kt new file mode 100644 index 00000000..9e8d15b0 --- /dev/null +++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/Streams.kt @@ -0,0 +1,220 @@ +/* + * 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 + +import kotlinx.serialization.* + +internal class ByteArrayInput(private var array: ByteArray) { + private var position: Int = 0 + public val availableBytes: Int get() = array.size - position + + fun read(): Int { + return if (position < array.size) array[position++].toInt() and 0xFF else -1 + } + + 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 + } + + fun readString(length: Int): String { + val result = array.decodeToString(position, position + length) + position += length + return result + } + + fun readVarint32(): Int { + if (position == array.size) { + error("Unexpected EOF") + } + + // Fast-path: unrolled loop for 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() + } + + 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") + } +} + +internal class ByteArrayOutput { + private var array: ByteArray = ByteArray(32) + private var position: Int = 0 + + private fun ensureCapacity(elementsToAppend: Int) { + if (position + elementsToAppend <= array.size) { + return + } + val newArray = ByteArray((position + elementsToAppend).takeHighestOneBit() shl 1) + array.copyInto(newArray) + array = newArray + } + + public fun size(): Int { + return position + } + + public fun toByteArray(): ByteArray { + val newArray = ByteArray(position) + array.copyInto(newArray, startIndex = 0, endIndex = this.position) + return newArray + } + + fun write(buffer: ByteArray, offset: Int = 0, count: Int = buffer.size) { + // avoid int overflow + if (offset < 0 || offset > buffer.size || count < 0 + || count > buffer.size - offset + ) { + throw IndexOutOfBoundsException() + } + if (count == 0) { + return + } + + ensureCapacity(count) + buffer.copyInto( + destination = array, + destinationOffset = this.position, + startIndex = offset, + endIndex = offset + count + ) + this.position += count + } + + fun write(byteValue: Int) { + ensureCapacity(1) + array[position++] = byteValue.toByte() + } + + fun writeInt(intValue: Int) { + ensureCapacity(4) + for (i in 3 downTo 0) { + array[position++] = (intValue shr i * 8).toByte() + } + } + + fun writeLong(longValue: Long) { + ensureCapacity(8) + for (i in 7 downTo 0) { + array[position++] = (longValue shr i * 8).toByte() + } + } + + fun encodeVarint32(value: Int) { + ensureCapacity(5) + // Fast-path: unrolled loop for single byte + if (value and 0x7F.inv() == 0) { + array[position++] = value.toByte() + return + } + // Fast-path: unrolled loop for two bytes + var current = value + array[position++] = (current or 0x80).toByte() + current = current ushr 7 + if (value and 0x7F.inv() == 0) { + array[position++] = value.toByte() + return + } + encodeVarint32SlowPath(current) + } + + private fun encodeVarint32SlowPath(value: Int) { + var current = value + while (current and 0x7F.inv() != 0) { + array[position++] = ((current and 0x7F) or 0x80).toByte() + current = current ushr 7 + } + array[position++] = (current and 0x7F).toByte() + } + + fun encodeVarint64(value: Long) { + ensureCapacity(10) + var currentValue = value + while (true) { + if (currentValue and 0x7F.inv() == 0L) { + array[position++] = currentValue.toByte() + return + } + array[position++] = (currentValue.toInt() and 0x7F or 0x80).toByte() + currentValue = currentValue ushr 7 + } + } +} diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/Varint.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/Varint.kt index 74edf39e..42300d32 100644 --- a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/Varint.kt +++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/Varint.kt @@ -4,7 +4,6 @@ package kotlinx.serialization.protobuf.internal -import kotlinx.io.* import kotlin.jvm.* /** @@ -13,7 +12,7 @@ import kotlin.jvm.* */ internal object Varint { @JvmStatic - internal fun decodeSignedVarintInt(input: Input): Int { + internal fun decodeSignedVarintInt(input: ByteArrayInput): 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 @@ -23,7 +22,7 @@ internal object Varint { } @JvmStatic - internal fun decodeSignedVarintLong(input: Input): Long { + internal fun decodeSignedVarintLong(input: ByteArrayInput): 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 |