diff --git a/dataforge-io/dataforge-io-yaml/src/main/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt b/dataforge-io/dataforge-io-yaml/src/main/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt index be6e1d75..be74d080 100644 --- a/dataforge-io/dataforge-io-yaml/src/main/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt +++ b/dataforge-io/dataforge-io-yaml/src/main/kotlin/hep/dataforge/io/yaml/FrontMatterEnvelopeFormat.kt @@ -5,8 +5,10 @@ import hep.dataforge.io.* import hep.dataforge.meta.DFExperimental import hep.dataforge.meta.EmptyMeta import hep.dataforge.meta.Meta -import kotlinx.io.Input -import kotlinx.io.Output +import kotlinx.io.* +import kotlinx.io.text.readUtf8Line +import kotlinx.io.text.writeRawString +import kotlinx.io.text.writeUtf8String import kotlinx.serialization.toUtf8Bytes @DFExperimental @@ -27,14 +29,17 @@ class FrontMatterEnvelopeFormat( metaTypeRegex.matchEntire(line)?.groupValues?.first() ?.let { io.metaFormat(it) } ?: YamlMetaFormat - val metaBlock = buildPacket { + val meta = buildBytes { do { - line = readUtf8Line() ?: error("Input does not contain closing front matter separator") - appendln(line) + line = readUtf8Line() + writeUtf8String(line + "\r\n") offset += line.toUtf8Bytes().size.toUInt() } while (!line.startsWith(SEPARATOR)) + }.read { + readMetaFormat.run { + readMeta() + } } - val meta = readMetaFormat.fromBytes(metaBlock) return PartialEnvelope(meta, offset, null) } @@ -48,23 +53,29 @@ class FrontMatterEnvelopeFormat( metaTypeRegex.matchEntire(line)?.groupValues?.first() ?.let { io.metaFormat(it) } ?: YamlMetaFormat - val metaBlock = buildPacket { + val meta = buildBytes { do { - appendln(readUtf8Line() ?: error("Input does not contain closing front matter separator")) + writeUtf8String(readUtf8Line() + "\r\n") } while (!line.startsWith(SEPARATOR)) + }.read { + readMetaFormat.run { + readMeta() + } } - val meta = readMetaFormat.fromBytes(metaBlock) - val bytes = readBytes() + val bytes = readRemaining() val data = bytes.asBinary() return SimpleEnvelope(meta, data) } override fun Output.writeEnvelope(envelope: Envelope, metaFormatFactory: MetaFormatFactory, formatMeta: Meta) { val metaFormat = metaFormatFactory(formatMeta, io.context) - writeText("$SEPARATOR\r\n") + writeRawString("$SEPARATOR\r\n") metaFormat.run { writeObject(envelope.meta) } - writeText("$SEPARATOR\r\n") - envelope.data?.read { copyTo(this@writeEnvelope) } + writeRawString("$SEPARATOR\r\n") + //Printing data + envelope.data?.let { data -> + writeBinary(data) + } } companion object : EnvelopeFormatFactory { @@ -77,7 +88,7 @@ class FrontMatterEnvelopeFormat( } override fun peekFormat(io: IOPlugin, input: Input): EnvelopeFormat? { - val line = input.readUtf8Line(3, 30) + val line = input.readUtf8Line() return if (line != null && line.startsWith("---")) { invoke() } else { diff --git a/dataforge-io/dataforge-io-yaml/src/main/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt b/dataforge-io/dataforge-io-yaml/src/main/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt index 5e31a69e..acd19652 100644 --- a/dataforge-io/dataforge-io-yaml/src/main/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt +++ b/dataforge-io/dataforge-io-yaml/src/main/kotlin/hep/dataforge/io/yaml/YamlMetaFormat.kt @@ -11,13 +11,13 @@ import hep.dataforge.meta.toMeta import kotlinx.io.Input import kotlinx.io.Output import kotlinx.io.readUByte -import kotlinx.io.writeText +import kotlinx.io.text.writeUtf8String import org.yaml.snakeyaml.Yaml import java.io.InputStream private class InputAsStream(val input: Input) : InputStream() { override fun read(): Int { - if (input.endOfInput) return -1 + if (input.eof()) return -1 return input.readUByte().toInt() } @@ -34,7 +34,7 @@ class YamlMetaFormat(val meta: Meta) : MetaFormat { override fun Output.writeMeta(meta: Meta, descriptor: NodeDescriptor?) { val string = yaml.dump(meta.toMap(descriptor)) - writeText(string) + writeUtf8String(string) } override fun Input.readMeta(descriptor: NodeDescriptor?): Meta { diff --git a/dataforge-io/dataforge-io-yaml/src/test/kotlin/hep/dataforge/io/yaml/YamlMetaFormatTest.kt b/dataforge-io/dataforge-io-yaml/src/test/kotlin/hep/dataforge/io/yaml/YamlMetaFormatTest.kt index 5c5b3c18..2be83509 100644 --- a/dataforge-io/dataforge-io-yaml/src/test/kotlin/hep/dataforge/io/yaml/YamlMetaFormatTest.kt +++ b/dataforge-io/dataforge-io-yaml/src/test/kotlin/hep/dataforge/io/yaml/YamlMetaFormatTest.kt @@ -6,7 +6,7 @@ import hep.dataforge.meta.Meta import hep.dataforge.meta.buildMeta import hep.dataforge.meta.get import hep.dataforge.meta.seal -import org.junit.jupiter.api.Test +import kotlin.test.Test import kotlin.test.assertEquals diff --git a/dataforge-output-html/src/commonMain/kotlin/hep/dataforge/output/html/HtmlOutput.kt b/dataforge-output-html/src/commonMain/kotlin/hep/dataforge/output/html/HtmlRenderer.kt similarity index 86% rename from dataforge-output-html/src/commonMain/kotlin/hep/dataforge/output/html/HtmlOutput.kt rename to dataforge-output-html/src/commonMain/kotlin/hep/dataforge/output/html/HtmlRenderer.kt index 3ff23403..c0aeaaab 100644 --- a/dataforge-output-html/src/commonMain/kotlin/hep/dataforge/output/html/HtmlOutput.kt +++ b/dataforge-output-html/src/commonMain/kotlin/hep/dataforge/output/html/HtmlRenderer.kt @@ -3,7 +3,8 @@ package hep.dataforge.output.html import hep.dataforge.context.Context import hep.dataforge.meta.Meta import hep.dataforge.output.Output -import hep.dataforge.output.TextRenderer +import hep.dataforge.output.Renderer +import hep.dataforge.output.TextFormat import hep.dataforge.output.html.HtmlBuilder.Companion.HTML_CONVERTER_TYPE import hep.dataforge.provider.Type import hep.dataforge.provider.top @@ -14,11 +15,11 @@ import kotlinx.html.p import kotlin.reflect.KClass -class HtmlOutput(override val context: Context, private val consumer: TagConsumer<*>) : Output { +class HtmlRenderer(override val context: Context, private val consumer: TagConsumer<*>) : Renderer { private val cache = HashMap, HtmlBuilder<*>>() /** - * Find the first [TextRenderer] matching the given object type. + * Find the first [TextFormat] matching the given object type. */ override fun render(obj: T, meta: Meta) { @@ -47,7 +48,7 @@ class HtmlOutput(override val context: Context, private val consumer: T } /** - * A text or binary renderer based on [Output] + * A text or binary renderer based on [Renderer] */ @Type(HTML_CONVERTER_TYPE) interface HtmlBuilder { diff --git a/dataforge-output/src/commonMain/kotlin/hep/dataforge/output/OutputManager.kt b/dataforge-output/src/commonMain/kotlin/hep/dataforge/output/OutputManager.kt index d9f7c1b2..c05d5bb4 100644 --- a/dataforge-output/src/commonMain/kotlin/hep/dataforge/output/OutputManager.kt +++ b/dataforge-output/src/commonMain/kotlin/hep/dataforge/output/OutputManager.kt @@ -1,9 +1,6 @@ package hep.dataforge.output -import hep.dataforge.context.AbstractPlugin -import hep.dataforge.context.Context -import hep.dataforge.context.PluginFactory -import hep.dataforge.context.PluginTag +import hep.dataforge.context.* import hep.dataforge.context.PluginTag.Companion.DATAFORGE_GROUP import hep.dataforge.meta.EmptyMeta import hep.dataforge.meta.Meta @@ -22,14 +19,14 @@ interface OutputManager { * Get an output specialized for given type, name and stage. * @param stage represents the node or directory for the output. Empty means root node. * @param name represents the name inside the node. - * @param meta configuration for [Output] (not for rendered object) + * @param meta configuration for [Renderer] (not for rendered object) */ operator fun get( type: KClass, name: Name, stage: Name = EmptyName, meta: Meta = EmptyMeta - ): Output + ): Renderer } /** @@ -44,7 +41,7 @@ inline operator fun OutputManager.get( name: Name, stage: Name = EmptyName, meta: Meta = EmptyMeta -): Output { +): Renderer { return get(T::class, name, stage, meta) } @@ -56,14 +53,21 @@ fun OutputManager.render(obj: Any, name: Name, stage: Name = EmptyName, meta: Me /** * System console output. - * The [ConsoleOutput] is used when no other [OutputManager] is provided. + * The [CONSOLE_RENDERER] is used when no other [OutputManager] is provided. */ -expect val ConsoleOutput: Output +val CONSOLE_RENDERER: Renderer = object : Renderer { + override fun render(obj: Any, meta: Meta) { + println(obj) + } + + override val context: Context get() = Global + +} class ConsoleOutputManager : AbstractPlugin(), OutputManager { override val tag: PluginTag get() = ConsoleOutputManager.tag - override fun get(type: KClass, name: Name, stage: Name, meta: Meta): Output = ConsoleOutput + override fun get(type: KClass, name: Name, stage: Name, meta: Meta): Renderer = CONSOLE_RENDERER companion object : PluginFactory { override val tag = PluginTag("output.console", group = DATAFORGE_GROUP) diff --git a/dataforge-output/src/commonMain/kotlin/hep/dataforge/output/Output.kt b/dataforge-output/src/commonMain/kotlin/hep/dataforge/output/Renderer.kt similarity index 86% rename from dataforge-output/src/commonMain/kotlin/hep/dataforge/output/Output.kt rename to dataforge-output/src/commonMain/kotlin/hep/dataforge/output/Renderer.kt index 091cb999..c1bcf6e5 100644 --- a/dataforge-output/src/commonMain/kotlin/hep/dataforge/output/Output.kt +++ b/dataforge-output/src/commonMain/kotlin/hep/dataforge/output/Renderer.kt @@ -7,11 +7,11 @@ import hep.dataforge.meta.Meta /** * A generic way to render any object in the output. * - * An object could be rendered either in append or overlay mode. The mode is decided by the [Output] + * An object could be rendered either in append or overlay mode. The mode is decided by the [Renderer] * based on its configuration and provided meta * */ -interface Output : ContextAware { +interface Renderer : ContextAware { /** * Render specific object with configuration. * diff --git a/dataforge-output/src/commonMain/kotlin/hep/dataforge/output/TextOutput.kt b/dataforge-output/src/commonMain/kotlin/hep/dataforge/output/TextRenderer.kt similarity index 56% rename from dataforge-output/src/commonMain/kotlin/hep/dataforge/output/TextOutput.kt rename to dataforge-output/src/commonMain/kotlin/hep/dataforge/output/TextRenderer.kt index 77f589df..cc9d24f3 100644 --- a/dataforge-output/src/commonMain/kotlin/hep/dataforge/output/TextOutput.kt +++ b/dataforge-output/src/commonMain/kotlin/hep/dataforge/output/TextRenderer.kt @@ -2,48 +2,50 @@ package hep.dataforge.output import hep.dataforge.context.Context import hep.dataforge.meta.Meta -import hep.dataforge.output.TextRenderer.Companion.TEXT_RENDERER_TYPE +import hep.dataforge.output.TextFormat.Companion.TEXT_RENDERER_TYPE import hep.dataforge.provider.Type import hep.dataforge.provider.top import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.io.Output +import kotlinx.io.text.writeUtf8String import kotlin.reflect.KClass -class TextOutput(override val context: Context, private val output: kotlinx.io.Output) : Output { - private val cache = HashMap, TextRenderer>() +class TextRenderer(override val context: Context, private val output: Output) : Renderer { + private val cache = HashMap, TextFormat>() /** - * Find the first [TextRenderer] matching the given object type. + * Find the first [TextFormat] matching the given object type. */ override fun render(obj: Any, meta: Meta) { - val renderer: TextRenderer = if (obj is CharSequence) { - DefaultTextRenderer + val format: TextFormat = if (obj is CharSequence) { + DefaultTextFormat } else { val value = cache[obj::class] if (value == null) { val answer = - context.top(TEXT_RENDERER_TYPE).values.firstOrNull { it.type.isInstance(obj) } + context.top(TEXT_RENDERER_TYPE).values.firstOrNull { it.type.isInstance(obj) } if (answer != null) { cache[obj::class] = answer answer } else { - DefaultTextRenderer + DefaultTextFormat } } else { value } } context.launch(Dispatchers.Output) { - renderer.run { output.render(obj) } + format.run { output.render(obj) } } } } /** - * A text or binary renderer based on [kotlinx.io.Output] + * A text or binary renderer based on [Output] */ @Type(TEXT_RENDERER_TYPE) -interface TextRenderer { +interface TextFormat { /** * The priority of this renderer compared to other renderers */ @@ -53,19 +55,18 @@ interface TextRenderer { */ val type: KClass<*> - suspend fun kotlinx.io.Output.render(obj: Any) + suspend fun Output.render(obj: Any) companion object { const val TEXT_RENDERER_TYPE = "dataforge.textRenderer" } } -object DefaultTextRenderer : TextRenderer { +object DefaultTextFormat : TextFormat { override val priority: Int = Int.MAX_VALUE override val type: KClass<*> = Any::class - override suspend fun kotlinx.io.Output.render(obj: Any) { - append(obj.toString()) - append('\n') + override suspend fun Output.render(obj: Any) { + writeUtf8String(obj.toString() + "\n") } } \ No newline at end of file diff --git a/dataforge-output/src/jsMain/kotlin/hep/dataforge/output/ConsoleOutput.kt b/dataforge-output/src/jsMain/kotlin/hep/dataforge/output/ConsoleOutput.kt deleted file mode 100644 index b927a386..00000000 --- a/dataforge-output/src/jsMain/kotlin/hep/dataforge/output/ConsoleOutput.kt +++ /dev/null @@ -1,22 +0,0 @@ -package hep.dataforge.output - -import hep.dataforge.context.Context -import hep.dataforge.context.Global -import hep.dataforge.meta.Meta -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.Dispatchers - -/** - * System console output. - * The [ConsoleOutput] is used when no other [OutputManager] is provided. - */ -actual val ConsoleOutput: Output = object : Output { - override fun render(obj: Any, meta: Meta) { - println(obj) - } - - override val context: Context get() = Global - -} - -actual val Dispatchers.Output: CoroutineDispatcher get() = Dispatchers.Default \ No newline at end of file diff --git a/dataforge-output/src/jsMain/kotlin/hep/dataforge/output/outputJS.kt b/dataforge-output/src/jsMain/kotlin/hep/dataforge/output/outputJS.kt new file mode 100644 index 00000000..18a71f07 --- /dev/null +++ b/dataforge-output/src/jsMain/kotlin/hep/dataforge/output/outputJS.kt @@ -0,0 +1,7 @@ +package hep.dataforge.output + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers + + +actual val Dispatchers.Output: CoroutineDispatcher get() = Dispatchers.Default \ No newline at end of file diff --git a/dataforge-output/src/jvmMain/kotlin/hep/dataforge/output/ConsoleOutput.kt b/dataforge-output/src/jvmMain/kotlin/hep/dataforge/output/ConsoleOutput.kt deleted file mode 100644 index 57ae4294..00000000 --- a/dataforge-output/src/jvmMain/kotlin/hep/dataforge/output/ConsoleOutput.kt +++ /dev/null @@ -1,13 +0,0 @@ -package hep.dataforge.output - -import hep.dataforge.context.Global -import kotlinx.coroutines.Dispatchers -import kotlinx.io.streams.asOutput - -/** - * System console output. - * The [ConsoleOutput] is used when no other [OutputManager] is provided. - */ -actual val ConsoleOutput: Output = TextOutput(Global, System.out.asOutput()) - -actual val Dispatchers.Output get() = Dispatchers.IO \ No newline at end of file diff --git a/dataforge-output/src/jvmMain/kotlin/hep/dataforge/output/outputJVM.kt b/dataforge-output/src/jvmMain/kotlin/hep/dataforge/output/outputJVM.kt new file mode 100644 index 00000000..ea7c416c --- /dev/null +++ b/dataforge-output/src/jvmMain/kotlin/hep/dataforge/output/outputJVM.kt @@ -0,0 +1,5 @@ +package hep.dataforge.output + +import kotlinx.coroutines.Dispatchers + +actual val Dispatchers.Output get() = Dispatchers.IO \ No newline at end of file diff --git a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/envelopeData.kt b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/envelopeData.kt index 1b3a27cd..1a713e37 100644 --- a/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/envelopeData.kt +++ b/dataforge-workspace/src/commonMain/kotlin/hep/dataforge/workspace/envelopeData.kt @@ -7,8 +7,7 @@ import hep.dataforge.io.IOFormat import hep.dataforge.io.SimpleEnvelope import hep.dataforge.io.readWith import kotlinx.coroutines.coroutineScope -import kotlinx.io.Input -import kotlinx.io.buildPacket +import kotlinx.io.ArrayBinary import kotlin.reflect.KClass /** @@ -22,15 +21,8 @@ suspend fun Data.toEnvelope(format: IOFormat): Envelope { val obj = coroutineScope { await(this) } - val binary = object : Binary { - override fun read(block: Input.() -> R): R { - //TODO optimize away additional copy by creating inputs that reads directly from output - val packet = buildPacket { - format.run { writeObject(obj) } - } - return packet.block() - } - + val binary = ArrayBinary.write { + format.run { writeObject(obj) } } return SimpleEnvelope(meta, binary) } \ No newline at end of file diff --git a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt index 8d8bc385..4fc9e9a4 100644 --- a/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt +++ b/dataforge-workspace/src/jvmTest/kotlin/hep/dataforge/workspace/FileDataTest.kt @@ -8,8 +8,8 @@ import hep.dataforge.meta.DFExperimental import kotlinx.coroutines.runBlocking import kotlinx.io.Input import kotlinx.io.Output -import kotlinx.io.readText -import kotlinx.io.writeText +import kotlinx.io.text.readUtf8String +import kotlinx.io.text.writeUtf8String import java.nio.file.Files import kotlin.test.Ignore import kotlin.test.Test @@ -30,11 +30,11 @@ class FileDataTest { object StringIOFormat : IOFormat { override fun Output.writeObject(obj: String) { - writeText(obj) + writeUtf8String(obj) } override fun Input.readObject(): String { - return readText() + return readUtf8String() } }