refs #35 serializatino framework refactored: implementation put in open methods of Obj/ObjClass for flexibility
This commit is contained in:
1 parent
6ab438b1f6
commit
a9f65bdbe3
20 files changed
+340
-203
No files matched your search
@@ -1,14 +1,23 @@
|
||||
package net.sergeych.lynon
|
||||
|
||||
import kotlinx.datetime.Instant
|
||||
import net.sergeych.lyng.Scope
|
||||
import net.sergeych.lyng.obj.*
|
||||
import net.sergeych.lyng.obj.Obj
|
||||
import net.sergeych.lyng.obj.ObjClass
|
||||
|
||||
open class LynonDecoder(val bin: BitInput, val settings: LynonSettings = LynonSettings.default) {
|
||||
|
||||
val cache = mutableListOf<Obj>()
|
||||
fun getBitsAsInt(bitsSize: Int): Int {
|
||||
return bin.getBits(bitsSize).toInt()
|
||||
}
|
||||
|
||||
inline fun decodeCached(f: LynonDecoder.() -> Obj): Obj {
|
||||
fun unpackUnsignedInt(): Int = bin.unpackUnsigned().toInt()
|
||||
|
||||
fun decompress() = bin.decompress()
|
||||
|
||||
val cache = mutableListOf<Any>()
|
||||
|
||||
|
||||
inline fun <T : Any>decodeCached(f: LynonDecoder.() -> T): T {
|
||||
return if (bin.getBit() == 0) {
|
||||
// unpack and cache
|
||||
f().also {
|
||||
@@ -20,46 +29,19 @@ open class LynonDecoder(val bin: BitInput, val settings: LynonSettings = LynonSe
|
||||
val id = bin.getBitsOrNull(size)?.toInt()
|
||||
?: throw RuntimeException("Invalid object id: unexpected end of stream")
|
||||
if (id >= cache.size) throw RuntimeException("Invalid object id: $id should be in 0..<${cache.size}")
|
||||
cache[id]
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
cache[id] as T
|
||||
}
|
||||
}
|
||||
|
||||
fun decodeAny(scope: Scope): Obj = decodeCached {
|
||||
suspend fun decodeAny(scope: Scope): Obj = decodeCached {
|
||||
val type = LynonType.entries[bin.getBits(4).toInt()]
|
||||
return when (type) {
|
||||
LynonType.Null -> ObjNull
|
||||
LynonType.Int0 -> ObjInt.Zero
|
||||
LynonType.IntPositive -> ObjInt(bin.unpackUnsigned().toLong())
|
||||
LynonType.IntNegative -> ObjInt(-bin.unpackUnsigned().toLong())
|
||||
LynonType.Bool -> ObjBool(bin.getBit() == 1)
|
||||
LynonType.Real -> ObjReal(bin.unpackDouble())
|
||||
LynonType.Instant -> {
|
||||
val mode = LynonSettings.InstantTruncateMode.entries[bin.getBits(2).toInt()]
|
||||
when (mode) {
|
||||
LynonSettings.InstantTruncateMode.Microsecond -> ObjInstant(
|
||||
Instant.fromEpochSeconds(
|
||||
bin.unpackSigned(), bin.unpackUnsigned().toInt() * 1000
|
||||
)
|
||||
)
|
||||
LynonSettings.InstantTruncateMode.Millisecond -> ObjInstant(
|
||||
Instant.fromEpochMilliseconds(
|
||||
bin.unpackSigned()
|
||||
)
|
||||
)
|
||||
LynonSettings.InstantTruncateMode.Second -> ObjInstant(
|
||||
Instant.fromEpochSeconds(bin.unpackSigned())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
scope.raiseNotImplemented("lynon type $type")
|
||||
}
|
||||
}
|
||||
type.objClass.deserialize(scope, this, type)
|
||||
}
|
||||
|
||||
fun unpackObject(scope: Scope, type: ObjClass): Obj {
|
||||
return decodeCached { type.deserialize(scope, this) }
|
||||
// todo: rewrite/remove?
|
||||
suspend fun unpackObject(scope: Scope, type: ObjClass): Obj {
|
||||
return decodeCached { type.deserialize(scope, this, null) }
|
||||
}
|
||||
|
||||
fun unpackBinaryData(): ByteArray = bin.decompress()
|
||||
@@ -79,4 +61,8 @@ open class LynonDecoder(val bin: BitInput, val settings: LynonSettings = LynonSe
|
||||
return bin.unpackSigned()
|
||||
}
|
||||
|
||||
fun unpackUnsigned(): ULong {
|
||||
return bin.unpackUnsigned()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,30 +1,31 @@
|
||||
package net.sergeych.lynon
|
||||
|
||||
import net.sergeych.bintools.ByteChunk
|
||||
import net.sergeych.lyng.Scope
|
||||
import net.sergeych.lyng.obj.*
|
||||
|
||||
enum class LynonType {
|
||||
Null,
|
||||
Int0,
|
||||
IntNegative,
|
||||
IntPositive,
|
||||
String,
|
||||
Real,
|
||||
Bool,
|
||||
List,
|
||||
Map,
|
||||
Set,
|
||||
Buffer,
|
||||
Instant,
|
||||
Duration,
|
||||
Other;
|
||||
enum class LynonType(val objClass: ObjClass) {
|
||||
Null(ObjNull.objClass),
|
||||
Int0(ObjInt.type),
|
||||
IntNegative(ObjInt.type),
|
||||
IntPositive(ObjInt.type),
|
||||
String(ObjString.type),
|
||||
Real(ObjReal.type),
|
||||
Bool(ObjBool.type),
|
||||
List(ObjList.type),
|
||||
Map(ObjMap.type),
|
||||
Set(ObjSet.type),
|
||||
Buffer(ObjBuffer.type),
|
||||
Instant(ObjInstant.type),
|
||||
Duration(ObjDuration.type),
|
||||
Other(Obj.rootObjectType);
|
||||
}
|
||||
|
||||
open class LynonEncoder(val bout: BitOutput,val settings: LynonSettings = LynonSettings.default) {
|
||||
|
||||
val cache = mutableMapOf<Any, Int>()
|
||||
|
||||
private suspend fun encodeCached(item: Any, packer: suspend LynonEncoder.() -> Unit) {
|
||||
suspend fun encodeCached(item: Any, packer: suspend LynonEncoder.() -> Unit) {
|
||||
|
||||
suspend fun serializeAndCache(key: Any=item) {
|
||||
bout.putBit(0)
|
||||
@@ -40,7 +41,8 @@ open class LynonEncoder(val bout: BitOutput,val settings: LynonSettings = LynonS
|
||||
bout.putBits(cacheId.toULong(), size)
|
||||
} ?: serializeAndCache()
|
||||
|
||||
is ByteArray, is UByteArray -> serializeAndCache()
|
||||
is ByteArray -> serializeAndCache(ByteChunk(item.asUByteArray()))
|
||||
is UByteArray -> serializeAndCache(ByteChunk(item))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,48 +54,9 @@ open class LynonEncoder(val bout: BitOutput,val settings: LynonSettings = LynonS
|
||||
*/
|
||||
suspend fun encodeAny(scope: Scope,value: Obj) {
|
||||
encodeCached(value) {
|
||||
when(value) {
|
||||
is ObjNull -> putType(LynonType.Null)
|
||||
is ObjInt -> {
|
||||
when {
|
||||
value.value == 0L -> putType(LynonType.Int0)
|
||||
value.value < 0 -> {
|
||||
putType(LynonType.IntNegative)
|
||||
encodeUnsigned((-value.value).toULong())
|
||||
}
|
||||
else -> {
|
||||
putType(LynonType.IntPositive)
|
||||
encodeUnsigned(value.value.toULong())
|
||||
}
|
||||
}
|
||||
}
|
||||
is ObjBool -> {
|
||||
putType(LynonType.Bool)
|
||||
encodeBoolean(value.value)
|
||||
}
|
||||
is ObjReal -> {
|
||||
putType(LynonType.Real)
|
||||
encodeReal(value.value)
|
||||
}
|
||||
is ObjInstant -> {
|
||||
putType(LynonType.Instant)
|
||||
bout.putBits(value.truncateMode.ordinal, 2)
|
||||
// todo: favor truncation mode from ObjInstant
|
||||
when(value.truncateMode) {
|
||||
LynonSettings.InstantTruncateMode.Millisecond ->
|
||||
encodeSigned(value.instant.toEpochMilliseconds())
|
||||
LynonSettings.InstantTruncateMode.Second ->
|
||||
encodeSigned(value.instant.epochSeconds)
|
||||
LynonSettings.InstantTruncateMode.Microsecond -> {
|
||||
encodeSigned(value.instant.epochSeconds)
|
||||
encodeUnsigned(value.instant.nanosecondsOfSecond.toULong() / 1000UL)
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
TODO()
|
||||
}
|
||||
}
|
||||
val type = value.lynonType()
|
||||
putType(type)
|
||||
value.serialize(scope, this, type)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +66,7 @@ open class LynonEncoder(val bout: BitOutput,val settings: LynonSettings = LynonS
|
||||
|
||||
suspend fun encodeObj(scope: Scope, obj: Obj) {
|
||||
encodeCached(obj) {
|
||||
obj.serialize(scope, this)
|
||||
obj.serialize(scope, this, null)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,4 +96,12 @@ open class LynonEncoder(val bout: BitOutput,val settings: LynonSettings = LynonS
|
||||
bout.putBit(if (value) 1 else 0)
|
||||
}
|
||||
|
||||
fun putBits(value: Int, sizeInBits: Int) {
|
||||
bout.putBits(value.toULong(), sizeInBits)
|
||||
}
|
||||
|
||||
fun putBit(bit: Int) {
|
||||
bout.putBit(bit)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package net.sergeych.lynon
|
||||
|
||||
import net.sergeych.lyng.Scope
|
||||
import net.sergeych.lyng.obj.Obj
|
||||
import net.sergeych.lyng.obj.ObjBuffer
|
||||
import net.sergeych.lyng.obj.ObjBitBuffer
|
||||
import net.sergeych.lyng.obj.ObjClass
|
||||
import net.sergeych.lyng.obj.ObjString
|
||||
|
||||
@@ -15,11 +15,12 @@ val ObjLynonClass = object : ObjClass("Lynon") {
|
||||
val bout = MemoryBitOutput()
|
||||
val serializer = LynonEncoder(bout)
|
||||
serializer.encodeAny(this, obj)
|
||||
return ObjBuffer(bout.toBitArray().bytes)
|
||||
return ObjBitBuffer(bout.toBitArray())
|
||||
}
|
||||
|
||||
suspend fun Scope.decodeAny(buffer: ObjBuffer): Obj {
|
||||
val bin = BitArray(buffer.byteArray,8).toInput()
|
||||
suspend fun Scope.decodeAny(source: Obj): Obj {
|
||||
if( source !is ObjBitBuffer) throw Exception("Invalid source: $source")
|
||||
val bin = source.bitArray.toInput()
|
||||
val deserializer = LynonDecoder(bin)
|
||||
return deserializer.decodeAny(this)
|
||||
}
|
||||
@@ -30,6 +31,6 @@ val ObjLynonClass = object : ObjClass("Lynon") {
|
||||
encodeAny(requireOnlyArg<Obj>())
|
||||
}
|
||||
addClassFn("decode") {
|
||||
decodeAny(requireOnlyArg<ObjBuffer>())
|
||||
decodeAny(requireOnlyArg<Obj>())
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user