refs #35 minimal serialization of simple types in bit-effective format
This commit is contained in:
1 parent
d969993997
commit
f26ee7cd7c
44 files changed
+537
-49
No files matched your search
@@ -55,7 +55,7 @@ abstract class BitInput {
|
||||
val tetrades = getBits(4).toInt()
|
||||
var result = 0UL
|
||||
var shift = 0
|
||||
for (i in 0..<tetrades) {
|
||||
for (i in 0.. tetrades) {
|
||||
result = result or (getBits(4) shl shift)
|
||||
shift += 4
|
||||
}
|
||||
@@ -67,5 +67,18 @@ abstract class BitInput {
|
||||
val value = unpackUnsigned().toLong()
|
||||
return if( isNegative == 1) -value else value
|
||||
}
|
||||
|
||||
fun getBool(): Boolean {
|
||||
return getBit() == 1
|
||||
}
|
||||
|
||||
fun getBytes(count: Int): ByteArray? {
|
||||
val result = ByteArray(count)
|
||||
for (i in 0..<count) {
|
||||
val b = getBitsOrNull(8) ?: return null
|
||||
result[i] = b.toByte()
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ abstract class BitOutput {
|
||||
|
||||
fun packUnsigned(value: ULong) {
|
||||
val tetrades = sizeInTetrades(value)
|
||||
putBits(tetrades, 4)
|
||||
putBits(tetrades - 1, 4)
|
||||
var rest = value
|
||||
for( i in 0..<tetrades ) {
|
||||
putBits( rest and 0xFu, 4 )
|
||||
@@ -67,4 +67,11 @@ abstract class BitOutput {
|
||||
isClosed = true
|
||||
}
|
||||
}
|
||||
|
||||
fun putBytes(data: ByteArray) {
|
||||
for( b in data ) {
|
||||
putBits(b.toULong(), 8)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package net.sergeych.lynon
|
||||
|
||||
import net.sergeych.lyng.Scope
|
||||
import net.sergeych.lyng.obj.Obj
|
||||
import net.sergeych.lyng.obj.ObjClass
|
||||
|
||||
open class LynonDecoder(private val bin: BitInput) {
|
||||
|
||||
val cache = mutableListOf<Obj>()
|
||||
|
||||
fun unpackObject(scope: Scope, type: ObjClass): Obj {
|
||||
return if( bin.getBit() == 0 ) {
|
||||
// unpack and cache
|
||||
val cached = bin.getBool()
|
||||
type.deserialize(scope, this).also {
|
||||
if( cached ) cache.add(it)
|
||||
}
|
||||
}
|
||||
else {
|
||||
// get cache reference
|
||||
val size = sizeInBits(cache.size)
|
||||
val id = bin.getBitsOrNull(size)?.toInt() ?: scope.raiseError("Invalid object id: unexpected end of stream")
|
||||
if( id >= cache.size ) scope.raiseError("Invalid object id: $id should be in 0..<${cache.size}")
|
||||
cache[id]
|
||||
}
|
||||
}
|
||||
|
||||
fun unpackBinaryData(): ByteArray? {
|
||||
val size = bin.unpackUnsigned()
|
||||
return bin.getBytes(size.toInt())
|
||||
}
|
||||
|
||||
fun unpackBoolean(): Boolean {
|
||||
return bin.getBit() == 1
|
||||
}
|
||||
|
||||
fun unpackDouble(): Double {
|
||||
return Double.fromBits(bin.getBits(64).toLong())
|
||||
}
|
||||
|
||||
fun unpackSigned(): Long {
|
||||
return bin.unpackSigned()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package net.sergeych.lynon
|
||||
|
||||
import net.sergeych.lyng.Scope
|
||||
import net.sergeych.lyng.obj.Obj
|
||||
import net.sergeych.lyng.obj.ObjBool
|
||||
import net.sergeych.lyng.obj.ObjChar
|
||||
import net.sergeych.lyng.obj.ObjInt
|
||||
|
||||
class LynonPacker(private val bout: MemoryBitOutput= MemoryBitOutput()) : LynonEncoder(bout) {
|
||||
fun toUByteArray(): UByteArray = bout.toUByteArray()
|
||||
}
|
||||
|
||||
class LynonUnpacker(source: UByteArray) : LynonDecoder(MemoryBitInput(source))
|
||||
|
||||
open class LynonEncoder(private val bout: BitOutput) {
|
||||
|
||||
fun shouldCache(obj: Obj): Boolean = when (obj) {
|
||||
is ObjChar -> false
|
||||
is ObjInt -> obj.value > 0x10000FF
|
||||
is ObjBool -> false
|
||||
else -> true
|
||||
}
|
||||
|
||||
val cache = mutableMapOf<Obj,Int>()
|
||||
|
||||
suspend fun packObject(scope: Scope,obj: Obj) {
|
||||
cache[obj]?.let { cacheId ->
|
||||
val size = sizeInBits(cache.size)
|
||||
bout.putBit(1)
|
||||
bout.putBits(cacheId, size)
|
||||
} ?: run {
|
||||
bout.putBit(0)
|
||||
if( shouldCache(obj) ) {
|
||||
bout.putBit(1)
|
||||
cache[obj] = cache.size
|
||||
}
|
||||
else
|
||||
bout.putBit(0)
|
||||
obj.serialize(scope, this)
|
||||
}
|
||||
}
|
||||
|
||||
fun packBinaryData(data: ByteArray) {
|
||||
bout.packUnsigned(data.size.toULong())
|
||||
bout.putBytes(data)
|
||||
}
|
||||
|
||||
fun packSigned(value: Long) { bout.packSigned(value) }
|
||||
@Suppress("unused")
|
||||
fun packUnsigned(value: ULong) { bout.packUnsigned(value) }
|
||||
|
||||
@Suppress("unused")
|
||||
fun packBool(value: Boolean) { bout.putBit(if (value) 1 else 0) }
|
||||
fun packReal(value: Double) {
|
||||
bout.putBits(value.toRawBits().toULong(), 64)
|
||||
}
|
||||
|
||||
fun packBoolean(value: Boolean) {
|
||||
bout.putBit(if (value) 1 else 0)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package net.sergeych.lynon
|
||||
|
||||
class MemoryBitInput(val packedBits: UByteArray): BitInput() {
|
||||
constructor(bout: MemoryBitOutput): this(bout.toUByteArray())
|
||||
|
||||
private var index = 0
|
||||
|
||||
override fun getByte(): Int {
|
||||
|
||||
@@ -30,4 +30,6 @@ fun sizeInBits(value: ULong): Int {
|
||||
rest = rest shr 1
|
||||
}
|
||||
return size
|
||||
}
|
||||
}
|
||||
|
||||
fun sizeInBits(value: Int): Int = sizeInBits(value.toULong())
|
||||
Reference in new issue
Block a user