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