mirror of
https://github.com/mamoe/mirai.git
synced 2025-03-24 06:10:09 +08:00
Support maps and nesting
This commit is contained in:
parent
6731525e49
commit
e375d17f82
@ -7,11 +7,12 @@
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("PrivatePropertyName")
|
||||
|
||||
package net.mamoe.mirai.qqandroid.io.serialization.jce
|
||||
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.builtins.AbstractDecoder
|
||||
import kotlinx.serialization.builtins.ByteArraySerializer
|
||||
import kotlinx.serialization.internal.TaggedDecoder
|
||||
import kotlinx.serialization.modules.SerialModule
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.Jce
|
||||
@ -38,16 +39,14 @@ internal class JceDecoder(
|
||||
}
|
||||
|
||||
private fun SerialDescriptor.getJceTagId(index: Int): Int {
|
||||
return getElementAnnotations(index).filterIsInstance<JceId>().single().id
|
||||
println("getTag: ${getElementName(index)}")
|
||||
return getElementAnnotations(index).filterIsInstance<JceId>().singleOrNull()?.id
|
||||
?: error("missing @JceId for ${getElementName(index)} in ${this.serialName}")
|
||||
}
|
||||
|
||||
private val SimpleByteArrayReader: SimpleByteArrayReaderImpl = SimpleByteArrayReaderImpl()
|
||||
|
||||
companion object {
|
||||
private val ByteArraySerializer: KSerializer<ByteArray> = ByteArraySerializer()
|
||||
}
|
||||
|
||||
// TODO: 2020/3/6 can be object
|
||||
private inner class SimpleByteArrayReader : AbstractDecoder() {
|
||||
private inner class SimpleByteArrayReaderImpl : AbstractDecoder() {
|
||||
override fun decodeSequentially(): Boolean = true
|
||||
|
||||
override fun endStructure(descriptor: SerialDescriptor) {
|
||||
@ -80,8 +79,9 @@ internal class JceDecoder(
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: 2020/3/6 can be object
|
||||
private inner class ListReader : AbstractDecoder() {
|
||||
private val ListReader: ListReaderImpl = ListReaderImpl()
|
||||
|
||||
private inner class ListReaderImpl : AbstractDecoder() {
|
||||
override fun decodeSequentially(): Boolean = true
|
||||
override fun decodeElementIndex(descriptor: SerialDescriptor): Int = error("should not be reached")
|
||||
override fun endStructure(descriptor: SerialDescriptor) {
|
||||
@ -113,33 +113,73 @@ internal class JceDecoder(
|
||||
|
||||
override fun endStructure(descriptor: SerialDescriptor) {
|
||||
println("endStructure: $descriptor")
|
||||
if (descriptor == ByteArraySerializer.descriptor) {
|
||||
jce.prepareNextHead() // list 里面没读 head
|
||||
} else jce.prepareNextHead() // TODO ?? 测试这里
|
||||
super.endStructure(descriptor)
|
||||
if (currentTagOrNull?.isSimpleByteArray == true) {
|
||||
jce.prepareNextHead() // read to next head
|
||||
}
|
||||
if (descriptor.kind == StructureKind.CLASS) {
|
||||
if (currentTagOrNull == null) {
|
||||
return
|
||||
}
|
||||
while (true) {
|
||||
val currentHead = jce.currentHeadOrNull ?: return
|
||||
if (currentHead.type == Jce.STRUCT_END) {
|
||||
break
|
||||
}
|
||||
println("skipping")
|
||||
jce.skipField(currentHead.type)
|
||||
jce.prepareNextHead()
|
||||
}
|
||||
// pushTag(JceTag(0, true))
|
||||
// skip STRUCT_END
|
||||
// popTag()
|
||||
}
|
||||
}
|
||||
|
||||
override fun beginStructure(descriptor: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder {
|
||||
println()
|
||||
println("beginStructure: ${descriptor.serialName}")
|
||||
return when (descriptor.kind) {
|
||||
is PrimitiveKind -> this@JceDecoder
|
||||
|
||||
StructureKind.MAP -> {
|
||||
error("map")
|
||||
println("!! MAP")
|
||||
return jce.skipToHeadAndUseIfPossibleOrFail(popTag().id) {
|
||||
it.checkType(Jce.MAP)
|
||||
ListReader
|
||||
}
|
||||
}
|
||||
StructureKind.LIST -> {
|
||||
println("!! ByteArray")
|
||||
println("decoderTag: $currentTagOrNull")
|
||||
println("jceHead: " + jce.currentHeadOrNull)
|
||||
return jce.skipToHeadAndUseIfPossibleOrFail(popTag().id) {
|
||||
return jce.skipToHeadAndUseIfPossibleOrFail(currentTag.id) {
|
||||
println("listHead: $it")
|
||||
when (it.type) {
|
||||
Jce.SIMPLE_LIST -> SimpleByteArrayReader().also { jce.prepareNextHead() } // 无用的元素类型
|
||||
Jce.LIST -> ListReader()
|
||||
Jce.SIMPLE_LIST -> {
|
||||
currentTag.isSimpleByteArray = true
|
||||
jce.prepareNextHead() // 无用的元素类型
|
||||
SimpleByteArrayReader
|
||||
}
|
||||
Jce.LIST -> ListReader
|
||||
else -> error("type mismatch. Expected SIMPLE_LIST or LIST, got ${it.type} instead")
|
||||
}
|
||||
}
|
||||
}
|
||||
StructureKind.CLASS -> {
|
||||
val currentTag = currentTagOrNull ?: return this@JceDecoder
|
||||
|
||||
else -> this@JceDecoder
|
||||
println("!! CLASS")
|
||||
println("decoderTag: $currentTag")
|
||||
println("jceHead: " + jce.currentHeadOrNull)
|
||||
return jce.skipToHeadAndUseIfPossibleOrFail(popTag().id) {
|
||||
it.checkType(Jce.STRUCT_BEGIN)
|
||||
this@JceDecoder
|
||||
}
|
||||
}
|
||||
|
||||
StructureKind.OBJECT -> error("unsupported StructureKind.OBJECT: ${descriptor.serialName}")
|
||||
is UnionKind -> error("unsupported UnionKind: ${descriptor.serialName}")
|
||||
is PolymorphicKind -> error("unsupported PolymorphicKind: ${descriptor.serialName}")
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,6 +194,10 @@ internal class JceDecoder(
|
||||
override fun decodeSequentially(): Boolean = false
|
||||
override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
|
||||
val jceHead = jce.currentHeadOrNull ?: return CompositeDecoder.READ_DONE
|
||||
if (jceHead.type == Jce.STRUCT_END) {
|
||||
return CompositeDecoder.READ_DONE
|
||||
}
|
||||
|
||||
repeat(descriptor.elementsCount) {
|
||||
val tag = descriptor.getJceTagId(it)
|
||||
if (tag == jceHead.tag) {
|
||||
|
@ -30,7 +30,9 @@ annotation class JceId(val id: Int)
|
||||
internal data class JceTag(
|
||||
val id: Int,
|
||||
val isNullable: Boolean
|
||||
)
|
||||
){
|
||||
internal var isSimpleByteArray: Boolean = false
|
||||
}
|
||||
|
||||
fun JceHead.checkType(type: Byte) {
|
||||
check(this.type == type) {"type mismatch. Expected $type, actual ${this.type}"}
|
||||
|
@ -41,6 +41,57 @@ internal const val ZERO_TYPE: Byte = 12
|
||||
@Suppress("INVISIBLE_MEMBER") // bug
|
||||
internal class JceInputTest {
|
||||
|
||||
@Test
|
||||
fun testNestedJceStruct() {
|
||||
@Serializable
|
||||
data class TestSerializableClassC(
|
||||
@JceId(5) val value3: Int = 123123
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class TestSerializableClassB(
|
||||
@JceId(0) val value: Int,
|
||||
@JceId(123) val nested2: TestSerializableClassC
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class TestSerializableClassA(
|
||||
@JceId(0) val value1: Int,
|
||||
@JceId(1) val nestedStruct: TestSerializableClassB,
|
||||
@JceId(2) val optional: Int = 3,
|
||||
@JceId(4) val notOptional: Int
|
||||
)
|
||||
|
||||
val input = buildPacket {
|
||||
writeJceHead(INT, 0)
|
||||
writeInt(444)
|
||||
|
||||
writeJceHead(STRUCT_BEGIN, 1); // TestSerializableClassB
|
||||
{
|
||||
writeJceHead(INT, 0)
|
||||
writeInt(123)
|
||||
|
||||
writeJceHead(STRUCT_BEGIN, 123); // TestSerializableClassC
|
||||
{
|
||||
writeJceHead(INT, 5)
|
||||
writeInt(123123)
|
||||
}()
|
||||
writeJceHead(STRUCT_END, 0)
|
||||
|
||||
writeJceHead(INT, 2) // 多余
|
||||
writeInt(123)
|
||||
}()
|
||||
writeJceHead(STRUCT_END, 0)
|
||||
|
||||
writeJceHead(INT, 4)
|
||||
writeInt(5)
|
||||
}
|
||||
|
||||
assertEquals(
|
||||
TestSerializableClassA(444, TestSerializableClassB(123, TestSerializableClassC(123123)), notOptional = 5),
|
||||
JceNew.UTF_8.load(TestSerializableClassA.serializer(), input)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNestedList() {
|
||||
@ -80,6 +131,44 @@ internal class JceInputTest {
|
||||
assertEquals(TestSerializableClassA(), JceNew.UTF_8.load(TestSerializableClassA.serializer(), input))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMap() {
|
||||
@Serializable
|
||||
data class TestSerializableClassA(
|
||||
@JceId(0) val byteArray: Map<Int, Int>
|
||||
)
|
||||
|
||||
val input = buildPacket {
|
||||
writeJceHead(MAP, 0)
|
||||
|
||||
mapOf(1 to 2, 33 to 44).let {
|
||||
writeJceHead(BYTE, 0)
|
||||
writeByte(it.size.toByte())
|
||||
|
||||
it.forEach { (key, value) ->
|
||||
writeJceHead(INT, 0)
|
||||
writeInt(key)
|
||||
|
||||
writeJceHead(INT, 1)
|
||||
writeInt(value)
|
||||
}
|
||||
}
|
||||
|
||||
writeJceHead(SIMPLE_LIST, 3)
|
||||
writeJceHead(BYTE, 0)
|
||||
|
||||
byteArrayOf(1, 2, 3, 4).let {
|
||||
writeJceHead(BYTE, 0)
|
||||
writeByte(it.size.toByte())
|
||||
writeFully(it)
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(
|
||||
TestSerializableClassA(mapOf(1 to 2, 33 to 44)),
|
||||
JceNew.UTF_8.load(TestSerializableClassA.serializer(), input)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSimpleByteArray() {
|
||||
|
Loading…
Reference in New Issue
Block a user