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

commonMain.ch.softappeal.yass2.serialize.binary.BinarySerializer.kt Maven / Gradle / Ivy

There is a newer version: 5.0.0
Show newest version
package ch.softappeal.yass2.serialize.binary

import ch.softappeal.yass2.serialize.*
import kotlin.reflect.*

abstract class Encoder internal constructor(internal val type: KClass<*>) {
    internal abstract fun write(writer: EncoderWriter, value: Any?)
    internal abstract fun read(reader: EncoderReader): Any?
}

internal class EncoderId(val id: Int, val encoder: Encoder)

class EncoderWriter(internal val writer: Writer, private val serializer: BinarySerializer) {
    fun writeWithId(value: Any?) {
        val encoderId = when (value) {
            null -> NullEncoderId
            is List<*> -> ListEncoderId
            else -> serializer.type2encoderId[value::class] ?: error("missing type '${value::class}'")
        }
        writer.writeVarInt(encoderId.id)
        encoderId.encoder.write(this, value)
    }

    fun writeNoIdRequired(encoderId: Int, value: Any) = serializer.encoders[encoderId].write(this, value)
    fun writeNoIdOptional(encoderId: Int, value: Any?) {
        if (value == null) writer.writeByte(0) else {
            writer.writeByte(1)
            writeNoIdRequired(encoderId, value)
        }
    }
}

class EncoderReader(internal val reader: Reader, private val serializer: BinarySerializer) {
    fun readWithId(): Any? = serializer.encoders[reader.readVarInt()].read(this)

    fun readNoIdRequired(encoderId: Int): Any = serializer.encoders[encoderId].read(this)!!
    fun readNoIdOptional(encoderId: Int): Any? {
        if (reader.readByte().toInt() == 0) return null
        return readNoIdRequired(encoderId)
    }
}

private class NullType

private val NullEncoderId = EncoderId(0, object : Encoder(NullType::class) {
    override fun write(writer: EncoderWriter, value: Any?) = Unit
    override fun read(reader: EncoderReader): Any? = null
})

internal val ListEncoderId = EncoderId(1, object : Encoder(List::class) {
    override fun write(writer: EncoderWriter, value: Any?) {
        val list = value as List<*>
        writer.writer.writeVarInt(list.size)
        for (e in list) writer.writeWithId(e)
    }

    override fun read(reader: EncoderReader): MutableList<*> {
        var length = reader.reader.readVarInt()
        val list = ArrayList(minOf(length, 1000)) // prevents out-of-memory attack
        while (length-- > 0) list.add(reader.readWithId())
        return list
    }
})

internal const val FirstEncoderId = 2

/** Supports the following types: `null`, [List], [BaseEncoder] and [ClassEncoder]. */
class BinarySerializer(encoders: List) : Serializer {
    internal val encoders = (listOf(NullEncoderId.encoder, ListEncoderId.encoder) + encoders).toTypedArray()
    internal val type2encoderId = HashMap, EncoderId>(this.encoders.size)

    init {
        this.encoders.withIndex().forEach { (id, encoder) ->
            require(type2encoderId.put(encoder.type, EncoderId(id, encoder)) == null) {
                "duplicated type '${encoder.type}'"
            }
        }
    }

    override fun write(writer: Writer, value: Any?) = EncoderWriter(writer, this).writeWithId(value)
    override fun read(reader: Reader) = EncoderReader(reader, this).readWithId()
}

class ClassEncoder(
    type: KClass,
    private val writeProperties: (writer: EncoderWriter, instance: T) -> Unit,
    private val readInstance: (reader: EncoderReader) -> T
) : Encoder(type) {
    @Suppress("UNCHECKED_CAST")
    override fun write(writer: EncoderWriter, value: Any?) = writeProperties(writer, value as T)

    override fun read(reader: EncoderReader) = readInstance(reader)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy