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

View File

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

View File

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

View File

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

View File

@ -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()) {

View File

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