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

commonMain.space.kscience.dataforge.io.EnvelopeParts.kt Maven / Gradle / Ivy

package space.kscience.dataforge.io

import kotlinx.io.bytestring.ByteString
import kotlinx.io.bytestring.decodeToString
import kotlinx.io.write
import space.kscience.dataforge.io.Envelope.Companion.ENVELOPE_NODE_KEY
import space.kscience.dataforge.io.PartDescriptor.Companion.DEFAULT_MULTIPART_DATA_SEPARATOR
import space.kscience.dataforge.io.PartDescriptor.Companion.MULTIPART_DATA_TYPE
import space.kscience.dataforge.io.PartDescriptor.Companion.MULTIPART_KEY
import space.kscience.dataforge.io.PartDescriptor.Companion.PARTS_KEY
import space.kscience.dataforge.io.PartDescriptor.Companion.SEPARATOR_KEY
import space.kscience.dataforge.meta.*
import space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.plus

private class PartDescriptor : Scheme() {
    var offset by int(0)
    var size by int(0)
    var partMeta by node("meta".asName())

    companion object : SchemeSpec(::PartDescriptor) {
        val MULTIPART_KEY = ENVELOPE_NODE_KEY + "multipart"
        val PARTS_KEY = MULTIPART_KEY + "parts"
        val SEPARATOR_KEY = MULTIPART_KEY + "separator"

        val DEFAULT_MULTIPART_DATA_SEPARATOR = "\r\n#~PART~#\r\n".toAsciiByteString()

        const val MULTIPART_DATA_TYPE = "envelope.multipart"
    }
}

public data class EnvelopePart(val binary: Binary, val description: Meta?)

public typealias EnvelopeParts = List

public fun EnvelopeBuilder.multipart(
    parts: EnvelopeParts,
    separator: ByteString = DEFAULT_MULTIPART_DATA_SEPARATOR,
) {
    dataType = MULTIPART_DATA_TYPE

    var offsetCounter = 0
    val separatorSize = separator.size
    val partDescriptors = parts.map { (binary, description) ->
        offsetCounter += separatorSize
        PartDescriptor {
            offset = offsetCounter
            size = binary.size
            partMeta = description
        }.also {
            offsetCounter += binary.size
        }
    }

    meta {
        if (separator != DEFAULT_MULTIPART_DATA_SEPARATOR) {
            SEPARATOR_KEY put separator.decodeToString()
        }
        setIndexed(PARTS_KEY, partDescriptors.map { it.toMeta() })
    }

    data {
        parts.forEach {
            write(separator)
            writeBinary(it.binary)
        }
    }
}

/**
 * Put a list of envelopes as parts of given envelope
 */
public fun EnvelopeBuilder.envelopes(
    envelopes: List,
    separator: ByteString = DEFAULT_MULTIPART_DATA_SEPARATOR,
) {
    val parts = envelopes.map {
        val binary = Binary(it, TaggedEnvelopeFormat)
        EnvelopePart(binary, null)
    }
    multipart(parts, separator)
}

public fun Envelope.parts(): EnvelopeParts {
    if (data == null) return emptyList()
    //TODO add zip folder reader
    val parts = meta.getIndexed(PARTS_KEY).values.map {
        PartDescriptor.read(it)
    }
    return if (parts.isEmpty()) {
        listOf(EnvelopePart(data!!, meta[MULTIPART_KEY]))
    } else {
        parts.map {
            val binary = data!!.view(it.offset, it.size)
            val meta = Laminate(it.partMeta, meta[MULTIPART_KEY])
            EnvelopePart(binary, meta)
        }
    }
}

public fun EnvelopePart.envelope(format: EnvelopeFormat): Envelope = binary.readWith(format)

public val EnvelopePart.name: String? get() = description?.get("name").string

/**
 * Represent envelope part by an envelope
 */
public fun EnvelopePart.envelope(): Envelope = binary.readWith(TaggedEnvelopeFormat)




© 2015 - 2024 Weber Informatics LLC | Privacy Policy