mirror of
https://github.com/mamoe/mirai.git
synced 2025-02-24 10:50:14 +08:00
Fix deserializing
This commit is contained in:
parent
f48e27d743
commit
320bb1b76f
@ -35,8 +35,8 @@ internal class JceDecoder(
|
||||
}
|
||||
|
||||
private fun SerialDescriptor.getJceTagId(index: Int): Int {
|
||||
//println("getTag: ${getElementName(index)}")
|
||||
return getElementAnnotations(index).filterIsInstance<JceId>().singleOrNull()?.id
|
||||
// higher performance, don't use filterIsInstance
|
||||
return (getElementAnnotations(index).first { it is JceId } as? JceId)?.id
|
||||
?: error("missing @JceId for ${getElementName(index)} in ${this.serialName}")
|
||||
}
|
||||
|
||||
@ -119,11 +119,15 @@ internal class JceDecoder(
|
||||
this@JceDecoder.endStructure(descriptor)
|
||||
}
|
||||
|
||||
private var state: Boolean = true
|
||||
|
||||
override fun beginStructure(descriptor: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder {
|
||||
this@JceDecoder.pushTag(if (jce.currentHead.tag == 0) JceTagMapEntryKey else JceTagMapEntryValue)
|
||||
state = !state
|
||||
println { "MapReader.beginStructure: ${jce.currentHead}" }
|
||||
this@JceDecoder.pushTag(
|
||||
when (jce.currentHead.tag) {
|
||||
0 -> JceTagMapEntryKey
|
||||
1 -> JceTagMapEntryValue
|
||||
else -> error("illegal map entry head: ${jce.currentHead.tag}")
|
||||
}
|
||||
)
|
||||
return this@JceDecoder.beginStructure(descriptor, *typeParams)
|
||||
}
|
||||
|
||||
@ -140,7 +144,7 @@ internal class JceDecoder(
|
||||
override fun decodeString(): String = jce.useHead { jce.readJceStringValue(it) }
|
||||
|
||||
override fun decodeCollectionSize(descriptor: SerialDescriptor): Int {
|
||||
//println("decodeCollectionSize in MapReader: ${descriptor.serialName}")
|
||||
println { "decodeCollectionSize in MapReader: ${descriptor.serialName}" }
|
||||
// 不读下一个 head
|
||||
return jce.useHead { jce.readJceIntValue(it) }
|
||||
}
|
||||
@ -148,7 +152,8 @@ internal class JceDecoder(
|
||||
|
||||
|
||||
override fun endStructure(descriptor: SerialDescriptor) {
|
||||
//println("endStructure: $descriptor")
|
||||
structureHierarchy--
|
||||
println { "endStructure: ${descriptor.serialName}" }
|
||||
if (currentTagOrNull?.isSimpleByteArray == true) {
|
||||
jce.prepareNextHead() // read to next head
|
||||
}
|
||||
@ -173,9 +178,31 @@ internal class JceDecoder(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
val debuggingMode: Boolean by lazy { false }
|
||||
|
||||
private var structureHierarchy: Int = 0
|
||||
|
||||
inline fun println(value: () -> String) {
|
||||
if (debuggingMode) {
|
||||
kotlin.io.println(" ".repeat(structureHierarchy) + value())
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun println(value: Any? = "") {
|
||||
if (debuggingMode) {
|
||||
kotlin.io.println(" ".repeat(structureHierarchy) + value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun beginStructure(descriptor: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder {
|
||||
//println()
|
||||
//println("beginStructure: ${descriptor.serialName}")
|
||||
println()
|
||||
println { "beginStructure: ${descriptor.serialName}" }
|
||||
structureHierarchy++
|
||||
return when (descriptor.kind) {
|
||||
is PrimitiveKind -> this@JceDecoder
|
||||
|
||||
@ -198,7 +225,7 @@ internal class JceDecoder(
|
||||
when (it.type) {
|
||||
Jce.SIMPLE_LIST -> {
|
||||
currentTag.isSimpleByteArray = true
|
||||
jce.prepareNextHead() // 无用的元素类型
|
||||
jce.nextHead() // 无用的元素类型
|
||||
SimpleByteArrayReader
|
||||
}
|
||||
Jce.LIST -> ListReader
|
||||
@ -232,23 +259,29 @@ internal class JceDecoder(
|
||||
override fun decodeSequentially(): Boolean = false
|
||||
override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
|
||||
var jceHead = jce.currentHeadOrNull ?: return CompositeDecoder.READ_DONE
|
||||
if (jceHead.type == Jce.STRUCT_END) {
|
||||
return CompositeDecoder.READ_DONE
|
||||
}
|
||||
|
||||
while (!jce.input.endOfInput){
|
||||
println { "decodeElementIndex: ${jce.currentHead}" }
|
||||
while (!jce.input.endOfInput) {
|
||||
if (jceHead.type == Jce.STRUCT_END) {
|
||||
println { "decodeElementIndex: ${jce.currentHead}" }
|
||||
return CompositeDecoder.READ_DONE
|
||||
}
|
||||
|
||||
repeat(descriptor.elementsCount) {
|
||||
val tag = descriptor.getJceTagId(it)
|
||||
if (tag == jceHead.tag) {
|
||||
println { "name=" + descriptor.getElementName(it) }
|
||||
return it
|
||||
}
|
||||
}
|
||||
|
||||
jce.skipField(jceHead.type)
|
||||
if (!jce.prepareNextHead()) {
|
||||
println { "decodeElementIndex EOF" }
|
||||
break
|
||||
}
|
||||
jceHead = jce.currentHead
|
||||
println { "next! $jceHead" }
|
||||
}
|
||||
|
||||
return CompositeDecoder.READ_DONE // optional support
|
||||
|
@ -126,6 +126,7 @@ internal class JceInput(
|
||||
Jce.STRING1 -> this.input.discardExact(this.input.readUByte().toInt())
|
||||
Jce.STRING4 -> this.input.discardExact(this.input.readInt())
|
||||
Jce.MAP -> { // map
|
||||
//println("skip map!")
|
||||
nextHead()
|
||||
repeat(skipToHeadAndUseIfPossibleOrFail(0, message = { "tag 0 not found when skipping map" }) {
|
||||
readJceIntValue(it)
|
||||
@ -136,6 +137,7 @@ internal class JceInput(
|
||||
}
|
||||
}
|
||||
Jce.LIST -> { // list
|
||||
JceDecoder.println {"skip list!"}
|
||||
nextHead()
|
||||
repeat(skipToHeadAndUseIfPossibleOrFail(0, message = { "tag 0 not found when skipping list" }) {
|
||||
readJceIntValue(it)
|
||||
@ -146,26 +148,28 @@ internal class JceInput(
|
||||
}
|
||||
}
|
||||
Jce.STRUCT_BEGIN -> {
|
||||
JceDecoder.println {"skip struct!"}
|
||||
fun skipToStructEnd() {
|
||||
var head: JceHead
|
||||
do {
|
||||
head = nextHead()
|
||||
skipField(head.type)
|
||||
} while (head.type.toInt() != 11)
|
||||
} while (head.type != Jce.STRUCT_END)
|
||||
}
|
||||
skipToStructEnd()
|
||||
}
|
||||
Jce.STRUCT_END, Jce.ZERO_TYPE -> {
|
||||
|
||||
Unit
|
||||
}
|
||||
Jce.SIMPLE_LIST -> {
|
||||
val head = nextHead()
|
||||
check(head.type.toInt() == 0) { "skipField with invalid type, type value: " + type + ", " + head.type }
|
||||
this.input.discardExact(
|
||||
skipToHeadAndUseIfPossibleOrFail(0) {
|
||||
readJceIntValue(it)
|
||||
}
|
||||
)
|
||||
JceDecoder.println { "skip simple list!" }
|
||||
var head = nextHead()
|
||||
check(head.type == Jce.BYTE) { "bad simple list element type: " + head.type }
|
||||
check(head.tag == 0) { "simple list element tag must be 0, but was ${head.tag}" }
|
||||
|
||||
head = nextHead()
|
||||
check(head.tag == 0) { "tag for size for simple list must be 0, but was ${head.tag}" }
|
||||
this.input.discardExact(readJceIntValue(head))
|
||||
}
|
||||
else -> error("invalid type: $type")
|
||||
}
|
||||
|
@ -114,7 +114,7 @@ internal class FriendInfo(
|
||||
@JceId(16) val vecIMGroupID: ByteArray? = null,
|
||||
@JceId(17) val vecMSFGroupID: ByteArray? = null,
|
||||
@JceId(18) val iTermType: Int? = null,
|
||||
@JceId(19) val oVipInfo: VipBaseInfo? = null,
|
||||
@JceId(19) val oVipInfo: VipBaseInfo? = null, //? bad
|
||||
@JceId(20) val network: Byte? = null,
|
||||
@JceId(21) val vecRing: ByteArray? = null,
|
||||
@JceId(22) val uAbiFlag: Long? = null,
|
||||
@ -156,12 +156,12 @@ internal class FriendInfo(
|
||||
|
||||
@Serializable
|
||||
internal class VipBaseInfo(
|
||||
@JceId(0) val mOpenInfo: Map<Int, VipOpenInfo>
|
||||
@JceId(0) val mOpenInfo: Map<Int, VipOpenInfo>? = null // 原本是 0
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
internal class VipOpenInfo(
|
||||
@JceId(0) val open: Boolean,
|
||||
@JceId(0) val open: Boolean? = false,
|
||||
@JceId(1) val iVipType: Int = -1,
|
||||
@JceId(2) val iVipLevel: Int = -1,
|
||||
@JceId(3) val iVipFlag: Int? = null,
|
||||
|
@ -7,6 +7,7 @@ import kotlinx.io.core.toByteArray
|
||||
import kotlinx.io.core.writeFully
|
||||
import kotlinx.serialization.MissingFieldException
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.qqandroid.io.JceStruct
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.jce.Jce
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.jce.JceId
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.jce.JceInput
|
||||
@ -42,6 +43,48 @@ internal const val ZERO_TYPE: Byte = 12
|
||||
@Suppress("INVISIBLE_MEMBER") // bug
|
||||
internal class JceInputTest {
|
||||
|
||||
@Test
|
||||
fun testIntToStructMap() {
|
||||
@Serializable
|
||||
data class VipOpenInfo(
|
||||
@JceId(0) val open: Boolean? = false,
|
||||
@JceId(1) val iVipType: Int = -1,
|
||||
@JceId(2) val iVipLevel: Int = -1,
|
||||
@JceId(3) val iVipFlag: Int? = null,
|
||||
@JceId(4) val nameplateId: Long? = null
|
||||
) : JceStruct
|
||||
|
||||
@Serializable
|
||||
data class VipBaseInfo(
|
||||
@JceId(0) val mOpenInfo: Map<Int, VipOpenInfo>? = null
|
||||
) : JceStruct
|
||||
|
||||
|
||||
@Serializable
|
||||
data class FriendInfo(
|
||||
@JceId(0) val friendUin: Long,
|
||||
@JceId(14) val nick: String = "",
|
||||
@JceId(19) val oVipInfo: VipBaseInfo? = null, //? bad
|
||||
@JceId(20) val network: Byte? = null
|
||||
) : JceStruct
|
||||
|
||||
val value = FriendInfo(
|
||||
friendUin = 123,
|
||||
nick = "h",
|
||||
oVipInfo = VipBaseInfo(
|
||||
mapOf(
|
||||
1 to VipOpenInfo(true, -1),
|
||||
999999999 to VipOpenInfo(true, -1)
|
||||
)
|
||||
),
|
||||
network = 1
|
||||
)
|
||||
assertEquals(
|
||||
value.toString(),
|
||||
Jce.UTF_8.load(FriendInfo.serializer(), value.toByteArray(FriendInfo.serializer())).toString()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSkippingMap() {
|
||||
@Serializable
|
||||
|
Loading…
Reference in New Issue
Block a user