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