refs #35 serialize any: null, int, real, boolean, Instant. Added unary minus as general operator, not only to numbers. Instant truncation (nice for serialization)
This commit is contained in:
1 parent
cffe4eaffc
commit
6ab438b1f6
14 files changed
+208
-51
No files matched your search
@@ -82,5 +82,9 @@ interface BitInput {
|
||||
fun decompressStringOrNull(): String? = decompressOrNull()?.decodeToString()
|
||||
|
||||
fun decompressString(): String = decompress().decodeToString()
|
||||
fun unpackDouble(): Double {
|
||||
val bits = getBits(64)
|
||||
return Double.fromBits(bits.toLong())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +1,57 @@
|
||||
package net.sergeych.lynon
|
||||
|
||||
import kotlinx.datetime.Instant
|
||||
import net.sergeych.lyng.Scope
|
||||
import net.sergeych.lyng.obj.Obj
|
||||
import net.sergeych.lyng.obj.ObjClass
|
||||
import net.sergeych.lyng.obj.ObjInt
|
||||
import net.sergeych.lyng.obj.ObjNull
|
||||
import net.sergeych.lyng.obj.*
|
||||
|
||||
open class LynonDecoder(val bin: BitInput,val settings: LynonSettings = LynonSettings.default) {
|
||||
open class LynonDecoder(val bin: BitInput, val settings: LynonSettings = LynonSettings.default) {
|
||||
|
||||
val cache = mutableListOf<Obj>()
|
||||
|
||||
inline fun decodeCached(f: LynonDecoder.() -> Obj): Obj {
|
||||
return if( bin.getBit() == 0 ) {
|
||||
return if (bin.getBit() == 0) {
|
||||
// unpack and cache
|
||||
f().also {
|
||||
if( settings.shouldCache(it) ) cache.add(it)
|
||||
if (settings.shouldCache(it)) cache.add(it)
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// get cache reference
|
||||
val size = sizeInBits(cache.size)
|
||||
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}")
|
||||
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]
|
||||
}
|
||||
}
|
||||
|
||||
fun decodeAny(scope: Scope): Obj = decodeCached {
|
||||
val type = LynonType.entries[bin.getBits(4).toInt()]
|
||||
return when(type) {
|
||||
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")
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package net.sergeych.lynon
|
||||
|
||||
import net.sergeych.lyng.Scope
|
||||
import net.sergeych.lyng.obj.Obj
|
||||
import net.sergeych.lyng.obj.ObjInt
|
||||
import net.sergeych.lyng.obj.ObjNull
|
||||
import net.sergeych.lyng.obj.*
|
||||
|
||||
enum class LynonType {
|
||||
Null,
|
||||
@@ -69,6 +67,29 @@ open class LynonEncoder(val bout: BitOutput,val settings: LynonSettings = LynonS
|
||||
}
|
||||
}
|
||||
}
|
||||
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()
|
||||
}
|
||||
|
||||
@@ -6,7 +6,12 @@ import net.sergeych.lyng.obj.ObjInt
|
||||
import net.sergeych.lyng.obj.ObjNull
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
open class LynonSettings() {
|
||||
open class LynonSettings {
|
||||
enum class InstantTruncateMode {
|
||||
Second,
|
||||
Millisecond,
|
||||
Microsecond
|
||||
}
|
||||
|
||||
open fun shouldCache(obj: Any): Boolean = when (obj) {
|
||||
is ObjChar -> false
|
||||
|
||||
@@ -52,8 +52,10 @@ class BitArray(val bytes: UByteArray, val lastByteBits: Int) : BitList {
|
||||
return result.toString()
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
fun asByteArray(): ByteArray = bytes.asByteArray()
|
||||
|
||||
@Suppress("unused")
|
||||
fun asUbyteArray(): UByteArray = bytes
|
||||
|
||||
companion object {
|
||||
@@ -82,10 +84,10 @@ class BitArray(val bytes: UByteArray, val lastByteBits: Int) : BitList {
|
||||
* added by [putBit] will be stored in the bit 0x01 of the first byte, the second bit
|
||||
* in the bit 0x02 of the first byte, etc.
|
||||
*
|
||||
* This allow automatic fill of the last byte with zeros. This is important when
|
||||
* This allows automatic fill of the last byte with zeros. This is important when
|
||||
* using bytes stored from [asByteArray] or [asUbyteArray]. When converting to
|
||||
* bytes, automatic padding to byte size is applied. With such bit order, constrinting
|
||||
* [BitInput] to read from [asByteArray] result only provides 0 to 7 extra zeroes bits
|
||||
* bytes, automatic padding to byte size is applied. With such bit order, constructing
|
||||
* [BitInput] to read from [ByteArray.toUByteArray] result only provides 0 to 7 extra zeroes bits
|
||||
* at teh end which is often acceptable. To avoid this, use [toBitArray]; the [BitArray]
|
||||
* stores exact number of bits and [BitArray.toBitInput] provides [BitInput] that
|
||||
* decodes exactly same bits.
|
||||
|
||||
Reference in new issue
Block a user