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

commonMain.Nbt.kt Maven / Gradle / Ivy

There is a newer version: 0.11.8
Show newest version
package net.benwoodworth.knbt

import kotlinx.serialization.*
import kotlinx.serialization.modules.EmptySerializersModule
import kotlinx.serialization.modules.SerializersModule
import net.benwoodworth.knbt.internal.BinaryNbtReader
import net.benwoodworth.knbt.internal.BinaryNbtWriter
import okio.Buffer
import okio.Sink
import okio.Source
import okio.use

public sealed class Nbt(
    override val configuration: NbtConfiguration,
    override val serializersModule: SerializersModule,
) : NbtFormat, BinaryFormat {
    /**
     * Serializes and encodes the given [value] to the [sink] using the given [serializer].
     *
     * *Note*: It is the caller's responsibility to close the [sink].
     */
    @OkioApi
    public fun  encodeToSink(serializer: SerializationStrategy, value: T, sink: Sink): Unit =
        BinaryNbtWriter(this, sink).use { writer ->
            encodeToNbtWriter(writer, serializer, value)
        }

    /**
     * Decodes and deserializes from the given [source] to a value of type [T] using the given [deserializer].
     *
     * *Note*: It is the caller's responsibility to close the [source].
     */
    @OkioApi
    public fun  decodeFromSource(deserializer: DeserializationStrategy, source: Source): T =
        BinaryNbtReader(this, source).use { reader ->
            decodeFromNbtReader(reader, deserializer)
        }

    @OptIn(OkioApi::class)
    override fun  encodeToByteArray(serializer: SerializationStrategy, value: T): ByteArray =
        Buffer().apply { encodeToSink(serializer, value, this) }.readByteArray()

    @OptIn(OkioApi::class)
    override fun  decodeFromByteArray(deserializer: DeserializationStrategy, bytes: ByteArray): T =
        decodeFromSource(deserializer, Buffer().apply { write(bytes) })
}

private object DefaultNbt : Nbt(
    configuration = NbtConfiguration(
        variant = NbtVariant.Java, // Will be ignored by NbtBuilder
        compression = NbtCompression.None, // Will be ignored by NbtBuilder
        compressionLevel = null,
        encodeDefaults = false,
        ignoreUnknownKeys = false,
        classDiscriminator = "type",
        nameRootClasses = true,
    ),
    serializersModule = EmptySerializersModule(),
)

/**
 * Creates an instance of [Nbt] configured from the optionally given [Nbt instance][from]
 * and adjusted with [builderAction].
 *
 * [variant][NbtBuilder.variant] and [compression][NbtBuilder.compression] are required.
 */
public fun Nbt(from: Nbt = DefaultNbt, builderAction: NbtBuilder.() -> Unit): Nbt {
    val builder = NbtBuilder(from)
    builder.builderAction()
    return builder.build()
}

/**
 * Builder of the [Nbt] instance provided by `Nbt { ... }` factory function.
 */
@NbtDslMarker
public class NbtBuilder internal constructor(nbt: Nbt) : NbtFormatBuilder {
    /**
     * The variant of NBT binary format to use. Required.
     */
    public var variant: NbtVariant? =
        if (nbt === DefaultNbt) null else nbt.configuration.variant

    /**
     * The compression method to use when writing NBT binary. Required.
     */
    public var compression: NbtCompression? =
        if (nbt === DefaultNbt) null else nbt.configuration.compression

    /**
     * The compression level, in `0..9` or `null`.
     * `null` by default.
     *
     * - `0` gives no compression at all
     * - `1` gives the best speed
     * - `9` gives the best compression.
     * - `null` requests a compromise between speed and compression.
     */
    public var compressionLevel: Int? = nbt.configuration.compressionLevel
        set(value) {
            require(value == null || value in 0..9) { "Compression level must be in 0..9 or null." }
            field = value
        }

    override var encodeDefaults: Boolean = nbt.configuration.encodeDefaults

    override var ignoreUnknownKeys: Boolean = nbt.configuration.ignoreUnknownKeys

    override var classDiscriminator: String = nbt.configuration.classDiscriminator

    override var nameRootClasses: Boolean = nbt.configuration.nameRootClasses

    /**
     * Module with contextual and polymorphic serializers to be used in the resulting [Nbt] instance.
     */
    override var serializersModule: SerializersModule = nbt.serializersModule

    internal fun build(): Nbt {
        val variant = variant
        val compression = compression

        require(variant != null && compression != null) {
            when {
                variant == null && compression == null -> "Variant and compression are required but are null"
                variant == null -> "Variant is required but is null"
                else -> "Compression is required but is null"
            }
        }

        return NbtImpl(
            configuration = NbtConfiguration(
                variant = variant,
                compression = compression,
                compressionLevel = compressionLevel,
                encodeDefaults = encodeDefaults,
                ignoreUnknownKeys = ignoreUnknownKeys,
                classDiscriminator = classDiscriminator,
                nameRootClasses = nameRootClasses
            ),
            serializersModule = serializersModule,
        )
    }
}

private class NbtImpl(
    configuration: NbtConfiguration,
    serializersModule: SerializersModule,
) : Nbt(configuration, serializersModule)

/**
 * Encode NBT to a [Sink].
 *
 * *Note*: It is the caller's responsibility to close the [sink].
 */
@OkioApi
public inline fun  Nbt.encodeToSink(value: T, sink: Sink): Unit =
    encodeToSink(serializersModule.serializer(), value, sink)

/**
 * Decode NBT from a [Source].
 *
 * *Note*: It is the caller's responsibility to close the [source].
 */
@OkioApi
public inline fun  Nbt.decodeFromSource(source: Source): T =
    decodeFromSource(serializersModule.serializer(), source)




© 2015 - 2025 Weber Informatics LLC | Privacy Policy