diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/serialization/jce/JceDecoder.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/serialization/jce/JceDecoder.kt index 53ac4289e..dd963fc4e 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/serialization/jce/JceDecoder.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/serialization/jce/JceDecoder.kt @@ -181,9 +181,9 @@ internal class JceDecoder( companion object { @Suppress("MemberVisibilityCanBePrivate") - val debuggingMode: Boolean by lazy { false } + var debuggingMode: Boolean = false - private var structureHierarchy: Int = 0 + var structureHierarchy: Int = 0 inline fun println(value: () -> String) { if (debuggingMode) { @@ -258,7 +258,10 @@ internal class JceDecoder( override fun decodeSequentially(): Boolean = false override fun decodeElementIndex(descriptor: SerialDescriptor): Int { - var jceHead = jce.currentHeadOrNull ?: return CompositeDecoder.READ_DONE + var jceHead = jce.currentHeadOrNull ?: kotlin.run { + println("decodeElementIndex: currentHead == null") + return CompositeDecoder.READ_DONE + } println { "decodeElementIndex: ${jce.currentHead}" } while (!jce.input.endOfInput) { diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/serialization/jce/JceInput.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/serialization/jce/JceInput.kt index 1a3334fe7..595b41269 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/serialization/jce/JceInput.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/serialization/jce/JceInput.kt @@ -116,62 +116,69 @@ internal class JceInput( @OptIn(ExperimentalUnsignedTypes::class) @PublishedApi - internal fun skipField(type: Byte): Unit = when (type) { - Jce.BYTE -> this.input.discardExact(1) - Jce.SHORT -> this.input.discardExact(2) - Jce.INT -> this.input.discardExact(4) - Jce.LONG -> this.input.discardExact(8) - Jce.FLOAT -> this.input.discardExact(4) - Jce.DOUBLE -> this.input.discardExact(8) - 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) - } * 2) { - val currentHead = currentHead - prepareNextHead() - skipField(currentHead.type) + internal fun skipField(type: Byte): Unit { + JceDecoder.println { "skipping ${JceHead.findJceTypeName(type)}" } + when (type) { + Jce.BYTE -> this.input.discardExact(1) + Jce.SHORT -> this.input.discardExact(2) + Jce.INT -> println("readInt=" + this.input.readInt()) + Jce.LONG -> this.input.discardExact(8) + Jce.FLOAT -> this.input.discardExact(4) + Jce.DOUBLE -> this.input.discardExact(8) + Jce.STRING1 -> this.input.discardExact(this.input.readUByte().toInt()) + Jce.STRING4 -> this.input.discardExact(this.input.readInt()) + Jce.MAP -> { // map + JceDecoder.structureHierarchy++ + var count: Int = 0 + nextHead() // avoid shadowing, don't remove + repeat(skipToHeadAndUseIfPossibleOrFail(0, message = { "tag 0 not found when skipping map" }) { + readJceIntValue(it).also { count = it * 2 } + } * 2) { + skipField(currentHead.type) + if (it != count - 1) { // don't read last head + nextHead() + } + } + JceDecoder.structureHierarchy-- } - } - Jce.LIST -> { // list - JceDecoder.println {"skip list!"} - nextHead() - repeat(skipToHeadAndUseIfPossibleOrFail(0, message = { "tag 0 not found when skipping list" }) { - readJceIntValue(it) - }) { - val currentHead = currentHead - prepareNextHead() - skipField(currentHead.type) + Jce.LIST -> { // list + JceDecoder.structureHierarchy++ + var count: Int = 0 + nextHead() // avoid shadowing, don't remove + repeat(skipToHeadAndUseIfPossibleOrFail(0, message = { "tag 0 not found when skipping list" }) { head -> + readJceIntValue(head).also { count = it } + }) { + skipField(currentHead.type) + if (it != count - 1) { // don't read last head + nextHead() + } + } + JceDecoder.structureHierarchy-- } - } - Jce.STRUCT_BEGIN -> { - JceDecoder.println {"skip struct!"} - fun skipToStructEnd() { + Jce.STRUCT_BEGIN -> { + JceDecoder.structureHierarchy++ var head: JceHead do { head = nextHead() skipField(head.type) } while (head.type != Jce.STRUCT_END) + JceDecoder.structureHierarchy-- } - skipToStructEnd() - } - Jce.STRUCT_END, Jce.ZERO_TYPE -> { - Unit - } - Jce.SIMPLE_LIST -> { - 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}" } + Jce.STRUCT_END, Jce.ZERO_TYPE -> { + } + Jce.SIMPLE_LIST -> { + JceDecoder.structureHierarchy++ + 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)) + head = nextHead() + check(head.tag == 0) { "tag for size for simple list must be 0, but was ${head.tag}" } + this.input.discardExact(readJceIntValue(head)) + JceDecoder.structureHierarchy-- + } + else -> error("invalid type: $type") } - else -> error("invalid type: $type") } // region readers diff --git a/mirai-core-qqandroid/src/commonTest/kotlin/net.mamoe.mirai.qqandroid.io.serialization/JceInputTest.kt b/mirai-core-qqandroid/src/commonTest/kotlin/net.mamoe.mirai.qqandroid.io.serialization/JceInputTest.kt index f00ac3cfc..f49753793 100644 --- a/mirai-core-qqandroid/src/commonTest/kotlin/net.mamoe.mirai.qqandroid.io.serialization/JceInputTest.kt +++ b/mirai-core-qqandroid/src/commonTest/kotlin/net.mamoe.mirai.qqandroid.io.serialization/JceInputTest.kt @@ -8,10 +8,7 @@ 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 -import net.mamoe.mirai.qqandroid.io.serialization.jce.writeJceHead +import net.mamoe.mirai.qqandroid.io.serialization.jce.* import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith @@ -42,6 +39,9 @@ internal const val ZERO_TYPE: Byte = 12 */ @Suppress("INVISIBLE_MEMBER") // bug internal class JceInputTest { + init { + JceDecoder.debuggingMode = true + } @Test fun testIntToStructMap() { @@ -109,31 +109,33 @@ internal class JceInputTest { val input = buildPacket { writeJceHead(MAP, 0) // TestSerializableClassB writeJceHead(BYTE, 0) - writeByte(1) + writeByte(1); - writeJceHead(STRUCT_BEGIN, 0); { - writeJceHead(INT, 0) - writeInt(123) + writeJceHead(STRUCT_BEGIN, 0); + { + writeJceHead(INT, 0) + writeInt(123) - writeJceHead(STRUCT_BEGIN, 123); // TestSerializableClassC + writeJceHead(STRUCT_BEGIN, 123); // TestSerializableClassC + { + writeJceHead(INT, 5) + writeInt(123123) + }() + writeJceHead(STRUCT_END, 0) + + writeJceHead(INT, 5) + writeInt(9) + }() + writeJceHead(STRUCT_END, 0) + + writeJceHead(STRUCT_BEGIN, 1); { writeJceHead(INT, 5) writeInt(123123) }() writeJceHead(STRUCT_END, 0) - - writeJceHead(INT, 5) - writeInt(9) }() - writeJceHead(STRUCT_END, 0) - - writeJceHead(STRUCT_BEGIN, 1); - { - writeJceHead(INT, 5) - writeInt(123123) - }() - writeJceHead(STRUCT_END, 0) writeJceHead(STRING1, 1) writeByte(1)