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:OptIn(ExperimentalSerializationApi::class)
5 package kotlinx.serialization.internal
6
7 import kotlinx.serialization.*
8 import kotlinx.serialization.descriptors.*
9 import kotlin.native.concurrent.*
10 import kotlin.reflect.*
11
12 internal object InternalHexConverter {
13 private const val hexCode = "0123456789ABCDEF"
14
15 fun parseHexBinary(s: String): ByteArray {
16 val len = s.length
17 require(len % 2 == 0) { "HexBinary string must be even length" }
18 val bytes = ByteArray(len / 2)
19 var i = 0
20
21 while (i < len) {
22 val h = hexToInt(s[i])
23 val l = hexToInt(s[i + 1])
24 require(!(h == -1 || l == -1)) { "Invalid hex chars: ${s[i]}${s[i + 1]}" }
25
26 bytes[i / 2] = ((h shl 4) + l).toByte()
27 i += 2
28 }
29
30 return bytes
31 }
32
33 private fun hexToInt(ch: Char): Int = when (ch) {
34 in '0'..'9' -> ch - '0'
35 in 'A'..'F' -> ch - 'A' + 10
36 in 'a'..'f' -> ch - 'a' + 10
37 else -> -1
38 }
39
40 fun printHexBinary(data: ByteArray, lowerCase: Boolean = false): String {
41 val r = StringBuilder(data.size * 2)
42 for (b in data) {
43 r.append(hexCode[b.toInt() shr 4 and 0xF])
44 r.append(hexCode[b.toInt() and 0xF])
45 }
46 return if (lowerCase) r.toString().lowercase() else r.toString()
47 }
48
49 fun toHexString(n: Int): String {
50 val arr = ByteArray(4)
51 for (i in 0 until 4) {
52 arr[i] = (n shr (24 - i * 8)).toByte()
53 }
54 return printHexBinary(arr, true).trimStart('0').takeIf { it.isNotEmpty() } ?: "0"
55 }
56 }
57
58 @OptIn(ExperimentalSerializationApi::class)
cachedSerialNamesnull59 internal fun SerialDescriptor.cachedSerialNames(): Set<String> {
60 if (this is CachedNames) return serialNames
61 val result = HashSet<String>(elementsCount)
62 for (i in 0 until elementsCount) {
63 result += getElementName(i)
64 }
65 return result
66 }
67
68 private val EMPTY_DESCRIPTOR_ARRAY: Array<SerialDescriptor> = arrayOf()
69
70 /**
71 * Same as [toTypedArray], but uses special empty array constant, if [this]
72 * is null or empty.
73 */
compactArraynull74 internal fun List<SerialDescriptor>?.compactArray(): Array<SerialDescriptor> =
75 takeUnless { it.isNullOrEmpty() }?.toTypedArray() ?: EMPTY_DESCRIPTOR_ARRAY
76
77 @Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE")
78 @PublishedApi
castnull79 internal inline fun <T> KSerializer<*>.cast(): KSerializer<T> = this as KSerializer<T>
80
81 @Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE")
82 @PublishedApi
83 internal inline fun <T> SerializationStrategy<*>.cast(): SerializationStrategy<T> = this as SerializationStrategy<T>
84
85 @Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE")
86 @PublishedApi
87 internal inline fun <T> DeserializationStrategy<*>.cast(): DeserializationStrategy<T> =
88 this as DeserializationStrategy<T>
89
90 internal fun KClass<*>.serializerNotRegistered(): Nothing {
91 throw SerializationException(notRegisteredMessage())
92 }
93
notRegisteredMessagenull94 internal fun KClass<*>.notRegisteredMessage(): String = notRegisteredMessage(simpleName ?: "<local class name not available>")
95
96 internal fun notRegisteredMessage(className: String): String = "Serializer for class '$className' is not found.\n" +
97 "Please ensure that class is marked as '@Serializable' and that the serialization compiler plugin is applied.\n"
98
99 internal expect fun KClass<*>.platformSpecificSerializerNotRegistered(): Nothing
100
101 @Suppress("UNCHECKED_CAST")
102 internal fun KType.kclass() = when (val t = classifier) {
103 is KClass<*> -> t
104 is KTypeParameter -> {
105 // If you are going to change this error message, please also actualize the message in the compiler intrinsics here:
106 // Kotlin/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializationJvmIrIntrinsicSupport.kt#argumentTypeOrGenerateException
107 throw IllegalArgumentException(
108 "Captured type parameter $t from generic non-reified function. " +
109 "Such functionality cannot be supported because $t is erased, either specify serializer explicitly or make " +
110 "calling function inline with reified $t."
111 )
112 }
113
114 else -> throw IllegalArgumentException("Only KClass supported as classifier, got $t")
115 } as KClass<Any>
116
117 // If you are going to change this error message, please also actualize the message in the compiler intrinsics here:
118 // Kotlin/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializationJvmIrIntrinsicSupport.kt#argumentTypeOrGenerateException
<lambda>null119 internal fun KTypeProjection.typeOrThrow(): KType = requireNotNull(type) { "Star projections in type arguments are not allowed, but had $type" }
120
121 /**
122 * Constructs KSerializer<D<T0, T1, ...>> by given KSerializer<T0>, KSerializer<T1>, ...
123 * via reflection (on JVM) or compiler+plugin intrinsic `SerializerFactory` (on Native)
124 */
constructSerializerForGivenTypeArgsnull125 internal expect fun <T : Any> KClass<T>.constructSerializerForGivenTypeArgs(vararg args: KSerializer<Any?>): KSerializer<T>?
126
127 /**
128 * Checks whether given KType and its corresponding KClass represent a reference array
129 */
130 internal expect fun isReferenceArray(rootClass: KClass<Any>): Boolean
131
132 /**
133 * Array.get that checks indices on JS
134 */
135 internal expect fun <T> Array<T>.getChecked(index: Int): T
136
137 /**
138 * Array.get that checks indices on JS
139 */
140 internal expect fun BooleanArray.getChecked(index: Int): Boolean
141
142 internal expect fun <T : Any> KClass<T>.compiledSerializerImpl(): KSerializer<T>?
143
144 /**
145 * Create serializers cache for non-parametrized and non-contextual serializers.
146 * The activity and type of cache is determined for a specific platform and a specific environment.
147 */
148 internal expect fun <T> createCache(factory: (KClass<*>) -> KSerializer<T>?): SerializerCache<T>
149
150 /**
151 * Create serializers cache for parametrized and non-contextual serializers. Parameters also non-contextual.
152 * The activity and type of cache is determined for a specific platform and a specific environment.
153 */
154 internal expect fun <T> createParametrizedCache(factory: (KClass<Any>, List<KType>) -> KSerializer<T>?): ParametrizedSerializerCache<T>
155
156 internal expect fun <T : Any, E : T?> ArrayList<E>.toNativeArrayImpl(eClass: KClass<T>): Array<E>
157
158 internal inline fun <T, K> Iterable<T>.elementsHashCodeBy(selector: (T) -> K): Int {
159 return fold(1) { hash, element -> 31 * hash + selector(element).hashCode() }
160 }
161
162 /**
163 * Cache class for non-parametrized and non-contextual serializers.
164 */
165 internal interface SerializerCache<T> {
166 /**
167 * Returns cached serializer or `null` if serializer not found.
168 */
getnull169 fun get(key: KClass<Any>): KSerializer<T>?
170 }
171
172 /**
173 * Cache class for parametrized and non-contextual serializers.
174 */
175 internal interface ParametrizedSerializerCache<T> {
176 /**
177 * Returns successful result with cached serializer or `null` if root serializer not found.
178 * If no serializer was found for the parameters, then result contains an exception.
179 */
180 fun get(key: KClass<Any>, types: List<KType> = emptyList()): Result<KSerializer<T>?>
181 }
182