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

commonMain.kotlinx.serialization.internal.CollectionSerializers.kt Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
 */
@file:Suppress("DEPRECATION_ERROR")
@file:OptIn(ExperimentalSerializationApi::class)
package kotlinx.serialization.internal

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

@InternalSerializationApi
public sealed class AbstractCollectionSerializer : KSerializer {
    protected abstract fun Collection.collectionSize(): Int
    protected abstract fun Collection.collectionIterator(): Iterator
    protected abstract fun builder(): Builder
    protected abstract fun Builder.builderSize(): Int
    protected abstract fun Builder.toResult(): Collection
    protected abstract fun Collection.toBuilder(): Builder
    protected abstract fun Builder.checkCapacity(size: Int)

    abstract override fun serialize(encoder: Encoder, value: Collection)

    @InternalSerializationApi
    public fun merge(decoder: Decoder, previous: Collection?): Collection {
        val builder = previous?.toBuilder() ?: builder()
        val startIndex = builder.builderSize()
        val compositeDecoder = decoder.beginStructure(descriptor)
        if (compositeDecoder.decodeSequentially()) {
            readAll(compositeDecoder, builder, startIndex, readSize(compositeDecoder, builder))
        } else {
            while (true) {
                val index = compositeDecoder.decodeElementIndex(descriptor)
                if (index == CompositeDecoder.DECODE_DONE) break
                readElement(compositeDecoder, startIndex + index, builder)
            }
        }
        compositeDecoder.endStructure(descriptor)
        return builder.toResult()
    }

    override fun deserialize(decoder: Decoder): Collection = merge(decoder, null)

    private fun readSize(decoder: CompositeDecoder, builder: Builder): Int {
        val size = decoder.decodeCollectionSize(descriptor)
        builder.checkCapacity(size)
        return size
    }

    protected abstract fun readElement(decoder: CompositeDecoder, index: Int, builder: Builder, checkIndex: Boolean = true)

    protected abstract fun readAll(decoder: CompositeDecoder, builder: Builder, startIndex: Int, size: Int)
}

@PublishedApi
internal sealed class CollectionLikeSerializer(
    private val elementSerializer: KSerializer
) : AbstractCollectionSerializer() {

    protected abstract fun Builder.insert(index: Int, element: Element)
    abstract override val descriptor: SerialDescriptor

    override fun serialize(encoder: Encoder, value: Collection) {
        val size = value.collectionSize()
        encoder.encodeCollection(descriptor, size) {
            val iterator = value.collectionIterator()
            for (index in 0 until size)
                encodeSerializableElement(descriptor, index, elementSerializer, iterator.next())
        }
    }

    final override fun readAll(decoder: CompositeDecoder, builder: Builder, startIndex: Int, size: Int) {
        require(size >= 0) { "Size must be known in advance when using READ_ALL" }
        for (index in 0 until size)
            readElement(decoder, startIndex + index, builder, checkIndex = false)
    }

    override fun readElement(decoder: CompositeDecoder, index: Int, builder: Builder, checkIndex: Boolean) {
        builder.insert(index, decoder.decodeSerializableElement(descriptor, index, elementSerializer))
    }
}

@InternalSerializationApi // TODO tech debt: it's used in ProtoBuf
public sealed class MapLikeSerializer>(
    public val keySerializer: KSerializer,
    public val valueSerializer: KSerializer
) : AbstractCollectionSerializer, Collection, Builder>() {

    protected abstract fun Builder.insertKeyValuePair(index: Int, key: Key, value: Value)
    abstract override val descriptor: SerialDescriptor

    protected final override fun readAll(decoder: CompositeDecoder, builder: Builder, startIndex: Int, size: Int) {
        require(size >= 0) { "Size must be known in advance when using READ_ALL" }
        for (index in 0 until size * 2 step 2)
            readElement(decoder, startIndex + index, builder, checkIndex = false)
    }

    final override fun readElement(decoder: CompositeDecoder, index: Int, builder: Builder, checkIndex: Boolean) {
        val key: Key = decoder.decodeSerializableElement(descriptor, index, keySerializer)
        val vIndex = if (checkIndex) {
            decoder.decodeElementIndex(descriptor).also {
                require(it == index + 1) { "Value must follow key in a map, index for key: $index, returned index for value: $it" }
            }
        } else {
            index + 1
        }
        val value: Value = if (builder.containsKey(key) && valueSerializer.descriptor.kind !is PrimitiveKind) {
            decoder.decodeSerializableElement(descriptor, vIndex, valueSerializer, builder.getValue(key))
        } else {
            decoder.decodeSerializableElement(descriptor, vIndex, valueSerializer)
        }
        builder[key] = value
    }

    override fun serialize(encoder: Encoder, value: Collection) {
        val size = value.collectionSize()
        encoder.encodeCollection(descriptor, size) {
            val iterator = value.collectionIterator()
            var index = 0
            iterator.forEach { (k, v) ->
                encodeSerializableElement(descriptor, index++, keySerializer, k)
                encodeSerializableElement(descriptor, index++, valueSerializer, v)
            }
        }
    }
}

@PublishedApi
internal abstract class PrimitiveArrayBuilder internal constructor() {
    internal abstract val position: Int
    internal abstract fun ensureCapacity(requiredCapacity: Int = position + 1)
    internal abstract fun build(): Array
}

/**
 * Base serializer for all serializers for primitive arrays.
 *
 * It exists only to avoid code duplication and should not be used or implemented directly.
 * Use concrete serializers ([ByteArraySerializer], etc) instead.
 */
@PublishedApi
internal abstract class PrimitiveArraySerializer> internal constructor(
    primitiveSerializer: KSerializer
) : CollectionLikeSerializer(primitiveSerializer) {
    final override val descriptor: SerialDescriptor = PrimitiveArrayDescriptor(primitiveSerializer.descriptor)

    final override fun Builder.builderSize(): Int = position
    final override fun Builder.toResult(): Array = build()
    final override fun Builder.checkCapacity(size: Int): Unit = ensureCapacity(size)

    final override fun Array.collectionIterator(): Iterator =
        error("This method lead to boxing and must not be used, use writeContents instead")

    final override fun Builder.insert(index: Int, element: Element): Unit =
        error("This method lead to boxing and must not be used, use Builder.append instead")

    final override fun builder(): Builder = empty().toBuilder()

    protected abstract fun empty(): Array

    abstract override fun readElement(
        decoder: CompositeDecoder,
        index: Int,
        builder: Builder,
        checkIndex: Boolean
    )

    protected abstract fun writeContent(encoder: CompositeEncoder, content: Array, size: Int)

    final override fun serialize(encoder: Encoder, value: Array) {
        val size = value.collectionSize()
        encoder.encodeCollection(descriptor, size) {
            writeContent(this, value, size)
        }
    }

    final override fun deserialize(decoder: Decoder): Array = merge(decoder, null)
}

// todo: can be more efficient when array size is know in advance, this one always uses temporary ArrayList as builder
@PublishedApi
internal class ReferenceArraySerializer(
    private val kClass: KClass,
    eSerializer: KSerializer
) : CollectionLikeSerializer, ArrayList>(eSerializer) {
    override val descriptor: SerialDescriptor = ArrayClassDesc(eSerializer.descriptor)

    override fun Array.collectionSize(): Int = size
    override fun Array.collectionIterator(): Iterator = iterator()
    override fun builder(): ArrayList = arrayListOf()
    override fun ArrayList.builderSize(): Int = size

    @Suppress("UNCHECKED_CAST")
    override fun ArrayList.toResult(): Array = toNativeArrayImpl(kClass)

    override fun Array.toBuilder(): ArrayList = ArrayList(this.asList())
    override fun ArrayList.checkCapacity(size: Int): Unit = ensureCapacity(size)
    override fun ArrayList.insert(index: Int, element: Element) {
        add(index, element)
    }
}

@PublishedApi
internal abstract class CollectionSerializer, B>(element: KSerializer) : CollectionLikeSerializer(element) {
    override fun C.collectionSize(): Int = size
    override fun C.collectionIterator(): Iterator = iterator()
}

@InternalSerializationApi
@PublishedApi
internal class ArrayListSerializer(element: KSerializer) : CollectionSerializer, ArrayList>(element) {
    override val descriptor: SerialDescriptor = ArrayListClassDesc(element.descriptor)

    override fun builder(): ArrayList = arrayListOf()
    override fun ArrayList.builderSize(): Int = size
    override fun ArrayList.toResult(): List = this
    override fun List.toBuilder(): ArrayList = this as? ArrayList ?: ArrayList(this)
    override fun ArrayList.checkCapacity(size: Int): Unit = ensureCapacity(size)
    override fun ArrayList.insert(index: Int, element: E) { add(index, element) }
}

@PublishedApi
internal class LinkedHashSetSerializer(
    eSerializer: KSerializer
) : CollectionSerializer, LinkedHashSet>(eSerializer) {
    override val descriptor: SerialDescriptor = LinkedHashSetClassDesc(eSerializer.descriptor)

    override fun builder(): LinkedHashSet = linkedSetOf()
    override fun LinkedHashSet.builderSize(): Int = size
    override fun LinkedHashSet.toResult(): Set = this
    override fun Set.toBuilder(): LinkedHashSet = this as? LinkedHashSet ?: LinkedHashSet(this)
    override fun LinkedHashSet.checkCapacity(size: Int) {}
    override fun LinkedHashSet.insert(index: Int, element: E) { add(element) }
}

@PublishedApi
internal class HashSetSerializer(
    eSerializer: KSerializer
) : CollectionSerializer, HashSet>(eSerializer) {
    override val descriptor: SerialDescriptor = HashSetClassDesc(eSerializer.descriptor)

    override fun builder(): HashSet = HashSet()
    override fun HashSet.builderSize(): Int = size
    override fun HashSet.toResult(): Set = this
    override fun Set.toBuilder(): HashSet = this as? HashSet ?: HashSet(this)
    override fun HashSet.checkCapacity(size: Int) {}
    override fun HashSet.insert(index: Int, element: E) { add(element) }
}

@PublishedApi
internal class LinkedHashMapSerializer(
    kSerializer: KSerializer, vSerializer: KSerializer
) : MapLikeSerializer, LinkedHashMap>(kSerializer, vSerializer) {

    override val descriptor: SerialDescriptor = LinkedHashMapClassDesc(kSerializer.descriptor, vSerializer.descriptor)
    override fun Map.collectionSize(): Int = size
    override fun Map.collectionIterator(): Iterator> = iterator()
    override fun builder(): LinkedHashMap = LinkedHashMap()
    override fun LinkedHashMap.builderSize(): Int = size * 2
    override fun LinkedHashMap.toResult(): Map = this
    override fun Map.toBuilder(): LinkedHashMap = this as? LinkedHashMap ?: LinkedHashMap(this)
    override fun LinkedHashMap.checkCapacity(size: Int) {}
    override fun LinkedHashMap.insertKeyValuePair(index: Int, key: K, value: V): Unit = set(key, value)
}

@PublishedApi
internal class HashMapSerializer(
    kSerializer: KSerializer, vSerializer: KSerializer
) : MapLikeSerializer, HashMap>(kSerializer, vSerializer) {

    override val descriptor: SerialDescriptor = HashMapClassDesc(kSerializer.descriptor, vSerializer.descriptor)
    override fun Map.collectionSize(): Int = size
    override fun Map.collectionIterator(): Iterator> = iterator()
    override fun builder(): HashMap = HashMap()
    override fun HashMap.builderSize(): Int = size * 2
    override fun HashMap.toResult(): Map = this
    override fun Map.toBuilder(): HashMap = this as? HashMap ?: HashMap(this)
    override fun HashMap.checkCapacity(size: Int) {}
    override fun HashMap.insertKeyValuePair(index: Int, key: K, value: V): Unit = set(key, value)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy