Fix deserializing

This commit is contained in:
Him188 2020-03-19 14:05:32 +08:00
parent f48e27d743
commit 320bb1b76f
4 changed files with 107 additions and 27 deletions

View File

@ -35,8 +35,8 @@ internal class JceDecoder(
} }
private fun SerialDescriptor.getJceTagId(index: Int): Int { private fun SerialDescriptor.getJceTagId(index: Int): Int {
//println("getTag: ${getElementName(index)}") // higher performance, don't use filterIsInstance
return getElementAnnotations(index).filterIsInstance<JceId>().singleOrNull()?.id return (getElementAnnotations(index).first { it is JceId } as? JceId)?.id
?: error("missing @JceId for ${getElementName(index)} in ${this.serialName}") ?: error("missing @JceId for ${getElementName(index)} in ${this.serialName}")
} }
@ -119,11 +119,15 @@ internal class JceDecoder(
this@JceDecoder.endStructure(descriptor) this@JceDecoder.endStructure(descriptor)
} }
private var state: Boolean = true
override fun beginStructure(descriptor: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder { override fun beginStructure(descriptor: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder {
this@JceDecoder.pushTag(if (jce.currentHead.tag == 0) JceTagMapEntryKey else JceTagMapEntryValue) println { "MapReader.beginStructure: ${jce.currentHead}" }
state = !state 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) return this@JceDecoder.beginStructure(descriptor, *typeParams)
} }
@ -140,7 +144,7 @@ internal class JceDecoder(
override fun decodeString(): String = jce.useHead { jce.readJceStringValue(it) } override fun decodeString(): String = jce.useHead { jce.readJceStringValue(it) }
override fun decodeCollectionSize(descriptor: SerialDescriptor): Int { override fun decodeCollectionSize(descriptor: SerialDescriptor): Int {
//println("decodeCollectionSize in MapReader: ${descriptor.serialName}") println { "decodeCollectionSize in MapReader: ${descriptor.serialName}" }
// 不读下一个 head // 不读下一个 head
return jce.useHead { jce.readJceIntValue(it) } return jce.useHead { jce.readJceIntValue(it) }
} }
@ -148,7 +152,8 @@ internal class JceDecoder(
override fun endStructure(descriptor: SerialDescriptor) { override fun endStructure(descriptor: SerialDescriptor) {
//println("endStructure: $descriptor") structureHierarchy--
println { "endStructure: ${descriptor.serialName}" }
if (currentTagOrNull?.isSimpleByteArray == true) { if (currentTagOrNull?.isSimpleByteArray == true) {
jce.prepareNextHead() // read to next head 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 { override fun beginStructure(descriptor: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder {
//println() println()
//println("beginStructure: ${descriptor.serialName}") println { "beginStructure: ${descriptor.serialName}" }
structureHierarchy++
return when (descriptor.kind) { return when (descriptor.kind) {
is PrimitiveKind -> this@JceDecoder is PrimitiveKind -> this@JceDecoder
@ -198,7 +225,7 @@ internal class JceDecoder(
when (it.type) { when (it.type) {
Jce.SIMPLE_LIST -> { Jce.SIMPLE_LIST -> {
currentTag.isSimpleByteArray = true currentTag.isSimpleByteArray = true
jce.prepareNextHead() // 无用的元素类型 jce.nextHead() // 无用的元素类型
SimpleByteArrayReader SimpleByteArrayReader
} }
Jce.LIST -> ListReader Jce.LIST -> ListReader
@ -232,23 +259,29 @@ internal class JceDecoder(
override fun decodeSequentially(): Boolean = false override fun decodeSequentially(): Boolean = false
override fun decodeElementIndex(descriptor: SerialDescriptor): Int { override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
var jceHead = jce.currentHeadOrNull ?: return CompositeDecoder.READ_DONE 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) { repeat(descriptor.elementsCount) {
val tag = descriptor.getJceTagId(it) val tag = descriptor.getJceTagId(it)
if (tag == jceHead.tag) { if (tag == jceHead.tag) {
println { "name=" + descriptor.getElementName(it) }
return it return it
} }
} }
jce.skipField(jceHead.type) jce.skipField(jceHead.type)
if (!jce.prepareNextHead()) { if (!jce.prepareNextHead()) {
println { "decodeElementIndex EOF" }
break break
} }
jceHead = jce.currentHead jceHead = jce.currentHead
println { "next! $jceHead" }
} }
return CompositeDecoder.READ_DONE // optional support return CompositeDecoder.READ_DONE // optional support

View File

@ -126,6 +126,7 @@ internal class JceInput(
Jce.STRING1 -> this.input.discardExact(this.input.readUByte().toInt()) Jce.STRING1 -> this.input.discardExact(this.input.readUByte().toInt())
Jce.STRING4 -> this.input.discardExact(this.input.readInt()) Jce.STRING4 -> this.input.discardExact(this.input.readInt())
Jce.MAP -> { // map Jce.MAP -> { // map
//println("skip map!")
nextHead() nextHead()
repeat(skipToHeadAndUseIfPossibleOrFail(0, message = { "tag 0 not found when skipping map" }) { repeat(skipToHeadAndUseIfPossibleOrFail(0, message = { "tag 0 not found when skipping map" }) {
readJceIntValue(it) readJceIntValue(it)
@ -136,6 +137,7 @@ internal class JceInput(
} }
} }
Jce.LIST -> { // list Jce.LIST -> { // list
JceDecoder.println {"skip list!"}
nextHead() nextHead()
repeat(skipToHeadAndUseIfPossibleOrFail(0, message = { "tag 0 not found when skipping list" }) { repeat(skipToHeadAndUseIfPossibleOrFail(0, message = { "tag 0 not found when skipping list" }) {
readJceIntValue(it) readJceIntValue(it)
@ -146,26 +148,28 @@ internal class JceInput(
} }
} }
Jce.STRUCT_BEGIN -> { Jce.STRUCT_BEGIN -> {
JceDecoder.println {"skip struct!"}
fun skipToStructEnd() { fun skipToStructEnd() {
var head: JceHead var head: JceHead
do { do {
head = nextHead() head = nextHead()
skipField(head.type) skipField(head.type)
} while (head.type.toInt() != 11) } while (head.type != Jce.STRUCT_END)
} }
skipToStructEnd() skipToStructEnd()
} }
Jce.STRUCT_END, Jce.ZERO_TYPE -> { Jce.STRUCT_END, Jce.ZERO_TYPE -> {
Unit
} }
Jce.SIMPLE_LIST -> { Jce.SIMPLE_LIST -> {
val head = nextHead() JceDecoder.println { "skip simple list!" }
check(head.type.toInt() == 0) { "skipField with invalid type, type value: " + type + ", " + head.type } var head = nextHead()
this.input.discardExact( check(head.type == Jce.BYTE) { "bad simple list element type: " + head.type }
skipToHeadAndUseIfPossibleOrFail(0) { check(head.tag == 0) { "simple list element tag must be 0, but was ${head.tag}" }
readJceIntValue(it)
} 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") else -> error("invalid type: $type")
} }

View File

@ -114,7 +114,7 @@ internal class FriendInfo(
@JceId(16) val vecIMGroupID: ByteArray? = null, @JceId(16) val vecIMGroupID: ByteArray? = null,
@JceId(17) val vecMSFGroupID: ByteArray? = null, @JceId(17) val vecMSFGroupID: ByteArray? = null,
@JceId(18) val iTermType: Int? = 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(20) val network: Byte? = null,
@JceId(21) val vecRing: ByteArray? = null, @JceId(21) val vecRing: ByteArray? = null,
@JceId(22) val uAbiFlag: Long? = null, @JceId(22) val uAbiFlag: Long? = null,
@ -156,12 +156,12 @@ internal class FriendInfo(
@Serializable @Serializable
internal class VipBaseInfo( internal class VipBaseInfo(
@JceId(0) val mOpenInfo: Map<Int, VipOpenInfo> @JceId(0) val mOpenInfo: Map<Int, VipOpenInfo>? = null // 原本是 0
) : JceStruct ) : JceStruct
@Serializable @Serializable
internal class VipOpenInfo( internal class VipOpenInfo(
@JceId(0) val open: Boolean, @JceId(0) val open: Boolean? = false,
@JceId(1) val iVipType: Int = -1, @JceId(1) val iVipType: Int = -1,
@JceId(2) val iVipLevel: Int = -1, @JceId(2) val iVipLevel: Int = -1,
@JceId(3) val iVipFlag: Int? = null, @JceId(3) val iVipFlag: Int? = null,

View File

@ -7,6 +7,7 @@ import kotlinx.io.core.toByteArray
import kotlinx.io.core.writeFully import kotlinx.io.core.writeFully
import kotlinx.serialization.MissingFieldException import kotlinx.serialization.MissingFieldException
import kotlinx.serialization.Serializable 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.Jce
import net.mamoe.mirai.qqandroid.io.serialization.jce.JceId import net.mamoe.mirai.qqandroid.io.serialization.jce.JceId
import net.mamoe.mirai.qqandroid.io.serialization.jce.JceInput import net.mamoe.mirai.qqandroid.io.serialization.jce.JceInput
@ -42,6 +43,48 @@ internal const val ZERO_TYPE: Byte = 12
@Suppress("INVISIBLE_MEMBER") // bug @Suppress("INVISIBLE_MEMBER") // bug
internal class JceInputTest { 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 @Test
fun testSkippingMap() { fun testSkippingMap() {
@Serializable @Serializable