Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Properly cache serial names in order to improve performance of JSON p… #1209

Merged
merged 1 commit into from
Nov 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion core/api/kotlinx-serialization-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -858,7 +858,7 @@ public final class kotlinx/serialization/internal/Platform_commonKt {
public static final fun cast (Lkotlinx/serialization/SerializationStrategy;)Lkotlinx/serialization/SerializationStrategy;
}

public class kotlinx/serialization/internal/PluginGeneratedSerialDescriptor : kotlinx/serialization/descriptors/SerialDescriptor {
public class kotlinx/serialization/internal/PluginGeneratedSerialDescriptor : kotlinx/serialization/descriptors/SerialDescriptor, kotlinx/serialization/internal/CachedNames {
public fun <init> (Ljava/lang/String;Lkotlinx/serialization/internal/GeneratedSerializer;I)V
public synthetic fun <init> (Ljava/lang/String;Lkotlinx/serialization/internal/GeneratedSerializer;IILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun addElement (Ljava/lang/String;Z)V
Expand All @@ -872,6 +872,7 @@ public class kotlinx/serialization/internal/PluginGeneratedSerialDescriptor : ko
public final fun getElementsCount ()I
public fun getKind ()Lkotlinx/serialization/descriptors/SerialKind;
public fun getSerialName ()Ljava/lang/String;
public fun getSerialNames ()Ljava/util/Set;
public fun hashCode ()I
public fun isElementOptional (I)Z
public fun isNullable ()Z
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,9 +270,10 @@ internal class SerialDescriptorImpl(
override val elementsCount: Int,
typeParameters: List<SerialDescriptor>,
builder: ClassSerialDescriptorBuilder
) : SerialDescriptor {
) : SerialDescriptor, CachedNames {

public override val annotations: List<Annotation> = builder.annotations
override val annotations: List<Annotation> = builder.annotations
override val serialNames: Set<String> = builder.elementNames.toHashSet()

private val elementNames: Array<String> = builder.elementNames.toTypedArray()
private val elementDescriptors: Array<SerialDescriptor> = builder.elementDescriptors.compactArray()
Expand Down
20 changes: 20 additions & 0 deletions core/commonMain/src/kotlinx/serialization/internal/CachedNames.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* 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.descriptors.*

/**
* Internal interface used as a marker for [SerialDescriptor] in order
* to retrieve the set of all element names without allocations.
* Used by our implementations as a performance optimization.
* It's not an instance of [SerialDescriptor] to simplify implementation via delegation
*/
internal interface CachedNames {

/**
* A set of all names retrieved from [SerialDescriptor.getElementName]
*/
public val serialNames: Set<String>
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,12 @@ internal class NullableSerializer<T : Any>(private val serializer: KSerializer<T
}

@OptIn(ExperimentalSerializationApi::class)
internal class SerialDescriptorForNullable(internal val original: SerialDescriptor) : SerialDescriptor by original {
internal class SerialDescriptorForNullable(
internal val original: SerialDescriptor
) : SerialDescriptor by original, CachedNames {

override val serialName: String = original.serialName + "?"
override val serialNames: Set<String> = original.cachedSerialNames()
override val isNullable: Boolean
get() = true

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ package kotlinx.serialization.internal

import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
import kotlin.native.concurrent.*
import kotlin.reflect.*

Expand Down Expand Up @@ -56,8 +55,9 @@ internal object InternalHexConverter {
}
}

@OptIn(ExperimentalSerializationApi::class)
internal fun SerialDescriptor.cachedSerialNames(): Set<String> {
if (this is PluginGeneratedSerialDescriptor) return namesSet
if (this is CachedNames) return serialNames
val result = HashSet<String>(elementsCount)
for (i in 0 until elementsCount) {
result += getElementName(i)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ internal open class PluginGeneratedSerialDescriptor(
override val serialName: String,
private val generatedSerializer: GeneratedSerializer<*>? = null,
final override val elementsCount: Int
) : SerialDescriptor {
) : SerialDescriptor, CachedNames {
override val kind: SerialKind get() = StructureKind.CLASS
override val annotations: List<Annotation> get() = classAnnotations ?: emptyList()

Expand All @@ -29,7 +29,7 @@ internal open class PluginGeneratedSerialDescriptor(
// Classes rarely have annotations, so we can save up a bit of allocations here
private var classAnnotations: MutableList<Annotation>? = null
private val elementsOptionality = BooleanArray(elementsCount)
internal val namesSet: Set<String> get() = indices.keys
public override val serialNames: Set<String> get() = indices.keys

// don't change lazy mode: KT-32871, KT-32872
private val indices: Map<String, Int> by lazy { buildIndices() }
Expand Down