Fix memory leak in sealed builder. Fine-grained Meta builders.
This commit is contained in:
parent
cbbcd18df3
commit
851fdda311
@ -7,11 +7,11 @@ import kotlin.test.assertEquals
|
||||
|
||||
|
||||
fun Meta.toByteArray(format: MetaFormat = JsonMetaFormat) = ByteArray {
|
||||
format.writeObject(this@ByteArray, this@toByteArray)
|
||||
format.writeTo(this@ByteArray, this@toByteArray)
|
||||
}
|
||||
|
||||
fun MetaFormat.fromByteArray(packet: ByteArray): Meta {
|
||||
return packet.asBinary().read { readObject(this) }
|
||||
return packet.asBinary().read { readFrom(this) }
|
||||
}
|
||||
|
||||
class MetaFormatTest {
|
||||
|
@ -4,8 +4,8 @@ import kotlinx.io.buffered
|
||||
|
||||
|
||||
fun <T : Any> IOFormat<T>.writeToByteArray(obj: T): ByteArray = ByteArray {
|
||||
writeObject(this, obj)
|
||||
writeTo(this, obj)
|
||||
}
|
||||
fun <T : Any> IOFormat<T>.readFromByteArray(array: ByteArray): T = ByteArraySource(array).buffered().use {
|
||||
readObject(it)
|
||||
readFrom(it)
|
||||
}
|
@ -50,7 +50,9 @@ public interface Meta : MetaRepr, MetaProvider {
|
||||
override fun toMeta(): Meta = this
|
||||
|
||||
override fun toString(): String
|
||||
|
||||
override fun equals(other: Any?): Boolean
|
||||
|
||||
override fun hashCode(): Int
|
||||
|
||||
public companion object {
|
||||
|
@ -11,7 +11,7 @@ import kotlin.js.JsName
|
||||
* Mark a meta builder
|
||||
*/
|
||||
@DslMarker
|
||||
public annotation class MetaBuilder
|
||||
public annotation class MetaBuilderMarker
|
||||
|
||||
/**
|
||||
* A generic interface that gives access to getting and setting meta notes and values
|
||||
@ -27,7 +27,7 @@ public interface MutableMetaProvider : MetaProvider, MutableValueProvider {
|
||||
* TODO documentation
|
||||
*/
|
||||
@Serializable(MutableMetaSerializer::class)
|
||||
@MetaBuilder
|
||||
@MetaBuilderMarker
|
||||
public interface MutableMeta : Meta, MutableMetaProvider {
|
||||
|
||||
override val items: Map<NameToken, MutableMeta>
|
||||
@ -90,8 +90,8 @@ public interface MutableMeta : Meta, MutableMetaProvider {
|
||||
setMeta(this, repr.toMeta())
|
||||
}
|
||||
|
||||
public infix fun Name.put(mutableMeta: MutableMeta.() -> Unit) {
|
||||
setMeta(this, Meta(mutableMeta))
|
||||
public infix fun Name.put(builder: MutableMeta.() -> Unit) {
|
||||
getOrCreate(this).apply(builder)
|
||||
}
|
||||
|
||||
public infix fun String.put(meta: Meta) {
|
||||
@ -131,7 +131,7 @@ public interface MutableMeta : Meta, MutableMetaProvider {
|
||||
}
|
||||
|
||||
public infix fun String.put(builder: MutableMeta.() -> Unit) {
|
||||
setMeta(Name.parse(this), MutableMeta(builder))
|
||||
getOrCreate(parseAsName()).apply(builder)
|
||||
}
|
||||
}
|
||||
|
||||
@ -381,16 +381,14 @@ public fun Meta.toMutableMeta(): ObservableMutableMeta = MutableMetaImpl(value,
|
||||
|
||||
public fun Meta.asMutableMeta(): MutableMeta = (this as? MutableMeta) ?: toMutableMeta()
|
||||
|
||||
@Suppress("FunctionName")
|
||||
@JsName("newMutableMeta")
|
||||
public fun MutableMeta(): ObservableMutableMeta = MutableMetaImpl(null)
|
||||
@JsName("newObservableMutableMeta")
|
||||
public fun ObservableMutableMeta(): ObservableMutableMeta = MutableMetaImpl(null)
|
||||
|
||||
/**
|
||||
* Build a [MutableMeta] using given transformation
|
||||
*/
|
||||
@Suppress("FunctionName")
|
||||
public inline fun MutableMeta(builder: MutableMeta.() -> Unit = {}): ObservableMutableMeta =
|
||||
MutableMeta().apply(builder)
|
||||
public inline fun ObservableMutableMeta(builder: MutableMeta.() -> Unit = {}): ObservableMutableMeta =
|
||||
ObservableMutableMeta().apply(builder)
|
||||
|
||||
|
||||
/**
|
||||
|
@ -51,6 +51,8 @@ private class ObservableMetaWrapper(
|
||||
|
||||
override fun setMeta(name: Name, node: Meta?) {
|
||||
val oldMeta = get(name)
|
||||
//don't forget to remove listener
|
||||
oldMeta?.removeListener(this)
|
||||
root.setMeta(absoluteName + name, node)
|
||||
if (oldMeta != node) {
|
||||
invalidate(name)
|
||||
|
@ -1,16 +1,17 @@
|
||||
package space.kscience.dataforge.meta
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import space.kscience.dataforge.names.NameToken
|
||||
import space.kscience.dataforge.names.*
|
||||
import kotlin.js.JsName
|
||||
|
||||
/**
|
||||
* The meta implementation which is guaranteed to be immutable.
|
||||
*
|
||||
*/
|
||||
@Serializable
|
||||
public class SealedMeta internal constructor(
|
||||
public class SealedMeta(
|
||||
override val value: Value?,
|
||||
override val items: Map<NameToken, SealedMeta>
|
||||
override val items: Map<NameToken, SealedMeta>,
|
||||
) : TypedMeta<SealedMeta> {
|
||||
override fun toString(): String = Meta.toString(this)
|
||||
override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta)
|
||||
@ -26,7 +27,7 @@ public class SealedMeta internal constructor(
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate sealed node from [this]. If it is already sealed return it as is.
|
||||
* Generate sealed node from [this]. If it is already sealed, return it as is.
|
||||
*/
|
||||
public fun Meta.seal(): SealedMeta = this as? SealedMeta ?: SealedMeta(
|
||||
value,
|
||||
@ -47,7 +48,79 @@ public fun Meta(value: String): SealedMeta = Meta(value.asValue())
|
||||
@Suppress("FunctionName")
|
||||
public fun Meta(value: Boolean): SealedMeta = Meta(value.asValue())
|
||||
|
||||
@Suppress("FunctionName")
|
||||
public inline fun Meta(builder: MutableMeta.() -> Unit): SealedMeta =
|
||||
MutableMeta(builder).seal()
|
||||
|
||||
/**
|
||||
* A lightweight mutable meta used to create [SealedMeta] instances without bothering with
|
||||
*/
|
||||
@PublishedApi
|
||||
internal class MetaBuilder(
|
||||
override var value: Value? = null,
|
||||
override val items: MutableMap<NameToken, MetaBuilder> = hashMapOf(),
|
||||
) : MutableMeta {
|
||||
|
||||
override fun getOrCreate(name: Name): MetaBuilder {
|
||||
val existing = get(name) as? MetaBuilder
|
||||
return if (existing == null) {
|
||||
val newItem = MetaBuilder()
|
||||
setMeta(name, newItem)
|
||||
newItem
|
||||
} else {
|
||||
existing
|
||||
}
|
||||
}
|
||||
|
||||
private fun wrap(meta: Meta): MetaBuilder = meta as? MetaBuilder ?: MetaBuilder(
|
||||
meta.value,
|
||||
meta.items.mapValuesTo(hashMapOf()) { wrap(it.value) }
|
||||
)
|
||||
|
||||
|
||||
override fun setMeta(name: Name, node: Meta?) {
|
||||
when (name.length) {
|
||||
0 -> error("Can't set a meta with empty name")
|
||||
1 -> {
|
||||
val token = name.first()
|
||||
//remove child and invalidate if argument is null
|
||||
if (node == null) {
|
||||
items.remove(token)
|
||||
} else {
|
||||
items[token] = wrap(node)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
getOrCreate(name.first().asName()).setMeta(name.cutFirst(), node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String = Meta.toString(this)
|
||||
|
||||
override fun equals(other: Any?): Boolean = Meta.equals(this, other as? Meta)
|
||||
|
||||
override fun hashCode(): Int = Meta.hashCode(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a read-only meta.
|
||||
*/
|
||||
public inline fun Meta(builder: MutableMeta.() -> Unit): Meta =
|
||||
MetaBuilder().apply(builder).seal()
|
||||
|
||||
/**
|
||||
* Create an immutable meta.
|
||||
*/
|
||||
public inline fun SealedMeta(builder: MutableMeta.() -> Unit): SealedMeta =
|
||||
MetaBuilder().apply(builder).seal()
|
||||
|
||||
/**
|
||||
* Create an empty meta mutable meta.
|
||||
*/
|
||||
@JsName("newMutableMeta")
|
||||
public fun MutableMeta(): MutableMeta = MetaBuilder()
|
||||
|
||||
/**
|
||||
* Create a mutable meta with given builder.
|
||||
*/
|
||||
public inline fun MutableMeta(builder: MutableMeta.() -> Unit = {}): MutableMeta =
|
||||
MutableMeta().apply(builder)
|
||||
|
@ -105,7 +105,7 @@ public value class MetaTransformation(private val transformations: Collection<Tr
|
||||
* Generate an observable configuration that contains only elements defined by transformation rules and changes with the source
|
||||
*/
|
||||
@DFExperimental
|
||||
public fun generate(source: ObservableMeta): ObservableMeta = MutableMeta().apply {
|
||||
public fun generate(source: ObservableMeta): ObservableMeta = ObservableMutableMeta{
|
||||
transformations.forEach { rule ->
|
||||
rule.selectItems(source).forEach { name ->
|
||||
rule.transformItem(name, source[name], this)
|
||||
|
@ -1,15 +0,0 @@
|
||||
package space.kscience.dataforge.meta
|
||||
|
||||
import org.junit.jupiter.api.Test
|
||||
import kotlin.test.assertFails
|
||||
|
||||
class JvmMutableMetaTest {
|
||||
@Test
|
||||
fun recursiveMeta(){
|
||||
val meta = MutableMeta {
|
||||
"a" put 2
|
||||
}
|
||||
|
||||
assertFails { meta["child.a"] = meta }
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user