Data and DataNode file io update
This commit is contained in:
parent
6c1c49d15e
commit
71536051aa
@ -1,9 +1,9 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("scientifik.mpp") version "0.1.7-dev" apply false
|
id("scientifik.mpp") version "0.1.7" apply false
|
||||||
id("scientifik.publish") version "0.1.7-dev" apply false
|
id("scientifik.publish") version "0.1.7" apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
val dataforgeVersion by extra("0.1.4-dev-2")
|
val dataforgeVersion by extra("0.1.4-dev-3")
|
||||||
|
|
||||||
val bintrayRepo by extra("dataforge")
|
val bintrayRepo by extra("dataforge")
|
||||||
val githubProject by extra("dataforge-core")
|
val githubProject by extra("dataforge-core")
|
||||||
|
@ -140,7 +140,7 @@ private sealed class DataTreeBuilderItem<out T : Any> {
|
|||||||
/**
|
/**
|
||||||
* A builder for a DataTree.
|
* A builder for a DataTree.
|
||||||
*/
|
*/
|
||||||
class DataTreeBuilder<T : Any>(private val type: KClass<out T>) {
|
class DataTreeBuilder<T : Any>(val type: KClass<out T>) {
|
||||||
private val map = HashMap<NameToken, DataTreeBuilderItem<T>>()
|
private val map = HashMap<NameToken, DataTreeBuilderItem<T>>()
|
||||||
|
|
||||||
operator fun set(token: NameToken, node: DataTreeBuilder<out T>) {
|
operator fun set(token: NameToken, node: DataTreeBuilder<out T>) {
|
||||||
|
@ -5,8 +5,8 @@ plugins {
|
|||||||
description = "IO module"
|
description = "IO module"
|
||||||
|
|
||||||
scientifik{
|
scientifik{
|
||||||
serialization = true
|
withSerialization()
|
||||||
io = true
|
withIO()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ interface Envelope {
|
|||||||
/**
|
/**
|
||||||
* Build a static envelope using provided builder
|
* Build a static envelope using provided builder
|
||||||
*/
|
*/
|
||||||
fun build(block: EnvelopeBuilder.() -> Unit) = EnvelopeBuilder().apply(block).build()
|
operator fun invoke(block: EnvelopeBuilder.() -> Unit) = EnvelopeBuilder().apply(block).build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,6 +97,7 @@ class EnvelopeBuilder {
|
|||||||
|
|
||||||
var type by metaBuilder.string(key = Envelope.ENVELOPE_TYPE_KEY)
|
var type by metaBuilder.string(key = Envelope.ENVELOPE_TYPE_KEY)
|
||||||
var dataType by metaBuilder.string(key = Envelope.ENVELOPE_DATA_TYPE_KEY)
|
var dataType by metaBuilder.string(key = Envelope.ENVELOPE_DATA_TYPE_KEY)
|
||||||
|
var dataID by metaBuilder.string(key = Envelope.ENVELOPE_DATA_ID_KEY)
|
||||||
var description by metaBuilder.string(key = Envelope.ENVELOPE_DESCRIPTION_KEY)
|
var description by metaBuilder.string(key = Envelope.ENVELOPE_DESCRIPTION_KEY)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -12,7 +12,7 @@ class RemoteFunctionClient(override val context: Context, val responder: Respond
|
|||||||
private fun <T : Any> IOPlugin.encodeOne(
|
private fun <T : Any> IOPlugin.encodeOne(
|
||||||
meta: Meta,
|
meta: Meta,
|
||||||
value: T
|
value: T
|
||||||
): Envelope = Envelope.build {
|
): Envelope = Envelope.invoke {
|
||||||
meta(meta)
|
meta(meta)
|
||||||
type = REQUEST_TYPE
|
type = REQUEST_TYPE
|
||||||
data {
|
data {
|
||||||
@ -26,7 +26,7 @@ class RemoteFunctionClient(override val context: Context, val responder: Respond
|
|||||||
private fun <T : Any> IOPlugin.encodeMany(
|
private fun <T : Any> IOPlugin.encodeMany(
|
||||||
meta: Meta,
|
meta: Meta,
|
||||||
values: List<T>
|
values: List<T>
|
||||||
): Envelope = Envelope.build {
|
): Envelope = Envelope.invoke {
|
||||||
meta(meta)
|
meta(meta)
|
||||||
type = REQUEST_TYPE
|
type = REQUEST_TYPE
|
||||||
meta {
|
meta {
|
||||||
|
@ -40,7 +40,7 @@ class RemoteFunctionServer(
|
|||||||
input
|
input
|
||||||
)
|
)
|
||||||
|
|
||||||
return Envelope.build {
|
return Envelope.invoke {
|
||||||
meta {
|
meta {
|
||||||
meta(request.meta)
|
meta(request.meta)
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import kotlin.test.assertEquals
|
|||||||
|
|
||||||
|
|
||||||
class EnvelopeFormatTest {
|
class EnvelopeFormatTest {
|
||||||
val envelope = Envelope.build {
|
val envelope = Envelope.invoke {
|
||||||
type = "test.format"
|
type = "test.format"
|
||||||
meta{
|
meta{
|
||||||
"d" to 22.2
|
"d" to 22.2
|
||||||
|
@ -20,4 +20,6 @@ class FileBinary(val path: Path, private val offset: UInt = 0u, size: ULong? = n
|
|||||||
return ByteReadPacket(buffer).block()
|
return ByteReadPacket(buffer).block()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Path.asBinary(offset: UInt = 0u, size: ULong? = null): FileBinary = FileBinary(this, offset, size)
|
@ -22,7 +22,7 @@ class FileEnvelope internal constructor(val path: Path, val format: EnvelopeForm
|
|||||||
override val data: Binary? = FileBinary(path, partialEnvelope.dataOffset, partialEnvelope.dataSize)
|
override val data: Binary? = FileBinary(path, partialEnvelope.dataOffset, partialEnvelope.dataSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Path.readEnvelope(format: EnvelopeFormat) = FileEnvelope(this, format)
|
fun Path.readEnvelope(format: EnvelopeFormat = TaggedEnvelopeFormat) = FileEnvelope(this, format)
|
||||||
|
|
||||||
fun Path.writeEnvelope(
|
fun Path.writeEnvelope(
|
||||||
envelope: Envelope,
|
envelope: Envelope,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package hep.dataforge.io
|
package hep.dataforge.io
|
||||||
|
|
||||||
|
import hep.dataforge.descriptors.NodeDescriptor
|
||||||
import hep.dataforge.io.functions.FunctionServer
|
import hep.dataforge.io.functions.FunctionServer
|
||||||
import hep.dataforge.io.functions.FunctionServer.Companion.FUNCTION_NAME_KEY
|
import hep.dataforge.io.functions.FunctionServer.Companion.FUNCTION_NAME_KEY
|
||||||
import hep.dataforge.io.functions.FunctionServer.Companion.INPUT_FORMAT_KEY
|
import hep.dataforge.io.functions.FunctionServer.Companion.INPUT_FORMAT_KEY
|
||||||
@ -7,6 +8,10 @@ import hep.dataforge.io.functions.FunctionServer.Companion.OUTPUT_FORMAT_KEY
|
|||||||
import hep.dataforge.meta.Meta
|
import hep.dataforge.meta.Meta
|
||||||
import hep.dataforge.meta.buildMeta
|
import hep.dataforge.meta.buildMeta
|
||||||
import hep.dataforge.names.Name
|
import hep.dataforge.names.Name
|
||||||
|
import kotlinx.io.nio.asOutput
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.nio.file.StandardOpenOption
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.reflect.full.isSuperclassOf
|
import kotlin.reflect.full.isSuperclassOf
|
||||||
|
|
||||||
@ -31,4 +36,15 @@ inline fun <reified T : Any, reified R : Any> FunctionServer.function(
|
|||||||
val plugin = context.plugins.get<IOPlugin>() ?: error("IO plugin not loaded")
|
val plugin = context.plugins.get<IOPlugin>() ?: error("IO plugin not loaded")
|
||||||
val meta = plugin.generateFunctionMeta<T, R>(functionName)
|
val meta = plugin.generateFunctionMeta<T, R>(functionName)
|
||||||
return function(meta)
|
return function(meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write meta to file in a given [format]
|
||||||
|
*/
|
||||||
|
fun Meta.write(path: Path, format: MetaFormat, descriptor: NodeDescriptor? = null) {
|
||||||
|
format.run {
|
||||||
|
Files.newByteChannel(path, StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW)
|
||||||
|
.asOutput()
|
||||||
|
.writeMeta(this@write, descriptor)
|
||||||
|
}
|
||||||
}
|
}
|
@ -34,7 +34,7 @@ class EnvelopeClient(
|
|||||||
suspend fun close() {
|
suspend fun close() {
|
||||||
try {
|
try {
|
||||||
respond(
|
respond(
|
||||||
Envelope.build {
|
Envelope.invoke {
|
||||||
type = EnvelopeServer.SHUTDOWN_ENVELOPE_TYPE
|
type = EnvelopeServer.SHUTDOWN_ENVELOPE_TYPE
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
package hep.dataforge.io
|
||||||
|
|
||||||
|
import java.nio.file.Files
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
|
||||||
|
class FileEnvelopeTest {
|
||||||
|
val envelope = Envelope {
|
||||||
|
meta {
|
||||||
|
"a" to "AAA"
|
||||||
|
"b" to 22.2
|
||||||
|
}
|
||||||
|
dataType = "hep.dataforge.test"
|
||||||
|
dataID = "myData" // добавил только что
|
||||||
|
data {
|
||||||
|
writeDouble(16.7)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testFileWriteRead() {
|
||||||
|
val tmpPath = Files.createTempFile("dataforge_test", ".df")
|
||||||
|
tmpPath.writeEnvelope(envelope)
|
||||||
|
println(tmpPath.toUri())
|
||||||
|
val restored: Envelope = tmpPath.readEnvelope()
|
||||||
|
assertTrue { envelope.contentEquals(restored) }
|
||||||
|
}
|
||||||
|
}
|
@ -46,7 +46,7 @@ class EnvelopeServerTest {
|
|||||||
@Test
|
@Test
|
||||||
fun doEchoTest() {
|
fun doEchoTest() {
|
||||||
|
|
||||||
val request = Envelope.build {
|
val request = Envelope.invoke {
|
||||||
type = "test.echo"
|
type = "test.echo"
|
||||||
meta {
|
meta {
|
||||||
"test.value" to 22
|
"test.value" to 22
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
package hep.dataforge.workspace
|
package hep.dataforge.workspace
|
||||||
|
|
||||||
import hep.dataforge.data.Data
|
import hep.dataforge.data.Data
|
||||||
|
import hep.dataforge.data.DataNode
|
||||||
|
import hep.dataforge.data.DataTreeBuilder
|
||||||
|
import hep.dataforge.data.datum
|
||||||
import hep.dataforge.descriptors.NodeDescriptor
|
import hep.dataforge.descriptors.NodeDescriptor
|
||||||
import hep.dataforge.io.*
|
import hep.dataforge.io.*
|
||||||
import hep.dataforge.meta.EmptyMeta
|
import hep.dataforge.meta.EmptyMeta
|
||||||
import hep.dataforge.meta.Meta
|
import hep.dataforge.meta.Meta
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.coroutineScope
|
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.io.nio.asInput
|
import kotlinx.io.nio.asInput
|
||||||
import kotlinx.io.nio.asOutput
|
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.StandardOpenOption
|
import java.nio.file.StandardOpenOption
|
||||||
@ -18,69 +19,75 @@ import kotlin.reflect.KClass
|
|||||||
/**
|
/**
|
||||||
* Read meta from file in a given [format]
|
* Read meta from file in a given [format]
|
||||||
*/
|
*/
|
||||||
suspend fun Path.readMeta(format: MetaFormat, descriptor: NodeDescriptor? = null): Meta {
|
fun Path.readMeta(format: MetaFormat, descriptor: NodeDescriptor? = null): Meta {
|
||||||
return withContext(Dispatchers.IO) {
|
return format.run {
|
||||||
format.run {
|
Files.newByteChannel(this@readMeta, StandardOpenOption.READ)
|
||||||
Files.newByteChannel(this@readMeta, StandardOpenOption.READ)
|
.asInput()
|
||||||
.asInput()
|
.readMeta(descriptor)
|
||||||
.readMeta(descriptor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write meta to file in a given [format]
|
|
||||||
*/
|
|
||||||
suspend fun Meta.write(path: Path, format: MetaFormat, descriptor: NodeDescriptor? = null) {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
format.run {
|
|
||||||
Files.newByteChannel(path, StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW)
|
|
||||||
.asOutput()
|
|
||||||
.writeMeta(this@write, descriptor)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read data with supported envelope format and binary format. If envelope format is null, then read binary directly from file.
|
* Read data with supported envelope format and binary format. If envelope format is null, then read binary directly from file.
|
||||||
|
* The operation is blocking since it must read meta header. The reading of envelope body is lazy
|
||||||
* @param type explicit type of data read
|
* @param type explicit type of data read
|
||||||
* @param format binary format
|
* @param format binary format
|
||||||
* @param envelopeFormat the format of envelope. If null, file is read directly
|
* @param envelopeFormat the format of envelope. If null, file is read directly
|
||||||
* @param metaFile the relative file for optional meta override
|
* @param metaFile the relative file for optional meta override
|
||||||
* @param metaFileFormat the meta format for override
|
* @param metaFileFormat the meta format for override
|
||||||
*/
|
*/
|
||||||
suspend fun <T : Any> Path.readData(
|
fun <T : Any> Path.readData(
|
||||||
type: KClass<out T>,
|
type: KClass<out T>,
|
||||||
format: IOFormat<T>,
|
format: IOFormat<T>,
|
||||||
envelopeFormat: EnvelopeFormat? = null,
|
envelopeFormat: EnvelopeFormat? = null,
|
||||||
metaFile: Path = resolveSibling("$fileName.meta"),
|
metaFile: Path = resolveSibling("$fileName.meta"),
|
||||||
metaFileFormat: MetaFormat = JsonMetaFormat
|
metaFileFormat: MetaFormat = JsonMetaFormat
|
||||||
): Data<T> {
|
): Data<T> {
|
||||||
return coroutineScope {
|
val externalMeta = if (Files.exists(metaFile)) {
|
||||||
val externalMeta = if (Files.exists(metaFile)) {
|
metaFile.readMeta(metaFileFormat)
|
||||||
metaFile.readMeta(metaFileFormat)
|
} else {
|
||||||
} else {
|
null
|
||||||
null
|
}
|
||||||
}
|
return if (envelopeFormat == null) {
|
||||||
if (envelopeFormat == null) {
|
Data(type, externalMeta ?: EmptyMeta) {
|
||||||
Data(type, externalMeta ?: EmptyMeta) {
|
withContext(Dispatchers.IO) {
|
||||||
withContext(Dispatchers.IO) {
|
format.run {
|
||||||
format.run {
|
Files.newByteChannel(this@readData, StandardOpenOption.READ)
|
||||||
Files.newByteChannel(this@readData, StandardOpenOption.READ)
|
.asInput()
|
||||||
.asInput()
|
.readThis()
|
||||||
.readThis()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
withContext(Dispatchers.IO) {
|
} else {
|
||||||
readEnvelope(envelopeFormat).let {
|
readEnvelope(envelopeFormat).let {
|
||||||
if (externalMeta == null) {
|
if (externalMeta == null) {
|
||||||
it
|
it
|
||||||
} else {
|
} else {
|
||||||
it.withMetaLayers(externalMeta)
|
it.withMetaLayers(externalMeta)
|
||||||
}
|
}
|
||||||
}.toData(type, format)
|
}.toData(type, format)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T : Any> DataTreeBuilder<T>.file(path: Path, format: IOFormat<T>, envelopeFormat: EnvelopeFormat? = null) {
|
||||||
|
val data = path.readData(type, format, envelopeFormat)
|
||||||
|
val name = path.fileName.toString().replace(".df", "")
|
||||||
|
datum(name, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the directory as a data node
|
||||||
|
*/
|
||||||
|
fun <T : Any> Path.readDataNode(
|
||||||
|
type: KClass<out T>,
|
||||||
|
format: IOFormat<T>,
|
||||||
|
envelopeFormat: EnvelopeFormat? = null
|
||||||
|
): DataNode<T> {
|
||||||
|
if (!Files.isDirectory(this)) error("Provided path $this is not a directory")
|
||||||
|
return DataNode(type) {
|
||||||
|
Files.list(this@readDataNode).forEach { path ->
|
||||||
|
if (!path.fileName.toString().endsWith(".meta")) {
|
||||||
|
file(path, format, envelopeFormat)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user