diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/Helpers.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/Helpers.kt index 33e1c78cc..f9fe2dc78 100644 --- a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/Helpers.kt +++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/Helpers.kt @@ -68,12 +68,11 @@ internal val ProtoDesc.integerType: ProtoIntegerType } internal val SerialDescriptor.isPackable: Boolean - @OptIn(kotlinx.serialization.ExperimentalSerializationApi::class) get() = when (kind) { PrimitiveKind.STRING, !is PrimitiveKind -> false else -> true - } + } || isInline && elementsCount == 1 && getElementDescriptor(0).isPackable internal val ProtoDesc.isPacked: Boolean get() = (this and PACKEDMASK) != 0L diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufDecoding.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufDecoding.kt index 56884b12a..2014702f7 100644 --- a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufDecoding.kt +++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufDecoding.kt @@ -242,6 +242,7 @@ internal open class ProtobufDecoder( override fun decodeSerializableValue(deserializer: DeserializationStrategy): T = decodeSerializableValue(deserializer, null) + @OptIn(ExperimentalUnsignedTypes::class) @Suppress("UNCHECKED_CAST") override fun decodeSerializableValue(deserializer: DeserializationStrategy, previousValue: T?): T = try { when { @@ -250,6 +251,7 @@ internal open class ProtobufDecoder( } deserializer.descriptor == ByteArraySerializer().descriptor -> deserializeByteArray(previousValue as ByteArray?) as T + deserializer.descriptor == UByteArraySerializer().descriptor -> deserializeByteArray((previousValue as UByteArray?)?.asByteArray()).asUByteArray() as T deserializer is AbstractCollectionSerializer<*, *, *> -> (deserializer as AbstractCollectionSerializer<*, T, *>).merge(this, previousValue) diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufEncoding.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufEncoding.kt index b7d5dd28e..6f47e326d 100644 --- a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufEncoding.kt +++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufEncoding.kt @@ -138,11 +138,13 @@ internal open class ProtobufEncoder( override fun SerialDescriptor.getTag(index: Int) = extractParameters(index) + @OptIn(ExperimentalUnsignedTypes::class) override fun encodeSerializableValue(serializer: SerializationStrategy, value: T) = when { serializer is MapLikeSerializer<*, *, *, *> -> { serializeMap(serializer as SerializationStrategy, value) } serializer.descriptor == ByteArraySerializer().descriptor -> serializeByteArray(value as ByteArray) + serializer.descriptor == UByteArraySerializer().descriptor -> serializeByteArray((value as UByteArray).asByteArray()) else -> serializer.serialize(this, value) } diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedDecoder.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedDecoder.kt index 953c1b3ce..7cf3172c2 100644 --- a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedDecoder.kt +++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedDecoder.kt @@ -95,7 +95,7 @@ internal abstract class ProtobufTaggedDecoder : ProtobufTaggedBase(), Decoder, C } override fun decodeInline(descriptor: SerialDescriptor): Decoder { - return decodeTaggedInline(popTag(), descriptor) + return decodeTaggedInline(popTagOrDefault(), descriptor) } override fun decodeInlineElement( diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedEncoder.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedEncoder.kt index 6ba93e9e7..99ab2a1d2 100644 --- a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedEncoder.kt +++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedEncoder.kt @@ -175,7 +175,7 @@ internal abstract class ProtobufTaggedEncoder : ProtobufTaggedBase(), Encoder, C } override fun encodeInline(descriptor: SerialDescriptor): Encoder { - return encodeTaggedInline(popTag(), descriptor) + return encodeTaggedInline(popTagOrDefault(), descriptor) } override fun encodeInlineElement(descriptor: SerialDescriptor, index: Int): Encoder { diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/PackedArraySerializerTest.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/PackedArraySerializerTest.kt index 630602b13..74b185e95 100644 --- a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/PackedArraySerializerTest.kt +++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/PackedArraySerializerTest.kt @@ -2,6 +2,8 @@ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ +@file:OptIn(ExperimentalUnsignedTypes::class) + package kotlinx.serialization.protobuf import kotlinx.serialization.* @@ -57,6 +59,31 @@ class PackedArraySerializerTest { val i: List ) + @Serializable + data class PackedUByteCarrier( + @ProtoPacked + val b: UByteArray + ) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + + other as PackedUByteCarrier + + return b.contentEquals(other.b) + } + + override fun hashCode(): Int { + return b.contentHashCode() + } + } + + @Serializable + data class PackedUintCarrier( + @ProtoPacked + val i: List + ) + /** * Test that when packing is specified the array is encoded as packed */ @@ -164,4 +191,33 @@ class PackedArraySerializerTest { assertEquals(obj, decodedAbsentField) } + @Test + fun testDecodePackedUnsigned() { + val expectedByteCarrier = PackedUByteCarrier(ubyteArrayOf(1.toUByte(), 2.toUByte(), 128.toUByte())) + val expectedIntCarrier = PackedUintCarrier(listOf(1u, 2u, 3u, 128u)) + + val byteHex = """0a03010280""" + val intHex = """0a050102038001""" + + val decodedBytes = ProtoBuf.decodeFromHexString(byteHex) + val decodedInts = ProtoBuf.decodeFromHexString(intHex) + + assertEquals(expectedByteCarrier, decodedBytes) + assertEquals(expectedIntCarrier, decodedInts) + } + @Test + fun testEncodePackedUnsigned() { + val byteCarrier = PackedUByteCarrier(ubyteArrayOf(1.toUByte(), 2.toUByte(), 128.toUByte())) + val intCarrier = PackedUintCarrier(listOf(1u, 2u, 3u, 128u)) + + val expectedByteHex = """0a03010280""" + val expectedIntHex = """0a050102038001""" + + val byteHex = ProtoBuf.encodeToHexString(byteCarrier) + val intHex = ProtoBuf.encodeToHexString(intCarrier) + + assertEquals(expectedByteHex, byteHex) + assertEquals(expectedIntHex, intHex) + } + }