refs #35 caching moved to level above objec serializing so we can cache strings, etc that are not Obj instances
This commit is contained in:
		
							parent
							
								
									f26ee7cd7c
								
							
						
					
					
						commit
						77f9191387
					
				@ -31,7 +31,7 @@ data class ObjBool(val value: Boolean) : Obj() {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override suspend fun serialize(scope: Scope, encoder: LynonEncoder) {
 | 
					    override suspend fun serialize(scope: Scope, encoder: LynonEncoder) {
 | 
				
			||||||
        encoder.packBoolean(value)
 | 
					        encoder.encodeBoolean(value)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override fun equals(other: Any?): Boolean {
 | 
					    override fun equals(other: Any?): Boolean {
 | 
				
			||||||
 | 
				
			|||||||
@ -98,7 +98,7 @@ class ObjInt(var value: Long,override val isConst: Boolean = false) : Obj(), Num
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override suspend fun serialize(scope: Scope, encoder: LynonEncoder) {
 | 
					    override suspend fun serialize(scope: Scope, encoder: LynonEncoder) {
 | 
				
			||||||
        encoder.packSigned(value)
 | 
					        encoder.encodeSigned(value)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    companion object {
 | 
					    companion object {
 | 
				
			||||||
 | 
				
			|||||||
@ -62,7 +62,7 @@ data class ObjReal(val value: Double) : Obj(), Numeric {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override suspend fun serialize(scope: Scope, encoder: LynonEncoder) {
 | 
					    override suspend fun serialize(scope: Scope, encoder: LynonEncoder) {
 | 
				
			||||||
        encoder.packReal(value)
 | 
					        encoder.encodeReal(value)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    companion object {
 | 
					    companion object {
 | 
				
			||||||
 | 
				
			|||||||
@ -78,7 +78,7 @@ data class ObjString(val value: String) : Obj() {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override suspend fun serialize(scope: Scope, encoder: LynonEncoder) {
 | 
					    override suspend fun serialize(scope: Scope, encoder: LynonEncoder) {
 | 
				
			||||||
        encoder.packBinaryData(value.encodeToByteArray())
 | 
					        encoder.encodeBinaryData(value.encodeToByteArray())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    companion object {
 | 
					    companion object {
 | 
				
			||||||
 | 
				
			|||||||
@ -4,27 +4,31 @@ import net.sergeych.lyng.Scope
 | 
				
			|||||||
import net.sergeych.lyng.obj.Obj
 | 
					import net.sergeych.lyng.obj.Obj
 | 
				
			||||||
import net.sergeych.lyng.obj.ObjClass
 | 
					import net.sergeych.lyng.obj.ObjClass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
open class LynonDecoder(private val bin: BitInput) {
 | 
					open class LynonDecoder(val bin: BitInput) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    val cache = mutableListOf<Obj>()
 | 
					    val cache = mutableListOf<Obj>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun unpackObject(scope: Scope, type: ObjClass): Obj {
 | 
					    inline fun decodeCached(f: LynonDecoder.() -> Obj): Obj {
 | 
				
			||||||
        return if( bin.getBit() == 0 ) {
 | 
					        return if( bin.getBit() == 0 ) {
 | 
				
			||||||
            // unpack and cache
 | 
					            // unpack and cache
 | 
				
			||||||
            val cached = bin.getBool()
 | 
					            val cached = bin.getBool()
 | 
				
			||||||
            type.deserialize(scope, this).also {
 | 
					            f().also {
 | 
				
			||||||
                if( cached ) cache.add(it)
 | 
					                if( cached ) cache.add(it)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        else {
 | 
					        else {
 | 
				
			||||||
            // get cache reference
 | 
					            // get cache reference
 | 
				
			||||||
            val size = sizeInBits(cache.size)
 | 
					            val size = sizeInBits(cache.size)
 | 
				
			||||||
            val id = bin.getBitsOrNull(size)?.toInt() ?: scope.raiseError("Invalid object id: unexpected end of stream")
 | 
					            val id = bin.getBitsOrNull(size)?.toInt() ?: throw RuntimeException("Invalid object id: unexpected end of stream")
 | 
				
			||||||
            if( id >= cache.size ) scope.raiseError("Invalid object id: $id should be in 0..<${cache.size}")
 | 
					            if( id >= cache.size ) throw RuntimeException("Invalid object id: $id should be in 0..<${cache.size}")
 | 
				
			||||||
            cache[id]
 | 
					            cache[id]
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun unpackObject(scope: Scope, type: ObjClass): Obj {
 | 
				
			||||||
 | 
					        return decodeCached { type.deserialize(scope, this) }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun unpackBinaryData(): ByteArray? {
 | 
					    fun unpackBinaryData(): ByteArray? {
 | 
				
			||||||
        val size = bin.unpackUnsigned()
 | 
					        val size = bin.unpackUnsigned()
 | 
				
			||||||
        return bin.getBytes(size.toInt())
 | 
					        return bin.getBytes(size.toInt())
 | 
				
			||||||
 | 
				
			|||||||
@ -6,13 +6,13 @@ import net.sergeych.lyng.obj.ObjBool
 | 
				
			|||||||
import net.sergeych.lyng.obj.ObjChar
 | 
					import net.sergeych.lyng.obj.ObjChar
 | 
				
			||||||
import net.sergeych.lyng.obj.ObjInt
 | 
					import net.sergeych.lyng.obj.ObjInt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class LynonPacker(private val bout: MemoryBitOutput= MemoryBitOutput()) : LynonEncoder(bout) {
 | 
					class LynonPacker(bout: MemoryBitOutput = MemoryBitOutput()) : LynonEncoder(bout) {
 | 
				
			||||||
    fun toUByteArray(): UByteArray = bout.toUByteArray()
 | 
					    fun toUByteArray(): UByteArray = (bout as MemoryBitOutput).toUByteArray()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class LynonUnpacker(source: UByteArray) : LynonDecoder(MemoryBitInput(source))
 | 
					class LynonUnpacker(source: UByteArray) : LynonDecoder(MemoryBitInput(source))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
open class LynonEncoder(private val bout: BitOutput) {
 | 
					open class LynonEncoder(val bout: BitOutput) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun shouldCache(obj: Obj): Boolean = when (obj) {
 | 
					    fun shouldCache(obj: Obj): Boolean = when (obj) {
 | 
				
			||||||
        is ObjChar -> false
 | 
					        is ObjChar -> false
 | 
				
			||||||
@ -21,41 +21,56 @@ open class LynonEncoder(private val bout: BitOutput) {
 | 
				
			|||||||
        else -> true
 | 
					        else -> true
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    val cache = mutableMapOf<Obj,Int>()
 | 
					    val cache = mutableMapOf<Any, Int>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    suspend fun packObject(scope: Scope,obj: Obj) {
 | 
					    inline fun encodeCached(item: Any, packer: LynonEncoder.() -> Unit) {
 | 
				
			||||||
        cache[obj]?.let { cacheId ->
 | 
					        if (item is Obj) {
 | 
				
			||||||
            val size = sizeInBits(cache.size)
 | 
					            cache[item]?.let { cacheId ->
 | 
				
			||||||
            bout.putBit(1)
 | 
					                val size = sizeInBits(cache.size)
 | 
				
			||||||
            bout.putBits(cacheId, size)
 | 
					 | 
				
			||||||
        } ?: run {
 | 
					 | 
				
			||||||
            bout.putBit(0)
 | 
					 | 
				
			||||||
            if( shouldCache(obj) ) {
 | 
					 | 
				
			||||||
                bout.putBit(1)
 | 
					                bout.putBit(1)
 | 
				
			||||||
                cache[obj] = cache.size
 | 
					                bout.putBits(cacheId.toULong(), size)
 | 
				
			||||||
            }
 | 
					            } ?: run {
 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
                bout.putBit(0)
 | 
					                bout.putBit(0)
 | 
				
			||||||
 | 
					                if (shouldCache(item)) {
 | 
				
			||||||
 | 
					                    bout.putBit(1)
 | 
				
			||||||
 | 
					                    cache[item] = cache.size
 | 
				
			||||||
 | 
					                } else
 | 
				
			||||||
 | 
					                    bout.putBit(0)
 | 
				
			||||||
 | 
					                packer()
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    suspend fun encodeObj(scope: Scope, obj: Obj) {
 | 
				
			||||||
 | 
					        encodeCached(obj) {
 | 
				
			||||||
            obj.serialize(scope, this)
 | 
					            obj.serialize(scope, this)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun packBinaryData(data: ByteArray) {
 | 
					    fun encodeBinaryData(data: ByteArray) {
 | 
				
			||||||
        bout.packUnsigned(data.size.toULong())
 | 
					        bout.packUnsigned(data.size.toULong())
 | 
				
			||||||
        bout.putBytes(data)
 | 
					        bout.putBytes(data)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun packSigned(value: Long) { bout.packSigned(value) }
 | 
					    fun encodeSigned(value: Long) {
 | 
				
			||||||
    @Suppress("unused")
 | 
					        bout.packSigned(value)
 | 
				
			||||||
    fun packUnsigned(value: ULong) { bout.packUnsigned(value) }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Suppress("unused")
 | 
					    @Suppress("unused")
 | 
				
			||||||
    fun packBool(value: Boolean) { bout.putBit(if (value) 1 else 0) }
 | 
					    fun encodeUnsigned(value: ULong) {
 | 
				
			||||||
    fun packReal(value: Double) {
 | 
					        bout.packUnsigned(value)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Suppress("unused")
 | 
				
			||||||
 | 
					    fun encodeBool(value: Boolean) {
 | 
				
			||||||
 | 
					        bout.putBit(if (value) 1 else 0)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun encodeReal(value: Double) {
 | 
				
			||||||
        bout.putBits(value.toRawBits().toULong(), 64)
 | 
					        bout.putBits(value.toRawBits().toULong(), 64)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun packBoolean(value: Boolean) {
 | 
					    fun encodeBoolean(value: Boolean) {
 | 
				
			||||||
        bout.putBit(if (value) 1 else 0)
 | 
					        bout.putBit(if (value) 1 else 0)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -90,14 +90,14 @@ class LynonTests {
 | 
				
			|||||||
        val encoder = LynonEncoder(bout)
 | 
					        val encoder = LynonEncoder(bout)
 | 
				
			||||||
        val s = "Hello, World!".toObj()
 | 
					        val s = "Hello, World!".toObj()
 | 
				
			||||||
        val scope = Scope()
 | 
					        val scope = Scope()
 | 
				
			||||||
        encoder.packObject(scope, s) // 1
 | 
					        encoder.encodeObj(scope, s) // 1
 | 
				
			||||||
        encoder.packObject(scope, s)
 | 
					        encoder.encodeObj(scope, s)
 | 
				
			||||||
        encoder.packObject(scope, s)
 | 
					        encoder.encodeObj(scope, s)
 | 
				
			||||||
        encoder.packObject(scope, s)
 | 
					        encoder.encodeObj(scope, s)
 | 
				
			||||||
        encoder.packObject(scope, s)
 | 
					        encoder.encodeObj(scope, s)
 | 
				
			||||||
        encoder.packObject(scope, s)
 | 
					        encoder.encodeObj(scope, s)
 | 
				
			||||||
        encoder.packObject(scope, s)
 | 
					        encoder.encodeObj(scope, s)
 | 
				
			||||||
        encoder.packObject(scope, s) // 8
 | 
					        encoder.encodeObj(scope, s) // 8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        val decoder = LynonDecoder(MemoryBitInput(bout))
 | 
					        val decoder = LynonDecoder(MemoryBitInput(bout))
 | 
				
			||||||
        val s1 = decoder.unpackObject(scope, ObjString.type) // 1
 | 
					        val s1 = decoder.unpackObject(scope, ObjString.type) // 1
 | 
				
			||||||
@ -122,7 +122,7 @@ class LynonTests {
 | 
				
			|||||||
        val encoder = LynonPacker()
 | 
					        val encoder = LynonPacker()
 | 
				
			||||||
        val scope = Scope()
 | 
					        val scope = Scope()
 | 
				
			||||||
        for (s in source) {
 | 
					        for (s in source) {
 | 
				
			||||||
            encoder.packObject(scope, s)
 | 
					            encoder.encodeObj(scope, s)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        val decoder = LynonUnpacker(encoder.toUByteArray())
 | 
					        val decoder = LynonUnpacker(encoder.toUByteArray())
 | 
				
			||||||
        val restored = mutableListOf<Obj>()
 | 
					        val restored = mutableListOf<Obj>()
 | 
				
			||||||
@ -136,10 +136,10 @@ class LynonTests {
 | 
				
			|||||||
    fun testUnpackBoolean() = runTest {
 | 
					    fun testUnpackBoolean() = runTest {
 | 
				
			||||||
        val scope = Scope()
 | 
					        val scope = Scope()
 | 
				
			||||||
        val decoder = LynonUnpacker(LynonPacker().apply {
 | 
					        val decoder = LynonUnpacker(LynonPacker().apply {
 | 
				
			||||||
            packObject(scope, ObjBool(true))
 | 
					            encodeObj(scope, ObjBool(true))
 | 
				
			||||||
            packObject(scope, ObjBool(false))
 | 
					            encodeObj(scope, ObjBool(false))
 | 
				
			||||||
            packObject(scope, ObjBool(true))
 | 
					            encodeObj(scope, ObjBool(true))
 | 
				
			||||||
            packObject(scope, ObjBool(true))
 | 
					            encodeObj(scope, ObjBool(true))
 | 
				
			||||||
        }.toUByteArray())
 | 
					        }.toUByteArray())
 | 
				
			||||||
        assertEquals(ObjTrue, decoder.unpackObject(scope, ObjBool.type))
 | 
					        assertEquals(ObjTrue, decoder.unpackObject(scope, ObjBool.type))
 | 
				
			||||||
        assertEquals(ObjFalse, decoder.unpackObject(scope, ObjBool.type))
 | 
					        assertEquals(ObjFalse, decoder.unpackObject(scope, ObjBool.type))
 | 
				
			||||||
@ -151,15 +151,15 @@ class LynonTests {
 | 
				
			|||||||
    fun testUnpackReal() = runTest {
 | 
					    fun testUnpackReal() = runTest {
 | 
				
			||||||
        val scope = Scope()
 | 
					        val scope = Scope()
 | 
				
			||||||
        val decoder = LynonUnpacker(LynonPacker().apply {
 | 
					        val decoder = LynonUnpacker(LynonPacker().apply {
 | 
				
			||||||
            packObject(scope, ObjReal(-Math.PI))
 | 
					            encodeObj(scope, ObjReal(-Math.PI))
 | 
				
			||||||
            packObject(scope, ObjReal(Math.PI))
 | 
					            encodeObj(scope, ObjReal(Math.PI))
 | 
				
			||||||
            packObject(scope, ObjReal(-Math.PI))
 | 
					            encodeObj(scope, ObjReal(-Math.PI))
 | 
				
			||||||
            packObject(scope, ObjReal(Math.PI))
 | 
					            encodeObj(scope, ObjReal(Math.PI))
 | 
				
			||||||
            packObject(scope, ObjReal(Double.NaN))
 | 
					            encodeObj(scope, ObjReal(Double.NaN))
 | 
				
			||||||
            packObject(scope, ObjReal(Double.NEGATIVE_INFINITY))
 | 
					            encodeObj(scope, ObjReal(Double.NEGATIVE_INFINITY))
 | 
				
			||||||
            packObject(scope, ObjReal(Double.POSITIVE_INFINITY))
 | 
					            encodeObj(scope, ObjReal(Double.POSITIVE_INFINITY))
 | 
				
			||||||
            packObject(scope, ObjReal(Double.MIN_VALUE))
 | 
					            encodeObj(scope, ObjReal(Double.MIN_VALUE))
 | 
				
			||||||
            packObject(scope, ObjReal(Double.MAX_VALUE))
 | 
					            encodeObj(scope, ObjReal(Double.MAX_VALUE))
 | 
				
			||||||
        }.toUByteArray())
 | 
					        }.toUByteArray())
 | 
				
			||||||
        assertEquals(ObjReal(-Math.PI), decoder.unpackObject(scope, ObjReal.type))
 | 
					        assertEquals(ObjReal(-Math.PI), decoder.unpackObject(scope, ObjReal.type))
 | 
				
			||||||
        assertEquals(ObjReal(Math.PI), decoder.unpackObject(scope, ObjReal.type))
 | 
					        assertEquals(ObjReal(Math.PI), decoder.unpackObject(scope, ObjReal.type))
 | 
				
			||||||
@ -175,12 +175,12 @@ class LynonTests {
 | 
				
			|||||||
    fun testUnpackInt() = runTest {
 | 
					    fun testUnpackInt() = runTest {
 | 
				
			||||||
        val scope = Scope()
 | 
					        val scope = Scope()
 | 
				
			||||||
        val decoder = LynonUnpacker(LynonPacker().apply {
 | 
					        val decoder = LynonUnpacker(LynonPacker().apply {
 | 
				
			||||||
            packObject(scope, ObjInt(0))
 | 
					            encodeObj(scope, ObjInt(0))
 | 
				
			||||||
            packObject(scope, ObjInt(-1))
 | 
					            encodeObj(scope, ObjInt(-1))
 | 
				
			||||||
            packObject(scope, ObjInt(23))
 | 
					            encodeObj(scope, ObjInt(23))
 | 
				
			||||||
            packObject(scope, ObjInt(Long.MIN_VALUE))
 | 
					            encodeObj(scope, ObjInt(Long.MIN_VALUE))
 | 
				
			||||||
            packObject(scope, ObjInt(Long.MAX_VALUE))
 | 
					            encodeObj(scope, ObjInt(Long.MAX_VALUE))
 | 
				
			||||||
            packObject(scope, ObjInt(Long.MAX_VALUE))
 | 
					            encodeObj(scope, ObjInt(Long.MAX_VALUE))
 | 
				
			||||||
        }.toUByteArray())
 | 
					        }.toUByteArray())
 | 
				
			||||||
        assertEquals(ObjInt(0), decoder.unpackObject(scope, ObjInt.type))
 | 
					        assertEquals(ObjInt(0), decoder.unpackObject(scope, ObjInt.type))
 | 
				
			||||||
        assertEquals(ObjInt(-1), decoder.unpackObject(scope, ObjInt.type))
 | 
					        assertEquals(ObjInt(-1), decoder.unpackObject(scope, ObjInt.type))
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user