Root parsing complete

This commit is contained in:
Alexander Nozik 2021-08-24 16:04:21 +03:00
parent 64e084dc53
commit e39f79e4ab
7 changed files with 143 additions and 96 deletions

View File

@ -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)
}
}

View File

@ -7,12 +7,14 @@ import kotlinx.serialization.Serializable
@Serializable
@SerialName("TGeoNode")
public open class TGeoNode : TNamed() {
//val fGeoAtt: UInt
public val fGeoAtt: UInt = 0u
@Contextual
public val fVolume: TGeoVolume? = null
@Contextual
public val fMother: TGeoVolume? = null
// @Contextual
// public val fMother: TGeoVolume? = null
public val fNumber: Int = 0
public val fNovlp: Int = 0
public val fOverlaps: IntArray = intArrayOf()

View File

@ -127,3 +127,7 @@ public class TGeoShapeAssembly(
public val fVolume: TGeoVolumeAssembly,
public val fBBoxOK: Boolean = true
) : TGeoBBox()
public class TGeoShapeRef(provider: () -> TGeoShape) : TGeoShape() {
public val value: TGeoShape by lazy(provider)
}

View File

@ -7,29 +7,35 @@ import kotlinx.serialization.Serializable
@Serializable
@SerialName("TGeoVolume")
public open class TGeoVolume : TNamed() {
// "fGeoAtt" : 3084,
// "fLineColor" : 3,
// "fLineStyle" : 1,
// "fLineWidth" : 1,
// "fFillColor" : 19,
// "fFillStyle" : 1001,
@Contextual
public lateinit var fNodes: TObjArray
internal set
public val fGeoAtt: UInt = 0u
public val fLineColor: Int = 2
public val fLineStyle: Int? = null
public val fLineWidth: UInt = 1u
public val fFillColor: Int? = null
public val fFillStyle: Int? = null
@Contextual
public lateinit var fShape: TGeoShape
internal set
public val fNodes: TObjArray? = null
@Contextual
public lateinit var fMedium: TGeoMedium
internal set
public val fShape: TGeoShape? = null
@Contextual
public val fMedium: TGeoMedium? = null
public val fNumber: Int = 1
public val fNtotal: Int = 1
public val fRefCount: Int = 1
}
public class TGeoVolumeRef(provider: () -> TGeoVolume) : TGeoVolume() {
public val value: TGeoVolume by lazy(provider)
}
@Serializable
@SerialName("TGeoVolumeAssembly")
public class TGeoVolumeAssembly : TGeoVolume()
public open class TGeoVolumeAssembly : TGeoVolume()
public class TGeoVolumeAssemblyRef(provider: () -> TGeoVolumeAssembly) : TGeoVolumeAssembly() {
public val value: TGeoVolumeAssembly by lazy(provider)
}

View File

@ -1,5 +1,6 @@
package ru.mipt.npm.root
import kotlinx.serialization.Contextual
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@ -15,9 +16,10 @@ public open class TNamed : TObject() {
public val fTitle: String = ""
}
@Serializable
@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 val empty: TObjArray = TObjArray(emptyList())
}
@ -25,8 +27,8 @@ public class TObjArray(public val arr: List<TObject>): TObject() {
@Serializable
@SerialName("TList")
public class TList(public val arr: List<TObject>): TObject()
public class TList(public val arr: List<@Contextual TObject>): TObject()
@Serializable
@SerialName("THashList")
public class THashList(public val arr: List<TObject>): TObject()
public class THashList(public val arr: List<@Contextual TObject>): TObject()

View File

@ -1,12 +1,10 @@
package ru.mipt.npm.root
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.KSerializer
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.json.*
import kotlinx.serialization.modules.*
import kotlin.reflect.KClass
/**
@ -24,8 +22,8 @@ private object RootDecoder {
private class RootUnrefSerializer<T>(
private val tSerializer: KSerializer<T>,
private val refCache: MutableList<RefEntry>,// = ArrayList<RefEntry>(4096)
private val counter: ReferenceCounter
private val refCache: List<RefEntry>,// = ArrayList<RefEntry>(4096)
//private val counter: ReferenceCounter
) : KSerializer<T> by tSerializer {
override fun deserialize(decoder: Decoder): T {
@ -33,67 +31,74 @@ private object RootDecoder {
val element = input.decodeJsonElement()
val refId = (element as? JsonObject)?.get("\$ref")?.jsonPrimitive?.int
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
refCache[refId]
else -> refCache[refId]
}
} else {
refCache[counter.value].also {
counter.increment()
refCache.find { it.element == element } ?: error("Element '$element' not found in the cache")
}
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> =
RootUnrefSerializer(this, refCache, counter)
private fun <T> KSerializer<T>.unref(refCache: List<RefEntry>): KSerializer<T> =
RootUnrefSerializer(this, refCache)
@OptIn(ExperimentalSerializationApi::class)
fun unrefSerializersModule(
refCache: MutableList<RefEntry>, counter: ReferenceCounter
refCache: List<RefEntry>
): SerializersModule = SerializersModule {
val collector = this
val unrefCollector = object : SerializersModuleCollector {
include(serializersModule)
override fun <T : Any> contextual(
kClass: KClass<T>,
provider: (typeArgumentsSerializers: List<KSerializer<*>>) -> KSerializer<*>
) {
collector.contextual(kClass) { provider(it).unref(refCache, counter) }
contextual(TGeoManager.serializer().unref(refCache))
contextual(TObjArray.serializer().unref(refCache))
contextual(TGeoVolumeAssembly.serializer().unref(refCache))
contextual(TGeoShapeAssembly.serializer().unref(refCache))
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(
baseClass: KClass<Base>,
actualClass: KClass<Sub>,
actualSerializer: KSerializer<Sub>
) {
collector.polymorphic(baseClass, actualClass, actualSerializer.unref(refCache, counter))
polymorphicDefault(TGeoMatrix::class) {
TGeoMatrix.serializer().unref(refCache)
}
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.
*/
private fun unrefJson(refCache: MutableList<RefEntry>, counter: ReferenceCounter): Json = Json {
private fun unrefJson(refCache: MutableList<RefEntry>): Json = Json {
encodeDefaults = true
ignoreUnknownKeys = true
classDiscriminator = "_typename"
serializersModule = unrefSerializersModule(refCache, counter)
serializersModule = unrefSerializersModule(refCache)
}
fun decode(sourceDeserializer: KSerializer<out TObject>, source: JsonElement): TObject {
val counter = ReferenceCounter()
val refCache = ArrayList<RefEntry>()
fun fillCache(element: JsonElement) {
@ -112,34 +117,35 @@ private object RootDecoder {
}
}
else -> {
//ignore primitives
}
}
}
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) {
fun increment() {
value += 1
// class ReferenceCounter(var value: Int = 0) {
// fun increment() {
// 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()
}
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()
override fun toString(): String = element.toString()
}
private fun PolymorphicModuleBuilder<TGeoShape>.shapes() {
@ -166,27 +172,9 @@ private object RootDecoder {
}
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) {
default { JsonRootSerializer }
subclass(TObjArray.serializer())
shapes()
@ -202,6 +190,19 @@ private object RootDecoder {
subclass(TGeoNodeMatrix.serializer())
subclass(TGeoVolume.serializer())
subclass(TGeoVolumeAssembly.serializer())
subclass(TGeoManager.serializer())
}
polymorphic(TGeoShape::class) {
shapes()
}
polymorphic(TGeoMatrix::class) {
matrices()
}
polymorphic(TGeoBoolNode::class) {
boolNodes()
}
polymorphic(TGeoNode::class, TGeoNode.serializer()) {

View File

@ -1,6 +1,8 @@
package ru.mipt.npm.root
import kotlinx.serialization.json.*
import java.time.Duration
import kotlin.system.measureTimeMillis
private fun JsonElement.countTypes(): Sequence<String> = sequence {
val json = this@countTypes
@ -27,5 +29,9 @@ fun main() {
println(it)
}
val time = measureTimeMillis {
val geo = TObject.decodeFromString(TGeoManager.serializer(), string)
}
println(Duration.ofMillis(time))
}