
commonMain.kotlinx.serialization.internal.Platform.common.kt Maven / Gradle / Ivy
/*
* Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.serialization.internal
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
import kotlin.native.concurrent.*
import kotlin.reflect.*
@InternalSerializationApi
@Deprecated(
level = DeprecationLevel.ERROR, message = "HexConverter slipped into public API surface accidentally and will be removed in the future releases. " +
"You can copy-paste it to your project or (better) find a polished implementation that initially was intended for public uses."
)
public object HexConverter {
private const val hexCode = "0123456789ABCDEF"
public fun parseHexBinary(s: String): ByteArray = InternalHexConverter.parseHexBinary(s)
public fun printHexBinary(data: ByteArray, lowerCase: Boolean = false): String = InternalHexConverter.printHexBinary(data, lowerCase)
public fun toHexString(n: Int): String = InternalHexConverter.toHexString(n)
}
internal object InternalHexConverter {
private const val hexCode = "0123456789ABCDEF"
fun parseHexBinary(s: String): ByteArray {
val len = s.length
require(len % 2 == 0) { "HexBinary string must be even length" }
val bytes = ByteArray(len / 2)
var i = 0
while (i < len) {
val h = hexToInt(s[i])
val l = hexToInt(s[i + 1])
require(!(h == -1 || l == -1)) { "Invalid hex chars: ${s[i]}${s[i+1]}" }
bytes[i / 2] = ((h shl 4) + l).toByte()
i += 2
}
return bytes
}
private fun hexToInt(ch: Char): Int = when (ch) {
in '0'..'9' -> ch - '0'
in 'A'..'F' -> ch - 'A' + 10
in 'a'..'f' -> ch - 'a' + 10
else -> -1
}
fun printHexBinary(data: ByteArray, lowerCase: Boolean = false): String {
val r = StringBuilder(data.size * 2)
for (b in data) {
r.append(hexCode[b.toInt() shr 4 and 0xF])
r.append(hexCode[b.toInt() and 0xF])
}
return if (lowerCase) r.toString().toLowerCase() else r.toString()
}
fun toHexString(n: Int): String {
val arr = ByteArray(4)
for (i in 0 until 4) {
arr[i] = (n shr (24 - i * 8)).toByte()
}
return printHexBinary(arr, true).trimStart('0').takeIf { it.isNotEmpty() } ?: "0"
}
}
internal fun SerialDescriptor.cachedSerialNames(): Set {
@Suppress("DEPRECATION_ERROR")
if (this is PluginGeneratedSerialDescriptor) return namesSet
val result = HashSet(elementsCount)
for (i in 0 until elementsCount) {
result += getElementName(i)
}
return result
}
@SharedImmutable
private val EMPTY_DESCRIPTOR_ARRAY: Array = arrayOf()
/**
* Same as [toTypedArray], but uses special empty array constant, if [this]
* is null or empty.
*/
internal fun List?.compactArray(): Array =
takeUnless { it.isNullOrEmpty() }?.toTypedArray() ?: EMPTY_DESCRIPTOR_ARRAY
/**
* Returns serial descriptor that delegates all the calls to descriptor returned by [deferred] block.
* Used to resolve cyclic dependencies between recursive serializable structures.
*/
internal fun defer(deferred: () -> SerialDescriptor): SerialDescriptor = object : SerialDescriptor {
private val original: SerialDescriptor by lazy(deferred)
override val serialName: String
get() = original.serialName
override val kind: SerialKind
get() = original.kind
override val elementsCount: Int
get() = original.elementsCount
override fun getElementName(index: Int): String = original.getElementName(index)
override fun getElementIndex(name: String): Int = original.getElementIndex(name)
override fun getElementAnnotations(index: Int): List = original.getElementAnnotations(index)
override fun getElementDescriptor(index: Int): SerialDescriptor = original.getElementDescriptor(index)
override fun isElementOptional(index: Int): Boolean = original.isElementOptional(index)
}
@Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE")
@PublishedApi
internal inline fun KSerializer<*>.cast(): KSerializer = this as KSerializer
@Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE")
@PublishedApi
internal inline fun SerializationStrategy<*>.cast(): SerializationStrategy = this as SerializationStrategy
@Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE")
@PublishedApi
internal inline fun DeserializationStrategy<*>.cast(): DeserializationStrategy = this as DeserializationStrategy
internal fun KClass<*>.serializerNotRegistered(): Nothing {
throw SerializationException(
"Serializer for class '${simpleName}' is not found. " +
"Mark the class as @Serializable or provide the serializer explicitly."
)
}
@Suppress("UNCHECKED_CAST")
internal fun KType.kclass() = when (val t = classifier) {
is KClass<*> -> t
else -> error("Only KClass supported as classifier, got $t")
} as KClass
/**
* Constructs KSerializer> by given KSerializer, KSerializer, ...
* via reflection (on JVM) or compiler+plugin intrinsic `SerializerFactory` (on Native)
*/
internal expect fun KClass.constructSerializerForGivenTypeArgs(vararg args: KSerializer): KSerializer?
/**
* Checks whether given KType and its corresponding KClass represent a reference array
*/
internal expect fun isReferenceArray(rootClass: KClass): Boolean
/**
* Array.get that checks indices on JS
*/
internal expect fun Array.getChecked(index: Int): T
/**
* Array.get that checks indices on JS
*/
internal expect fun BooleanArray.getChecked(index: Int): Boolean
internal expect fun KClass.compiledSerializerImpl(): KSerializer?
internal expect fun ArrayList.toNativeArrayImpl(eClass: KClass): Array
/**
* Checks whether the receiver is an instance of a given kclass.
*
* This check is a replacement for [KClass.isInstance] because on JVM it requires kotlin-reflect.jar in classpath (see KT-14720).
*
* On JS and Native, this function delegates to aforementioned [KClass.isInstance] since it is supported there out-of-the-box;
* on JVM, it falls back to `java.lang.Class.isInstance`, which causes difference when applied to function types with big arity.
*/
internal expect fun Any.isInstanceOf(kclass: KClass<*>): Boolean
/**
* Same as [SerialDescriptor.getElementIndex], but throws [SerializationException] if
* given [name] is not associated with any element in the descriptor.
*/
internal fun SerialDescriptor.getElementIndexOrThrow(name: String): Int {
val index = getElementIndex(name)
if (index == CompositeDecoder.UNKNOWN_NAME)
throw SerializationException("$serialName does not contain element with name '$name'")
return index
}
internal inline fun Iterable.elementsHashCodeBy(selector: (T) -> K): Int {
return fold(1) { hash, element -> 31 * hash + selector(element).hashCode() }
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy