diff --git a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/protobuf/ProtoHuge.kt b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/protobuf/ProtoHuge.kt new file mode 100644 index 0000000000..622d61b779 --- /dev/null +++ b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/protobuf/ProtoHuge.kt @@ -0,0 +1,189 @@ +/* + * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ +package kotlinx.benchmarks.protobuf + +import kotlinx.serialization.Serializable +import kotlinx.serialization.protobuf.* +import org.openjdk.jmh.annotations.* +import java.util.concurrent.* + +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 5, time = 1) +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@State(Scope.Benchmark) +@Fork(1) +open class ProtoHuge { + + @Serializable + data class Values130( + val field0: Int, + val field1: Int, + val field2: Int, + val field3: Int, + val field4: Int, + val field5: Int, + val field6: Int, + val field7: Int, + val field8: Int, + val field9: Int, + + val field10: Int, + val field11: Int, + val field12: Int, + val field13: Int, + val field14: Int, + val field15: Int, + val field16: Int, + val field17: Int, + val field18: Int, + val field19: Int, + + val field20: Int, + val field21: Int, + val field22: Int, + val field23: Int, + val field24: Int, + val field25: Int, + val field26: Int, + val field27: Int, + val field28: Int, + val field29: Int, + + val field30: Int, + val field31: Int, + val field32: Int, + val field33: Int, + val field34: Int, + val field35: Int, + val field36: Int, + val field37: Int, + val field38: Int, + val field39: Int, + + val field40: Int, + val field41: Int, + val field42: Int, + val field43: Int, + val field44: Int, + val field45: Int, + val field46: Int, + val field47: Int, + val field48: Int, + val field49: Int, + + val field50: Int, + val field51: Int, + val field52: Int, + val field53: Int, + val field54: Int, + val field55: Int, + val field56: Int, + val field57: Int, + val field58: Int, + val field59: Int, + + val field60: Int, + val field61: Int, + val field62: Int, + val field63: Int, + val field64: Int, + val field65: Int, + val field66: Int, + val field67: Int, + val field68: Int, + val field69: Int, + + val field70: Int, + val field71: Int, + val field72: Int, + val field73: Int, + val field74: Int, + val field75: Int, + val field76: Int, + val field77: Int, + val field78: Int, + val field79: Int, + + val field80: Int, + val field81: Int, + val field82: Int, + val field83: Int, + val field84: Int, + val field85: Int, + val field86: Int, + val field87: Int, + val field88: Int, + val field89: Int, + + val field90: Int, + val field91: Int, + val field92: Int, + val field93: Int, + val field94: Int, + val field95: Int, + val field96: Int, + val field97: Int, + val field98: Int, + val field99: Int, + + val field100: Int, + val field101: Int, + val field102: Int, + val field103: Int, + val field104: Int, + val field105: Int, + val field106: Int, + val field107: Int, + val field108: Int, + val field109: Int, + + val field110: Int, + val field111: Int, + val field112: Int, + val field113: Int, + val field114: Int, + val field115: Int, + val field116: Int, + val field117: Int, + val field118: Int, + val field119: Int, + + val field120: Int, + val field121: Int, + val field122: Int, + val field123: Int, + val field124: Int, + val field125: Int, + val field126: Int, + val field127: Int, + val field128: Int, + val field129: Int + ) + + private val values130 = Values130( + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, + 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, 128, 129 + ) + + private val values130Bytes = ProtoBuf.encodeToByteArray(Values130.serializer(), values130) + + @Benchmark + fun toBytes130() = ProtoBuf.encodeToByteArray(Values130.serializer(), values130) + + @Benchmark + fun fromBytes130() = ProtoBuf.decodeFromByteArray(Values130.serializer(), values130Bytes) + +} 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 8c75d5aa3c..35b024e8e1 100644 --- a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufDecoding.kt +++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufDecoding.kt @@ -28,10 +28,46 @@ internal open class ProtobufDecoder( private var indexCache: IntArray? = null private var sparseIndexCache: MutableMap? = null + /* + Element decoding marks from given bytes. + The element number is the same as the bit position. + Marks for the lowest 64 elements are always stored in a single Long value, higher elements stores in long array. + */ + private var lowerReadMark: Long = 0 + private val highReadMarks: LongArray? + + private var valueIsNull: Boolean = false + init { + highReadMarks = prepareReadMarks(descriptor) populateCache(descriptor) } + private fun prepareReadMarks(descriptor: SerialDescriptor): LongArray? { + val elementsCount = descriptor.elementsCount + return if (elementsCount <= Long.SIZE_BITS) { + lowerReadMark = if (elementsCount == Long.SIZE_BITS) { + // number og bits in the mark is equal to the number of fields + 0 + } else { + // (1 - elementsCount) bits are always 1 since there are no fields for them + -1L shl elementsCount + } + null + } else { + // (elementsCount - 1) because only one Long value is needed to store 64 fields etc + val slotsCount = (elementsCount - 1) / Long.SIZE_BITS + val elementsInLastSlot = elementsCount % Long.SIZE_BITS + val highReadMarks = LongArray(slotsCount) + // (elementsCount % Long.SIZE_BITS) == 0 this means that the fields occupy all bits in mark + if (elementsInLastSlot != 0) { + // all marks except the higher are always 0 + highReadMarks[highReadMarks.lastIndex] = -1L shl elementsCount + } + highReadMarks + } + } + public fun populateCache(descriptor: SerialDescriptor) { val elements = descriptor.elementsCount if (elements < 32) { @@ -97,7 +133,18 @@ internal open class ProtobufDecoder( override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder { return when (descriptor.kind) { - StructureKind.LIST -> RepeatedDecoder(proto, reader, currentTagOrDefault, descriptor) + StructureKind.LIST -> { + val tag = currentTagOrDefault + return if (this.descriptor.kind == StructureKind.LIST && tag != MISSING_TAG && this.descriptor != descriptor) { + val reader = makeDelimited(reader, tag) + // repeated decoder expects the first tag to be read already + reader.readTag() + // all elements always have id = 1 + RepeatedDecoder(proto, reader, ProtoDesc(1, ProtoIntegerType.DEFAULT), descriptor) + } else { + RepeatedDecoder(proto, reader, tag, descriptor) + } + } StructureKind.CLASS, StructureKind.OBJECT, is PolymorphicKind -> { val tag = currentTagOrDefault // Do not create redundant copy @@ -200,20 +247,98 @@ internal open class ProtobufDecoder( override fun SerialDescriptor.getTag(index: Int) = extractParameters(index) + private fun findUnreadElementIndex(): Int { + val elementsCount = descriptor.elementsCount + while (lowerReadMark != -1L) { + val index = lowerReadMark.inv().countTrailingZeroBits() + lowerReadMark = lowerReadMark or (1L shl index) + + if (!descriptor.isElementOptional(index)) { + val elementDescriptor = descriptor.getElementDescriptor(index) + val kind = elementDescriptor.kind + if (kind == StructureKind.MAP || kind == StructureKind.LIST) { + return index + } else if (elementDescriptor.isNullable) { + valueIsNull = true + return index + } + } + } + + if (elementsCount > Long.SIZE_BITS) { + val higherMarks = highReadMarks!! + + for (slot in higherMarks.indices) { + // (slot + 1) because first element in high marks has index 64 + val slotOffset = (slot + 1) * Long.SIZE_BITS + // store in a variable so as not to frequently use the array + var mark = higherMarks[slot] + + while (mark != -1L) { + val indexInSlot = mark.inv().countTrailingZeroBits() + mark = mark or (1L shl indexInSlot) + + val index = slotOffset + indexInSlot + if (!descriptor.isElementOptional(index)) { + val elementDescriptor = descriptor.getElementDescriptor(index) + val kind = elementDescriptor.kind + if (kind == StructureKind.MAP || kind == StructureKind.LIST) { + higherMarks[slot] = mark + return index + } else if (elementDescriptor.isNullable) { + higherMarks[slot] = mark + valueIsNull = true + return index + } + } + } + + higherMarks[slot] = mark + } + return -1 + } + return -1 + } + + private fun markElementAsRead(index: Int) { + if (index < Long.SIZE_BITS) { + lowerReadMark = lowerReadMark or (1L shl index) + } else { + val slot = (index / Long.SIZE_BITS) - 1 + val offsetInSlot = index % Long.SIZE_BITS + highReadMarks!![slot] = highReadMarks[slot] or (1L shl offsetInSlot) + } + } + override fun decodeElementIndex(descriptor: SerialDescriptor): Int { while (true) { val protoId = reader.readTag() if (protoId == -1) { // EOF - return CompositeDecoder.DECODE_DONE + val absenceIndex = findUnreadElementIndex() + return if (absenceIndex == -1) { + CompositeDecoder.DECODE_DONE + } else { + absenceIndex + } } val index = getIndexByTag(protoId) if (index == -1) { // not found reader.skipElement() } else { + markElementAsRead(index) return index } } } + + override fun decodeNotNullMark(): Boolean { + return if (valueIsNull) { + valueIsNull = false + false + } else { + true + } + } } private class RepeatedDecoder( 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 d95f8bcbdb..7425ef3699 100644 --- a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufEncoding.kt +++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufEncoding.kt @@ -32,7 +32,11 @@ internal open class ProtobufEncoder( if (tag == MISSING_TAG) { writer.writeInt(collectionSize) } - RepeatedEncoder(proto, writer, tag, descriptor) + if (this.descriptor.kind == StructureKind.LIST && tag != MISSING_TAG && this.descriptor != descriptor) { + NestedRepeatedEncoder(proto, writer, tag, descriptor) + } else { + RepeatedEncoder(proto, writer, tag, descriptor) + } } StructureKind.MAP -> { // Size and missing tag are managed by the implementation that delegated to the list @@ -177,3 +181,18 @@ private class RepeatedEncoder( ) : ProtobufEncoder(proto, writer, descriptor) { override fun SerialDescriptor.getTag(index: Int) = curTag } + +private class NestedRepeatedEncoder( + proto: ProtoBuf, + @JvmField val writer: ProtobufWriter, + @JvmField val curTag: ProtoDesc, + descriptor: SerialDescriptor, + @JvmField val stream: ByteArrayOutput = ByteArrayOutput() +) : ProtobufEncoder(proto, ProtobufWriter(stream), descriptor) { + // all elements always have id = 1 + override fun SerialDescriptor.getTag(index: Int) = ProtoDesc(1, ProtoIntegerType.DEFAULT) + + override fun endEncode(descriptor: SerialDescriptor) { + writer.writeOutput(stream, curTag.protoId) + } +} 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 c84b96a3dc..8a5a38278d 100644 --- a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedDecoder.kt +++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedDecoder.kt @@ -26,7 +26,7 @@ internal abstract class ProtobufTaggedDecoder : ProtobufTaggedBase(), Decoder, C protected open fun decodeTaggedInline(tag: ProtoDesc, inlineDescriptor: SerialDescriptor): Decoder = this.apply { pushTag(tag) } - final override fun decodeNotNullMark(): Boolean = true + override fun decodeNotNullMark(): Boolean = true final override fun decodeNull(): Nothing? = null final override fun decodeBoolean(): Boolean = decodeTaggedBoolean(popTagOrDefault()) final override fun decodeByte(): Byte = decodeTaggedByte(popTagOrDefault()) 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 b4f2cb23ef..01532df3fc 100644 --- a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedEncoder.kt +++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedEncoder.kt @@ -10,10 +10,16 @@ import kotlinx.serialization.encoding.* @OptIn(ExperimentalSerializationApi::class) internal abstract class ProtobufTaggedEncoder : ProtobufTaggedBase(), Encoder, CompositeEncoder { + private enum class NullableMode { + ACCEPTABLE, + OPTIONAL, + COLLECTION, + NOT_NULL + } + private var nullableMode: NullableMode = NullableMode.NOT_NULL protected abstract fun SerialDescriptor.getTag(index: Int): ProtoDesc - protected fun encodeTaggedNull(): Unit = throw SerializationException("'null' is not supported in ProtoBuf") // TODO investigate null support separately protected abstract fun encodeTaggedInt(tag: ProtoDesc, value: Int) protected abstract fun encodeTaggedByte(tag: ProtoDesc, value: Byte) protected abstract fun encodeTaggedShort(tag: ProtoDesc, value: Short) @@ -27,7 +33,17 @@ internal abstract class ProtobufTaggedEncoder : ProtobufTaggedBase(), Encoder, C protected open fun encodeTaggedInline(tag: ProtoDesc, inlineDescriptor: SerialDescriptor): Encoder = this.apply { pushTag(tag) } - public final override fun encodeNull(): Unit = encodeTaggedNull() + public final override fun encodeNull() { + if (nullableMode != NullableMode.ACCEPTABLE) { + val message = when (nullableMode) { + NullableMode.OPTIONAL -> "'null' is not supported for optional properties in ProtoBuf" + NullableMode.COLLECTION -> "'null' is not supported for collection types in ProtoBuf" + NullableMode.NOT_NULL -> "'null' is not allowed for not-null properties" + else -> "'null' is not supported in ProtoBuf"; + } + throw SerializationException(message) + } + } public final override fun encodeBoolean(value: Boolean) { encodeTaggedBoolean(popTagOrDefault(), value) @@ -113,6 +129,8 @@ internal abstract class ProtobufTaggedEncoder : ProtobufTaggedBase(), Encoder, C serializer: SerializationStrategy, value: T ) { + nullableMode = NullableMode.NOT_NULL + pushTag(descriptor.getTag(index)) encodeSerializableValue(serializer, value) } @@ -123,6 +141,15 @@ internal abstract class ProtobufTaggedEncoder : ProtobufTaggedBase(), Encoder, C serializer: SerializationStrategy, value: T? ) { + val elementKind = descriptor.getElementDescriptor(index).kind + nullableMode = if (descriptor.isElementOptional(index)) { + NullableMode.OPTIONAL + } else if (elementKind == StructureKind.MAP || elementKind == StructureKind.LIST) { + NullableMode.COLLECTION + } else { + NullableMode.ACCEPTABLE + } + pushTag(descriptor.getTag(index)) encodeNullableSerializableValue(serializer, value) } diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/CustomSerializersProtobufTest.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/CustomSerializersProtobufTest.kt index 3f346feb74..8be8918fe8 100644 --- a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/CustomSerializersProtobufTest.kt +++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/CustomSerializersProtobufTest.kt @@ -49,7 +49,7 @@ class CustomSerializersProtobufTest { @Test fun writeCustomInvertedOrder() { val obj = C(1, 2) - val s = ProtoBuf.encodeToHexString(obj).toUpperCase() + val s = ProtoBuf.encodeToHexString(CSerializer, obj).toUpperCase() assertEquals("10020801", s) } @@ -63,7 +63,7 @@ class CustomSerializersProtobufTest { @Test fun writeCustomOmitDefault() { val obj = C(b = 2) - val s = ProtoBuf.encodeToHexString(obj).toUpperCase() + val s = ProtoBuf.encodeToHexString(CSerializer, obj).toUpperCase() assertEquals("1002", s) } @@ -77,7 +77,7 @@ class CustomSerializersProtobufTest { @Test fun writeOptionalList1() { val obj = CList1(listOf(C(a = 1), C(b = 2), C(3, 4))) - val s = ProtoBuf.encodeToHexString(obj).toUpperCase() + val s = ProtoBuf.encodeToHexString(CList1Serializer, obj).toUpperCase() assertEquals("0A04102A08010A0210020A0410040803", s) } @@ -91,7 +91,7 @@ class CustomSerializersProtobufTest { @Test fun writeOptionalList2a() { val obj = CList2(7, listOf(C(a = 5), C(b = 6), C(7, 8))) - val s = ProtoBuf.encodeToHexString(obj).toUpperCase() + val s = ProtoBuf.encodeToHexString(CList2Serializer, obj).toUpperCase() assertEquals("1204102A0805120210061204100808070807", s) } @@ -99,13 +99,13 @@ class CustomSerializersProtobufTest { fun readOptionalList2a() { val obj = CList2(7, listOf(C(a = 5), C(b = 6), C(7, 8))) val j = "08071204102A080512021006120410080807" - assertEquals(obj, ProtoBuf.decodeFromHexString(j)) + assertEquals(obj, ProtoBuf.decodeFromHexString(CList2Serializer, j)) } @Test fun writeOptionalList2b() { val obj = CList2(c = listOf(C(a = 5), C(b = 6), C(7, 8))) - val s = ProtoBuf.encodeToHexString(obj).toUpperCase() + val s = ProtoBuf.encodeToHexString(CList2Serializer, obj).toUpperCase() assertEquals("1204102A080512021006120410080807", s) } @@ -113,13 +113,13 @@ class CustomSerializersProtobufTest { fun readOptionalList2b() { val obj = CList2(c = listOf(C(a = 5), C(b = 6), C(7, 8))) val j = "1204102A080512021006120410080807" - assertEquals(obj, ProtoBuf.decodeFromHexString(j)) + assertEquals(obj, ProtoBuf.decodeFromHexString(CList2Serializer, j)) } @Test fun writeOptionalList3a() { val obj = CList3(listOf(C(a = 1), C(b = 2), C(3, 4)), 99) - val s = ProtoBuf.encodeToHexString(obj).toUpperCase() + val s = ProtoBuf.encodeToHexString(CList3Serializer, obj).toUpperCase() assertEquals("0A04102A08010A0210020A04100408031063", s) } @@ -127,13 +127,13 @@ class CustomSerializersProtobufTest { fun readOptionalList3a() { val obj = CList3(listOf(C(a = 1), C(b = 2), C(3, 4)), 99) val j = "10630A04102A08010A0210020A0410040803" - assertEquals(obj, ProtoBuf.decodeFromHexString(j)) + assertEquals(obj, ProtoBuf.decodeFromHexString(CList3Serializer, j)) } @Test fun writeOptionalList3b() { val obj = CList3(f = 99) - val s = ProtoBuf.encodeToHexString(obj).toUpperCase() + val s = ProtoBuf.encodeToHexString(CList3Serializer, obj).toUpperCase() assertEquals("1063", s) } @@ -141,13 +141,13 @@ class CustomSerializersProtobufTest { fun readOptionalList3b() { val obj = CList3(f = 99) val j = "1063" - assertEquals(obj, ProtoBuf.decodeFromHexString(j)) + assertEquals(obj, ProtoBuf.decodeFromHexString(CList3Serializer, j)) } @Test fun writeOptionalList4a() { val obj = CList4(listOf(C(a = 1), C(b = 2), C(3, 4)), 54) - val s = ProtoBuf.encodeToHexString(obj).toUpperCase() + val s = ProtoBuf.encodeToHexString(CList4Serializer, obj).toUpperCase() assertEquals("10360A04102A08010A0210020A0410040803", s) } @@ -155,14 +155,14 @@ class CustomSerializersProtobufTest { fun readOptionalList4a() { val obj = CList4(listOf(C(a = 1), C(b = 2), C(3, 4)), 54) val j = "10360A04102A08010A0210020A0410040803" - assertEquals(obj, ProtoBuf.decodeFromHexString(j)) + assertEquals(obj, ProtoBuf.decodeFromHexString(CList4Serializer, j)) } @Test fun writeOptionalList4b() { val obj = CList4(h = 97) val j = "1061" - val s = ProtoBuf.encodeToHexString(obj).toUpperCase() + val s = ProtoBuf.encodeToHexString(CList4Serializer, obj).toUpperCase() assertEquals(j, s) } @@ -170,13 +170,13 @@ class CustomSerializersProtobufTest { fun readOptionalList4b() { val obj = CList4(h = 97) val j = "1061" - assertEquals(obj, ProtoBuf.decodeFromHexString(j)) + assertEquals(obj, ProtoBuf.decodeFromHexString(CList4Serializer, j)) } @Test fun writeOptionalList5a() { val obj = CList5(listOf(9, 8, 7, 6, 5), 5) - val s = ProtoBuf.encodeToHexString(obj).toUpperCase() + val s = ProtoBuf.encodeToHexString(CList5Serializer, obj).toUpperCase() assertEquals("100508090808080708060805", s) } @@ -184,13 +184,13 @@ class CustomSerializersProtobufTest { fun readOptionalList5a() { val obj = CList5(listOf(9, 8, 7, 6, 5), 5) val j = "100508090808080708060805" - assertEquals(obj, ProtoBuf.decodeFromHexString(j)) + assertEquals(obj, ProtoBuf.decodeFromHexString(CList5Serializer, j)) } @Test fun writeOptionalList5b() { val obj = CList5(h = 999) - val s = ProtoBuf.encodeToHexString(obj).toUpperCase() + val s = ProtoBuf.encodeToHexString(CList5Serializer, obj).toUpperCase() assertEquals("10E707", s) } @@ -198,6 +198,6 @@ class CustomSerializersProtobufTest { fun readOptionalList5b() { val obj = CList5(h = 999) val j = "10E707" - assertEquals(obj, ProtoBuf.decodeFromHexString(j)) + assertEquals(obj, ProtoBuf.decodeFromHexString(CList5Serializer, j)) } } diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/CustomizedSerializableTestClasses.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/CustomizedSerializableTestClasses.kt index f6fe945a9b..24e5543eeb 100644 --- a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/CustomizedSerializableTestClasses.kt +++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/CustomizedSerializableTestClasses.kt @@ -31,73 +31,99 @@ object BSerializer : KSerializer { data class BList(@ProtoNumber(1) val bs: List) @Serializable -data class C(@ProtoNumber(1) val a: Int = 31, @ProtoNumber(2) val b: Int = 42) { - @Serializer(forClass = C::class) - companion object: KSerializer { - override fun serialize(encoder: Encoder, value: C) { - val elemOutput = encoder.beginStructure(descriptor) - elemOutput.encodeIntElement(descriptor, 1, value.b) - if (value.a != 31) elemOutput.encodeIntElement(descriptor, 0, value.a) - elemOutput.endStructure(descriptor) - } +data class C(@ProtoNumber(1) val a: Int = 31, @ProtoNumber(2) val b: Int = 42) + +object CSerializer : KSerializer { + override fun serialize(encoder: Encoder, value: C) { + val elemOutput = encoder.beginStructure(descriptor) + elemOutput.encodeIntElement(descriptor, 1, value.b) + if (value.a != 31) elemOutput.encodeIntElement(descriptor, 0, value.a) + elemOutput.endStructure(descriptor) + } + + override fun deserialize(decoder: Decoder): C { + return C.serializer().deserialize(decoder) } + + override val descriptor: SerialDescriptor = C.serializer().descriptor } +object CList1Serializer : KSerializer { + override fun serialize(encoder: Encoder, value: CList1) { + val elemOutput = encoder.beginStructure(descriptor) + elemOutput.encodeSerializableElement(descriptor, 0, ListSerializer(CSerializer), value.c) + elemOutput.endStructure(descriptor) + } + override fun deserialize(decoder: Decoder): CList1 { + return CList1.serializer().deserialize(decoder) + } + override val descriptor: SerialDescriptor = CList1.serializer().descriptor +} @Serializable data class CList1(@ProtoNumber(1) val c: List) -@Serializable -data class CList2(@ProtoNumber(1) val d: Int = 5, @ProtoNumber(2) val c: List) { - @Serializer(forClass = CList2::class) - companion object: KSerializer { - override fun serialize(encoder: Encoder, value: CList2) { - val elemOutput = encoder.beginStructure(descriptor) - elemOutput.encodeSerializableElement(descriptor, 1, ListSerializer(C), value.c) - if (value.d != 5) elemOutput.encodeIntElement(descriptor, 0, value.d) - elemOutput.endStructure(descriptor) - } +object CList2Serializer : KSerializer { + override fun serialize(encoder: Encoder, value: CList2) { + val elemOutput = encoder.beginStructure(descriptor) + elemOutput.encodeSerializableElement(descriptor, 1, ListSerializer(CSerializer), value.c) + if (value.d != 5) elemOutput.encodeIntElement(descriptor, 0, value.d) + elemOutput.endStructure(descriptor) + } + override fun deserialize(decoder: Decoder): CList2 { + return CList2.serializer().deserialize(decoder) } + override val descriptor: SerialDescriptor = CList2.serializer().descriptor } @Serializable -data class CList3(@ProtoNumber(1) val e: List = emptyList(), @ProtoNumber(2) val f: Int) { - @Serializer(forClass = CList3::class) - companion object: KSerializer { - override fun serialize(encoder: Encoder, value: CList3) { - val elemOutput = encoder.beginStructure(descriptor) - if (value.e.isNotEmpty()) elemOutput.encodeSerializableElement(descriptor, 0, ListSerializer(C), value.e) - elemOutput.encodeIntElement(descriptor, 1, value.f) - elemOutput.endStructure(descriptor) - } +data class CList2(@ProtoNumber(1) val d: Int = 5, @ProtoNumber(2) val c: List) + +object CList3Serializer : KSerializer { + override fun serialize(encoder: Encoder, value: CList3) { + val elemOutput = encoder.beginStructure(descriptor) + if (value.e.isNotEmpty()) elemOutput.encodeSerializableElement(descriptor, 0, ListSerializer(CSerializer), value.e) + elemOutput.encodeIntElement(descriptor, 1, value.f) + elemOutput.endStructure(descriptor) + } + override fun deserialize(decoder: Decoder): CList3 { + return CList3.serializer().deserialize(decoder) } + override val descriptor: SerialDescriptor = CList3.serializer().descriptor } @Serializable -data class CList4(@ProtoNumber(1) val g: List = emptyList(), @ProtoNumber(2) val h: Int) { - @Serializer(forClass = CList4::class) - companion object: KSerializer { - override fun serialize(encoder: Encoder, value: CList4) { - val elemOutput = encoder.beginStructure(descriptor) - elemOutput.encodeIntElement(descriptor, 1, value.h) - if (value.g.isNotEmpty()) elemOutput.encodeSerializableElement(descriptor, 0, ListSerializer(C), value.g) - elemOutput.endStructure(descriptor) - } +data class CList3(@ProtoNumber(1) val e: List = emptyList(), @ProtoNumber(2) val f: Int) + +object CList4Serializer : KSerializer { + override fun serialize(encoder: Encoder, value: CList4) { + val elemOutput = encoder.beginStructure(descriptor) + elemOutput.encodeIntElement(descriptor, 1, value.h) + if (value.g.isNotEmpty()) elemOutput.encodeSerializableElement(descriptor, 0, ListSerializer(CSerializer), value.g) + elemOutput.endStructure(descriptor) + } + override fun deserialize(decoder: Decoder): CList4 { + return CList4.serializer().deserialize(decoder) } + override val descriptor: SerialDescriptor = CList4.serializer().descriptor } - @Serializable -data class CList5(@ProtoNumber(1) val g: List = emptyList(), @ProtoNumber(2) val h: Int) { - @Serializer(forClass = CList5::class) - companion object: KSerializer { - override fun serialize(encoder: Encoder, value: CList5) { - val elemOutput = encoder.beginStructure(descriptor) - elemOutput.encodeIntElement(descriptor, 1, value.h) - if (value.g.isNotEmpty()) elemOutput.encodeSerializableElement( - descriptor, 0, - ListSerializer(Int.serializer()), - value.g - ) - elemOutput.endStructure(descriptor) - } +data class CList4(@ProtoNumber(1) val g: List = emptyList(), @ProtoNumber(2) val h: Int) + +object CList5Serializer : KSerializer { + override fun serialize(encoder: Encoder, value: CList5) { + val elemOutput = encoder.beginStructure(descriptor) + elemOutput.encodeIntElement(descriptor, 1, value.h) + if (value.g.isNotEmpty()) elemOutput.encodeSerializableElement( + descriptor, 0, + ListSerializer(Int.serializer()), + value.g + ) + elemOutput.endStructure(descriptor) } + override fun deserialize(decoder: Decoder): CList5 { + return CList5.serializer().deserialize(decoder) + } + override val descriptor: SerialDescriptor = CList5.serializer().descriptor } +@Serializable +data class CList5(@ProtoNumber(1) val g: List = emptyList(), @ProtoNumber(2) val h: Int) diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufAbsenceTest.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufAbsenceTest.kt new file mode 100644 index 0000000000..a37f84cb35 --- /dev/null +++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufAbsenceTest.kt @@ -0,0 +1,152 @@ +package kotlinx.serialization.protobuf + +import kotlinx.serialization.Serializable +import kotlinx.serialization.SerializationException +import kotlinx.serialization.decodeFromByteArray +import kotlinx.serialization.encodeToByteArray +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith + +class ProtobufAbsenceTest { + @Serializable + data class SimpleValue(val i: Int) + @Serializable + data class DefaultValue(val i: Int = 42) + @Serializable + data class NullableValue(val i: Int?) + @Serializable + data class DefaultAndNullValue(val i: Int? = 42) + + @Serializable + data class SimpleList(val l: List) + @Serializable + data class DefaultList(val l: List = listOf(42)) + @Serializable + data class NullableList(val l: List?) + @Serializable + data class DefaultNullableList(val l: List? = listOf(42)) + + @Serializable + data class SimpleMap(val m: Map) + @Serializable + data class DefaultMap(val m: Map = mapOf(42 to 43)) + @Serializable + data class NullableMap(val m: Map?) + @Serializable + data class DefaultNullableMap(val m: Map? = mapOf(42 to 43)) + + @Test + fun testSimpleValue() { + assertFailsWith(SerializationException::class) { ProtoBuf.decodeFromByteArray(ByteArray(0)) } + } + + @Test + fun testDefaultValue() { + val bytes = ProtoBuf.encodeToByteArray(DefaultValue(42)) + assertEquals(0, bytes.size) + + val decoded = ProtoBuf.decodeFromByteArray(bytes) + assertEquals(42, decoded.i) + } + + @Test + fun testNullableValue() { + val bytes = ProtoBuf.encodeToByteArray(NullableValue(null)) + assertEquals(0, bytes.size) + + val decoded = ProtoBuf.decodeFromByteArray(bytes) + assertEquals(null, decoded.i) + } + + @Test + fun testDefaultAndNullValue() { + assertFailsWith(SerializationException::class) { ProtoBuf.encodeToByteArray(DefaultAndNullValue(null)) } + + val bytes = ProtoBuf.encodeToByteArray(DefaultAndNullValue(42)) + assertEquals(0, bytes.size) + + val decoded = ProtoBuf.decodeFromByteArray(bytes) + assertEquals(42, decoded.i) + } + + + @Test + fun testSimpleList() { + val bytes = ProtoBuf.encodeToByteArray(SimpleList(emptyList())) + assertEquals(0, bytes.size) + + val decoded = ProtoBuf.decodeFromByteArray(bytes) + assertEquals(emptyList(), decoded.l) + } + + @Test + fun testDefaultList() { + val bytes = ProtoBuf.encodeToByteArray(DefaultList(listOf(42))) + assertEquals(0, bytes.size) + + val decoded = ProtoBuf.decodeFromByteArray(bytes) + assertEquals(listOf(42), decoded.l) + } + + @Test + fun testNullableList() { + assertFailsWith(SerializationException::class) { ProtoBuf.encodeToByteArray(NullableList(null)) } + + val bytes = ProtoBuf.encodeToByteArray(NullableList(emptyList())) + assertEquals(0, bytes.size) + + val decoded = ProtoBuf.decodeFromByteArray(bytes) + assertEquals(emptyList(), decoded.l) + } + + + @Test + fun testDefaultNullableList() { + assertFailsWith(SerializationException::class) { ProtoBuf.encodeToByteArray(DefaultNullableList(null)) } + + val bytes = ProtoBuf.encodeToByteArray(DefaultNullableList(listOf(42))) + assertEquals(0, bytes.size) + + val decoded = ProtoBuf.decodeFromByteArray(bytes) + assertEquals(listOf(42), decoded.l) + } + + @Test + fun testSimpleMap() { + val bytes = ProtoBuf.encodeToByteArray(SimpleMap(emptyMap())) + assertEquals(0, bytes.size) + val decoded = ProtoBuf.decodeFromByteArray(bytes) + assertEquals(emptyMap(), decoded.m) + } + + @Test + fun testDefaultMap() { + val bytes = ProtoBuf.encodeToByteArray(DefaultMap(mapOf(42 to 43))) + assertEquals(0, bytes.size) + val decoded = ProtoBuf.decodeFromByteArray(bytes) + assertEquals(mapOf(42 to 43), decoded.m) + } + + @Test + fun testNullableMap() { + assertFailsWith(SerializationException::class) { ProtoBuf.encodeToByteArray(NullableMap(null)) } + + val bytes = ProtoBuf.encodeToByteArray(NullableMap(emptyMap())) + assertEquals(0, bytes.size) + + val decoded = ProtoBuf.decodeFromByteArray(bytes) + assertEquals(emptyMap(), decoded.m) + } + + @Test + fun testDefaultNullableMap() { + assertFailsWith(SerializationException::class) { ProtoBuf.encodeToByteArray(DefaultNullableMap(null)) } + + val bytes = ProtoBuf.encodeToByteArray(DefaultNullableMap(mapOf(42 to 43))) + assertEquals(0, bytes.size) + + val decoded = ProtoBuf.decodeFromByteArray(bytes) + assertEquals(mapOf(42 to 43), decoded.m) + } +} diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufCollectionsTest.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufCollectionsTest.kt new file mode 100644 index 0000000000..43b667bf05 --- /dev/null +++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufCollectionsTest.kt @@ -0,0 +1,104 @@ +package kotlinx.serialization.protobuf + +import kotlinx.serialization.* +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertNotEquals + +class ProtobufCollectionsTest { + @Serializable + data class ListWithNestedList(val l: List?>) + + @Serializable + data class ListWithNestedMap(val l: List>) + + @Serializable + data class MapWithNullableNestedLists(val m: Map?, List?>) + + @Serializable + data class NullableListElement(val l: List) + + @Serializable + data class NullableMapKey(val m: Map) + + @Serializable + data class NullableMapValue(val m: Map) + + @Test + fun testEncodeNullAsListElement() { + assertFailsWith(SerializationException::class) { ProtoBuf.encodeToByteArray(NullableListElement(listOf(null))) } + } + + @Test + fun testEncodeNullAsMapKey() { + assertFailsWith(SerializationException::class) { ProtoBuf.encodeToByteArray(NullableMapKey(mapOf(null to 42))) } + } + + @Test + fun testEmptyListIsNotListOfEmpty() { + val emptyListBytes = ProtoBuf.encodeToByteArray(ListWithNestedList(emptyList())) + val listOfEmptyBytes = ProtoBuf.encodeToByteArray(ListWithNestedList(listOf(emptyList()))) + val emptyList = ProtoBuf.decodeFromByteArray(emptyListBytes) + val listOfEmpty = ProtoBuf.decodeFromByteArray(listOfEmptyBytes) + + assertNotEquals(emptyList, listOfEmpty) + } + + @Test + fun testEncodeMapWithNullableKey() { + val map = NullableMapKey(mapOf(42 to 43)) + val bytes = ProtoBuf.encodeToByteArray(map) + val decoded = ProtoBuf.decodeFromByteArray(bytes) + assertEquals(map, decoded) + } + + @Test + fun testEncodeNullAsMapValue() { + assertFailsWith(SerializationException::class) { ProtoBuf.encodeToByteArray(NullableMapValue(mapOf(42 to null))) } + } + + @Test + fun testEncodeMapWithNullableValue() { + val map = NullableMapValue(mapOf(42 to 43)) + val bytes = ProtoBuf.encodeToByteArray(map) + val decoded = ProtoBuf.decodeFromByteArray(bytes) + assertEquals(map, decoded) + } + + @Test + fun testNestedList() { + val lists = listOf(listOf(42, 0), emptyList(), listOf(43)) + val bytes = ProtoBuf.encodeToByteArray(ListWithNestedList(lists)) + val decoded = ProtoBuf.decodeFromByteArray(bytes) + assertEquals(lists, decoded.l) + } + + @Test + fun testNestedListIsNull() { + assertFailsWith(SerializationException::class) { ProtoBuf.encodeToByteArray(ListWithNestedList(listOf(null))) } + } + + @Test + fun testNestedMapInList() { + val list = listOf(mapOf(1 to 2, 2 to 4), emptyMap(), mapOf(3 to 8)) + val bytes = ProtoBuf.encodeToByteArray(ListWithNestedMap(list)) + val decoded = ProtoBuf.decodeFromByteArray(bytes) + assertEquals(list, decoded.l) + } + + @Test + fun testNestedListsInMap() { + val map = mapOf?, List?>(listOf(42, 0) to listOf(43, 1), listOf(5) to listOf(20, 11)) + val bytes = ProtoBuf.encodeToByteArray(MapWithNullableNestedLists(map)) + val decoded = ProtoBuf.decodeFromByteArray(bytes) + assertEquals(map, decoded.m) + } + + @Test + fun testNestedListsAreNullInMap() { + assertFailsWith(SerializationException::class) { ProtoBuf.encodeToByteArray(MapWithNullableNestedLists(mapOf(null to emptyList()))) } + assertFailsWith(SerializationException::class) { ProtoBuf.encodeToByteArray(MapWithNullableNestedLists(mapOf(emptyList() to null))) } + assertFailsWith(SerializationException::class) { ProtoBuf.encodeToByteArray(MapWithNullableNestedLists(mapOf(null to null))) } + } +} diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufHugeClassTest.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufHugeClassTest.kt new file mode 100644 index 0000000000..6f323e6798 --- /dev/null +++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufHugeClassTest.kt @@ -0,0 +1,605 @@ +package kotlinx.serialization.protobuf + +import kotlinx.serialization.HexConverter +import kotlinx.serialization.Serializable +import kotlinx.serialization.decodeFromByteArray +import kotlinx.serialization.encodeToByteArray +import kotlin.test.Test +import kotlin.test.assertEquals + +class ProtobufHugeClassTest { + @Serializable + data class Lists64( + val field0: List, + val field1: List, + val field2: List, + val field3: List, + val field4: List, + val field5: List, + val field6: List, + val field7: List, + val field8: List, + val field9: List, + + val field10: List, + val field11: List, + val field12: List, + val field13: List, + val field14: List, + val field15: List, + val field16: List, + val field17: List, + val field18: List, + val field19: List, + + val field20: List, + val field21: List, + val field22: List, + val field23: List, + val field24: List, + val field25: List, + val field26: List, + val field27: List, + val field28: List, + val field29: List, + + val field30: List, + val field31: List, + val field32: List, + val field33: List, + val field34: List, + val field35: List, + val field36: List, + val field37: List, + val field38: List, + val field39: List, + + val field40: List, + val field41: List, + val field42: List, + val field43: List, + val field44: List, + val field45: List, + val field46: List, + val field47: List, + val field48: List, + val field49: List, + + val field50: List, + val field51: List, + val field52: List, + val field53: List, + val field54: List, + val field55: List, + val field56: List, + val field57: List, + val field58: List, + val field59: List, + + val field60: List, + val field61: List, + val field62: List, + val field63: List + ) + + @Serializable + data class Values70( + val field0: Int?, + val field1: Int?, + val field2: Int?, + val field3: Int?, + val field4: Int?, + val field5: Int?, + val field6: Int?, + val field7: Int?, + val field8: Int?, + val field9: Int?, + + val field10: Int?, + val field11: Int?, + val field12: Int?, + val field13: Int?, + val field14: Int?, + val field15: Int?, + val field16: Int?, + val field17: Int?, + val field18: Int?, + val field19: Int?, + + val field20: Int?, + val field21: Int?, + val field22: Int?, + val field23: Int?, + val field24: Int?, + val field25: Int?, + val field26: Int?, + val field27: Int?, + val field28: Int?, + val field29: Int?, + + val field30: Int?, + val field31: Int?, + val field32: Int?, + val field33: Int?, + val field34: Int?, + val field35: Int?, + val field36: Int?, + val field37: Int?, + val field38: Int?, + val field39: Int?, + + val field40: Int?, + val field41: Int?, + val field42: Int?, + val field43: Int?, + val field44: Int?, + val field45: Int?, + val field46: Int?, + val field47: Int?, + val field48: Int?, + val field49: Int?, + + val field50: Int?, + val field51: Int?, + val field52: Int?, + val field53: Int?, + val field54: Int?, + val field55: Int?, + val field56: Int?, + val field57: Int?, + val field58: Int?, + val field59: Int?, + + val field60: Int?, + val field61: Int?, + val field62: Int?, + val field63: Int?, + val field64: Int?, + val field65: Int?, + val field66: Int?, + val field67: Int?, + val field68: Int?, + val field69: Int? + ) + + @Serializable + data class Values128( + val field0: Int?, + val field1: Int?, + val field2: Int?, + val field3: Int?, + val field4: Int?, + val field5: Int?, + val field6: Int?, + val field7: Int?, + val field8: Int?, + val field9: Int?, + + val field10: Int?, + val field11: Int?, + val field12: Int?, + val field13: Int?, + val field14: Int?, + val field15: Int?, + val field16: Int?, + val field17: Int?, + val field18: Int?, + val field19: Int?, + + val field20: Int?, + val field21: Int?, + val field22: Int?, + val field23: Int?, + val field24: Int?, + val field25: Int?, + val field26: Int?, + val field27: Int?, + val field28: Int?, + val field29: Int?, + + val field30: Int?, + val field31: Int?, + val field32: Int?, + val field33: Int?, + val field34: Int?, + val field35: Int?, + val field36: Int?, + val field37: Int?, + val field38: Int?, + val field39: Int?, + + val field40: Int?, + val field41: Int?, + val field42: Int?, + val field43: Int?, + val field44: Int?, + val field45: Int?, + val field46: Int?, + val field47: Int?, + val field48: Int?, + val field49: Int?, + + val field50: Int?, + val field51: Int?, + val field52: Int?, + val field53: Int?, + val field54: Int?, + val field55: Int?, + val field56: Int?, + val field57: Int?, + val field58: Int?, + val field59: Int?, + + val field60: Int?, + val field61: Int?, + val field62: Int?, + val field63: Int?, + val field64: Int?, + val field65: Int?, + val field66: Int?, + val field67: Int?, + val field68: Int?, + val field69: Int?, + + val field70: Int?, + val field71: Int?, + val field72: Int?, + val field73: Int?, + val field74: Int?, + val field75: Int?, + val field76: Int?, + val field77: Int?, + val field78: Int?, + val field79: Int?, + + val field80: Int?, + val field81: Int?, + val field82: Int?, + val field83: Int?, + val field84: Int?, + val field85: Int?, + val field86: Int?, + val field87: Int?, + val field88: Int?, + val field89: Int?, + + val field90: Int?, + val field91: Int?, + val field92: Int?, + val field93: Int?, + val field94: Int?, + val field95: Int?, + val field96: Int?, + val field97: Int?, + val field98: Int?, + val field99: Int?, + + val field100: Int?, + val field101: Int?, + val field102: Int?, + val field103: Int?, + val field104: Int?, + val field105: Int?, + val field106: Int?, + val field107: Int?, + val field108: Int?, + val field109: Int?, + + val field110: Int?, + val field111: Int?, + val field112: Int?, + val field113: Int?, + val field114: Int?, + val field115: Int?, + val field116: Int?, + val field117: Int?, + val field118: Int?, + val field119: Int?, + + val field120: Int?, + val field121: Int?, + val field122: Int?, + val field123: Int?, + val field124: Int?, + val field125: Int?, + val field126: Int?, + val field127: Int? + ) + @Serializable + data class Values130( + val field0: Int?, + val field1: Int?, + val field2: Int?, + val field3: Int?, + val field4: Int?, + val field5: Int?, + val field6: Int?, + val field7: Int?, + val field8: Int?, + val field9: Int?, + + val field10: Int?, + val field11: Int?, + val field12: Int?, + val field13: Int?, + val field14: Int?, + val field15: Int?, + val field16: Int?, + val field17: Int?, + val field18: Int?, + val field19: Int?, + + val field20: Int?, + val field21: Int?, + val field22: Int?, + val field23: Int?, + val field24: Int?, + val field25: Int?, + val field26: Int?, + val field27: Int?, + val field28: Int?, + val field29: Int?, + + val field30: Int?, + val field31: Int?, + val field32: Int?, + val field33: Int?, + val field34: Int?, + val field35: Int?, + val field36: Int?, + val field37: Int?, + val field38: Int?, + val field39: Int?, + + val field40: Int?, + val field41: Int?, + val field42: Int?, + val field43: Int?, + val field44: Int?, + val field45: Int?, + val field46: Int?, + val field47: Int?, + val field48: Int?, + val field49: Int?, + + val field50: Int?, + val field51: Int?, + val field52: Int?, + val field53: Int?, + val field54: Int?, + val field55: Int?, + val field56: Int?, + val field57: Int?, + val field58: Int?, + val field59: Int?, + + val field60: Int?, + val field61: Int?, + val field62: Int?, + val field63: Int?, + val field64: Int?, + val field65: Int?, + val field66: Int?, + val field67: Int?, + val field68: Int?, + val field69: Int?, + + val field70: Int?, + val field71: Int?, + val field72: Int?, + val field73: Int?, + val field74: Int?, + val field75: Int?, + val field76: Int?, + val field77: Int?, + val field78: Int?, + val field79: Int?, + + val field80: Int?, + val field81: Int?, + val field82: Int?, + val field83: Int?, + val field84: Int?, + val field85: Int?, + val field86: Int?, + val field87: Int?, + val field88: Int?, + val field89: Int?, + + val field90: Int?, + val field91: Int?, + val field92: Int?, + val field93: Int?, + val field94: Int?, + val field95: Int?, + val field96: Int?, + val field97: Int?, + val field98: Int?, + val field99: Int?, + + val field100: Int?, + val field101: Int?, + val field102: Int?, + val field103: Int?, + val field104: Int?, + val field105: Int?, + val field106: Int?, + val field107: Int?, + val field108: Int?, + val field109: Int?, + + val field110: Int?, + val field111: Int?, + val field112: Int?, + val field113: Int?, + val field114: Int?, + val field115: Int?, + val field116: Int?, + val field117: Int?, + val field118: Int?, + val field119: Int?, + + val field120: Int?, + val field121: Int?, + val field122: Int?, + val field123: Int?, + val field124: Int?, + val field125: Int?, + val field126: Int?, + val field127: Int?, + val field128: Int?, + val field129: Int? + ) + + private val lists64: Lists64 = + Lists64( + emptyList(), + listOf(1, 42), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + + emptyList(), + emptyList(), + listOf(12, 43), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + emptyList(), + + emptyList(), + emptyList(), + emptyList(), + emptyList() + ) + + private val values70: Values70 = Values70( + null, null, null, 3, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, + null, null, 42, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, 66, null, null, null + ) + + private val values128: Values128 = Values128( + null, null, null, 3, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, + null, null, 42, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, 66, null, null, null, + null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, 115, null, null, null, null, + null, null, null, null, null, null, null, null + ) + + private val values130: Values130 = Values130( + null, null, null, 3, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, + null, null, 42, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, 66, null, null, null, + null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, 115, null, null, null, null, + null, null, null, null, null, null, null, null, 128, null + ) + + @Test + fun testLists64() { + val bytes = ProtoBuf.encodeToByteArray(lists64) + println(HexConverter.printHexBinary(bytes)) + + val decoded = ProtoBuf.decodeFromByteArray(bytes) + assertEquals(lists64, decoded) + } + + @Test + fun testValues70() { + val bytes = ProtoBuf.encodeToByteArray(values70) + println(HexConverter.printHexBinary(bytes)) + + val decoded = ProtoBuf.decodeFromByteArray(bytes) + assertEquals(values70, decoded) + } + + @Test + fun testValues128() { + val bytes = ProtoBuf.encodeToByteArray(values128) + println(HexConverter.printHexBinary(bytes)) + + val decoded = ProtoBuf.decodeFromByteArray(bytes) + assertEquals(values128, decoded) + } + + @Test + fun testValues130() { + val bytes = ProtoBuf.encodeToByteArray(values130) + println(HexConverter.printHexBinary(bytes)) + + val decoded = ProtoBuf.decodeFromByteArray(bytes) + assertEquals(values130, decoded) + } +}