All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jvmMain.kotlinx.serialization.internal.Caching.kt Maven / Gradle / Ivy

/*
 * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
 */

package kotlinx.serialization.internal

import kotlinx.serialization.KSerializer
import java.util.concurrent.ConcurrentHashMap
import kotlin.reflect.KClass
import kotlin.reflect.KType

/*
 * By default, we use ClassValue-based caches to avoid classloader leaks,
 * but ClassValue is not available on Android, thus we attempt to check it dynamically
 * and fallback to ConcurrentHashMap-based cache.
 */
private val useClassValue = runCatching {
    Class.forName("java.lang.ClassValue")
}.map { true }.getOrDefault(false)

/**
 * Creates a **strongly referenced** cache of values associated with [Class].
 * Serializers are computed using provided [factory] function.
 *
 * `null` values are not supported, though there aren't any technical limitations.
 */
internal actual fun  createCache(factory: (KClass<*>) -> KSerializer?): SerializerCache {
    return if (useClassValue) ClassValueCache(factory) else ConcurrentHashMapCache(factory)
}

/**
 * Creates a **strongly referenced** cache of values associated with [Class].
 * Serializers are computed using provided [factory] function.
 *
 * `null` values are not supported, though there aren't any technical limitations.
 */
internal actual fun  createParametrizedCache(factory: (KClass, List) -> KSerializer?): ParametrizedSerializerCache {
    return if (useClassValue) ClassValueParametrizedCache(factory) else ConcurrentHashMapParametrizedCache(factory)
}

@SuppressAnimalSniffer
private class ClassValueCache(private val compute: (KClass<*>) -> KSerializer?) : SerializerCache {
    private val classValue = initClassValue()

    private fun initClassValue() = object : ClassValue>() {
        /*
         * Since during the computing of the value for the `ClassValue` entry, we do not know whether a nullable
         *  serializer is needed, so we may need to differentiate nullable/non-null  caches by a level higher
         */
        override fun computeValue(type: Class<*>): CacheEntry {
            return CacheEntry(compute(type.kotlin))
        }
    }

    override fun get(key: KClass): KSerializer? = classValue[key.java].serializer
}

@SuppressAnimalSniffer
private class ClassValueParametrizedCache(private val compute: (KClass, List) -> KSerializer?) : ParametrizedSerializerCache {
    private val classValue = initClassValue()

    private fun initClassValue() = object : ClassValue>() {
        /*
        * Since during the computing of the value for the `ClassValue` entry, we do not know whether a nullable
        *  serializer is needed, so we may need to differentiate nullable/non-null  caches by a level higher
        */
        override fun computeValue(type: Class<*>): ParametrizedCacheEntry {
            return ParametrizedCacheEntry()
        }
    }

    override fun get(key: KClass, types: List): Result?> =
        classValue[key.java].computeIfAbsent(types) { compute(key, types) }
}

/**
 * We no longer support Java 6, so the only place we use this cache is Android, where there
 * are no classloader leaks issue, thus we can safely use strong references and do not bother
 * with WeakReference wrapping.
 */
private class ConcurrentHashMapCache(private val compute: (KClass<*>) -> KSerializer?) : SerializerCache {
    private val cache = ConcurrentHashMap, CacheEntry>()

    override fun get(key: KClass): KSerializer? {
        return cache.getOrPut(key.java) {
            CacheEntry(compute(key))
        }.serializer
    }
}



private class ConcurrentHashMapParametrizedCache(private val compute: (KClass, List) -> KSerializer?) : ParametrizedSerializerCache {
    private val cache = ConcurrentHashMap, ParametrizedCacheEntry>()

    override fun get(key: KClass, types: List): Result?> {
        return cache.getOrPut(key.java) { ParametrizedCacheEntry() }
            .computeIfAbsent(types) { compute(key, types) }
    }
}

private class CacheEntry(@JvmField val serializer: KSerializer?)

private class ParametrizedCacheEntry {
    private val serializers: ConcurrentHashMap, Result?>> = ConcurrentHashMap()
    inline fun computeIfAbsent(types: List, producer: () -> KSerializer?): Result?> {
        return serializers.getOrPut(types) {
            kotlin.runCatching { producer() }
        }
    }
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy