1 /*
<lambda>null2 * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3 */
4 @file:Suppress("UNUSED")
5
6 package kotlinx.serialization.internal
7
8 import kotlinx.serialization.*
9 import kotlinx.serialization.descriptors.*
10 import kotlinx.serialization.encoding.CompositeDecoder.Companion.UNKNOWN_NAME
11
12 /**
13 * Implementation that plugin uses to implement descriptors for auto-generated serializers.
14 */
15 @PublishedApi
16 @OptIn(ExperimentalSerializationApi::class)
17 internal open class PluginGeneratedSerialDescriptor(
18 override val serialName: String,
19 private val generatedSerializer: GeneratedSerializer<*>? = null,
20 final override val elementsCount: Int
21 ) : SerialDescriptor, CachedNames {
22 override val kind: SerialKind get() = StructureKind.CLASS
23 override val annotations: List<Annotation> get() = classAnnotations ?: emptyList()
24
25 private var added = -1
26 private val names = Array(elementsCount) { "[UNINITIALIZED]" }
27 private val propertiesAnnotations = arrayOfNulls<MutableList<Annotation>?>(elementsCount)
28
29 // Classes rarely have annotations, so we can save up a bit of allocations here
30 private var classAnnotations: MutableList<Annotation>? = null
31 private val elementsOptionality = BooleanArray(elementsCount)
32 public override val serialNames: Set<String> get() = indices.keys
33
34 private var indices: Map<String, Int> = emptyMap()
35 // Cache child serializers, they are not cached by the implementation for nullable types
36 private val childSerializers: Array<KSerializer<*>> by lazy(LazyThreadSafetyMode.PUBLICATION) { generatedSerializer?.childSerializers() ?: EMPTY_SERIALIZER_ARRAY }
37
38 // Lazy because of JS specific initialization order (#789)
39 internal val typeParameterDescriptors: Array<SerialDescriptor> by lazy(LazyThreadSafetyMode.PUBLICATION) {
40 generatedSerializer?.typeParametersSerializers()?.map { it.descriptor }.compactArray()
41 }
42
43 // Can be without synchronization but Native will likely break due to freezing
44 private val _hashCode: Int by lazy(LazyThreadSafetyMode.PUBLICATION) { hashCodeImpl(typeParameterDescriptors) }
45
46 public fun addElement(name: String, isOptional: Boolean = false) {
47 names[++added] = name
48 elementsOptionality[added] = isOptional
49 propertiesAnnotations[added] = null
50 if (added == elementsCount - 1) {
51 indices = buildIndices()
52 }
53 }
54
55 public fun pushAnnotation(annotation: Annotation) {
56 val list = propertiesAnnotations[added].let {
57 if (it == null) {
58 val result = ArrayList<Annotation>(1)
59 propertiesAnnotations[added] = result
60 result
61 } else {
62 it
63 }
64 }
65 list.add(annotation)
66 }
67
68 public fun pushClassAnnotation(a: Annotation) {
69 if (classAnnotations == null) {
70 classAnnotations = ArrayList(1)
71 }
72 classAnnotations!!.add(a)
73 }
74
75 override fun getElementDescriptor(index: Int): SerialDescriptor {
76 return childSerializers.getChecked(index).descriptor
77 }
78
79 override fun isElementOptional(index: Int): Boolean = elementsOptionality.getChecked(index)
80 override fun getElementAnnotations(index: Int): List<Annotation> =
81 propertiesAnnotations.getChecked(index) ?: emptyList()
82 override fun getElementName(index: Int): String = names.getChecked(index)
83 override fun getElementIndex(name: String): Int = indices[name] ?: UNKNOWN_NAME
84
85 private fun buildIndices(): Map<String, Int> {
86 val indices = HashMap<String, Int>()
87 for (i in names.indices) {
88 indices[names[i]] = i
89 }
90 return indices
91 }
92
93 override fun equals(other: Any?): Boolean = equalsImpl(other) { otherDescriptor ->
94 typeParameterDescriptors.contentEquals(otherDescriptor.typeParameterDescriptors)
95 }
96
97 override fun hashCode(): Int = _hashCode
98
99 override fun toString(): String {
100 return (0 until elementsCount).joinToString(", ", "$serialName(", ")") { i ->
101 getElementName(i) + ": " + getElementDescriptor(i).serialName
102 }
103 }
104 }
105
106 @OptIn(ExperimentalSerializationApi::class)
equalsImplnull107 internal inline fun <reified SD : SerialDescriptor> SD.equalsImpl(
108 other: Any?,
109 typeParamsAreEqual: (otherDescriptor: SD) -> Boolean
110 ): Boolean {
111 if (this === other) return true
112 if (other !is SD) return false
113 if (serialName != other.serialName) return false
114 if (!typeParamsAreEqual(other)) return false
115 if (this.elementsCount != other.elementsCount) return false
116 for (index in 0 until elementsCount) {
117 if (getElementDescriptor(index).serialName != other.getElementDescriptor(index).serialName) return false
118 if (getElementDescriptor(index).kind != other.getElementDescriptor(index).kind) return false
119 }
120 return true
121 }
122
123 @OptIn(ExperimentalSerializationApi::class)
hashCodeImplnull124 internal fun SerialDescriptor.hashCodeImpl(typeParams: Array<SerialDescriptor>): Int {
125 var result = serialName.hashCode()
126 result = 31 * result + typeParams.contentHashCode()
127 val elementDescriptors = elementDescriptors
128 val namesHash = elementDescriptors.elementsHashCodeBy { it.serialName }
129 val kindHash = elementDescriptors.elementsHashCodeBy { it.kind }
130 result = 31 * result + namesHash
131 result = 31 * result + kindHash
132 return result
133 }
134