IO refactoring

This commit is contained in:
Alexander Nozik 2020-09-07 13:44:02 +03:00
parent a767f279a3
commit eeb4643d93
34 changed files with 371 additions and 413 deletions

View File

@ -3,7 +3,9 @@ plugins {
id("org.jetbrains.changelog") version "0.4.0" id("org.jetbrains.changelog") version "0.4.0"
} }
val dataforgeVersion by extra("0.1.9-dev-5") apply(plugin = "org.jetbrains.dokka")
val dataforgeVersion by extra("0.2.0-dev-1")
val bintrayRepo by extra("dataforge") val bintrayRepo by extra("dataforge")
val githubProject by extra("dataforge-core") val githubProject by extra("dataforge-core")

View File

@ -1,7 +1,7 @@
plugins { plugins {
id("ru.mipt.npm.mpp") id("ru.mipt.npm.mpp")
id("ru.mipt.npm.node") id("ru.mipt.npm.node")
// id("ru.mipt.npm.native") id("ru.mipt.npm.native")
} }
description = "Context and provider definitions" description = "Context and provider definitions"

View File

@ -6,7 +6,7 @@ import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
abstract class AbstractPlugin(override val meta: Meta = Meta.EMPTY) : Plugin { public abstract class AbstractPlugin(override val meta: Meta = Meta.EMPTY) : Plugin {
private var _context: Context? = null private var _context: Context? = null
private val dependencies = ArrayList<PluginFactory<*>>() private val dependencies = ArrayList<PluginFactory<*>>()
@ -32,7 +32,7 @@ abstract class AbstractPlugin(override val meta: Meta = Meta.EMPTY) : Plugin {
} }
} }
fun <T : Named> Collection<T>.toMap(): Map<Name, T> = associate { it.name to it } public fun <T : Named> Collection<T>.toMap(): Map<Name, T> = associate { it.name to it }
private class PluginDependencyDelegate<P : Plugin>(val type: KClass<out P>) : ReadOnlyProperty<AbstractPlugin, P> { private class PluginDependencyDelegate<P : Plugin>(val type: KClass<out P>) : ReadOnlyProperty<AbstractPlugin, P> {
override fun getValue(thisRef: AbstractPlugin, property: KProperty<*>): P { override fun getValue(thisRef: AbstractPlugin, property: KProperty<*>): P {

View File

@ -2,13 +2,11 @@ package hep.dataforge.context
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.asName
import hep.dataforge.names.plus import hep.dataforge.names.plus
import hep.dataforge.provider.Provider import hep.dataforge.provider.Provider
import hep.dataforge.provider.top import hep.dataforge.provider.top
import hep.dataforge.values.Value import hep.dataforge.values.Value
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import mu.KLogger import mu.KLogger
@ -28,9 +26,9 @@ import kotlin.jvm.JvmName
* Since plugins could contain mutable state, context has two states: active and inactive. No changes are allowed to active context. * Since plugins could contain mutable state, context has two states: active and inactive. No changes are allowed to active context.
* @author Alexander Nozik * @author Alexander Nozik
*/ */
open class Context( public open class Context(
final override val name: Name, final override val name: Name,
val parent: Context? = Global public val parent: Context? = Global,
) : Named, MetaRepr, Provider, CoroutineScope { ) : Named, MetaRepr, Provider, CoroutineScope {
private val config = Config() private val config = Config()
@ -38,7 +36,7 @@ open class Context(
/** /**
* Context properties. Working as substitute for environment variables * Context properties. Working as substitute for environment variables
*/ */
val properties: Meta = if (parent == null) { private val properties: Meta = if (parent == null) {
config config
} else { } else {
Laminate(config, parent.properties) Laminate(config, parent.properties)
@ -47,19 +45,19 @@ open class Context(
/** /**
* Context logger * Context logger
*/ */
val logger: KLogger = KotlinLogging.logger(name.toString()) public val logger: KLogger = KotlinLogging.logger(name.toString())
/** /**
* A [PluginManager] for current context * A [PluginManager] for current context
*/ */
val plugins: PluginManager by lazy { PluginManager(this) } public val plugins: PluginManager by lazy { PluginManager(this) }
private val activators = HashSet<Any>() private val activators = HashSet<Any>()
/** /**
* Defines if context is used in any kind of active computations. Active context properties and plugins could not be changed * Defines if context is used in any kind of active computations. Active context properties and plugins could not be changed
*/ */
val isActive: Boolean = activators.isNotEmpty() public val isActive: Boolean = activators.isNotEmpty()
override val defaultTarget: String get() = Plugin.PLUGIN_TARGET override val defaultTarget: String get() = Plugin.PLUGIN_TARGET
@ -74,21 +72,21 @@ open class Context(
/** /**
* Mark context as active and used by [activator] * Mark context as active and used by [activator]
*/ */
fun activate(activator: Any) { public fun activate(activator: Any) {
activators.add(activator) activators.add(activator)
} }
/** /**
* Mark context unused by [activator] * Mark context unused by [activator]
*/ */
fun deactivate(activator: Any) { public fun deactivate(activator: Any) {
activators.remove(activator) activators.remove(activator)
} }
/** /**
* Change the properties of the context. If active, throw an exception * Change the properties of the context. If active, throw an exception
*/ */
fun configure(action: Config.() -> Unit) { public fun configure(action: Config.() -> Unit) {
if (isActive) error("Can't configure active context") if (isActive) error("Can't configure active context")
config.action() config.action()
} }
@ -102,7 +100,7 @@ open class Context(
/** /**
* Detach all plugins and terminate context * Detach all plugins and terminate context
*/ */
open fun close() { public open fun close() {
if (isActive) error("Can't close active context") if (isActive) error("Can't close active context")
//detach all plugins //detach all plugins
plugins.forEach { it.detach() } plugins.forEach { it.detach() }
@ -115,71 +113,30 @@ open class Context(
} }
} }
fun Context.content(target: String): Map<Name, Any> = content<Any>(target)
/** /**
* A map of all objects provided by plugins with given target and type * A map of all objects provided by plugins with given target and type
*/ */
@JvmName("typedContent") @JvmName("typedContent")
inline fun <reified T : Any> Context.content(target: String): Map<Name, T> = public inline fun <reified T : Any> Context.resolve(target: String): Map<Name, T> = plugins.flatMap { plugin ->
plugins.flatMap { plugin ->
plugin.top<T>(target).entries.map { (plugin.name + it.key) to it.value } plugin.top<T>(target).entries.map { (plugin.name + it.key) to it.value }
}.associate { it } }.associate { it }
/** public fun Context.resolve(target: String): Map<Name, Any> = resolve<Any>(target)
* A global root context. Closing [Global] terminates the framework.
*/
object Global : Context("GLOBAL".asName(), null) {
override val coroutineContext: CoroutineContext = GlobalScope.coroutineContext + SupervisorJob()
/**
* Closing all contexts
*
* @throws Exception
*/
override fun close() {
logger.info { "Shutting down GLOBAL" }
for (ctx in contextRegistry.values) {
ctx.close()
}
super.close()
}
private val contextRegistry = HashMap<String, Context>()
/**
* Get previously built context
*
* @param name
* @return
*/
fun getContext(name: String): Context? {
return contextRegistry[name]
}
fun context(name: String, parent: Context = this, block: ContextBuilder.() -> Unit = {}): Context =
ContextBuilder(name, parent).apply(block).build()
}
/** /**
* The interface for something that encapsulated in context * The interface for something that encapsulated in context
* *
* @author Alexander Nozik
* @version $Id: $Id
*/ */
interface ContextAware { public interface ContextAware {
/** /**
* Get context for this object * Get context for this object
* *
* @return * @return
*/ */
val context: Context public val context: Context
val logger: KLogger public val logger: KLogger
get() = if (this is Named) { get() = if (this is Named) {
KotlinLogging.logger((context.name + this.name).toString()) KotlinLogging.logger((context.name + this.name).toString())
} else { } else {

View File

@ -9,31 +9,34 @@ import hep.dataforge.names.toName
* A convenience builder for context * A convenience builder for context
*/ */
@DFBuilder @DFBuilder
class ContextBuilder(var name: String = "@anonymous", val parent: Context = Global) { public class ContextBuilder(private val parent: Context = Global, public var name: String = "@anonymous") {
private val plugins = ArrayList<Plugin>() private val plugins = ArrayList<Plugin>()
private var meta = MetaBuilder() private var meta = MetaBuilder()
fun properties(action: MetaBuilder.() -> Unit) { public fun properties(action: MetaBuilder.() -> Unit) {
meta.action() meta.action()
} }
fun plugin(plugin: Plugin) { public fun plugin(plugin: Plugin) {
plugins.add(plugin) plugins.add(plugin)
} }
fun plugin(tag: PluginTag, action: MetaBuilder.() -> Unit = {}) { public fun plugin(tag: PluginTag, action: MetaBuilder.() -> Unit = {}) {
plugins.add(PluginRepository.fetch(tag, Meta(action))) val factory = parent.resolve<PluginFactory<*>>(PluginFactory.TYPE).values
.find { it.tag.matches(tag) } ?: error("Can't resolve plugin factory for $tag")
val plugin = factory.invoke(Meta(action), parent)
plugins.add(plugin)
} }
fun plugin(builder: PluginFactory<*>, action: MetaBuilder.() -> Unit = {}) { public fun plugin(builder: PluginFactory<*>, action: MetaBuilder.() -> Unit = {}) {
plugins.add(builder.invoke(Meta(action))) plugins.add(builder.invoke(Meta(action)))
} }
fun plugin(name: String, group: String = "", version: String = "", action: MetaBuilder.() -> Unit = {}) { public fun plugin(name: String, group: String = "", version: String = "", action: MetaBuilder.() -> Unit = {}) {
plugin(PluginTag(name, group, version), action) plugin(PluginTag(name, group, version), action)
} }
fun build(): Context { public fun build(): Context {
return Context(name.toName(), parent).apply { return Context(name.toName(), parent).apply {
this@ContextBuilder.plugins.forEach { this@ContextBuilder.plugins.forEach {
plugins.load(it) plugins.load(it)

View File

@ -0,0 +1,43 @@
package hep.dataforge.context
import hep.dataforge.names.asName
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.SupervisorJob
import kotlin.coroutines.CoroutineContext
/**
* A global root context. Closing [Global] terminates the framework.
*/
public object Global : Context("GLOBAL".asName(), null) {
override val coroutineContext: CoroutineContext = GlobalScope.coroutineContext + SupervisorJob()
/**
* Closing all contexts
*
* @throws Exception
*/
override fun close() {
logger.info { "Shutting down GLOBAL" }
for (ctx in contextRegistry.values) {
ctx.close()
}
super.close()
}
private val contextRegistry = HashMap<String, Context>()
/**
* Get previously built context
*
* @param name
* @return
*/
public fun getContext(name: String): Context? {
return contextRegistry[name]
}
public fun context(name: String, parent: Context = this, block: ContextBuilder.() -> Unit = {}): Context =
ContextBuilder(parent, name).apply(block).build()
}

View File

@ -4,6 +4,17 @@ import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaBuilder import hep.dataforge.meta.MetaBuilder
import kotlin.reflect.KClass import kotlin.reflect.KClass
interface PluginFactory<T : Plugin> : Factory<T> {
val tag: PluginTag
val type: KClass<out T>
companion object{
const val TYPE = "pluginFactory"
}
}
/** /**
* The manager for plugin system. Should monitor plugin dependencies and locks. * The manager for plugin system. Should monitor plugin dependencies and locks.
* *

View File

@ -1,48 +0,0 @@
package hep.dataforge.context
import hep.dataforge.meta.Meta
import kotlin.reflect.KClass
interface PluginFactory<T : Plugin> : Factory<T> {
val tag: PluginTag
val type: KClass<out T>
}
expect object PluginRepository {
fun register(factory: PluginFactory<*>)
/**
* List plugins available in the repository
*/
fun list(): Sequence<PluginFactory<*>>
}
/**
* Fetch specific plugin and instantiate it with given meta
*/
fun PluginRepository.fetch(tag: PluginTag, meta: Meta = Meta.EMPTY): Plugin =
list().find { it.tag.matches(tag) }?.invoke(meta = meta)
?: error("Plugin with tag $tag not found in the repository")
fun <T : Plugin> PluginRepository.register(
tag: PluginTag,
type: KClass<out T>,
constructor: (Context, Meta) -> T
): PluginFactory<T> {
val factory = object : PluginFactory<T> {
override val tag: PluginTag = tag
override val type: KClass<out T> = type
override fun invoke(meta: Meta, context: Context): T = constructor(context, meta)
}
register(factory)
return factory
}
inline fun <reified T : Plugin> PluginRepository.register(tag: PluginTag, noinline constructor: (Context, Meta) -> T) =
register(tag, T::class, constructor)
fun PluginRepository.register(plugin: Plugin) = register(plugin.tag, plugin::class) { _, _ -> plugin }

View File

@ -22,7 +22,7 @@ class ContextTest {
@Test @Test
fun testPluginManager() { fun testPluginManager() {
Global.plugins.load(DummyPlugin()) Global.plugins.load(DummyPlugin())
val members = Global.content<Name>("test") val members = Global.resolve<Name>("test")
assertEquals(3, members.count()) assertEquals(3, members.count())
members.forEach { members.forEach {
assertEquals(it.key, it.value.appendLeft("test")) assertEquals(it.key, it.value.appendLeft("test"))

View File

@ -1,16 +0,0 @@
package hep.dataforge.context
actual object PluginRepository {
private val factories: MutableSet<PluginFactory<*>> = HashSet()
actual fun register(factory: PluginFactory<*>) {
factories.add(factory)
}
/**
* List plugins available in the repository
*/
actual fun list(): Sequence<PluginFactory<*>> = factories.asSequence()
}

View File

@ -1,17 +0,0 @@
package hep.dataforge.context
actual object PluginRepository {
private val factories: MutableSet<PluginFactory<*>> = HashSet()
actual fun register(factory: PluginFactory<*>) {
factories.add(factory)
}
/**
* List plugins available in the repository
*/
actual fun list(): Sequence<PluginFactory<*>> =
factories.asSequence() + Global.services()
}

View File

@ -1,7 +1,7 @@
package hep.dataforge.provider package hep.dataforge.provider
import hep.dataforge.context.Context import hep.dataforge.context.Context
import hep.dataforge.context.content import hep.dataforge.context.resolve
import hep.dataforge.names.Name import hep.dataforge.names.Name
import kotlin.reflect.KClass import kotlin.reflect.KClass
import kotlin.reflect.full.findAnnotation import kotlin.reflect.full.findAnnotation
@ -40,5 +40,5 @@ inline fun <reified T : Any> Provider.top(): Map<Name, T> {
/** /**
* A sequences of all objects provided by plugins with given target and type * A sequences of all objects provided by plugins with given target and type
*/ */
inline fun <reified T : Any> Context.content(): Map<Name, T> = content<T>(Types[T::class]) inline fun <reified T : Any> Context.content(): Map<Name, T> = resolve<T>(Types[T::class])

View File

@ -13,14 +13,14 @@ import kotlinx.io.text.writeUtf8String
@DFExperimental @DFExperimental
class FrontMatterEnvelopeFormat( class FrontMatterEnvelopeFormat(
val io: IOPlugin, val io: IOPlugin,
val meta: Meta = Meta.EMPTY val meta: Meta = Meta.EMPTY,
) : EnvelopeFormat { ) : EnvelopeFormat {
override fun Input.readPartial(): PartialEnvelope { override fun readPartial(input: Input): PartialEnvelope {
var line = "" var line = ""
var offset = 0u var offset = 0u
do { do {
line = readUtf8Line() //?: error("Input does not contain front matter separator") line = input.readUtf8Line() //?: error("Input does not contain front matter separator")
offset += line.toByteArray().size.toUInt() offset += line.toByteArray().size.toUInt()
} while (!line.startsWith(SEPARATOR)) } while (!line.startsWith(SEPARATOR))
@ -31,22 +31,21 @@ class FrontMatterEnvelopeFormat(
//TODO replace by preview //TODO replace by preview
val meta = Binary { val meta = Binary {
do { do {
line = readUtf8Line() line = input.readUtf8Line()
writeUtf8String(line + "\r\n") writeUtf8String(line + "\r\n")
offset += line.toByteArray().size.toUInt() offset += line.toByteArray().size.toUInt()
} while (!line.startsWith(SEPARATOR)) } while (!line.startsWith(SEPARATOR))
}.read { }.read {
readMetaFormat.run { readMetaFormat.readMeta(input)
readMeta()
}
} }
return PartialEnvelope(meta, offset, null) return PartialEnvelope(meta, offset, null)
} }
override fun Input.readObject(): Envelope { override fun readObject(input: Input): Envelope {
var line = "" var line = ""
do { do {
line = readUtf8Line() //?: error("Input does not contain front matter separator") line = input.readUtf8Line() //?: error("Input does not contain front matter separator")
} while (!line.startsWith(SEPARATOR)) } while (!line.startsWith(SEPARATOR))
val readMetaFormat = val readMetaFormat =
@ -55,14 +54,12 @@ class FrontMatterEnvelopeFormat(
val meta = Binary { val meta = Binary {
do { do {
writeUtf8String(readUtf8Line() + "\r\n") writeUtf8String(input.readUtf8Line() + "\r\n")
} while (!line.startsWith(SEPARATOR)) } while (!line.startsWith(SEPARATOR))
}.read { }.read {
readMetaFormat.run { readMetaFormat.readMeta(input)
readMeta()
} }
} val bytes = input.readByteArray()
val bytes = readByteArray()
val data = bytes.asBinary() val data = bytes.asBinary()
return SimpleEnvelope(meta, data) return SimpleEnvelope(meta, data)
} }
@ -70,7 +67,7 @@ class FrontMatterEnvelopeFormat(
override fun Output.writeEnvelope(envelope: Envelope, metaFormatFactory: MetaFormatFactory, formatMeta: Meta) { override fun Output.writeEnvelope(envelope: Envelope, metaFormatFactory: MetaFormatFactory, formatMeta: Meta) {
val metaFormat = metaFormatFactory(formatMeta, io.context) val metaFormat = metaFormatFactory(formatMeta, io.context)
writeRawString("$SEPARATOR\r\n") writeRawString("$SEPARATOR\r\n")
metaFormat.run { writeObject(envelope.meta) } metaFormat.run { writeObject(this@writeEnvelope, envelope.meta) }
writeRawString("$SEPARATOR\r\n") writeRawString("$SEPARATOR\r\n")
//Printing data //Printing data
envelope.data?.let { data -> envelope.data?.let { data ->
@ -105,14 +102,14 @@ class FrontMatterEnvelopeFormat(
private val default by lazy { invoke() } private val default by lazy { invoke() }
override fun Input.readPartial(): PartialEnvelope = override fun readPartial(input: Input): PartialEnvelope =
default.run { readPartial() } default.readPartial(input)
override fun Output.writeEnvelope(envelope: Envelope, metaFormatFactory: MetaFormatFactory, formatMeta: Meta) = override fun Output.writeEnvelope(envelope: Envelope, metaFormatFactory: MetaFormatFactory, formatMeta: Meta): Unit =
default.run { writeEnvelope(envelope, metaFormatFactory, formatMeta) } default.run { writeEnvelope(envelope, metaFormatFactory, formatMeta) }
override fun Input.readObject(): Envelope = override fun readObject(input: Input): Envelope =
default.run { readObject() } default.readObject(input)
} }
} }

View File

@ -16,17 +16,20 @@ import kotlinx.io.asInputStream
import kotlinx.io.text.writeUtf8String import kotlinx.io.text.writeUtf8String
import org.yaml.snakeyaml.Yaml import org.yaml.snakeyaml.Yaml
/**
* Represent meta as Yaml
*/
@DFExperimental @DFExperimental
class YamlMetaFormat(val meta: Meta) : MetaFormat { public class YamlMetaFormat(private val meta: Meta) : MetaFormat {
private val yaml = Yaml() private val yaml = Yaml()
override fun Output.writeMeta(meta: Meta, descriptor: NodeDescriptor?) { override fun writeMeta(output: Output, meta: Meta, descriptor: NodeDescriptor?) {
val string = yaml.dump(meta.toMap(descriptor)) val string = yaml.dump(meta.toMap(descriptor))
writeUtf8String(string) output.writeUtf8String(string)
} }
override fun Input.readMeta(descriptor: NodeDescriptor?): Meta { override fun readMeta(input: Input, descriptor: NodeDescriptor?): Meta {
val map: Map<String, Any?> = yaml.load(asInputStream()) val map: Map<String, Any?> = yaml.load(input.asInputStream())
return map.toMeta(descriptor) return map.toMeta(descriptor)
} }
@ -35,19 +38,19 @@ class YamlMetaFormat(val meta: Meta) : MetaFormat {
META_KEY put meta META_KEY put meta
} }
companion object : MetaFormatFactory { public companion object : MetaFormatFactory {
override fun invoke(meta: Meta, context: Context): MetaFormat = YamlMetaFormat(meta) override fun invoke(meta: Meta, context: Context): MetaFormat = YamlMetaFormat(meta)
override val shortName = "yaml" override val shortName: String = "yaml"
override val key: Short = 0x594d //YM override val key: Short = 0x594d //YM
private val default = YamlMetaFormat() private val default = YamlMetaFormat()
override fun Output.writeMeta(meta: Meta, descriptor: NodeDescriptor?) = override fun writeMeta(output: Output, meta: Meta, descriptor: NodeDescriptor?): Unit =
default.run { writeMeta(meta, descriptor) } default.writeMeta(output, meta, descriptor)
override fun Input.readMeta(descriptor: NodeDescriptor?): Meta = override fun readMeta(input: kotlinx.io.Input, descriptor: NodeDescriptor?): Meta =
default.run { readMeta(descriptor) } default.readMeta(input, descriptor)
} }
} }

View File

@ -11,14 +11,18 @@ import kotlinx.io.*
import kotlinx.io.text.readUtf8String import kotlinx.io.text.readUtf8String
import kotlinx.io.text.writeUtf8String import kotlinx.io.text.writeUtf8String
object BinaryMetaFormat : MetaFormat, MetaFormatFactory { /**
* A DataForge-specific simplified binary format for meta
* TODO add description
*/
public object BinaryMetaFormat : MetaFormat, MetaFormatFactory {
override val shortName: String = "bin" override val shortName: String = "bin"
override val key: Short = 0x4249//BI override val key: Short = 0x4249//BI
override fun invoke(meta: Meta, context: Context): MetaFormat = this override fun invoke(meta: Meta, context: Context): MetaFormat = this
override fun Input.readMeta(descriptor: NodeDescriptor?): Meta { override fun readMeta(input: Input, descriptor: NodeDescriptor?): Meta {
return (readMetaItem() as MetaItem.NodeItem).node return (input.readMetaItem() as MetaItem.NodeItem).node
} }
private fun Output.writeChar(char: Char) = writeByte(char.toByte()) private fun Output.writeChar(char: Char) = writeByte(char.toByte())
@ -28,7 +32,7 @@ object BinaryMetaFormat : MetaFormat, MetaFormatFactory {
writeUtf8String(str) writeUtf8String(str)
} }
fun Output.writeValue(value: Value) { public fun Output.writeValue(value: Value) {
if (value.isList()) { if (value.isList()) {
writeChar('L') writeChar('L')
writeInt(value.list.size) writeInt(value.list.size)
@ -75,17 +79,21 @@ object BinaryMetaFormat : MetaFormat, MetaFormatFactory {
} }
} }
override fun Output.writeMeta(meta: Meta, descriptor: NodeDescriptor?) { override fun writeMeta(
writeChar('M') output: kotlinx.io.Output,
writeInt(meta.items.size) meta: hep.dataforge.meta.Meta,
descriptor: hep.dataforge.meta.descriptors.NodeDescriptor?
) {
output.writeChar('M')
output.writeInt(meta.items.size)
meta.items.forEach { (key, item) -> meta.items.forEach { (key, item) ->
writeString(key.toString()) output.writeString(key.toString())
when (item) { when (item) {
is MetaItem.ValueItem -> { is MetaItem.ValueItem -> {
writeValue(item.value) output.writeValue(item.value)
} }
is MetaItem.NodeItem -> { is MetaItem.NodeItem -> {
writeObject(item.node) writeObject(output, item.node)
} }
} }
} }
@ -97,7 +105,7 @@ object BinaryMetaFormat : MetaFormat, MetaFormatFactory {
} }
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
fun Input.readMetaItem(): MetaItem<MetaBuilder> { public fun Input.readMetaItem(): MetaItem<MetaBuilder> {
return when (val keyChar = readByte().toChar()) { return when (val keyChar = readByte().toChar()) {
'S' -> MetaItem.ValueItem(StringValue(readString())) 'S' -> MetaItem.ValueItem(StringValue(readString()))
'N' -> MetaItem.ValueItem(Null) 'N' -> MetaItem.ValueItem(Null)

View File

@ -4,64 +4,66 @@ import hep.dataforge.meta.Laminate
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.get import hep.dataforge.meta.get
import hep.dataforge.meta.string import hep.dataforge.meta.string
import hep.dataforge.names.Name
import hep.dataforge.names.asName import hep.dataforge.names.asName
import hep.dataforge.names.plus import hep.dataforge.names.plus
import kotlinx.io.Binary import kotlinx.io.Binary
interface Envelope { public interface Envelope {
val meta: Meta public val meta: Meta
val data: Binary? public val data: Binary?
companion object { public companion object {
/** /**
* meta keys * meta keys
*/ */
val ENVELOPE_NODE_KEY = "@envelope".asName() public val ENVELOPE_NODE_KEY: Name = "@envelope".asName()
val ENVELOPE_TYPE_KEY = ENVELOPE_NODE_KEY + "type" public val ENVELOPE_TYPE_KEY: Name = ENVELOPE_NODE_KEY + "type"
val ENVELOPE_DATA_TYPE_KEY = ENVELOPE_NODE_KEY + "dataType" public val ENVELOPE_DATA_TYPE_KEY: Name = ENVELOPE_NODE_KEY + "dataType"
val ENVELOPE_DATA_ID_KEY = ENVELOPE_NODE_KEY + "dataID" public val ENVELOPE_DATA_ID_KEY: Name = ENVELOPE_NODE_KEY + "dataID"
val ENVELOPE_DESCRIPTION_KEY = ENVELOPE_NODE_KEY + "description" public val ENVELOPE_DESCRIPTION_KEY: Name = ENVELOPE_NODE_KEY + "description"
val ENVELOPE_NAME_KEY = ENVELOPE_NODE_KEY + "name" public val ENVELOPE_NAME_KEY: Name = ENVELOPE_NODE_KEY + "name"
//const val ENVELOPE_TIME_KEY = "@envelope.time" //const val ENVELOPE_TIME_KEY = "@envelope.time"
/** /**
* Build a static envelope using provided builder * Build a static envelope using provided builder
*/ */
inline operator fun invoke(block: EnvelopeBuilder.() -> Unit) = EnvelopeBuilder().apply(block).seal() public inline operator fun invoke(block: EnvelopeBuilder.() -> Unit): Envelope =
EnvelopeBuilder().apply(block).seal()
} }
} }
class SimpleEnvelope(override val meta: Meta, override val data: Binary?) : Envelope public class SimpleEnvelope(override val meta: Meta, override val data: Binary?) : Envelope
/** /**
* The purpose of the envelope * The purpose of the envelope
* *
*/ */
val Envelope.type: String? get() = meta[Envelope.ENVELOPE_TYPE_KEY].string public val Envelope.type: String? get() = meta[Envelope.ENVELOPE_TYPE_KEY].string
/** /**
* The type of data encoding * The type of data encoding
* *
*/ */
val Envelope.dataType: String? get() = meta[Envelope.ENVELOPE_DATA_TYPE_KEY].string public val Envelope.dataType: String? get() = meta[Envelope.ENVELOPE_DATA_TYPE_KEY].string
/** /**
* Textual user friendly description * Textual user friendly description
* *
*/ */
val Envelope.description: String? get() = meta[Envelope.ENVELOPE_DESCRIPTION_KEY].string public val Envelope.description: String? get() = meta[Envelope.ENVELOPE_DESCRIPTION_KEY].string
/** /**
* An optional unique identifier that is used for data comparison. Data without identifier could not be compared to another data. * An optional unique identifier that is used for data comparison. Data without identifier could not be compared to another data.
*/ */
val Envelope.dataID: String? get() = meta[Envelope.ENVELOPE_DATA_ID_KEY].string public val Envelope.dataID: String? get() = meta[Envelope.ENVELOPE_DATA_ID_KEY].string
fun Envelope.metaEquals(other: Envelope): Boolean = this.meta == other.meta public fun Envelope.metaEquals(other: Envelope): Boolean = this.meta == other.meta
fun Envelope.dataEquals(other: Envelope): Boolean = this.dataID != null && this.dataID == other.dataID public fun Envelope.dataEquals(other: Envelope): Boolean = this.dataID != null && this.dataID == other.dataID
fun Envelope.contentEquals(other: Envelope): Boolean { public fun Envelope.contentEquals(other: Envelope): Boolean {
return (this === other || (metaEquals(other) && dataEquals(other))) return (this === other || (metaEquals(other) && dataEquals(other)))
} }
@ -69,7 +71,7 @@ fun Envelope.contentEquals(other: Envelope): Boolean {
/** /**
* An envelope, which wraps existing envelope and adds one or several additional layers of meta * An envelope, which wraps existing envelope and adds one or several additional layers of meta
*/ */
class ProxyEnvelope(val source: Envelope, vararg meta: Meta) : Envelope { public class ProxyEnvelope(public val source: Envelope, vararg meta: Meta) : Envelope {
override val meta: Laminate = Laminate(*meta, source.meta) override val meta: Laminate = Laminate(*meta, source.meta)
override val data: Binary? get() = source.data override val data: Binary? get() = source.data
} }
@ -77,7 +79,7 @@ class ProxyEnvelope(val source: Envelope, vararg meta: Meta) : Envelope {
/** /**
* Add few meta layers to existing envelope (on top of existing meta) * Add few meta layers to existing envelope (on top of existing meta)
*/ */
fun Envelope.withMetaLayers(vararg layers: Meta): Envelope { public fun Envelope.withMetaLayers(vararg layers: Meta): Envelope {
return when { return when {
layers.isEmpty() -> this layers.isEmpty() -> this
this is ProxyEnvelope -> ProxyEnvelope(source, *layers, *this.meta.layers.toTypedArray()) this is ProxyEnvelope -> ProxyEnvelope(source, *layers, *this.meta.layers.toTypedArray())

View File

@ -13,31 +13,28 @@ import kotlin.reflect.KClass
/** /**
* A partially read envelope with meta, but without data * A partially read envelope with meta, but without data
*/ */
@ExperimentalUnsignedTypes public data class PartialEnvelope(val meta: Meta, val dataOffset: UInt, val dataSize: ULong?)
data class PartialEnvelope(val meta: Meta, val dataOffset: UInt, val dataSize: ULong?)
interface EnvelopeFormat : IOFormat<Envelope> { public interface EnvelopeFormat : IOFormat<Envelope> {
val defaultMetaFormat: MetaFormatFactory get() = JsonMetaFormat public val defaultMetaFormat: MetaFormatFactory get() = JsonMetaFormat
fun Input.readPartial(): PartialEnvelope public fun readPartial(input: Input): PartialEnvelope
fun Output.writeEnvelope( public fun Output.writeEnvelope(
envelope: Envelope, envelope: Envelope,
metaFormatFactory: MetaFormatFactory = defaultMetaFormat, metaFormatFactory: MetaFormatFactory = defaultMetaFormat,
formatMeta: Meta = Meta.EMPTY formatMeta: Meta = Meta.EMPTY
) )
override fun Input.readObject(): Envelope override fun readObject(input: Input): Envelope
override fun Output.writeObject(obj: Envelope): Unit = writeEnvelope(obj) override fun writeObject(output: Output, obj: Envelope): Unit = output.writeEnvelope(obj)
} }
fun EnvelopeFormat.readPartial(input: Input) = input.readPartial() public fun EnvelopeFormat.read(input: Input): Envelope = readObject(input)
fun EnvelopeFormat.read(input: Input) = input.readObject()
@Type(ENVELOPE_FORMAT_TYPE) @Type(ENVELOPE_FORMAT_TYPE)
interface EnvelopeFormatFactory : IOFormatFactory<Envelope>, EnvelopeFormat { public interface EnvelopeFormatFactory : IOFormatFactory<Envelope>, EnvelopeFormat {
override val name: Name get() = "envelope".asName() override val name: Name get() = "envelope".asName()
override val type: KClass<out Envelope> get() = Envelope::class override val type: KClass<out Envelope> get() = Envelope::class
@ -47,9 +44,9 @@ interface EnvelopeFormatFactory : IOFormatFactory<Envelope>, EnvelopeFormat {
* Try to infer specific format from input and return null if the attempt is failed. * Try to infer specific format from input and return null if the attempt is failed.
* This method does **not** return Input into initial state. * This method does **not** return Input into initial state.
*/ */
fun peekFormat(io: IOPlugin, input: Input): EnvelopeFormat? public fun peekFormat(io: IOPlugin, input: Input): EnvelopeFormat?
companion object { public companion object {
const val ENVELOPE_FORMAT_TYPE = "io.format.envelope" public const val ENVELOPE_FORMAT_TYPE: String = "io.format.envelope"
} }
} }

View File

@ -31,11 +31,11 @@ private class PartDescriptor : Scheme() {
} }
} }
data class EnvelopePart(val binary: Binary, val description: Meta?) public data class EnvelopePart(val binary: Binary, val description: Meta?)
typealias EnvelopeParts = List<EnvelopePart> public typealias EnvelopeParts = List<EnvelopePart>
fun EnvelopeBuilder.multipart( public fun EnvelopeBuilder.multipart(
parts: EnvelopeParts, parts: EnvelopeParts,
separator: String = DEFAULT_MULTIPART_DATA_SEPARATOR separator: String = DEFAULT_MULTIPART_DATA_SEPARATOR
) { ) {
@ -69,7 +69,7 @@ fun EnvelopeBuilder.multipart(
} }
} }
fun EnvelopeBuilder.envelopes( public fun EnvelopeBuilder.envelopes(
envelopes: List<Envelope>, envelopes: List<Envelope>,
format: EnvelopeFormat = TaggedEnvelopeFormat, format: EnvelopeFormat = TaggedEnvelopeFormat,
separator: String = DEFAULT_MULTIPART_DATA_SEPARATOR separator: String = DEFAULT_MULTIPART_DATA_SEPARATOR
@ -84,7 +84,7 @@ fun EnvelopeBuilder.envelopes(
multipart(parts, separator) multipart(parts, separator)
} }
fun Envelope.parts(): EnvelopeParts { public fun Envelope.parts(): EnvelopeParts {
if (data == null) return emptyList() if (data == null) return emptyList()
//TODO add zip folder reader //TODO add zip folder reader
val parts = meta.getIndexed(PARTS_KEY).values.mapNotNull { it.node }.map { val parts = meta.getIndexed(PARTS_KEY).values.mapNotNull { it.node }.map {
@ -101,14 +101,14 @@ fun Envelope.parts(): EnvelopeParts {
} }
} }
fun EnvelopePart.envelope(format: EnvelopeFormat): Envelope = binary.readWith(format) public fun EnvelopePart.envelope(format: EnvelopeFormat): Envelope = binary.readWith(format)
val EnvelopePart.name: String? get() = description?.get("name").string public val EnvelopePart.name: String? get() = description?.get("name").string
/** /**
* Represent envelope part by an envelope * Represent envelope part by an envelope
*/ */
fun EnvelopePart.envelope(plugin: IOPlugin): Envelope { public fun EnvelopePart.envelope(plugin: IOPlugin): Envelope {
val formatItem = description?.get(PART_FORMAT_KEY) val formatItem = description?.get(PART_FORMAT_KEY)
return if (formatItem != null) { return if (formatItem != null) {
val format: EnvelopeFormat = plugin.resolveEnvelopeFormat(formatItem) val format: EnvelopeFormat = plugin.resolveEnvelopeFormat(formatItem)

View File

@ -20,41 +20,42 @@ import kotlin.reflect.KClass
/** /**
* And interface for reading and writing objects into with IO streams * And interface for reading and writing objects into with IO streams
*/ */
interface IOFormat<T : Any> : MetaRepr { public interface IOFormat<T : Any> : MetaRepr {
fun Output.writeObject(obj: T) public fun writeObject(output: Output, obj: T)
fun Input.readObject(): T public fun readObject(input: Input): T
companion object{ public companion object {
val NAME_KEY = "name".asName() public val NAME_KEY: Name = "name".asName()
val META_KEY = "meta".asName() public val META_KEY: Name = "meta".asName()
} }
} }
fun <T : Any> Input.readWith(format: IOFormat<T>): T = format.run { readObject() } public fun <T : Any> Input.readWith(format: IOFormat<T>): T = format.run { readObject(this@readWith) }
/** /**
* Read given binary as object using given format * Read given binary as object using given format
*/ */
fun <T : Any> Binary.readWith(format: IOFormat<T>): T = read { public fun <T : Any> Binary.readWith(format: IOFormat<T>): T = read {
readWith(format) readWith(format)
} }
fun <T : Any> Output.writeWith(format: IOFormat<T>, obj: T) = format.run { writeObject(obj) } public fun <T : Any> Output.writeWith(format: IOFormat<T>, obj: T): Unit =
format.run { writeObject(this@writeWith, obj) }
class ListIOFormat<T : Any>(val format: IOFormat<T>) : IOFormat<List<T>> { public class ListIOFormat<T : Any>(public val format: IOFormat<T>) : IOFormat<List<T>> {
override fun Output.writeObject(obj: List<T>) { override fun writeObject(output: Output, obj: List<T>) {
writeInt(obj.size) output.writeInt(obj.size)
format.run { this.format.run {
obj.forEach { obj.forEach {
writeObject(it) writeObject(output, it)
} }
} }
} }
override fun Input.readObject(): List<T> { override fun readObject(input: Input): List<T> {
val size = readInt() val size = input.readInt()
return format.run { return format.run {
List(size) { readObject() } List(size) { readObject(input) }
} }
} }
@ -64,9 +65,9 @@ class ListIOFormat<T : Any>(val format: IOFormat<T>) : IOFormat<List<T>> {
} }
} }
val <T : Any> IOFormat<T>.list get() = ListIOFormat(this) //public val <T : Any> IOFormat<T>.list: ListIOFormat<T> get() = ListIOFormat(this)
fun ObjectPool<Buffer>.fill(block: Buffer.() -> Unit): Buffer { public fun ObjectPool<Buffer>.fill(block: Buffer.() -> Unit): Buffer {
val buffer = borrow() val buffer = borrow()
return try { return try {
buffer.apply(block) buffer.apply(block)
@ -77,50 +78,50 @@ fun ObjectPool<Buffer>.fill(block: Buffer.() -> Unit): Buffer {
} }
@Type(IO_FORMAT_TYPE) @Type(IO_FORMAT_TYPE)
interface IOFormatFactory<T : Any> : Factory<IOFormat<T>>, Named, MetaRepr { public interface IOFormatFactory<T : Any> : Factory<IOFormat<T>>, Named, MetaRepr {
/** /**
* Explicit type for dynamic type checks * Explicit type for dynamic type checks
*/ */
val type: KClass<out T> public val type: KClass<out T>
override fun toMeta(): Meta = Meta { override fun toMeta(): Meta = Meta {
NAME_KEY put name.toString() NAME_KEY put name.toString()
} }
companion object { public companion object {
const val IO_FORMAT_TYPE = "io.format" public const val IO_FORMAT_TYPE: String = "io.format"
} }
} }
fun <T : Any> IOFormat<T>.toBinary(obj: T): Binary = Binary { writeObject(obj) } public fun <T : Any> IOFormat<T>.toBinary(obj: T): Binary = Binary { writeObject(this, obj) }
object DoubleIOFormat : IOFormat<Double>, IOFormatFactory<Double> { public object DoubleIOFormat : IOFormat<Double>, IOFormatFactory<Double> {
override fun invoke(meta: Meta, context: Context): IOFormat<Double> = this override fun invoke(meta: Meta, context: Context): IOFormat<Double> = this
override val name: Name = "double".asName() override val name: Name = "double".asName()
override val type: KClass<out Double> get() = Double::class override val type: KClass<out Double> get() = Double::class
override fun Output.writeObject(obj: Double) { override fun writeObject(output: Output, obj: kotlin.Double) {
writeDouble(obj) output.writeDouble(obj)
} }
override fun Input.readObject(): Double = readDouble() override fun readObject(input: Input): Double = input.readDouble()
} }
object ValueIOFormat : IOFormat<Value>, IOFormatFactory<Value> { public object ValueIOFormat : IOFormat<Value>, IOFormatFactory<Value> {
override fun invoke(meta: Meta, context: Context): IOFormat<Value> = this override fun invoke(meta: Meta, context: Context): IOFormat<Value> = this
override val name: Name = "value".asName() override val name: Name = "value".asName()
override val type: KClass<out Value> get() = Value::class override val type: KClass<out Value> get() = Value::class
override fun Output.writeObject(obj: Value) { override fun writeObject(output: Output, obj: Value) {
BinaryMetaFormat.run { writeValue(obj) } BinaryMetaFormat.run { output.writeValue(obj) }
} }
override fun Input.readObject(): Value { override fun readObject(input: Input): Value {
return (BinaryMetaFormat.run { readMetaItem() } as? MetaItem.ValueItem)?.value return (BinaryMetaFormat.run { input.readMetaItem() } as? MetaItem.ValueItem)?.value
?: error("The item is not a value") ?: error("The item is not a value")
} }
} }

View File

@ -11,14 +11,14 @@ import hep.dataforge.names.Name
import hep.dataforge.names.toName import hep.dataforge.names.toName
import kotlin.reflect.KClass import kotlin.reflect.KClass
class IOPlugin(meta: Meta) : AbstractPlugin(meta) { public class IOPlugin(meta: Meta) : AbstractPlugin(meta) {
override val tag: PluginTag get() = Companion.tag override val tag: PluginTag get() = Companion.tag
val ioFormatFactories by lazy { public val ioFormatFactories: Collection<IOFormatFactory<*>> by lazy {
context.content<IOFormatFactory<*>>(IO_FORMAT_TYPE).values context.resolve<IOFormatFactory<*>>(IO_FORMAT_TYPE).values
} }
fun <T : Any> resolveIOFormat(item: MetaItem<*>, type: KClass<out T>): IOFormat<T>? { public fun <T : Any> resolveIOFormat(item: MetaItem<*>, type: KClass<out T>): IOFormat<T>? {
val key = item.string ?: item.node[NAME_KEY]?.string ?: error("Format name not defined") val key = item.string ?: item.node[NAME_KEY]?.string ?: error("Format name not defined")
val name = key.toName() val name = key.toName()
return ioFormatFactories.find { it.name == name }?.let { return ioFormatFactories.find { it.name == name }?.let {
@ -29,24 +29,24 @@ class IOPlugin(meta: Meta) : AbstractPlugin(meta) {
} }
val metaFormatFactories by lazy { public val metaFormatFactories: Collection<MetaFormatFactory> by lazy {
context.content<MetaFormatFactory>(META_FORMAT_TYPE).values context.resolve<MetaFormatFactory>(META_FORMAT_TYPE).values
} }
fun resolveMetaFormat(key: Short, meta: Meta = Meta.EMPTY): MetaFormat? = public fun resolveMetaFormat(key: Short, meta: Meta = Meta.EMPTY): MetaFormat? =
metaFormatFactories.find { it.key == key }?.invoke(meta) metaFormatFactories.find { it.key == key }?.invoke(meta)
fun resolveMetaFormat(name: String, meta: Meta = Meta.EMPTY): MetaFormat? = public fun resolveMetaFormat(name: String, meta: Meta = Meta.EMPTY): MetaFormat? =
metaFormatFactories.find { it.shortName == name }?.invoke(meta) metaFormatFactories.find { it.shortName == name }?.invoke(meta)
val envelopeFormatFactories by lazy { public val envelopeFormatFactories: Collection<EnvelopeFormatFactory> by lazy {
context.content<EnvelopeFormatFactory>(ENVELOPE_FORMAT_TYPE).values context.resolve<EnvelopeFormatFactory>(ENVELOPE_FORMAT_TYPE).values
} }
fun resolveEnvelopeFormat(name: Name, meta: Meta = Meta.EMPTY): EnvelopeFormat? = private fun resolveEnvelopeFormat(name: Name, meta: Meta = Meta.EMPTY): EnvelopeFormat? =
envelopeFormatFactories.find { it.name == name }?.invoke(meta, context) envelopeFormatFactories.find { it.name == name }?.invoke(meta, context)
fun resolveEnvelopeFormat(item: MetaItem<*>): EnvelopeFormat? { public fun resolveEnvelopeFormat(item: MetaItem<*>): EnvelopeFormat? {
val name = item.string ?: item.node[NAME_KEY]?.string ?: error("Envelope format name not defined") val name = item.string ?: item.node[NAME_KEY]?.string ?: error("Envelope format name not defined")
val meta = item.node[META_KEY].node ?: Meta.EMPTY val meta = item.node[META_KEY].node ?: Meta.EMPTY
return resolveEnvelopeFormat(name.toName(), meta) return resolveEnvelopeFormat(name.toName(), meta)
@ -60,9 +60,9 @@ class IOPlugin(meta: Meta) : AbstractPlugin(meta) {
} }
} }
companion object : PluginFactory<IOPlugin> { public companion object : PluginFactory<IOPlugin> {
val defaultMetaFormats: List<MetaFormatFactory> = listOf(JsonMetaFormat, BinaryMetaFormat) public val defaultMetaFormats: List<MetaFormatFactory> = listOf(JsonMetaFormat, BinaryMetaFormat)
val defaultEnvelopeFormats = listOf(TaggedEnvelopeFormat, TaglessEnvelopeFormat) public val defaultEnvelopeFormats: List<EnvelopeFormatFactory> = listOf(TaggedEnvelopeFormat, TaglessEnvelopeFormat)
override val tag: PluginTag = PluginTag("io", group = PluginTag.DATAFORGE_GROUP) override val tag: PluginTag = PluginTag("io", group = PluginTag.DATAFORGE_GROUP)
@ -71,4 +71,4 @@ class IOPlugin(meta: Meta) : AbstractPlugin(meta) {
} }
} }
val Context.io: IOPlugin get() = plugins.fetch(IOPlugin) public val Context.io: IOPlugin get() = plugins.fetch(IOPlugin)

View File

@ -17,39 +17,41 @@ import kotlinx.io.text.writeUtf8String
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonObject
/**
* A Json format for Meta representation
*/
public class JsonMetaFormat(private val json: Json = DEFAULT_JSON) : MetaFormat {
class JsonMetaFormat(private val json: Json = DEFAULT_JSON) : MetaFormat { override fun writeMeta(output: Output, meta: Meta, descriptor: NodeDescriptor?) {
override fun Output.writeMeta(meta: Meta, descriptor: NodeDescriptor?) {
val jsonObject = meta.toJson(descriptor) val jsonObject = meta.toJson(descriptor)
writeUtf8String(json.encodeToString(JsonObject.serializer(), jsonObject)) output.writeUtf8String(this.json.encodeToString(JsonObject.serializer(), jsonObject))
} }
override fun toMeta(): Meta = Meta { override fun toMeta(): Meta = Meta {
NAME_KEY put name.toString() NAME_KEY put name.toString()
} }
override fun Input.readMeta(descriptor: NodeDescriptor?): Meta { override fun readMeta(input: Input, descriptor: NodeDescriptor?): Meta {
val str = readByteArray().decodeToString() val str = input.readByteArray().decodeToString()
val jsonElement = json.parseToJsonElement(str) val jsonElement = json.parseToJsonElement(str)
val item = jsonElement.toMetaItem(descriptor) val item = jsonElement.toMetaItem(descriptor)
return item.node ?: Meta.EMPTY return item.node ?: Meta.EMPTY
} }
companion object : MetaFormatFactory { public companion object : MetaFormatFactory {
val DEFAULT_JSON = Json { prettyPrint = true } public val DEFAULT_JSON: Json = Json { prettyPrint = true }
override fun invoke(meta: Meta, context: Context): MetaFormat = default override fun invoke(meta: Meta, context: Context): MetaFormat = default
override val shortName = "json" override val shortName: String = "json"
override val key: Short = 0x4a53//"JS" override val key: Short = 0x4a53//"JS"
private val default = JsonMetaFormat() private val default = JsonMetaFormat()
override fun Output.writeMeta(meta: Meta, descriptor: NodeDescriptor?) = override fun writeMeta(output: Output, meta: Meta, descriptor: NodeDescriptor?): Unit =
default.run { writeMeta(meta, descriptor) } default.run { writeMeta(output, meta, descriptor) }
override fun Input.readMeta(descriptor: NodeDescriptor?): Meta = override fun readMeta(input: Input, descriptor: NodeDescriptor?): Meta =
default.run { readMeta(descriptor) } default.run { readMeta(input, descriptor) }
} }
} }

View File

@ -17,46 +17,50 @@ import kotlin.reflect.KClass
/** /**
* A format for meta serialization * A format for meta serialization
*/ */
public interface MetaFormat : IOFormat<Meta> {
interface MetaFormat : IOFormat<Meta> { override fun writeObject(output: Output, obj: Meta) {
writeMeta(output, obj, null)
override fun Output.writeObject(obj: Meta) {
writeMeta(obj, null)
} }
override fun Input.readObject(): Meta = readMeta() override fun readObject(input: Input): Meta = readMeta(input)
fun Output.writeMeta(meta: Meta, descriptor: NodeDescriptor? = null) public fun writeMeta(
fun Input.readMeta(descriptor: NodeDescriptor? = null): Meta output: Output,
meta: Meta,
descriptor: NodeDescriptor? = null,
)
public fun readMeta(input: Input, descriptor: NodeDescriptor? = null): Meta
} }
@Type(META_FORMAT_TYPE) @Type(META_FORMAT_TYPE)
interface MetaFormatFactory : IOFormatFactory<Meta>, MetaFormat { public interface MetaFormatFactory : IOFormatFactory<Meta>, MetaFormat {
val shortName: String public val shortName: String
override val name: Name get() = "meta".asName() + shortName override val name: Name get() = "meta".asName() + shortName
override val type: KClass<out Meta> get() = Meta::class override val type: KClass<out Meta> get() = Meta::class
val key: Short get() = name.hashCode().toShort() public val key: Short get() = name.hashCode().toShort()
override operator fun invoke(meta: Meta, context: Context): MetaFormat override operator fun invoke(meta: Meta, context: Context): MetaFormat
companion object { public companion object {
const val META_FORMAT_TYPE = "io.format.meta" public const val META_FORMAT_TYPE: String = "io.format.meta"
} }
} }
fun Meta.toString(format: MetaFormat): String = buildByteArray { public fun Meta.toString(format: MetaFormat): String = buildByteArray {
format.run { writeObject(this@toString) } format.run { writeObject(this@buildByteArray, this@toString) }
}.decodeToString() }.decodeToString()
fun Meta.toString(formatFactory: MetaFormatFactory): String = toString(formatFactory()) public fun Meta.toString(formatFactory: MetaFormatFactory): String = toString(formatFactory())
fun MetaFormat.parse(str: String): Meta { public fun MetaFormat.parse(str: String): Meta {
return ByteArrayInput(str.encodeToByteArray()).use { it.readObject() } return ByteArrayInput(str.encodeToByteArray()).use { readObject(it) }
} }
fun MetaFormatFactory.parse(str: String, formatMeta: Meta): Meta = invoke(formatMeta).parse(str) public fun MetaFormatFactory.parse(str: String, formatMeta: Meta): Meta = invoke(formatMeta).parse(str)

View File

@ -12,9 +12,13 @@ import hep.dataforge.names.plus
import hep.dataforge.names.toName import hep.dataforge.names.toName
import kotlinx.io.* import kotlinx.io.*
class TaggedEnvelopeFormat( /**
val io: IOPlugin, * A streaming-friendly envelope format with a short binary tag.
val version: VERSION = VERSION.DF02 * TODO add description
*/
public class TaggedEnvelopeFormat(
public val io: IOPlugin,
public val version: VERSION = VERSION.DF02
) : EnvelopeFormat { ) : EnvelopeFormat {
// private val metaFormat = io.metaFormat(metaFormatKey) // private val metaFormat = io.metaFormat(metaFormatKey)
@ -57,32 +61,32 @@ class TaggedEnvelopeFormat(
* @param input an input to read from * @param input an input to read from
* @param formats a collection of meta formats to resolve * @param formats a collection of meta formats to resolve
*/ */
override fun Input.readObject(): Envelope { override fun readObject(input: Input): Envelope {
val tag = readTag(version) val tag = input.readTag(this.version)
val metaFormat = io.resolveMetaFormat(tag.metaFormatKey) val metaFormat = io.resolveMetaFormat(tag.metaFormatKey)
?: error("Meta format with key ${tag.metaFormatKey} not found") ?: error("Meta format with key ${tag.metaFormatKey} not found")
val meta: Meta = limit(tag.metaSize.toInt()).run { val meta: Meta = input.limit(tag.metaSize.toInt()).run {
metaFormat.run { metaFormat.run {
readObject() readObject(input)
} }
} }
val data = readBinary(tag.dataSize.toInt()) val data = input.readBinary(tag.dataSize.toInt())
return SimpleEnvelope(meta, data) return SimpleEnvelope(meta, data)
} }
override fun Input.readPartial(): PartialEnvelope { override fun readPartial(input: Input): PartialEnvelope {
val tag = readTag(version) val tag = input.readTag(this.version)
val metaFormat = io.resolveMetaFormat(tag.metaFormatKey) val metaFormat = io.resolveMetaFormat(tag.metaFormatKey)
?: error("Meta format with key ${tag.metaFormatKey} not found") ?: error("Meta format with key ${tag.metaFormatKey} not found")
val meta: Meta = limit(tag.metaSize.toInt()).run { val meta: Meta = input.limit(tag.metaSize.toInt()).run {
metaFormat.run { metaFormat.run {
readObject() readObject(input)
} }
} }
@ -95,7 +99,7 @@ class TaggedEnvelopeFormat(
val dataSize: ULong val dataSize: ULong
) )
enum class VERSION(val tagSize: UInt) { public enum class VERSION(public val tagSize: UInt) {
DF02(20u), DF02(20u),
DF03(24u) DF03(24u)
} }
@ -107,7 +111,7 @@ class TaggedEnvelopeFormat(
} }
} }
companion object : EnvelopeFormatFactory { public companion object : EnvelopeFormatFactory {
private const val START_SEQUENCE = "#~" private const val START_SEQUENCE = "#~"
private const val END_SEQUENCE = "~#\r\n" private const val END_SEQUENCE = "~#\r\n"
@ -158,14 +162,14 @@ class TaggedEnvelopeFormat(
private val default by lazy { invoke() } private val default by lazy { invoke() }
override fun Input.readPartial(): PartialEnvelope = override fun readPartial(input: Input): PartialEnvelope =
default.run { readPartial() } default.run { readPartial(input) }
override fun Output.writeEnvelope(envelope: Envelope, metaFormatFactory: MetaFormatFactory, formatMeta: Meta) = override fun Output.writeEnvelope(envelope: Envelope, metaFormatFactory: MetaFormatFactory, formatMeta: Meta): Unit =
default.run { writeEnvelope(envelope, metaFormatFactory, formatMeta) } default.run { writeEnvelope(envelope, metaFormatFactory, formatMeta) }
override fun Input.readObject(): Envelope = override fun readObject(input: Input): Envelope =
default.run { readObject() } default.run { readObject(input) }
} }

View File

@ -7,15 +7,20 @@ import hep.dataforge.meta.Meta
import hep.dataforge.meta.get import hep.dataforge.meta.get
import hep.dataforge.meta.isEmpty import hep.dataforge.meta.isEmpty
import hep.dataforge.meta.string import hep.dataforge.meta.string
import hep.dataforge.names.Name
import hep.dataforge.names.asName import hep.dataforge.names.asName
import kotlinx.io.* import kotlinx.io.*
import kotlinx.io.text.readUtf8Line import kotlinx.io.text.readUtf8Line
import kotlinx.io.text.writeUtf8String import kotlinx.io.text.writeUtf8String
import kotlin.collections.set import kotlin.collections.set
class TaglessEnvelopeFormat( /**
val io: IOPlugin, * A text envelope format with human-readable tag.
val meta: Meta = Meta.EMPTY * TODO add description
*/
public class TaglessEnvelopeFormat(
public val io: IOPlugin,
public val meta: Meta = Meta.EMPTY
) : EnvelopeFormat { ) : EnvelopeFormat {
private val metaStart = meta[META_START_PROPERTY].string ?: DEFAULT_META_START private val metaStart = meta[META_START_PROPERTY].string ?: DEFAULT_META_START
@ -54,10 +59,10 @@ class TaglessEnvelopeFormat(
} }
} }
override fun Input.readObject(): Envelope { override fun readObject(input: Input): Envelope {
var line: String var line: String
do { do {
line = readUtf8Line() // ?: error("Input does not contain tagless envelope header") line = input.readUtf8Line() // ?: error("Input does not contain tagless envelope header")
} while (!line.startsWith(TAGLESS_ENVELOPE_HEADER)) } while (!line.startsWith(TAGLESS_ENVELOPE_HEADER))
val properties = HashMap<String, String>() val properties = HashMap<String, String>()
@ -70,8 +75,8 @@ class TaglessEnvelopeFormat(
properties[key] = value properties[key] = value
} }
//If can't read line, return envelope without data //If can't read line, return envelope without data
if (exhausted()) return SimpleEnvelope(Meta.EMPTY, null) if (input.exhausted()) return SimpleEnvelope(Meta.EMPTY, null)
line = readUtf8Line() line = input.readUtf8Line()
} }
var meta: Meta = Meta.EMPTY var meta: Meta = Meta.EMPTY
@ -80,19 +85,19 @@ class TaglessEnvelopeFormat(
val metaFormat = properties[META_TYPE_PROPERTY]?.let { io.resolveMetaFormat(it) } ?: JsonMetaFormat val metaFormat = properties[META_TYPE_PROPERTY]?.let { io.resolveMetaFormat(it) } ?: JsonMetaFormat
val metaSize = properties[META_LENGTH_PROPERTY]?.toInt() val metaSize = properties[META_LENGTH_PROPERTY]?.toInt()
meta = if (metaSize != null) { meta = if (metaSize != null) {
limit(metaSize).run { input.limit(metaSize).run {
metaFormat.run { readObject() } metaFormat.run { readObject(input) }
} }
} else { } else {
metaFormat.run { metaFormat.run {
readObject() readObject(input)
} }
} }
} }
do { do {
try { try {
line = readUtf8Line() line = input.readUtf8Line()
} catch (ex: EOFException) { } catch (ex: EOFException) {
//returning an Envelope without data if end of input is reached //returning an Envelope without data if end of input is reached
return SimpleEnvelope(meta, null) return SimpleEnvelope(meta, null)
@ -100,24 +105,24 @@ class TaglessEnvelopeFormat(
} while (!line.startsWith(dataStart)) } while (!line.startsWith(dataStart))
val data: Binary? = if (properties.containsKey(DATA_LENGTH_PROPERTY)) { val data: Binary? = if (properties.containsKey(DATA_LENGTH_PROPERTY)) {
readBinary(properties[DATA_LENGTH_PROPERTY]!!.toInt()) input.readBinary(properties[DATA_LENGTH_PROPERTY]!!.toInt())
// val bytes = ByteArray(properties[DATA_LENGTH_PROPERTY]!!.toInt()) // val bytes = ByteArray(properties[DATA_LENGTH_PROPERTY]!!.toInt())
// readByteArray(bytes) // readByteArray(bytes)
// bytes.asBinary() // bytes.asBinary()
} else { } else {
Binary { Binary {
copyTo(this) input.copyTo(this)
} }
} }
return SimpleEnvelope(meta, data) return SimpleEnvelope(meta, data)
} }
override fun Input.readPartial(): PartialEnvelope { override fun readPartial(input: Input): PartialEnvelope {
var offset = 0u var offset = 0u
var line: String var line: String
do { do {
line = readUtf8Line()// ?: error("Input does not contain tagless envelope header") line = input.readUtf8Line()// ?: error("Input does not contain tagless envelope header")
offset += line.encodeToByteArray().size.toUInt() offset += line.encodeToByteArray().size.toUInt()
} while (!line.startsWith(TAGLESS_ENVELOPE_HEADER)) } while (!line.startsWith(TAGLESS_ENVELOPE_HEADER))
val properties = HashMap<String, String>() val properties = HashMap<String, String>()
@ -131,7 +136,7 @@ class TaglessEnvelopeFormat(
properties[key] = value properties[key] = value
} }
try { try {
line = readUtf8Line() line = input.readUtf8Line()
offset += line.encodeToByteArray().size.toUInt() offset += line.encodeToByteArray().size.toUInt()
} catch (ex: EOFException) { } catch (ex: EOFException) {
return PartialEnvelope(Meta.EMPTY, offset.toUInt(), 0.toULong()) return PartialEnvelope(Meta.EMPTY, offset.toUInt(), 0.toULong())
@ -145,8 +150,8 @@ class TaglessEnvelopeFormat(
val metaSize = properties[META_LENGTH_PROPERTY]?.toInt() val metaSize = properties[META_LENGTH_PROPERTY]?.toInt()
meta = if (metaSize != null) { meta = if (metaSize != null) {
offset += metaSize.toUInt() offset += metaSize.toUInt()
limit(metaSize).run { input.limit(metaSize).run {
metaFormat.run { readObject() } metaFormat.run { readObject(input) }
} }
} else { } else {
error("Can't partially read an envelope with undefined meta size") error("Can't partially read an envelope with undefined meta size")
@ -154,7 +159,7 @@ class TaglessEnvelopeFormat(
} }
do { do {
line = readUtf8Line() //?: return PartialEnvelope(Meta.EMPTY, offset.toUInt(), 0.toULong()) line = input.readUtf8Line() //?: return PartialEnvelope(Meta.EMPTY, offset.toUInt(), 0.toULong())
offset += line.encodeToByteArray().size.toUInt() offset += line.encodeToByteArray().size.toUInt()
//returning an Envelope without data if end of input is reached //returning an Envelope without data if end of input is reached
} while (!line.startsWith(dataStart)) } while (!line.startsWith(dataStart))
@ -168,26 +173,26 @@ class TaglessEnvelopeFormat(
META_KEY put meta META_KEY put meta
} }
companion object : EnvelopeFormatFactory { public companion object : EnvelopeFormatFactory {
private val propertyPattern = "#\\?\\s*(?<key>[\\w.]*)\\s*:\\s*(?<value>[^;]*);?".toRegex() private val propertyPattern = "#\\?\\s*(?<key>[\\w.]*)\\s*:\\s*(?<value>[^;]*);?".toRegex()
const val META_TYPE_PROPERTY = "metaType" public const val META_TYPE_PROPERTY: String = "metaType"
const val META_LENGTH_PROPERTY = "metaLength" public const val META_LENGTH_PROPERTY: String = "metaLength"
const val DATA_LENGTH_PROPERTY = "dataLength" public const val DATA_LENGTH_PROPERTY: String = "dataLength"
const val TAGLESS_ENVELOPE_TYPE = "tagless" public const val TAGLESS_ENVELOPE_TYPE: String = "tagless"
const val TAGLESS_ENVELOPE_HEADER = "#~DFTL~#" public const val TAGLESS_ENVELOPE_HEADER: String = "#~DFTL~#"
const val META_START_PROPERTY = "metaSeparator" public const val META_START_PROPERTY: String = "metaSeparator"
const val DEFAULT_META_START = "#~META~#" public const val DEFAULT_META_START: String = "#~META~#"
const val DATA_START_PROPERTY = "dataSeparator" public const val DATA_START_PROPERTY: String = "dataSeparator"
const val DEFAULT_DATA_START = "#~DATA~#" public const val DEFAULT_DATA_START: String = "#~DATA~#"
const val code: Int = 0x4446544c //DFTL public const val code: Int = 0x4446544c //DFTL
override val name = TAGLESS_ENVELOPE_TYPE.asName() override val name: Name = TAGLESS_ENVELOPE_TYPE.asName()
override fun invoke(meta: Meta, context: Context): EnvelopeFormat { override fun invoke(meta: Meta, context: Context): EnvelopeFormat {
return TaglessEnvelopeFormat(context.io, meta) return TaglessEnvelopeFormat(context.io, meta)
@ -195,14 +200,14 @@ class TaglessEnvelopeFormat(
private val default by lazy { invoke() } private val default by lazy { invoke() }
override fun Input.readPartial(): PartialEnvelope = override fun readPartial(input: Input): PartialEnvelope =
default.run { readPartial() } default.run { readPartial(input) }
override fun Output.writeEnvelope(envelope: Envelope, metaFormatFactory: MetaFormatFactory, formatMeta: Meta) = override fun Output.writeEnvelope(envelope: Envelope, metaFormatFactory: MetaFormatFactory, formatMeta: Meta): Unit =
default.run { writeEnvelope(envelope, metaFormatFactory, formatMeta) } default.run { writeEnvelope(envelope, metaFormatFactory, formatMeta) }
override fun Input.readObject(): Envelope = override fun readObject(input: Input): Envelope =
default.run { readObject() } default.run { readObject(input) }
override fun peekFormat(io: IOPlugin, input: Input): EnvelopeFormat? { override fun peekFormat(io: IOPlugin, input: Input): EnvelopeFormat? {
return try { return try {

View File

@ -8,11 +8,11 @@ import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
fun Meta.toByteArray(format: MetaFormat = JsonMetaFormat) = buildByteArray { fun Meta.toByteArray(format: MetaFormat = JsonMetaFormat) = buildByteArray {
format.run { writeObject(this@toByteArray) } format.writeObject(this@buildByteArray, this@toByteArray)
} }
fun MetaFormat.fromByteArray(packet: ByteArray): Meta { fun MetaFormat.fromByteArray(packet: ByteArray): Meta {
return packet.asBinary().read { readObject() } return packet.asBinary().read { readObject(this) }
} }
class MetaFormatTest { class MetaFormatTest {

View File

@ -3,5 +3,5 @@ package hep.dataforge.io
import kotlinx.io.ByteArrayInput import kotlinx.io.ByteArrayInput
import kotlinx.io.use import kotlinx.io.use
fun <T : Any> IOFormat<T>.toByteArray(obj: T): ByteArray = buildByteArray { writeObject(obj) } fun <T : Any> IOFormat<T>.toByteArray(obj: T): ByteArray = buildByteArray { writeObject(this, obj) }
fun <T : Any> IOFormat<T>.readByteArray(array: ByteArray): T = ByteArrayInput(array).use { it.readObject() } fun <T : Any> IOFormat<T>.readByteArray(array: ByteArray): T = ByteArrayInput(array).use { readObject(it) }

View File

@ -11,12 +11,12 @@ import java.nio.file.StandardOpenOption
import kotlin.reflect.full.isSuperclassOf import kotlin.reflect.full.isSuperclassOf
import kotlin.streams.asSequence import kotlin.streams.asSequence
fun <R> Path.read(block: Input.() -> R): R = asBinary().read(block = block) public fun <R> Path.read(block: Input.() -> R): R = asBinary().read(block = block)
/** /**
* Write a live output to a newly created file. If file does not exist, throws error * Write a live output to a newly created file. If file does not exist, throws error
*/ */
fun Path.write(block: Output.() -> Unit): Unit { public fun Path.write(block: Output.() -> Unit): Unit {
val stream = Files.newOutputStream(this, StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW) val stream = Files.newOutputStream(this, StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW)
stream.asOutput().use(block) stream.asOutput().use(block)
} }
@ -24,7 +24,7 @@ fun Path.write(block: Output.() -> Unit): Unit {
/** /**
* Create a new file or append to exiting one with given output [block] * Create a new file or append to exiting one with given output [block]
*/ */
fun Path.append(block: Output.() -> Unit): Unit { public fun Path.append(block: Output.() -> Unit): Unit {
val stream = Files.newOutputStream( val stream = Files.newOutputStream(
this, this,
StandardOpenOption.WRITE, StandardOpenOption.APPEND, StandardOpenOption.CREATE StandardOpenOption.WRITE, StandardOpenOption.APPEND, StandardOpenOption.CREATE
@ -35,7 +35,7 @@ fun Path.append(block: Output.() -> Unit): Unit {
/** /**
* Create a new file or replace existing one using given output [block] * Create a new file or replace existing one using given output [block]
*/ */
fun Path.rewrite(block: Output.() -> Unit): Unit { public fun Path.rewrite(block: Output.() -> Unit): Unit {
val stream = Files.newOutputStream( val stream = Files.newOutputStream(
this, this,
StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE
@ -43,9 +43,9 @@ fun Path.rewrite(block: Output.() -> Unit): Unit {
stream.asOutput().use(block) stream.asOutput().use(block)
} }
fun Path.readEnvelope(format: EnvelopeFormat): Envelope { public fun Path.readEnvelope(format: EnvelopeFormat): Envelope {
val partialEnvelope: PartialEnvelope = asBinary().read { val partialEnvelope: PartialEnvelope = asBinary().read {
format.run { readPartial() } format.run { readPartial(this@read) }
} }
val offset: Int = partialEnvelope.dataOffset.toInt() val offset: Int = partialEnvelope.dataOffset.toInt()
val size: Int = partialEnvelope.dataSize?.toInt() ?: (Files.size(this).toInt() - offset) val size: Int = partialEnvelope.dataSize?.toInt() ?: (Files.size(this).toInt() - offset)
@ -58,7 +58,7 @@ fun Path.readEnvelope(format: EnvelopeFormat): Envelope {
*/ */
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
@DFExperimental @DFExperimental
inline fun <reified T : Any> IOPlugin.resolveIOFormat(): IOFormat<T>? { public inline fun <reified T : Any> IOPlugin.resolveIOFormat(): IOFormat<T>? {
return ioFormatFactories.find { it.type.isSuperclassOf(T::class) } as IOFormat<T>? return ioFormatFactories.find { it.type.isSuperclassOf(T::class) } as IOFormat<T>?
} }
@ -66,7 +66,7 @@ inline fun <reified T : Any> IOPlugin.resolveIOFormat(): IOFormat<T>? {
* Read file containing meta using given [formatOverride] or file extension to infer meta type. * Read file containing meta using given [formatOverride] or file extension to infer meta type.
* If [path] is a directory search for file starting with `meta` in it * If [path] is a directory search for file starting with `meta` in it
*/ */
fun IOPlugin.readMetaFile(path: Path, formatOverride: MetaFormat? = null, descriptor: NodeDescriptor? = null): Meta { public fun IOPlugin.readMetaFile(path: Path, formatOverride: MetaFormat? = null, descriptor: NodeDescriptor? = null): Meta {
if (!Files.exists(path)) error("Meta file $path does not exist") if (!Files.exists(path)) error("Meta file $path does not exist")
val actualPath: Path = if (Files.isDirectory(path)) { val actualPath: Path = if (Files.isDirectory(path)) {
@ -80,7 +80,7 @@ fun IOPlugin.readMetaFile(path: Path, formatOverride: MetaFormat? = null, descri
val metaFormat = formatOverride ?: resolveMetaFormat(extension) ?: error("Can't resolve meta format $extension") val metaFormat = formatOverride ?: resolveMetaFormat(extension) ?: error("Can't resolve meta format $extension")
return metaFormat.run { return metaFormat.run {
actualPath.read { actualPath.read {
readMeta(descriptor) readMeta(this, descriptor)
} }
} }
} }
@ -89,7 +89,7 @@ fun IOPlugin.readMetaFile(path: Path, formatOverride: MetaFormat? = null, descri
* Write meta to file using [metaFormat]. If [path] is a directory, write a file with name equals name of [metaFormat]. * Write meta to file using [metaFormat]. If [path] is a directory, write a file with name equals name of [metaFormat].
* Like "meta.json" * Like "meta.json"
*/ */
fun IOPlugin.writeMetaFile( public fun IOPlugin.writeMetaFile(
path: Path, path: Path,
meta: Meta, meta: Meta,
metaFormat: MetaFormatFactory = JsonMetaFormat, metaFormat: MetaFormatFactory = JsonMetaFormat,
@ -102,7 +102,7 @@ fun IOPlugin.writeMetaFile(
} }
metaFormat.run { metaFormat.run {
actualPath.write { actualPath.write {
writeMeta(meta, descriptor) writeMeta(this, meta, descriptor)
} }
} }
} }
@ -111,7 +111,7 @@ fun IOPlugin.writeMetaFile(
* Return inferred [EnvelopeFormat] if only one format could read given file. If no format accepts file, return null. If * Return inferred [EnvelopeFormat] if only one format could read given file. If no format accepts file, return null. If
* multiple formats accepts file, throw an error. * multiple formats accepts file, throw an error.
*/ */
fun IOPlugin.peekBinaryFormat(path: Path): EnvelopeFormat? { public fun IOPlugin.peekBinaryFormat(path: Path): EnvelopeFormat? {
val binary = path.asBinary() val binary = path.asBinary()
val formats = envelopeFormatFactories.mapNotNull { factory -> val formats = envelopeFormatFactories.mapNotNull { factory ->
binary.read { binary.read {
@ -126,8 +126,8 @@ fun IOPlugin.peekBinaryFormat(path: Path): EnvelopeFormat? {
} }
} }
val IOPlugin.Companion.META_FILE_NAME: String get() = "@meta" public val IOPlugin.Companion.META_FILE_NAME: String get() = "@meta"
val IOPlugin.Companion.DATA_FILE_NAME: String get() = "@data" public val IOPlugin.Companion.DATA_FILE_NAME: String get() = "@data"
/** /**
* Read and envelope from file if the file exists, return null if file does not exist. * Read and envelope from file if the file exists, return null if file does not exist.
@ -143,7 +143,7 @@ val IOPlugin.Companion.DATA_FILE_NAME: String get() = "@data"
* Return null otherwise. * Return null otherwise.
*/ */
@DFExperimental @DFExperimental
fun IOPlugin.readEnvelopeFile( public fun IOPlugin.readEnvelopeFile(
path: Path, path: Path,
readNonEnvelopes: Boolean = false, readNonEnvelopes: Boolean = false,
formatPeeker: IOPlugin.(Path) -> EnvelopeFormat? = IOPlugin::peekBinaryFormat formatPeeker: IOPlugin.(Path) -> EnvelopeFormat? = IOPlugin::peekBinaryFormat
@ -182,9 +182,9 @@ fun IOPlugin.readEnvelopeFile(
/** /**
* Write a binary into file. Throws an error if file already exists * Write a binary into file. Throws an error if file already exists
*/ */
fun <T : Any> IOFormat<T>.writeToFile(path: Path, obj: T) { public fun <T : Any> IOFormat<T>.writeToFile(path: Path, obj: T) {
path.write { path.write {
writeObject(obj) writeObject(this, obj)
} }
} }
@ -192,7 +192,7 @@ fun <T : Any> IOFormat<T>.writeToFile(path: Path, obj: T) {
* Write envelope file to given [path] using [envelopeFormat] and optional [metaFormat] * Write envelope file to given [path] using [envelopeFormat] and optional [metaFormat]
*/ */
@DFExperimental @DFExperimental
fun IOPlugin.writeEnvelopeFile( public fun IOPlugin.writeEnvelopeFile(
path: Path, path: Path,
envelope: Envelope, envelope: Envelope,
envelopeFormat: EnvelopeFormat = TaggedEnvelopeFormat, envelopeFormat: EnvelopeFormat = TaggedEnvelopeFormat,

View File

@ -56,10 +56,10 @@ public class EnvelopeClient(
val outputStream = socket.getOutputStream() val outputStream = socket.getOutputStream()
format.run { format.run {
outputStream.write { outputStream.write {
writeObject(request) writeObject(this, request)
} }
logger.debug { "Sent request with type ${request.type} to ${socket.remoteSocketAddress}" } logger.debug { "Sent request with type ${request.type} to ${socket.remoteSocketAddress}" }
val res = inputStream.readBlocking { readObject() } val res = inputStream.readBlocking { readObject(this) }
logger.debug { "Received response with type ${res.type} from ${socket.remoteSocketAddress}" } logger.debug { "Received response with type ${res.type} from ${socket.remoteSocketAddress}" }
return@withContext res return@withContext res
} }

View File

@ -74,12 +74,12 @@ public class EnvelopeServer(
val outputStream = socket.getOutputStream() val outputStream = socket.getOutputStream()
format.run { format.run {
while (socket.isConnected) { while (socket.isConnected) {
val request = inputStream.readBlocking { readObject() } val request = inputStream.readBlocking { readObject(this) }
logger.debug { "Accepted request with type ${request.type} from ${socket.remoteSocketAddress}" } logger.debug { "Accepted request with type ${request.type} from ${socket.remoteSocketAddress}" }
if (request.type == SHUTDOWN_ENVELOPE_TYPE) { if (request.type == SHUTDOWN_ENVELOPE_TYPE) {
//Echo shutdown command //Echo shutdown command
outputStream.write { outputStream.write {
writeObject(request) writeObject(this, request)
} }
logger.info { "Accepted graceful shutdown signal from ${socket.inetAddress}" } logger.info { "Accepted graceful shutdown signal from ${socket.inetAddress}" }
socket.close() socket.close()
@ -89,7 +89,7 @@ public class EnvelopeServer(
runBlocking { runBlocking {
val response = responder.respond(request) val response = responder.respond(request)
outputStream.write { outputStream.write {
writeObject(response) writeObject(this, response)
} }
logger.debug { "Sent response with type ${response.type} to ${socket.remoteSocketAddress}" } logger.debug { "Sent response with type ${response.type} to ${socket.remoteSocketAddress}" }
} }

View File

@ -1,7 +1,7 @@
package hep.dataforge.workspace package hep.dataforge.workspace
import hep.dataforge.context.Context import hep.dataforge.context.Context
import hep.dataforge.context.content import hep.dataforge.context.resolve
import hep.dataforge.context.toMap import hep.dataforge.context.toMap
import hep.dataforge.data.DataNode import hep.dataforge.data.DataNode
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
@ -19,7 +19,7 @@ class SimpleWorkspace(
) : Workspace { ) : Workspace {
override val tasks: Map<Name, Task<*>> by lazy { override val tasks: Map<Name, Task<*>> by lazy {
context.content<Task<*>>(Task.TYPE) + tasks.toMap() context.resolve<Task<*>>(Task.TYPE) + tasks.toMap()
} }
companion object { companion object {

View File

@ -27,7 +27,7 @@ interface WorkspaceBuilder {
* Set the context for future workspcace * Set the context for future workspcace
*/ */
fun WorkspaceBuilder.context(name: String = "WORKSPACE", block: ContextBuilder.() -> Unit = {}) { fun WorkspaceBuilder.context(name: String = "WORKSPACE", block: ContextBuilder.() -> Unit = {}) {
context = ContextBuilder(name, parentContext).apply(block).build() context = ContextBuilder(parentContext, name).apply(block).build()
} }
inline fun <reified T : Any> WorkspaceBuilder.data( inline fun <reified T : Any> WorkspaceBuilder.data(

View File

@ -168,7 +168,7 @@ private suspend fun <T : Any> ZipOutputStream.writeNode(
val entry = ZipEntry(name) val entry = ZipEntry(name)
putNextEntry(entry) putNextEntry(entry)
envelopeFormat.run { envelopeFormat.run {
asOutput().writeObject(envelope) writeObject(asOutput(), envelope)
} }
} }
is DataItem.Node -> { is DataItem.Node -> {

View File

@ -30,12 +30,12 @@ class FileDataTest {
} }
object StringIOFormat : IOFormat<String> { object StringIOFormat : IOFormat<String> {
override fun Output.writeObject(obj: String) { override fun writeObject(output: Output, obj: String) {
writeUtf8String(obj) output.writeUtf8String(obj)
} }
override fun Input.readObject(): String { override fun readObject(input: Input): String {
return readUtf8String() return input.readUtf8String()
} }
override fun toMeta(): Meta = Meta { override fun toMeta(): Meta = Meta {

View File

@ -8,7 +8,7 @@ pluginManagement {
maven("https://dl.bintray.com/mipt-npm/dev") maven("https://dl.bintray.com/mipt-npm/dev")
} }
val toolsVersion = "0.6.0-dev-3" val toolsVersion = "0.6.0-dev-4"
val kotlinVersion = "1.4.0" val kotlinVersion = "1.4.0"
plugins { plugins {