From 2260d331d402d09e97562fb1002ac6bc0c90a1d0 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 6 Mar 2020 18:33:01 +0800 Subject: [PATCH] Support ByteArray --- .../io/serialization/jce/JceDecoder.kt | 63 ++++++------ .../JceInputTest.kt | 95 ++++++++++++++++--- 2 files changed, 119 insertions(+), 39 deletions(-) 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 05fd2529a..35047be7d 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 @@ -35,6 +35,7 @@ internal class JceDecoder( this.getElementDescriptor(index).isNullable ) } + fun SerialDescriptor.getJceTagId(index: Int): Int { return getElementAnnotations(index).filterIsInstance().single().id } @@ -43,26 +44,32 @@ internal class JceDecoder( private val ByteArraySerializer = ByteArraySerializer() // TODO: 2020/3/6 can be object - private inner class SimpleByteArrayReader() : CompositeDecoder by this { + private inner class SimpleByteArrayReader : CompositeDecoder by this { override fun decodeSequentially(): Boolean = true override fun decodeByteElement(descriptor: SerialDescriptor, index: Int): Byte { - return jce.input.readByte() + println("decodeByteElement: $index") + return jce.input.readByte().also { println("read: $it") } } override fun decodeElementIndex(descriptor: SerialDescriptor): Int { - return 0 + error("should not be reached") } override fun decodeCollectionSize(descriptor: SerialDescriptor): Int { - return jce.useHead { jce.readJceIntValue(it) } + // 不要读下一个 head + return jce.currentHead.let { jce.readJceIntValue(it) }.also { println("simpleListSize=$it") } } } // TODO: 2020/3/6 can be object - private inner class ListReader() : CompositeDecoder by this { - override fun decodeSequentially(): Boolean = false + private inner class ListReader : CompositeDecoder by this { + override fun decodeSequentially(): Boolean = true override fun decodeElementIndex(descriptor: SerialDescriptor): Int { - return 0 + error("should not be reached") + } + + override fun endStructure(descriptor: SerialDescriptor) { + println("endStructure: ${descriptor.serialName}") } override fun decodeCollectionSize(descriptor: SerialDescriptor): Int { @@ -70,12 +77,24 @@ internal class JceDecoder( } } - override fun beginStructure(descriptor: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder { + override fun endStructure(descriptor: SerialDescriptor) { if (descriptor == ByteArraySerializer.descriptor) { + jce.prepareNextHead() // list 里面没读 head + } + super.endStructure(descriptor) + } + + override fun beginStructure(descriptor: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder { + println("beginStructure: ${descriptor.serialName}") + if (descriptor == ByteArraySerializer.descriptor) { + println("!! ByteArray") + println("decoderTag: $currentTagOrNull") + println("jceHead: " + jce.currentHeadOrNull) return jce.skipToHeadAndUseIfPossibleOrFail(popTag().id) { + println("listHead: $it") when (it.type) { - Jce.SIMPLE_LIST -> SimpleByteArrayReader() - Jce.LIST -> ListReader() + Jce.SIMPLE_LIST -> SimpleByteArrayReader().also { jce.prepareNextHead() } // 无用的元素类型 + Jce.LIST -> error(""); //ListReader() else -> error("type mismatch. Expected SIMPLE_LIST or LIST, got ${it.type} instead") } } @@ -86,22 +105,22 @@ internal class JceDecoder( } StructureKind.LIST -> ListReader() - else -> this + else -> this@JceDecoder } } override fun decodeSerializableValue(deserializer: DeserializationStrategy): T { - println("decodeSerializableValue: ${deserializer.descriptor}") + println( + "decodeSerializableValue: ${deserializer.descriptor.toString().substringBefore('(') + .substringAfterLast('.')}" + ) return super.decodeSerializableValue(deserializer) } - private var currentIndex = 0 - - override fun decodeSequentially(): Boolean = false override fun decodeElementIndex(descriptor: SerialDescriptor): Int { val jceHead = jce.currentHeadOrNull ?: return CompositeDecoder.READ_DONE - repeat(descriptor.elementsCount){ + repeat(descriptor.elementsCount) { val tag = descriptor.getJceTagId(it) if (tag == jceHead.tag) { return it @@ -116,10 +135,6 @@ internal class JceDecoder( return super.decodeTaggedNull(tag) } - override fun decodeNullableSerializableValue(deserializer: DeserializationStrategy): T? { - return super.decodeNullableSerializableValue(deserializer) - } - override fun decodeTaggedValue(tag: JceTag): Any { println("decodeTaggedValue") return super.decodeTaggedValue(tag) @@ -149,14 +164,6 @@ internal class JceDecoder( override fun decodeTaggedString(tag: JceTag): String = jce.skipToHeadAndUseIfPossibleOrFail(tag.id) { jce.readJceStringValue(it) } - override fun decodeTaggedEnum(tag: JceTag, enumDescription: SerialDescriptor): Int { - return super.decodeTaggedEnum(tag, enumDescription) - } - - override fun decodeTaggedChar(tag: JceTag): Char { - return super.decodeTaggedChar(tag) - } - override fun decodeTaggedNotNullMark(tag: JceTag): Boolean { return jce.skipToHeadOrNull(tag.id) != null } 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 54a438c73..63b7d54f7 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 @@ -3,6 +3,7 @@ package net.mamoe.mirai.qqandroid.io.serialization import io.ktor.utils.io.core.* +import kotlinx.serialization.MissingFieldException import kotlinx.serialization.Serializable import net.mamoe.mirai.qqandroid.io.serialization.jce.JceId import net.mamoe.mirai.qqandroid.io.serialization.jce.JceInput @@ -38,20 +39,73 @@ internal const val ZERO_TYPE: Byte = 12 */ @Suppress("INVISIBLE_MEMBER") // bug internal class JceInputTest { - @Serializable - data class TestSerializableClassA( - @JceId(0) val byte: Byte = 66, - @JceId(1) val short: Short = 123, - @JceId(3) val int: Int = 123456, - @JceId(8) val float: Float = 123f, - @JceId(15) val long: Long = 123456789123456789L, - @JceId(16) val double: Double = 123456.0, - @JceId(17) val boolean: Boolean = true, - @JceId(11111) val nullable: Int? = null - ) + + @Test + fun testSimpleByteArray() { + @Serializable + data class TestSerializableClassA( + @JceId(0) val byteArray: ByteArray = byteArrayOf(1, 2, 3), + @JceId(3) val byteArray2: ByteArray = byteArrayOf(1, 2, 3, 4) + ) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + + other as TestSerializableClassA + + if (!byteArray.contentEquals(other.byteArray)) return false + if (!byteArray2.contentEquals(other.byteArray2)) return false + + return true + } + + override fun hashCode(): Int { + var result = byteArray.contentHashCode() + result = 31 * result + byteArray2.contentHashCode() + return result + } + } + + val input = buildPacket { + writeJceHead(SIMPLE_LIST, 0) + writeJceHead(BYTE, 0) + + writeJceHead(BYTE, 0) + byteArrayOf(1, 2, 3).let { + writeByte(it.size.toByte()) + writeFully(it) + } + + writeJceHead(SIMPLE_LIST, 3) + writeJceHead(BYTE, 0) + + writeJceHead(BYTE, 0) + byteArrayOf(1, 2, 3, 4).let { + writeByte(it.size.toByte()) + writeFully(it) + } + } + + assertEquals(TestSerializableClassA(), JceNew.UTF_8.load(TestSerializableClassA.serializer(), input)) + } + @Test fun testSerializableClassA() { + @Serializable + data class TestSerializableClassA( + @JceId(0) val byte: Byte = 66, + @JceId(1) val short: Short = 123, + @JceId(3) val int: Int = 123456, + @JceId(8) val float: Float = 123f, + @JceId(15) val long: Long = 123456789123456789L, + @JceId(16) val double: Double = 123456.0, + @JceId(17) val boolean: Boolean = true, + @JceId(11111) val nullable: Int? = null, + @JceId(111112) val nullable2: Int? = null, + @JceId(111113) val optional: Int = 123 + ) + val input = buildPacket { writeJceHead(BYTE, 0) writeByte(66) @@ -72,6 +126,25 @@ internal class JceInputTest { assertEquals(TestSerializableClassA(), JceNew.UTF_8.load(TestSerializableClassA.serializer(), input)) } + @Test + fun testNoSuchField() { + @Serializable + data class TestSerializableClassA( + @JceId(0) val byte: Byte = 66, + @JceId(1) val short: Short = 123, + @JceId(3) val int: Int + ) + + val input = buildPacket { + writeJceHead(BYTE, 0) + writeByte(66) + writeJceHead(SHORT, 1) + writeShort(123) + } + + assertFailsWith { JceNew.UTF_8.load(TestSerializableClassA.serializer(), input) } + } + @Test fun testHeadSkip() { val input = JceInput(buildPacket {