0.2.0 #71
@ -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
|
||||
@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()
|
||||
|
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
@ -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()
|
@ -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()) {
|
||||
|
@ -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))
|
||||
}
|
Loading…
Reference in New Issue
Block a user