maps serialization
This commit is contained in:
		
							parent
							
								
									2339130241
								
							
						
					
					
						commit
						790cce0d24
					
				@ -7,12 +7,14 @@ Important thing is that maps can't contain `null`: it is used to return from mis
 | 
			
		||||
 | 
			
		||||
Constructed map instance is of class `Map` and implements `Collection` (and therefore `Iterable`)
 | 
			
		||||
 | 
			
		||||
    val map = Map( ["foo", 1], ["bar", "buzz"] )
 | 
			
		||||
    val map = Map( "foo" => 1, "bar" => "buzz" )
 | 
			
		||||
    assert(map is Map)
 | 
			
		||||
    assert(map.size == 2)
 | 
			
		||||
    assert(map is Iterable)
 | 
			
		||||
    >>> void
 | 
			
		||||
 | 
			
		||||
Notice usage of the `=>` operator that creates `MapEntry`, which implements also [Collection] of
 | 
			
		||||
two items, first, at index zero, is a key, second, at index 1, is the value. You can use lists too.
 | 
			
		||||
Map keys could be any objects (hashable, e.g. with reasonable hashCode, most of standard types are). You can access elements with indexing operator:
 | 
			
		||||
 | 
			
		||||
    val map = Map( ["foo", 1], ["bar", "buzz"], [42, "answer"] )
 | 
			
		||||
@ -31,7 +33,7 @@ To remove item from the collection. use `remove`. It returns last removed item o
 | 
			
		||||
hold nulls in the map - this is not a recommended practice when using `remove` returned value. `clear()` 
 | 
			
		||||
removes all.
 | 
			
		||||
 | 
			
		||||
    val map = Map( ["foo", 1], ["bar", "buzz"], [42, "answer"] )
 | 
			
		||||
    val map = Map( "foo" => 1, "bar" => "buzz", [42, "answer"] )
 | 
			
		||||
    assertEquals( 1, map.remove("foo") )
 | 
			
		||||
    assert( map.getOrNull("foo") == null)
 | 
			
		||||
    assert( map.size == 2 )
 | 
			
		||||
 | 
			
		||||
@ -94,6 +94,15 @@ class ObjMap(val map: MutableMap<Obj, Obj> = mutableMapOf()) : Obj() {
 | 
			
		||||
        return map == other.map
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override suspend fun lynonType(): LynonType = LynonType.Map
 | 
			
		||||
 | 
			
		||||
    override suspend fun serialize(scope: Scope, encoder: LynonEncoder, lynonType: LynonType?) {
 | 
			
		||||
        val keys = map.keys.map { it.toObj() }
 | 
			
		||||
        val values = map.values.map { it.toObj() }
 | 
			
		||||
        encoder.encodeAnyList(scope, keys)
 | 
			
		||||
        encoder.encodeAnyList(scope, values, fixedSize = true)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
 | 
			
		||||
        suspend fun listToMap(scope: Scope, list: List<Obj>): MutableMap<Obj, Obj> {
 | 
			
		||||
@ -121,6 +130,13 @@ class ObjMap(val map: MutableMap<Obj, Obj> = mutableMapOf()) : Obj() {
 | 
			
		||||
            override suspend fun callOn(scope: Scope): Obj {
 | 
			
		||||
                return ObjMap(listToMap(scope, scope.args.list))
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            override suspend fun deserialize(scope: Scope, decoder: LynonDecoder, lynonType: LynonType?): Obj {
 | 
			
		||||
                val keys = decoder.decodeAnyList(scope)
 | 
			
		||||
                val values = decoder.decodeAnyList(scope,fixedSize = keys.size)
 | 
			
		||||
                if( keys.size != values.size) scope.raiseIllegalArgument("map keys and values should be same size")
 | 
			
		||||
                return ObjMap(keys.zip(values).toMap().toMutableMap())
 | 
			
		||||
            }
 | 
			
		||||
        }.apply {
 | 
			
		||||
            addFn("getOrNull") {
 | 
			
		||||
                val key = args.firstAndOnly(pos)
 | 
			
		||||
 | 
			
		||||
@ -56,7 +56,7 @@ open class LynonDecoder(val bin: BitInput, val settings: LynonSettings = LynonSe
 | 
			
		||||
        } ?: scope.raiseSymbolNotFound("can't deserialize: not found type $className")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    suspend fun decodeAnyList(scope: Scope): MutableList<Obj> {
 | 
			
		||||
    suspend fun decodeAnyList(scope: Scope,fixedSize: Int?=null): MutableList<Obj> {
 | 
			
		||||
        return if (bin.getBit() == 1) {
 | 
			
		||||
            // homogenous
 | 
			
		||||
            val type = LynonType.entries[getBitsAsInt(4)]
 | 
			
		||||
@ -64,7 +64,7 @@ open class LynonDecoder(val bin: BitInput, val settings: LynonSettings = LynonSe
 | 
			
		||||
            val objClass = if (type == LynonType.Other)
 | 
			
		||||
                decodeClassObj(scope).also { println("detected class obj: $it") }
 | 
			
		||||
            else type.objClass
 | 
			
		||||
            val size = bin.unpackUnsigned().toInt()
 | 
			
		||||
            val size = fixedSize ?: bin.unpackUnsigned().toInt()
 | 
			
		||||
            println("detected homogenous list type $type, $size items")
 | 
			
		||||
            for (i in 0..<size) {
 | 
			
		||||
                list += decodeObject(scope, objClass, type).also {
 | 
			
		||||
@ -73,7 +73,7 @@ open class LynonDecoder(val bin: BitInput, val settings: LynonSettings = LynonSe
 | 
			
		||||
            }
 | 
			
		||||
            list
 | 
			
		||||
        } else {
 | 
			
		||||
            val size = unpackUnsigned().toInt()
 | 
			
		||||
            val size = fixedSize ?: unpackUnsigned().toInt()
 | 
			
		||||
            (0..<size).map { decodeAny(scope) }.toMutableList()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -108,30 +108,38 @@ open class LynonEncoder(val bout: BitOutput, val settings: LynonSettings = Lynon
 | 
			
		||||
     * for each item.
 | 
			
		||||
     *
 | 
			
		||||
     */
 | 
			
		||||
    suspend fun encodeAnyList(scope: Scope, list: List<Obj>) {
 | 
			
		||||
        val objClass = list[0].objClass
 | 
			
		||||
        var type = list[0].lynonType()
 | 
			
		||||
        var isHomogeneous = true
 | 
			
		||||
        for (i in list.drop(1))
 | 
			
		||||
            if (i.objClass != objClass) {
 | 
			
		||||
                isHomogeneous = false
 | 
			
		||||
                break
 | 
			
		||||
            } else {
 | 
			
		||||
                // same class but type might need generalization
 | 
			
		||||
                type = type.generalizeTo(i.lynonType())
 | 
			
		||||
                    ?: scope.raiseError("inner error: can't generalize lynon type $type to ${i.lynonType()}")
 | 
			
		||||
            }
 | 
			
		||||
        if (isHomogeneous) {
 | 
			
		||||
            putBit(1)
 | 
			
		||||
            putTypeRecord(scope, list[0], type)
 | 
			
		||||
            encodeUnsigned(list.size.toULong())
 | 
			
		||||
            for (i in list) encodeObject(scope, i, type)
 | 
			
		||||
        } else {
 | 
			
		||||
    suspend fun encodeAnyList(scope: Scope, list: List<Obj>,fixedSize: Boolean = false) {
 | 
			
		||||
        if( list.isEmpty()) {
 | 
			
		||||
            // any-typed, empty, save space
 | 
			
		||||
            putBit(0)
 | 
			
		||||
            encodeUnsigned(list.size.toULong())
 | 
			
		||||
            for (i in list) encodeAny(scope, i)
 | 
			
		||||
            encodeUnsigned(0u)
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            val objClass = list[0].objClass
 | 
			
		||||
            var type = list[0].lynonType()
 | 
			
		||||
            var isHomogeneous = true
 | 
			
		||||
            for (i in list.drop(1))
 | 
			
		||||
                if (i.objClass != objClass) {
 | 
			
		||||
                    isHomogeneous = false
 | 
			
		||||
                    break
 | 
			
		||||
                } else {
 | 
			
		||||
                    // same class but type might need generalization
 | 
			
		||||
                    type = type.generalizeTo(i.lynonType())
 | 
			
		||||
                        ?: scope.raiseError("inner error: can't generalize lynon type $type to ${i.lynonType()}")
 | 
			
		||||
                }
 | 
			
		||||
            if (isHomogeneous) {
 | 
			
		||||
                putBit(1)
 | 
			
		||||
                putTypeRecord(scope, list[0], type)
 | 
			
		||||
                if (!fixedSize)
 | 
			
		||||
                    encodeUnsigned(list.size.toULong())
 | 
			
		||||
                for (i in list) encodeObject(scope, i, type)
 | 
			
		||||
            } else {
 | 
			
		||||
                putBit(0)
 | 
			
		||||
                if (!fixedSize)
 | 
			
		||||
                    encodeUnsigned(list.size.toULong())
 | 
			
		||||
                for (i in list) encodeAny(scope, i)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
@ -508,6 +508,7 @@ class LynonTests {
 | 
			
		||||
            testEncode(["the", "the", "wall", "the", "wall", "wall"])
 | 
			
		||||
            testEncode([1,2,3, "the", "wall", "wall"])
 | 
			
		||||
            testEncode([false, false, false, true,true])
 | 
			
		||||
            testEncode([])
 | 
			
		||||
        """.trimIndent()
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
@ -534,6 +535,28 @@ class LynonTests {
 | 
			
		||||
            testEncode( [1 => "one", 1 => "one", "foo" => "MapEntry"] )
 | 
			
		||||
        """.trimIndent())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testHomogenousMap() = runTest {
 | 
			
		||||
        val s = testScope()
 | 
			
		||||
        s.eval("""
 | 
			
		||||
            testEncode( Map("one" => 1, "two" => 2) )
 | 
			
		||||
            testEncode( Map() )
 | 
			
		||||
        """.trimIndent())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testHeterogeneousMap() = runTest {
 | 
			
		||||
        val s = testScope()
 | 
			
		||||
        s.eval("""
 | 
			
		||||
//            testEncode(["one", 2])
 | 
			
		||||
//            testEncode([1, "2"])
 | 
			
		||||
//            testEncode( Map("one" => 1, 2 => 2) )
 | 
			
		||||
            testEncode( Map("one" => 1, 2 => "2") )
 | 
			
		||||
        """.trimIndent())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user