feature/root #69
@ -0,0 +1,26 @@
|
|||||||
|
package ru.mipt.npm.root
|
||||||
|
|
||||||
|
import kotlinx.serialization.KSerializer
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||||
|
import kotlinx.serialization.encoding.Decoder
|
||||||
|
import kotlinx.serialization.encoding.Encoder
|
||||||
|
import kotlinx.serialization.json.JsonElement
|
||||||
|
|
||||||
|
@Serializable(JsonRootSerializer::class)
|
||||||
|
public class JsonRootObject(public val element: JsonElement): TObject()
|
||||||
|
|
||||||
|
public object JsonRootSerializer: KSerializer<JsonRootObject>{
|
||||||
|
private val jsonElementSerializer = JsonElement.serializer()
|
||||||
|
|
||||||
|
override val descriptor: SerialDescriptor
|
||||||
|
get() = jsonElementSerializer.descriptor
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): JsonRootObject {
|
||||||
|
return JsonRootObject(decoder.decodeSerializableValue(jsonElementSerializer))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: JsonRootObject) {
|
||||||
|
encoder.encodeSerializableValue(jsonElementSerializer, value.element)
|
||||||
|
}
|
||||||
|
}
|
@ -7,12 +7,14 @@ import kotlinx.serialization.Serializable
|
|||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("TGeoNode")
|
@SerialName("TGeoNode")
|
||||||
public open class TGeoNode : TNamed() {
|
public open class TGeoNode : TNamed() {
|
||||||
//val fGeoAtt: UInt
|
public val fGeoAtt: UInt = 0u
|
||||||
|
|
||||||
@Contextual
|
@Contextual
|
||||||
public val fVolume: TGeoVolume? = null
|
public val fVolume: TGeoVolume? = null
|
||||||
|
|
||||||
@Contextual
|
// @Contextual
|
||||||
public val fMother: TGeoVolume? = null
|
// public val fMother: TGeoVolume? = null
|
||||||
|
|
||||||
public val fNumber: Int = 0
|
public val fNumber: Int = 0
|
||||||
public val fNovlp: Int = 0
|
public val fNovlp: Int = 0
|
||||||
public val fOverlaps: IntArray = intArrayOf()
|
public val fOverlaps: IntArray = intArrayOf()
|
||||||
|
@ -127,3 +127,7 @@ public class TGeoShapeAssembly(
|
|||||||
public val fVolume: TGeoVolumeAssembly,
|
public val fVolume: TGeoVolumeAssembly,
|
||||||
public val fBBoxOK: Boolean = true
|
public val fBBoxOK: Boolean = true
|
||||||
) : TGeoBBox()
|
) : TGeoBBox()
|
||||||
|
|
||||||
|
public class TGeoShapeRef(provider: () -> TGeoShape) : TGeoShape() {
|
||||||
|
public val value: TGeoShape by lazy(provider)
|
||||||
|
}
|
@ -7,29 +7,35 @@ import kotlinx.serialization.Serializable
|
|||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("TGeoVolume")
|
@SerialName("TGeoVolume")
|
||||||
public open class TGeoVolume : TNamed() {
|
public open class TGeoVolume : TNamed() {
|
||||||
// "fGeoAtt" : 3084,
|
public val fGeoAtt: UInt = 0u
|
||||||
// "fLineColor" : 3,
|
public val fLineColor: Int = 2
|
||||||
// "fLineStyle" : 1,
|
public val fLineStyle: Int? = null
|
||||||
// "fLineWidth" : 1,
|
public val fLineWidth: UInt = 1u
|
||||||
// "fFillColor" : 19,
|
public val fFillColor: Int? = null
|
||||||
// "fFillStyle" : 1001,
|
public val fFillStyle: Int? = null
|
||||||
@Contextual
|
|
||||||
public lateinit var fNodes: TObjArray
|
|
||||||
internal set
|
|
||||||
|
|
||||||
@Contextual
|
@Contextual
|
||||||
public lateinit var fShape: TGeoShape
|
public val fNodes: TObjArray? = null
|
||||||
internal set
|
|
||||||
|
|
||||||
@Contextual
|
@Contextual
|
||||||
public lateinit var fMedium: TGeoMedium
|
public val fShape: TGeoShape? = null
|
||||||
internal set
|
|
||||||
|
@Contextual
|
||||||
|
public val fMedium: TGeoMedium? = null
|
||||||
|
|
||||||
public val fNumber: Int = 1
|
public val fNumber: Int = 1
|
||||||
public val fNtotal: Int = 1
|
public val fNtotal: Int = 1
|
||||||
public val fRefCount: Int = 1
|
public val fRefCount: Int = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class TGeoVolumeRef(provider: () -> TGeoVolume) : TGeoVolume() {
|
||||||
|
public val value: TGeoVolume by lazy(provider)
|
||||||
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("TGeoVolumeAssembly")
|
@SerialName("TGeoVolumeAssembly")
|
||||||
public class TGeoVolumeAssembly : TGeoVolume()
|
public open class TGeoVolumeAssembly : TGeoVolume()
|
||||||
|
|
||||||
|
public class TGeoVolumeAssemblyRef(provider: () -> TGeoVolumeAssembly) : TGeoVolumeAssembly() {
|
||||||
|
public val value: TGeoVolumeAssembly by lazy(provider)
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package ru.mipt.npm.root
|
package ru.mipt.npm.root
|
||||||
|
|
||||||
|
import kotlinx.serialization.Contextual
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@ -15,9 +16,10 @@ public open class TNamed : TObject() {
|
|||||||
public val fTitle: String = ""
|
public val fTitle: String = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("TObjArray")
|
@SerialName("TObjArray")
|
||||||
public class TObjArray(public val arr: List<TObject>): TObject() {
|
public class TObjArray(public val arr: List<@Contextual TObject>): TObject() {
|
||||||
public companion object{
|
public companion object{
|
||||||
public val empty: TObjArray = TObjArray(emptyList())
|
public val empty: TObjArray = TObjArray(emptyList())
|
||||||
}
|
}
|
||||||
@ -25,8 +27,8 @@ public class TObjArray(public val arr: List<TObject>): TObject() {
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("TList")
|
@SerialName("TList")
|
||||||
public class TList(public val arr: List<TObject>): TObject()
|
public class TList(public val arr: List<@Contextual TObject>): TObject()
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("THashList")
|
@SerialName("THashList")
|
||||||
public class THashList(public val arr: List<TObject>): TObject()
|
public class THashList(public val arr: List<@Contextual TObject>): TObject()
|
@ -1,12 +1,10 @@
|
|||||||
package ru.mipt.npm.root
|
package ru.mipt.npm.root
|
||||||
|
|
||||||
import kotlinx.serialization.DeserializationStrategy
|
|
||||||
import kotlinx.serialization.ExperimentalSerializationApi
|
import kotlinx.serialization.ExperimentalSerializationApi
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
||||||
import kotlinx.serialization.encoding.Decoder
|
import kotlinx.serialization.encoding.Decoder
|
||||||
import kotlinx.serialization.json.*
|
import kotlinx.serialization.json.*
|
||||||
import kotlinx.serialization.modules.*
|
import kotlinx.serialization.modules.*
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -24,8 +22,8 @@ private object RootDecoder {
|
|||||||
|
|
||||||
private class RootUnrefSerializer<T>(
|
private class RootUnrefSerializer<T>(
|
||||||
private val tSerializer: KSerializer<T>,
|
private val tSerializer: KSerializer<T>,
|
||||||
private val refCache: MutableList<RefEntry>,// = ArrayList<RefEntry>(4096)
|
private val refCache: List<RefEntry>,// = ArrayList<RefEntry>(4096)
|
||||||
private val counter: ReferenceCounter
|
//private val counter: ReferenceCounter
|
||||||
) : KSerializer<T> by tSerializer {
|
) : KSerializer<T> by tSerializer {
|
||||||
|
|
||||||
override fun deserialize(decoder: Decoder): T {
|
override fun deserialize(decoder: Decoder): T {
|
||||||
@ -33,67 +31,74 @@ private object RootDecoder {
|
|||||||
val element = input.decodeJsonElement()
|
val element = input.decodeJsonElement()
|
||||||
val refId = (element as? JsonObject)?.get("\$ref")?.jsonPrimitive?.int
|
val refId = (element as? JsonObject)?.get("\$ref")?.jsonPrimitive?.int
|
||||||
val ref = if (refId != null) {
|
val ref = if (refId != null) {
|
||||||
|
//println("Substituting ref $refId")
|
||||||
|
//Forward ref for shapes
|
||||||
|
when (tSerializer.descriptor.serialName) {
|
||||||
|
"TGeoShape" -> return TGeoShapeRef{
|
||||||
|
//Should be not null at the moment of actualization
|
||||||
|
refCache[refId].value as TGeoShape
|
||||||
|
} as T
|
||||||
|
"TGeoVolumeAssembly" -> return TGeoVolumeAssemblyRef{
|
||||||
|
//Should be not null at the moment of actualization
|
||||||
|
refCache[refId].value as TGeoVolumeAssembly
|
||||||
|
} as T
|
||||||
//Do unref
|
//Do unref
|
||||||
refCache[refId]
|
else -> refCache[refId]
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
refCache[counter.value].also {
|
refCache.find { it.element == element } ?: error("Element '$element' not found in the cache")
|
||||||
counter.increment()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ref.getOrPutValue {
|
||||||
|
// val actualTypeName = it.jsonObject["_typename"]?.jsonPrimitive?.content
|
||||||
|
input.json.decodeFromJsonElement(tSerializer, it)
|
||||||
}
|
}
|
||||||
return ref.value(tSerializer) as T //TODO research means to make it safe
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <T> KSerializer<T>.unref(refCache: MutableList<RefEntry>, counter: ReferenceCounter): KSerializer<T> =
|
private fun <T> KSerializer<T>.unref(refCache: List<RefEntry>): KSerializer<T> =
|
||||||
RootUnrefSerializer(this, refCache, counter)
|
RootUnrefSerializer(this, refCache)
|
||||||
|
|
||||||
@OptIn(ExperimentalSerializationApi::class)
|
@OptIn(ExperimentalSerializationApi::class)
|
||||||
fun unrefSerializersModule(
|
fun unrefSerializersModule(
|
||||||
refCache: MutableList<RefEntry>, counter: ReferenceCounter
|
refCache: List<RefEntry>
|
||||||
): SerializersModule = SerializersModule {
|
): SerializersModule = SerializersModule {
|
||||||
val collector = this
|
include(serializersModule)
|
||||||
val unrefCollector = object : SerializersModuleCollector {
|
|
||||||
|
|
||||||
override fun <T : Any> contextual(
|
contextual(TGeoManager.serializer().unref(refCache))
|
||||||
kClass: KClass<T>,
|
contextual(TObjArray.serializer().unref(refCache))
|
||||||
provider: (typeArgumentsSerializers: List<KSerializer<*>>) -> KSerializer<*>
|
contextual(TGeoVolumeAssembly.serializer().unref(refCache))
|
||||||
) {
|
contextual(TGeoShapeAssembly.serializer().unref(refCache))
|
||||||
collector.contextual(kClass) { provider(it).unref(refCache, counter) }
|
contextual(TGeoRotation.serializer().unref(refCache))
|
||||||
|
contextual(TGeoMedium.serializer().unref(refCache))
|
||||||
|
contextual(TGeoVolume.serializer().unref(refCache))
|
||||||
|
contextual(TGeoMatrix.serializer().unref(refCache))
|
||||||
|
contextual(TGeoNodeMatrix.serializer().unref(refCache))
|
||||||
|
contextual(TGeoShape.serializer().unref(refCache))
|
||||||
|
contextual(TObject.serializer().unref(refCache))
|
||||||
|
|
||||||
|
|
||||||
|
polymorphicDefault(TGeoShape::class) {
|
||||||
|
TGeoShape.serializer().unref(refCache)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun <Base : Any, Sub : Base> polymorphic(
|
polymorphicDefault(TGeoMatrix::class) {
|
||||||
baseClass: KClass<Base>,
|
TGeoMatrix.serializer().unref(refCache)
|
||||||
actualClass: KClass<Sub>,
|
|
||||||
actualSerializer: KSerializer<Sub>
|
|
||||||
) {
|
|
||||||
collector.polymorphic(baseClass, actualClass, actualSerializer.unref(refCache, counter))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun <Base : Any> polymorphicDefault(
|
|
||||||
baseClass: KClass<Base>,
|
|
||||||
defaultSerializerProvider: (className: String?) -> DeserializationStrategy<out Base>?
|
|
||||||
) {
|
|
||||||
collector.polymorphicDefault(baseClass) {
|
|
||||||
(defaultSerializerProvider(it) as KSerializer<out Base>).unref(refCache, counter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
serializersModule.dumpTo(unrefCollector)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an instance of Json with unfolding Root references. This instance could not be reused because of the cache.
|
* Create an instance of Json with unfolding Root references. This instance could not be reused because of the cache.
|
||||||
*/
|
*/
|
||||||
private fun unrefJson(refCache: MutableList<RefEntry>, counter: ReferenceCounter): Json = Json {
|
private fun unrefJson(refCache: MutableList<RefEntry>): Json = Json {
|
||||||
encodeDefaults = true
|
encodeDefaults = true
|
||||||
ignoreUnknownKeys = true
|
ignoreUnknownKeys = true
|
||||||
classDiscriminator = "_typename"
|
classDiscriminator = "_typename"
|
||||||
serializersModule = unrefSerializersModule(refCache, counter)
|
serializersModule = unrefSerializersModule(refCache)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun decode(sourceDeserializer: KSerializer<out TObject>, source: JsonElement): TObject {
|
fun decode(sourceDeserializer: KSerializer<out TObject>, source: JsonElement): TObject {
|
||||||
val counter = ReferenceCounter()
|
|
||||||
val refCache = ArrayList<RefEntry>()
|
val refCache = ArrayList<RefEntry>()
|
||||||
|
|
||||||
fun fillCache(element: JsonElement) {
|
fun fillCache(element: JsonElement) {
|
||||||
@ -112,34 +117,35 @@ private object RootDecoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
|
//ignore primitives
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fillCache(source)
|
fillCache(source)
|
||||||
|
|
||||||
return unrefJson(refCache, counter).decodeFromJsonElement(sourceDeserializer.unref(refCache, counter), source)
|
return unrefJson(refCache).decodeFromJsonElement(sourceDeserializer.unref(refCache), source)
|
||||||
}
|
}
|
||||||
|
|
||||||
class ReferenceCounter(var value: Int = 0) {
|
// class ReferenceCounter(var value: Int = 0) {
|
||||||
fun increment() {
|
// fun increment() {
|
||||||
value += 1
|
// value += 1
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// override fun toString(): String = value.toString()
|
||||||
|
// }
|
||||||
|
|
||||||
|
class RefEntry(val element: JsonElement) {
|
||||||
|
|
||||||
|
var value: Any? = null
|
||||||
|
|
||||||
|
fun <T> getOrPutValue(builder: (JsonElement) -> T): T {
|
||||||
|
if (value == null) {
|
||||||
|
value = builder(element)
|
||||||
|
}
|
||||||
|
return value as T
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String = value.toString()
|
override fun toString(): String = element.toString()
|
||||||
}
|
|
||||||
|
|
||||||
class RefEntry(val obj: JsonObject) {
|
|
||||||
|
|
||||||
private var cachedValue: Any? = null
|
|
||||||
|
|
||||||
fun value(serializer: KSerializer<*>): Any {
|
|
||||||
if (cachedValue == null) {
|
|
||||||
cachedValue = json.decodeFromJsonElement(serializer, obj)
|
|
||||||
}
|
|
||||||
return cachedValue!!
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String = obj.toString()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun PolymorphicModuleBuilder<TGeoShape>.shapes() {
|
private fun PolymorphicModuleBuilder<TGeoShape>.shapes() {
|
||||||
@ -166,27 +172,9 @@ private object RootDecoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val serializersModule = SerializersModule {
|
private val serializersModule = SerializersModule {
|
||||||
contextual(TGeoManager::class) { TGeoManager.serializer() }
|
|
||||||
contextual(TObjArray::class) { TObjArray.serializer() }
|
|
||||||
contextual(TGeoVolumeAssembly::class) { TGeoVolumeAssembly.serializer() }
|
|
||||||
contextual(TGeoShapeAssembly::class) { TGeoShapeAssembly.serializer() }
|
|
||||||
contextual(TGeoRotation::class) { TGeoRotation.serializer() }
|
|
||||||
contextual(TGeoMedium::class) { TGeoMedium.serializer() }
|
|
||||||
|
|
||||||
polymorphic(TGeoShape::class) {
|
|
||||||
default { TGeoBBox.serializer() }
|
|
||||||
shapes()
|
|
||||||
}
|
|
||||||
|
|
||||||
polymorphic(TGeoMatrix::class) {
|
|
||||||
matrices()
|
|
||||||
}
|
|
||||||
|
|
||||||
polymorphic(TGeoBoolNode::class) {
|
|
||||||
boolNodes()
|
|
||||||
}
|
|
||||||
|
|
||||||
polymorphic(TObject::class) {
|
polymorphic(TObject::class) {
|
||||||
|
default { JsonRootSerializer }
|
||||||
subclass(TObjArray.serializer())
|
subclass(TObjArray.serializer())
|
||||||
|
|
||||||
shapes()
|
shapes()
|
||||||
@ -202,6 +190,19 @@ private object RootDecoder {
|
|||||||
subclass(TGeoNodeMatrix.serializer())
|
subclass(TGeoNodeMatrix.serializer())
|
||||||
subclass(TGeoVolume.serializer())
|
subclass(TGeoVolume.serializer())
|
||||||
subclass(TGeoVolumeAssembly.serializer())
|
subclass(TGeoVolumeAssembly.serializer())
|
||||||
|
subclass(TGeoManager.serializer())
|
||||||
|
}
|
||||||
|
|
||||||
|
polymorphic(TGeoShape::class) {
|
||||||
|
shapes()
|
||||||
|
}
|
||||||
|
|
||||||
|
polymorphic(TGeoMatrix::class) {
|
||||||
|
matrices()
|
||||||
|
}
|
||||||
|
|
||||||
|
polymorphic(TGeoBoolNode::class) {
|
||||||
|
boolNodes()
|
||||||
}
|
}
|
||||||
|
|
||||||
polymorphic(TGeoNode::class, TGeoNode.serializer()) {
|
polymorphic(TGeoNode::class, TGeoNode.serializer()) {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package ru.mipt.npm.root
|
package ru.mipt.npm.root
|
||||||
|
|
||||||
import kotlinx.serialization.json.*
|
import kotlinx.serialization.json.*
|
||||||
|
import java.time.Duration
|
||||||
|
import kotlin.system.measureTimeMillis
|
||||||
|
|
||||||
private fun JsonElement.countTypes(): Sequence<String> = sequence {
|
private fun JsonElement.countTypes(): Sequence<String> = sequence {
|
||||||
val json = this@countTypes
|
val json = this@countTypes
|
||||||
@ -27,5 +29,9 @@ fun main() {
|
|||||||
println(it)
|
println(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val time = measureTimeMillis {
|
||||||
val geo = TObject.decodeFromString(TGeoManager.serializer(), string)
|
val geo = TObject.decodeFromString(TGeoManager.serializer(), string)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println(Duration.ofMillis(time))
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user