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

commonMain.serialize.binary.BinarySerializer.kt Maven / Gradle / Ivy

The newest version!
package ch.softappeal.yass2.serialize.binary

import ch.softappeal.yass2.serialize.Reader
import ch.softappeal.yass2.serialize.Serializer
import ch.softappeal.yass2.serialize.Writer
import kotlin.reflect.KClass

public abstract class Encoder internal constructor(public val type: KClass<*>) {
    internal abstract fun write(writer: EncoderWriter, value: Any?)
    internal abstract fun read(reader: EncoderReader): Any?
    internal open fun writeWithId(writer: EncoderWriter, id: Int, value: Any?) {
        writer.writer.writeVarInt(id)
        write(writer, value)
    }
}

public class EncoderId internal constructor(public val id: Int, internal val encoder: Encoder) {
    internal fun writeWithId(writer: EncoderWriter, value: Any?) = encoder.writeWithId(writer, id, value)
}

public class EncoderWriter internal constructor(internal val writer: Writer, private val serializer: BinarySerializer) {
    internal val object2reference: HashMap by lazy(LazyThreadSafetyMode.NONE) { HashMap(16) }

    public fun writeWithId(value: Any?) {
        when (value) {
            null -> NullEncoderId
            is List<*> -> ListEncoderId
            else -> serializer.type2encoderId[value::class] ?: error("missing type '${value::class}'")
        }.writeWithId(this, value)
    }

    public fun writeNoIdRequired(encoderId: Int, value: Any): Unit = serializer.encoders[encoderId].write(this, value)
    public fun writeNoIdOptional(encoderId: Int, value: Any?): Unit = if (value == null) {
        writer.writeBoolean(false)
    } else {
        writer.writeBoolean(true)
        writeNoIdRequired(encoderId, value)
    }
}

public class EncoderReader internal constructor(internal val reader: Reader, private val encoders: Array) {
    internal val objects: ArrayList by lazy(LazyThreadSafetyMode.NONE) { ArrayList(16) }

    public fun  created(value: T): T {
        objects.add(value)
        return value
    }

    public fun readWithId(): Any? = encoders[reader.readVarInt()].read(this)
    public fun readNoIdRequired(encoderId: Int): Any = encoders[encoderId].read(this)!!
    public fun readNoIdOptional(encoderId: Int): Any? = if (reader.readBoolean()) readNoIdRequired(encoderId) else null
}

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

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

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

private class ReferenceType

private val ReferenceEncoderId = EncoderId(2, object : Encoder(ReferenceType::class) {
    override fun write(writer: EncoderWriter, value: Any?) = writer.writer.writeVarInt(value as Int)
    override fun read(reader: EncoderReader) = reader.objects[reader.reader.readVarInt()]
})

public const val FIRST_ENCODER_ID: Int = 3

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

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

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

public abstract class BaseEncoder(
    type: KClass,
    public val write: (writer: Writer, value: T) -> Unit,
    public val read: (reader: Reader) -> T,
) : Encoder(type) {
    override fun write(writer: EncoderWriter, value: Any?) = write(writer.writer, @Suppress("UNCHECKED_CAST") (value as T))
    override fun read(reader: EncoderReader) = read(reader.reader)
}

/**
 * The class must be concrete and must have a primary constructor and all its parameters must be properties.
 * Body properties are allowed but must be of `var` kind.
 * Inheritance is supported.
 */
public class ClassEncoder(
    type: KClass,
    private val graph: Boolean,
    private val writeProperties: (writer: EncoderWriter, instance: T) -> Unit,
    private val readInstance: (reader: EncoderReader) -> T,
) : Encoder(type) {
    override fun write(writer: EncoderWriter, value: Any?) = writeProperties(writer, @Suppress("UNCHECKED_CAST") (value as T))
    override fun read(reader: EncoderReader) = readInstance(reader)
    override fun writeWithId(writer: EncoderWriter, id: Int, value: Any?) {
        if (graph) with(writer.object2reference) {
            val reference = this[value]
            if (reference != null) {
                ReferenceEncoderId.writeWithId(writer, reference)
                return
            }
            this[value!!] = size
        }
        super.writeWithId(writer, id, value)
    }
}

@Target(AnnotationTarget.PROPERTY)
public annotation class GenerateBinarySerializer(
    val baseEncoderClasses: Array>>,
    val enumClasses: Array>,
    val treeConcreteClasses: Array>,
    val graphConcreteClasses: Array>,
    val withDumper: Boolean,
)




© 2015 - 2025 Weber Informatics LLC | Privacy Policy