• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3  */
4 
5 @file:OptIn(ExperimentalSerializationApi::class)
6 
7 package kotlinx.serialization.protobuf.internal
8 
9 import kotlinx.serialization.*
10 import kotlinx.serialization.descriptors.*
11 import kotlinx.serialization.modules.*
12 import kotlinx.serialization.protobuf.*
13 
14 internal typealias ProtoDesc = Long
15 
16 internal enum class ProtoWireType(val typeId: Int) {
17     INVALID(-1),
18     VARINT(0),
19     i64(1),
20     SIZE_DELIMITED(2),
21     i32(5),
22     ;
23 
24     companion object {
25         fun from(typeId: Int): ProtoWireType {
26             return ProtoWireType.entries.find { it.typeId == typeId } ?: INVALID
27         }
28     }
29 
30     fun wireIntWithTag(tag: Int): Int {
31         return ((tag shl 3) or typeId)
32     }
33 
34     override fun toString(): String {
35         return "${this.name}($typeId)"
36     }
37 }
38 
39 internal const val ID_HOLDER_ONE_OF = -2
40 
41 private const val ONEOFMASK = 1L shl 36
42 private const val INTTYPEMASK = 3L shl 33
43 private const val PACKEDMASK = 1L shl 32
44 
45 @Suppress("NOTHING_TO_INLINE")
ProtoDescnull46 internal inline fun ProtoDesc(protoId: Int, type: ProtoIntegerType, packed: Boolean = false, oneOf: Boolean = false): ProtoDesc {
47     val packedBits = if (packed) PACKEDMASK else 0L
48     val oneOfBits = if (oneOf) ONEOFMASK else 0L
49     return packedBits or oneOfBits or type.signature or protoId.toLong()
50 }
51 
52 internal inline val ProtoDesc.protoId: Int get() = (this and Int.MAX_VALUE.toLong()).toInt()
53 
54 internal val ProtoDesc.integerType: ProtoIntegerType
55     get() = when(this and INTTYPEMASK) {
56     ProtoIntegerType.DEFAULT.signature -> ProtoIntegerType.DEFAULT
57     ProtoIntegerType.SIGNED.signature -> ProtoIntegerType.SIGNED
58     else -> ProtoIntegerType.FIXED
59 }
60 
61 internal val SerialDescriptor.isPackable: Boolean
62     @OptIn(kotlinx.serialization.ExperimentalSerializationApi::class)
63     get() = when (kind) {
64         PrimitiveKind.STRING,
65         !is PrimitiveKind -> false
66         else -> true
67     }
68 
69 internal val ProtoDesc.isPacked: Boolean
70     get() = (this and PACKEDMASK) != 0L
71 
72 internal val ProtoDesc.isOneOf: Boolean
73     get() = (this and ONEOFMASK) != 0L
74 
overrideIdnull75 internal fun ProtoDesc.overrideId(protoId: Int): ProtoDesc {
76     return this and (0xFFFFFFF00000000L) or protoId.toLong()
77 }
78 
extractParametersnull79 internal fun SerialDescriptor.extractParameters(index: Int): ProtoDesc {
80     val annotations = getElementAnnotations(index)
81     var protoId: Int = index + 1
82     var format: ProtoIntegerType = ProtoIntegerType.DEFAULT
83     var protoPacked = false
84     var isOneOf = false
85 
86     for (i in annotations.indices) { // Allocation-friendly loop
87         val annotation = annotations[i]
88         if (annotation is ProtoNumber) {
89             protoId = annotation.number
90             checkFieldNumber(protoId, i, this)
91         } else if (annotation is ProtoType) {
92             format = annotation.type
93         } else if (annotation is ProtoPacked) {
94             protoPacked = true
95         } else if (annotation is ProtoOneOf) {
96             isOneOf = true
97         }
98     }
99     if (isOneOf) {
100         // reset protoId to index-based for oneOf field,
101         // Decoder will restore the real proto id then from [ProtobufDecoder.index2IdMap]
102         // See [kotlinx.serialization.protobuf.internal.ProtobufDecoder.decodeElementIndex] for detail
103         protoId = index + 1
104     }
105     return ProtoDesc(protoId, format, protoPacked, isOneOf)
106 }
107 
108 /**
109  * Get the proto id from the descriptor of [index] element,
110  * or return [ID_HOLDER_ONE_OF] if such element is marked with [ProtoOneOf]
111  */
extractProtoIdnull112 internal fun extractProtoId(descriptor: SerialDescriptor, index: Int, zeroBasedDefault: Boolean): Int {
113     val annotations = descriptor.getElementAnnotations(index)
114     var result = if (zeroBasedDefault) index else index + 1
115     for (i in annotations.indices) { // Allocation-friendly loop
116         val annotation = annotations[i]
117         if (annotation is ProtoOneOf) {
118             // Fast return for one of field
119             return ID_HOLDER_ONE_OF
120         } else if (annotation is ProtoNumber) {
121             result = annotation.number
122             // 0 or negative numbers are acceptable for enums
123             if (!zeroBasedDefault) {
124                 checkFieldNumber(result, i, descriptor)
125             }
126         }
127     }
128     return result
129 }
130 
checkFieldNumbernull131 private fun checkFieldNumber(fieldNumber: Int, propertyIndex: Int, descriptor: SerialDescriptor) {
132     if (fieldNumber <= 0) {
133         throw SerializationException("$fieldNumber is not allowed in ProtoNumber for property '${descriptor.getElementName(propertyIndex)}' of '${descriptor.serialName}', because protobuf supports field numbers in range 1..${Int.MAX_VALUE}")
134     }
135 }
136 
137 internal class ProtobufDecodingException(message: String, e: Throwable? = null) : SerializationException(message, e)
138 
reverseBytesnull139 internal expect fun Int.reverseBytes(): Int
140 internal expect fun Long.reverseBytes(): Long
141 
142 
143 internal fun SerialDescriptor.getAllOneOfSerializerOfField(
144     serializersModule: SerializersModule,
145 ): List<SerialDescriptor> {
146     return when (this.kind) {
147         PolymorphicKind.OPEN -> serializersModule.getPolymorphicDescriptors(this)
148         PolymorphicKind.SEALED -> getElementDescriptor(1).elementDescriptors.toList()
149         else -> throw IllegalArgumentException("Class ${this.serialName} should be abstract or sealed or interface to be used as @ProtoOneOf property.")
150     }.onEach { desc ->
151         if (desc.getElementAnnotations(0).none { anno -> anno is ProtoNumber }) {
152             throw IllegalArgumentException("${desc.serialName} implementing oneOf type ${this.serialName} should have @ProtoNumber annotation in its single property.")
153         }
154     }
155 }
156 
getActualOneOfSerializernull157 internal fun SerialDescriptor.getActualOneOfSerializer(
158     serializersModule: SerializersModule,
159     protoId: Int
160 ): SerialDescriptor? {
161     return getAllOneOfSerializerOfField(serializersModule).find { it.extractParameters(0).protoId == protoId }
162 }
163