From 1305709d3e62068bd466f9bf5ad3f13c3d9ca639 Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Mon, 4 May 2020 14:48:01 +0800
Subject: [PATCH] Extract JCE serialization to him188/jcekt. close #300

---
 build.gradle.kts                              |   1 +
 buildSrc/src/main/kotlin/Versions.kt          |   2 +
 mirai-core-qqandroid/build.gradle.kts         |   2 +
 .../network/protocol/data/jce/ConfigPush.kt   |   2 +-
 .../network/protocol/data/jce/FriendList.kt   |   2 +-
 .../network/protocol/data/jce/GroupMngReq.kt  |   2 +-
 .../network/protocol/data/jce/MsgType0x210.kt |   2 +-
 .../protocol/data/jce/OnlinePushPack.kt       |   2 +-
 .../protocol/data/jce/PushNotifyPack.kt       |   2 +-
 .../data/jce/RequestMSFForceOffline.kt        |   2 +-
 .../protocol/data/jce/RequestPacket.kt        |   2 +-
 .../data/jce/RequestPushForceOffline.kt       |   2 +-
 .../protocol/data/jce/SvcReqRegister.kt       |   2 +-
 .../network/protocol/data/jce/TroopList.kt    |   2 +-
 .../network/protocol/packet/login/StatSvc.kt  |   3 +-
 .../utils/io/serialization/IOFormat.kt        |  14 -
 .../utils/io/serialization/JceOld.kt          | 850 ------------------
 .../ProtoBufWithNullableSupport.kt            |   3 +
 .../utils/io/serialization/README.md          |  10 -
 .../utils/io/serialization/jce/JceDecoder.kt  | 330 -------
 .../utils/io/serialization/jce/JceInput.kt    | 263 ------
 .../utils/io/serialization/jce/JceNew.kt      |  84 --
 .../utils/io/serialization/jce/common.kt      | 118 ---
 .../qqandroid/utils/io/serialization/utils.kt |  31 +-
 .../io/serialization/JceInputTest.kt          | 651 --------------
 25 files changed, 30 insertions(+), 2354 deletions(-)
 delete mode 100644 mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/IOFormat.kt
 delete mode 100644 mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/JceOld.kt
 delete mode 100644 mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/README.md
 delete mode 100644 mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/jce/JceDecoder.kt
 delete mode 100644 mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/jce/JceInput.kt
 delete mode 100644 mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/jce/JceNew.kt
 delete mode 100644 mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/jce/common.kt
 delete mode 100644 mirai-core-qqandroid/src/commonTest/kotlin/net.mamoe.mirai.qqandroid/io/serialization/JceInputTest.kt

diff --git a/build.gradle.kts b/build.gradle.kts
index 7d2ef354c..415386428 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -53,6 +53,7 @@ allprojects {
     repositories {
         maven(url = "https://mirrors.huaweicloud.com/repository/maven")
         maven(url = "https://dl.bintray.com/kotlin/kotlin-eap")
+        maven(url = "https://dl.bintray.com/him188moe/jcekt")
         jcenter()
         google()
     }
diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt
index d48b87a6b..5d39b82fa 100644
--- a/buildSrc/src/main/kotlin/Versions.kt
+++ b/buildSrc/src/main/kotlin/Versions.kt
@@ -24,6 +24,8 @@ object Versions {
         const val dokka = "0.10.1"
     }
 
+    const val jcekt = "1.0.0"
+
     object Android {
         const val androidGradlePlugin = "3.5.3"
     }
diff --git a/mirai-core-qqandroid/build.gradle.kts b/mirai-core-qqandroid/build.gradle.kts
index e579b4b46..2778938e5 100644
--- a/mirai-core-qqandroid/build.gradle.kts
+++ b/mirai-core-qqandroid/build.gradle.kts
@@ -51,6 +51,7 @@ kotlin {
                 api(kotlin("stdlib", Versions.Kotlin.stdlib))
                 api(kotlinx("serialization-runtime-common", Versions.Kotlin.serialization))
                 api(kotlinx("serialization-protobuf-common", Versions.Kotlin.serialization))
+                api("moe.him188:jcekt-common:${Versions.jcekt}")
                 api("org.jetbrains.kotlinx:atomicfu:${Versions.Kotlin.atomicFU}")
                 api(kotlinx("io", Versions.Kotlin.io))
                 api(kotlinx("coroutines-io", Versions.Kotlin.coroutinesIo))
@@ -86,6 +87,7 @@ kotlin {
             dependencies {
                 runtimeOnly(files("build/classes/kotlin/jvm/main")) // classpath is not properly set by IDE
                 //    api(kotlinx("coroutines-debug", "1.3.5"))
+                api("moe.him188:jcekt:${Versions.jcekt}")
                 api(kotlinx("serialization-runtime", Versions.Kotlin.serialization))
                 //api(kotlinx("serialization-protobuf", Versions.Kotlin.serialization))
 
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/ConfigPush.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/ConfigPush.kt
index c1f86c96d..296a697d2 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/ConfigPush.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/ConfigPush.kt
@@ -10,9 +10,9 @@
 package net.mamoe.mirai.qqandroid.network.protocol.data.jce
 
 import kotlinx.serialization.Serializable
+import moe.him188.jcekt.JceId
 import net.mamoe.mirai.qqandroid.network.Packet
 import net.mamoe.mirai.qqandroid.utils.io.JceStruct
-import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.JceId
 import kotlin.jvm.JvmField
 
 @Serializable
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/FriendList.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/FriendList.kt
index b78e6b4d0..58d2267e4 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/FriendList.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/FriendList.kt
@@ -10,8 +10,8 @@
 package net.mamoe.mirai.qqandroid.network.protocol.data.jce
 
 import kotlinx.serialization.Serializable
+import moe.him188.jcekt.JceId
 import net.mamoe.mirai.qqandroid.utils.io.JceStruct
-import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.JceId
 import kotlin.jvm.JvmField
 
 @Serializable
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/GroupMngReq.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/GroupMngReq.kt
index 73def5748..aaf198b5a 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/GroupMngReq.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/GroupMngReq.kt
@@ -1,8 +1,8 @@
 package net.mamoe.mirai.qqandroid.network.protocol.data.jce
 
 import kotlinx.serialization.Serializable
+import moe.him188.jcekt.JceId
 import net.mamoe.mirai.qqandroid.utils.io.JceStruct
-import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.JceId
 import kotlin.jvm.JvmField
 
 @Serializable
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/MsgType0x210.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/MsgType0x210.kt
index 7f62e4ad7..72cba5272 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/MsgType0x210.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/MsgType0x210.kt
@@ -1,9 +1,9 @@
 package net.mamoe.mirai.qqandroid.network.protocol.data.jce
 
 import kotlinx.serialization.Serializable
+import moe.him188.jcekt.JceId
 import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
 import net.mamoe.mirai.qqandroid.utils.io.JceStruct
-import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.JceId
 import kotlin.jvm.JvmField
 
 @Serializable
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/OnlinePushPack.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/OnlinePushPack.kt
index 5359dc44a..6dad2bb9d 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/OnlinePushPack.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/OnlinePushPack.kt
@@ -10,8 +10,8 @@
 package net.mamoe.mirai.qqandroid.network.protocol.data.jce
 
 import kotlinx.serialization.Serializable
+import moe.him188.jcekt.JceId
 import net.mamoe.mirai.qqandroid.utils.io.JceStruct
-import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.JceId
 import kotlin.jvm.JvmField
 
 internal class OnlinePushPack {
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/PushNotifyPack.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/PushNotifyPack.kt
index 2b827da3b..dec460165 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/PushNotifyPack.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/PushNotifyPack.kt
@@ -10,10 +10,10 @@
 package net.mamoe.mirai.qqandroid.network.protocol.data.jce
 
 import kotlinx.serialization.Serializable
+import moe.him188.jcekt.JceId
 import net.mamoe.mirai.qqandroid.network.Packet
 import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
 import net.mamoe.mirai.qqandroid.utils.io.JceStruct
-import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.JceId
 import kotlin.jvm.JvmField
 
 @Suppress("ArrayInDataClass")
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/RequestMSFForceOffline.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/RequestMSFForceOffline.kt
index 474ecffe1..e9ebbf658 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/RequestMSFForceOffline.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/RequestMSFForceOffline.kt
@@ -1,8 +1,8 @@
 package net.mamoe.mirai.qqandroid.network.protocol.data.jce
 
 import kotlinx.serialization.Serializable
+import moe.him188.jcekt.JceId
 import net.mamoe.mirai.qqandroid.utils.io.JceStruct
-import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.JceId
 import kotlin.jvm.JvmField
 
 @Serializable
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/RequestPacket.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/RequestPacket.kt
index bb8d0b2b3..eba2a751c 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/RequestPacket.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/RequestPacket.kt
@@ -10,9 +10,9 @@
 package net.mamoe.mirai.qqandroid.network.protocol.data.jce
 
 import kotlinx.serialization.Serializable
+import moe.him188.jcekt.JceId
 import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
 import net.mamoe.mirai.qqandroid.utils.io.JceStruct
-import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.JceId
 import kotlin.jvm.JvmField
 
 private val EMPTY_MAP = mapOf<String, String>()
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/RequestPushForceOffline.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/RequestPushForceOffline.kt
index a8c31cefe..4a3f05ea3 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/RequestPushForceOffline.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/RequestPushForceOffline.kt
@@ -10,8 +10,8 @@
 package net.mamoe.mirai.qqandroid.network.protocol.data.jce
 
 import kotlinx.serialization.Serializable
+import moe.him188.jcekt.JceId
 import net.mamoe.mirai.qqandroid.utils.io.JceStruct
-import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.JceId
 import kotlin.jvm.JvmField
 
 @Serializable
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/SvcReqRegister.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/SvcReqRegister.kt
index fb44f55e1..24147c7a2 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/SvcReqRegister.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/SvcReqRegister.kt
@@ -10,8 +10,8 @@
 package net.mamoe.mirai.qqandroid.network.protocol.data.jce
 
 import kotlinx.serialization.Serializable
+import moe.him188.jcekt.JceId
 import net.mamoe.mirai.qqandroid.utils.io.JceStruct
-import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.JceId
 import kotlin.jvm.JvmField
 
 @Serializable
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/TroopList.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/TroopList.kt
index ed692d803..c87f3f9e6 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/TroopList.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/TroopList.kt
@@ -10,8 +10,8 @@
 package net.mamoe.mirai.qqandroid.network.protocol.data.jce
 
 import kotlinx.serialization.Serializable
+import moe.him188.jcekt.JceId
 import net.mamoe.mirai.qqandroid.utils.io.JceStruct
-import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.JceId
 import kotlin.jvm.JvmField
 
 @Serializable
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/StatSvc.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/StatSvc.kt
index 8fddfddbc..52733ddf2 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/StatSvc.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/StatSvc.kt
@@ -10,6 +10,7 @@
 package net.mamoe.mirai.qqandroid.network.protocol.packet.login
 
 import kotlinx.io.core.ByteReadPacket
+import kotlinx.serialization.protobuf.ProtoBuf
 import net.mamoe.mirai.event.events.BotOfflineEvent
 import net.mamoe.mirai.qqandroid.QQAndroidBot
 import net.mamoe.mirai.qqandroid.network.Packet
@@ -151,7 +152,7 @@ internal class StatSvc {
                                 var44.strVendorName = ROMUtil.getRomName();
                                 var44.strVendorOSName = ROMUtil.getRomVersion(20);
                                 */
-                                bytes_0x769_reqbody = ProtoBufWithNullableSupport.dump(
+                                bytes_0x769_reqbody = ProtoBuf.dump(
                                     Oidb0x769.RequestBody.serializer(), Oidb0x769.RequestBody(
                                         rpt_config_list = listOf(
                                             Oidb0x769.ConfigSeq(
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/IOFormat.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/IOFormat.kt
deleted file mode 100644
index 642cad758..000000000
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/IOFormat.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-package net.mamoe.mirai.qqandroid.utils.io.serialization
-
-import kotlinx.io.core.Input
-import kotlinx.io.core.Output
-import kotlinx.serialization.DeserializationStrategy
-import kotlinx.serialization.SerialFormat
-import kotlinx.serialization.SerializationStrategy
-
-internal interface IOFormat : SerialFormat {
-
-    fun <T> dumpTo(serializer: SerializationStrategy<T>, ojb: T, output: Output)
-
-    fun <T> load(deserializer: DeserializationStrategy<T>, input: Input): T
-}
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/JceOld.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/JceOld.kt
deleted file mode 100644
index b850ffa56..000000000
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/JceOld.kt
+++ /dev/null
@@ -1,850 +0,0 @@
-/*
- * Copyright 2020 Mamoe Technologies and contributors.
- *
- * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
- * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
- *
- * https://github.com/mamoe/mirai/blob/master/LICENSE
- */
-
-package net.mamoe.mirai.qqandroid.utils.io.serialization
-
-import kotlinx.io.charsets.Charset
-import kotlinx.io.core.*
-import kotlinx.serialization.*
-import kotlinx.serialization.builtins.ByteArraySerializer
-import kotlinx.serialization.builtins.MapEntrySerializer
-import kotlinx.serialization.builtins.SetSerializer
-import kotlinx.serialization.internal.*
-import kotlinx.serialization.modules.EmptyModule
-import kotlinx.serialization.modules.SerialModule
-import net.mamoe.mirai.qqandroid.utils.io.JceStruct
-import net.mamoe.mirai.qqandroid.utils.io.ProtoBuf
-import net.mamoe.mirai.qqandroid.utils.io.readString
-import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.Jce.Companion.BYTE
-import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.Jce.Companion.DOUBLE
-import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.Jce.Companion.FLOAT
-import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.Jce.Companion.INT
-import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.Jce.Companion.JCE_MAX_STRING_LENGTH
-import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.Jce.Companion.LIST
-import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.Jce.Companion.LONG
-import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.Jce.Companion.MAP
-import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.Jce.Companion.SHORT
-import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.Jce.Companion.SIMPLE_LIST
-import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.Jce.Companion.STRING1
-import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.Jce.Companion.STRING4
-import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.Jce.Companion.STRUCT_BEGIN
-import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.Jce.Companion.STRUCT_END
-import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.Jce.Companion.ZERO_TYPE
-import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.JceHead
-import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.JceId
-import net.mamoe.mirai.qqandroid.utils.toReadPacket
-
-@PublishedApi
-internal val CharsetGBK = Charset.forName("GBK")
-
-@PublishedApi
-internal val CharsetUTF8 = Charset.forName("UTF8")
-
-internal enum class JceCharset(val kotlinCharset: Charset) {
-    GBK(Charset.forName("GBK")),
-    UTF8(Charset.forName("UTF8"))
-}
-
-internal fun getSerialId(desc: SerialDescriptor, index: Int): Int? = desc.findAnnotation<JceId>(index)?.id
-
-/**
- * Jce 数据结构序列化和反序列化工具, 能将 kotlinx.serialization 通用的注解标记格式的 `class` 序列化为 [ByteArray]
- */
-@Suppress("DEPRECATION_ERROR")
-@OptIn(InternalSerializationApi::class)
-internal class JceOld private constructor(private val charset: JceCharset, override val context: SerialModule = EmptyModule) :
-    SerialFormat, BinaryFormat {
-
-    private inner class ListWriter(
-        private val count: Int,
-        private val tag: Int,
-        private val parentEncoder: JceEncoder
-    ) : JceEncoder(BytePacketBuilder()) {
-        override fun SerialDescriptor.getTag(index: Int): Int {
-            return 0
-        }
-
-        override fun endEncode(descriptor: SerialDescriptor) {
-            parentEncoder.writeHead(LIST, this.tag)
-            parentEncoder.encodeTaggedInt(0, count)
-            parentEncoder.output.writePacket(this.output.build())
-        }
-    }
-
-    private inner class JceMapWriter(
-        output: BytePacketBuilder
-    ) : JceEncoder(output) {
-        override fun SerialDescriptor.getTag(index: Int): Int {
-            return if (index % 2 == 0) 0 else 1
-        }
-
-        /*
-        override fun endEncode(desc: SerialDescriptor) {
-            parentEncoder.writeHead(MAP, this.tag)
-            parentEncoder.encodeTaggedInt(Int.STUB_FOR_PRIMITIVE_NUMBERS_GBK, count)
-            // println(this.output.toByteArray().toUHexString())
-            parentEncoder.output.write(this.output.toByteArray())
-        }*/
-
-        override fun beginCollection(
-            descriptor: SerialDescriptor,
-            collectionSize: Int,
-            vararg typeSerializers: KSerializer<*>
-        ): CompositeEncoder {
-            return this
-        }
-
-        override fun beginStructure(
-            descriptor: SerialDescriptor,
-            vararg typeSerializers: KSerializer<*>
-        ): CompositeEncoder {
-            return this
-        }
-    }
-
-    /**
-     * From: com.qq.taf.jce.JceOutputStream
-     */
-    @Suppress("unused", "MemberVisibilityCanBePrivate")
-    @OptIn(ExperimentalIoApi::class)
-    private open inner class JceEncoder(
-        internal val output: BytePacketBuilder
-    ) : TaggedEncoder<Int>() {
-        override val context get() = this@JceOld.context
-
-        override fun SerialDescriptor.getTag(index: Int): Int {
-            return getSerialId(this, index) ?: error("cannot find @SerialId")
-        }
-
-        /**
-         * 序列化最开始的时候的
-         */
-        override fun beginStructure(
-            descriptor: SerialDescriptor,
-            vararg typeSerializers: KSerializer<*>
-        ): CompositeEncoder =
-            when (descriptor.kind) {
-                StructureKind.LIST -> this
-                StructureKind.MAP -> this
-                StructureKind.CLASS, StructureKind.OBJECT -> this
-                is PolymorphicKind -> this
-                else -> throw SerializationException("Primitives are not supported at top-level")
-            }
-
-        @OptIn(ImplicitReflectionSerializer::class)
-        @Suppress("UNCHECKED_CAST", "NAME_SHADOWING")
-        override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) = when {
-            serializer.descriptor.kind == StructureKind.MAP -> {
-                try {
-                    val entries = (value as Map<*, *>).entries
-                    val serializer = (serializer as MapLikeSerializer<Any?, Any?, T, *>)
-                    val mapEntrySerial = MapEntrySerializer(serializer.keySerializer, serializer.valueSerializer)
-
-                    this.writeHead(MAP, currentTag)
-                    this.encodeTaggedInt(0, entries.count())
-                    SetSerializer(mapEntrySerial).serialize(JceMapWriter(this.output), entries)
-                } catch (e: Exception) {
-                    super.encodeSerializableValue(serializer, value)
-                }
-            }
-            serializer.descriptor.kind == StructureKind.LIST
-                    && value is ByteArray -> encodeTaggedByteArray(popTag(), value as ByteArray)
-            serializer.descriptor.kind == StructureKind.LIST
-                    && serializer.descriptor.getElementDescriptor(0) is PrimitiveKind -> {
-                serializer.serialize(
-                    ListWriter(
-                        when (value) {
-                            is ShortArray -> value.size
-                            is IntArray -> value.size
-                            is LongArray -> value.size
-                            is FloatArray -> value.size
-                            is DoubleArray -> value.size
-                            is CharArray -> value.size
-                            is ByteArray -> value.size
-                            is BooleanArray -> value.size
-                            else -> error("unknown array type: ${value.getClassName()}")
-                        }, popTag(), this
-                    ),
-                    value
-                )
-            }
-            serializer.descriptor.kind == StructureKind.LIST && value is Array<*> -> {
-                if (serializer.descriptor.getElementDescriptor(0).kind is PrimitiveKind.BYTE) {
-                    encodeTaggedByteArray(popTag(), (value as Array<Byte>).toByteArray())
-                } else
-                    serializer.serialize(
-                        ListWriter((value as Array<*>).size, popTag(), this),
-                        value
-                    )
-            }
-            serializer.descriptor.kind == StructureKind.LIST -> {
-                serializer.serialize(
-                    ListWriter((value as Collection<*>).size, popTag(), this),
-                    value
-                )
-            }
-            else -> {
-                if (value is JceStruct) {
-                    if (currentTagOrNull == null) {
-                        serializer.serialize(this, value)
-                    } else {
-                        this.writeHead(STRUCT_BEGIN, popTag())
-                        serializer.serialize(JceEncoder(this.output), value)
-                        this.writeHead(STRUCT_END, 0)
-                    }
-                } else if (value is ProtoBuf) {
-                    this.encodeTaggedByteArray(popTag(), ProtoBufWithNullableSupport.dump(value))
-                } else {
-                    serializer.serialize(this, value)
-                }
-            }
-        }
-
-        override fun encodeTaggedByte(tag: Int, value: Byte) {
-            if (value.toInt() == 0) {
-                writeHead(ZERO_TYPE, tag)
-            } else {
-                writeHead(BYTE, tag)
-                output.writeByte(value)
-            }
-        }
-
-        override fun encodeTaggedShort(tag: Int, value: Short) {
-            if (value in Byte.MIN_VALUE..Byte.MAX_VALUE) {
-                encodeTaggedByte(tag, value.toByte())
-            } else {
-                writeHead(SHORT, tag)
-                output.writeShort(value)
-            }
-        }
-
-        override fun encodeTaggedInt(tag: Int, value: Int) {
-            if (value in Short.MIN_VALUE..Short.MAX_VALUE) {
-                encodeTaggedShort(tag, value.toShort())
-            } else {
-                writeHead(INT, tag)
-                output.writeInt(value)
-            }
-        }
-
-        override fun encodeTaggedFloat(tag: Int, value: Float) {
-            writeHead(FLOAT, tag)
-            output.writeFloat(value)
-        }
-
-        override fun encodeTaggedDouble(tag: Int, value: Double) {
-            writeHead(DOUBLE, tag)
-            output.writeDouble(value)
-        }
-
-        override fun encodeTaggedLong(tag: Int, value: Long) {
-            if (value in Int.MIN_VALUE..Int.MAX_VALUE) {
-                encodeTaggedInt(tag, value.toInt())
-            } else {
-                writeHead(LONG, tag)
-                output.writeLong(value)
-            }
-        }
-
-        override fun encodeTaggedBoolean(tag: Int, value: Boolean) {
-            encodeTaggedByte(tag, if (value) 1 else 0)
-        }
-
-        override fun encodeTaggedChar(tag: Int, value: Char) {
-            encodeTaggedByte(tag, value.toByte())
-        }
-
-        override fun encodeTaggedEnum(tag: Int, enumDescription: SerialDescriptor, ordinal: Int) {
-            encodeTaggedInt(tag, ordinal)
-        }
-
-        override fun encodeTaggedNull(tag: Int) {
-        }
-
-        override fun encodeTaggedUnit(tag: Int) {
-            encodeTaggedNull(tag)
-        }
-
-        fun encodeTaggedByteArray(tag: Int, bytes: ByteArray) {
-            writeHead(SIMPLE_LIST, tag)
-            writeHead(BYTE, 0)
-            encodeTaggedInt(0, bytes.size)
-            output.writeFully(bytes)
-        }
-
-        override fun encodeTaggedString(tag: Int, value: String) {
-            require(value.length <= JCE_MAX_STRING_LENGTH) { "string is too long for tag $tag" }
-            val array = value.toByteArray(charset.kotlinCharset)
-            if (array.size > 255) {
-                writeHead(STRING4, tag)
-                output.writeInt(array.size)
-                output.writeFully(array)
-            } else {
-                writeHead(STRING1, tag)
-                output.writeByte(array.size.toByte()) // one byte
-                output.writeFully(array)
-            }
-        }
-
-        override fun encodeTaggedValue(tag: Int, value: Any) {
-            when (value) {
-                is Byte -> encodeTaggedByte(tag, value)
-                is Short -> encodeTaggedShort(tag, value)
-                is Int -> encodeTaggedInt(tag, value)
-                is Long -> encodeTaggedLong(tag, value)
-                is Float -> encodeTaggedFloat(tag, value)
-                is Double -> encodeTaggedDouble(tag, value)
-                is Boolean -> encodeTaggedBoolean(tag, value)
-                is String -> encodeTaggedString(tag, value)
-                is Unit -> {
-                }
-                else -> error("unsupported type: ${value.getClassName()}")
-            }
-        }
-
-        @PublishedApi
-        internal fun writeHead(type: Byte, tag: Int) {
-            if (tag < 15) {
-                this.output.writeByte(((tag shl 4) or type.toInt()).toByte())
-                return
-            }
-            if (tag < 256) {
-                this.output.writeByte((type.toInt() or 0xF0).toByte())
-                this.output.writeByte(tag.toByte())
-                return
-            }
-            error("tag is too large: $tag")
-        }
-    }
-
-    private open inner class JceMapReader(
-        val size: Int,
-        input: JceInput
-    ) : JceDecoder(input) {
-        override fun decodeCollectionSize(descriptor: SerialDescriptor): Int {
-            return size
-        }
-
-        override fun SerialDescriptor.getTag(index: Int): Int {
-            // 奇数 0, 即 key; 偶数 1, 即 value
-            return if (index % 2 == 0) 0 else 1
-        }
-    }
-
-    private open inner class JceListReader(
-        val size: Int,
-        input: JceInput
-    ) : JceDecoder(input) {
-        override fun decodeCollectionSize(descriptor: SerialDescriptor): Int {
-            return size
-        }
-
-        override fun SerialDescriptor.getTag(index: Int): Int {
-            return 0
-        }
-    }
-
-    private open inner class JceStructReader(
-        input: JceInput
-    ) : JceDecoder(input) {
-        override fun endStructure(descriptor: SerialDescriptor) {
-
-        }
-    }
-
-    private open inner class NullReader(
-        input: JceInput
-    ) : JceDecoder(input)
-
-    private open inner class JceDecoder(
-        internal val input: JceInput
-    ) : TaggedDecoder<Int>() {
-        override fun SerialDescriptor.getTag(index: Int): Int {
-            return getSerialId(this, index) ?: error("cannot find tag with index $index")
-        }
-
-        override fun decodeTaggedByte(tag: Int): Byte = input.readByte(tag)
-        override fun decodeTaggedShort(tag: Int): Short = input.readShort(tag)
-        override fun decodeTaggedInt(tag: Int): Int = input.readInt(tag)
-        override fun decodeTaggedLong(tag: Int): Long = input.readLong(tag)
-        override fun decodeTaggedFloat(tag: Int): Float = input.readFloat(tag)
-        override fun decodeTaggedDouble(tag: Int): Double = input.readDouble(tag)
-        override fun decodeTaggedChar(tag: Int): Char = input.readByte(tag).toChar()
-        override fun decodeTaggedString(tag: Int): String = input.readString(tag)
-        override fun decodeTaggedBoolean(tag: Int): Boolean = input.readBoolean(tag)
-
-        override fun decodeTaggedEnum(tag: Int, enumDescription: SerialDescriptor): Int {
-            return input.readInt(tag)
-        }
-
-        override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
-            return 0
-        }
-
-        /**
-         * 在 [KSerializer.serialize] 前
-         */
-        override fun beginStructure(descriptor: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder {
-            //// println("beginStructure: desc=${desc.getClassName()}, typeParams: ${typeParams.contentToString()}")
-            when {
-                // 由于 Byte 的数组有两种方式写入, 需特定读取器
-                descriptor.kind == StructureKind.LIST
-                        && descriptor.getElementDescriptor(0).kind == PrimitiveKind.BYTE -> {
-                    // ByteArray, 交给 decodeSerializableValue 进行处理
-                    return this
-                }
-                descriptor.kind == StructureKind.LIST -> {
-                    // if (typeParams.isNotEmpty() && typeParams[0] is ByteSerializer) {
-                    //     // Array<Byte>
-                    //     return this // 交给 decodeSerializableValue
-                    // }
-
-                    val tag = currentTagOrNull
-                    @Suppress("SENSELESS_COMPARISON") // 推断 bug
-                    if (tag != null && input.skipToTagOrNull(tag) {
-                            popTag()
-                            if (it.type == SIMPLE_LIST) {
-                                input.readHead() // list 里面元素类型, 没必要知道
-                            }
-                            return when (it.type) {
-                                SIMPLE_LIST, LIST -> JceListReader(input.readInt(0), this.input)
-                                MAP -> JceMapReader(input.readInt(0), this.input)
-                                else -> error("type mismatch")
-                            }
-                        } == null && descriptor.isNullable) {
-                        return NullReader(this.input)
-                    }
-                }
-
-                descriptor.kind == StructureKind.MAP -> {
-                    val tag = currentTagOrNull
-                    if (tag != null) {
-                        popTag()
-                    }
-                    return JceMapReader(input.readInt(0), this.input)
-                }
-            }
-
-            val tag = currentTagOrNull
-            val jceHead = input.peakHeadOrNull()
-            if (tag != null && (jceHead == null || jceHead.tag > tag)) {
-                return NullReader(this.input)
-            }
-
-            return super.beginStructure(descriptor, *typeParams)
-        }
-
-        override fun decodeTaggedNull(tag: Int): Nothing? {
-            return null
-        }
-
-        override fun decodeTaggedNotNullMark(tag: Int): Boolean {
-            return !isTagMissing(tag)
-        }
-
-        fun isTagMissing(tag: Int): Boolean {
-            val head = input.peakHeadOrNull()
-            return input.isEndOfInput || head == null || head.tag > tag
-        }
-
-        @Suppress("UNCHECKED_CAST")
-        override fun <T : Any> decodeNullableSerializableValue(deserializer: DeserializationStrategy<T?>): T? {
-            //
-            println("decodeNullableSerializableValue: ${deserializer::class.qualifiedName}")
-            if (deserializer is NullReader) {
-                return null
-            }
-            currentTagOrNull?.let {
-                if (this.isTagMissing(it)) {
-                    return null
-                }
-            }
-            when {
-                deserializer.descriptor == ByteArraySerializer().descriptor -> {
-                    val tag = popTag()
-                    return if (isTagMissing(tag)) input.readByteArrayOrNull(tag) as? T
-                    else input.readByteArray(tag) as T
-                }
-                deserializer.descriptor.kind == StructureKind.LIST -> {
-                    if (deserializer is ReferenceArraySerializer<*, *>
-                        && (deserializer as ListLikeSerializer<Any?, T, Any?>).typeParams.isNotEmpty()
-                        && (deserializer as ListLikeSerializer<Any?, T, Any?>).typeParams[0] is ByteSerializer
-                    ) {
-                        val tag = popTag()
-                        return if (isTagMissing(tag)) input.readByteArrayOrNull(tag)?.toTypedArray() as? T
-                        else input.readByteArray(tag).toTypedArray() as T
-                    } else if (deserializer is ArrayListSerializer<*>
-                        && (deserializer as ArrayListSerializer<*>).typeParams.isNotEmpty()
-                        && (deserializer as ArrayListSerializer<*>).typeParams[0] is ByteSerializer
-                    ) {
-                        val tag = popTag()
-                        return if (isTagMissing(tag)) input.readByteArrayOrNull(tag)?.toMutableList() as? T
-                        else input.readByteArray(tag).toMutableList() as T
-                    }
-                    val tag = currentTag
-//                    // println(tag)
-                    @Suppress("SENSELESS_COMPARISON") // false positive
-                    if (input.skipToTagOrNull(tag) {
-                            return deserializer.deserialize(JceListReader(input.readInt(0), input))
-                        } == null) {
-                        if (isTagMissing(tag)) {
-                            return null
-                        } else error("property is notnull but cannot find tag $tag")
-                    }
-                    error("UNREACHABLE CODE")
-                }
-                deserializer.descriptor.kind == StructureKind.MAP -> {
-                    val tag = popTag()
-                    @Suppress("SENSELESS_COMPARISON")
-                    if (input.skipToTagOrNull(tag) { head ->
-                            check(head.type == MAP) { "type mismatch: ${head.type}" }
-                            // 将 mapOf(k1 to v1, k2 to v2, ...) 转换为 listOf(k1, v1, k2, v2, ...) 以便于写入.
-                            val serializer = (deserializer as MapLikeSerializer<Any?, Any?, T, *>)
-                            val mapEntrySerial =
-                                MapEntrySerializer(serializer.keySerializer, serializer.valueSerializer)
-                            val setOfEntries =
-                                SetSerializer(mapEntrySerial).deserialize(JceMapReader(input.readInt(0), input))
-                            return setOfEntries.associateBy({ it.key }, { it.value }) as T
-                        } == null) {
-                        if (isTagMissing(tag)) {
-                            return null
-                        } else error("property is notnull but cannot find tag $tag")
-                    }
-                    error("UNREACHABLE CODE")
-                }
-            }
-
-            if (deserializer.descriptor.kind == StructureKind.CLASS || deserializer.descriptor.kind == StructureKind.OBJECT) {
-                val tag = currentTagOrNull
-                if (tag != null) {
-                    @Suppress("SENSELESS_COMPARISON") // 推断 bug
-                    if (input.skipToTagOrNull(tag) {
-                            check(it.type == STRUCT_BEGIN) { "type mismatch: ${it.type}" }
-                            //popTag()
-                            return deserializer.deserialize(JceStructReader(input)).also {
-                                while (input.input.canRead() && input.peakHeadOrNull()?.type != STRUCT_END) {
-                                    input.readHeadOrNull() ?: return@also
-                                }
-                                input.readHeadOrNull()
-                            }
-                        } == null && isTagMissing(tag)) {
-                        return null
-                    } else error("cannot find tag $tag")
-                }
-
-                return deserializer.deserialize(JceDecoder(this.input))
-            }
-
-            val tag = currentTagOrNull ?: return deserializer.deserialize(JceDecoder(this.input))
-            return if (!this.isTagMissing(tag)) {
-                try {
-                    deserializer.deserialize(this)
-                } catch (e: Exception) {
-                    println("exception when tag=$tag")
-                    throw e
-                }
-            } else {
-                // popTag()
-                null
-            }
-        }
-
-        @Suppress("UNCHECKED_CAST")
-        override fun <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>): T {
-            return decodeNullableSerializableValue(deserializer as DeserializationStrategy<Any?>) as? T
-                ?: error("value with tag $currentTagOrNull(by ${deserializer.getClassName()}) is not optional but cannot find. currentJceHead = ${input.currentJceHead}")
-        }
-    }
-
-
-    @OptIn(ExperimentalUnsignedTypes::class)
-    internal inner class JceInput(
-        @PublishedApi
-        internal val input: ByteReadPacket,
-        maxReadSize: Long = input.remaining
-    ) : Closeable {
-        private val leastRemaining = input.remaining - maxReadSize
-        internal val isEndOfInput: Boolean get() = input.remaining <= leastRemaining
-
-        internal var currentJceHead: JceHead? = input.doReadHead()
-
-        override fun close() = input.close()
-
-        internal fun peakHeadOrNull(): JceHead? = currentJceHead ?: readHeadOrNull()
-
-        @PublishedApi
-        internal fun readHead(): JceHead = readHeadOrNull() ?: error("no enough data to read head")
-
-        @PublishedApi
-        internal fun readHeadOrNull(): JceHead? = input.doReadHead()
-
-        /**
-         * 读取下一个 head 存储到 [currentJceHead]
-         */
-        private fun ByteReadPacket.doReadHead(): JceHead? {
-            if (isEndOfInput) {
-                currentJceHead = null
-                // println("doReadHead: endOfInput")
-                return null
-            }
-            val var2 = readUByte()
-            val type = var2 and 15u
-            var tag = var2.toUInt() shr 4
-            if (tag == 15u) {
-                if (isEndOfInput) {
-                    currentJceHead = null
-                    // println("doReadHead: endOfInput2")
-                    return null
-                }
-                tag = readUByte().toUInt()
-            }
-            currentJceHead = JceHead(
-                tag = tag.toInt(),
-                type = type.toByte()
-            )
-            // println("doReadHead: $currentJceHead")
-            return currentJceHead
-        }
-
-        fun readBoolean(tag: Int): Boolean =
-            readBooleanOrNull(tag) ?: error("cannot find tag $tag, currentJceHead=$currentJceHead")
-
-        fun readByte(tag: Int): Byte =
-            readByteOrNull(tag) ?: error("cannot find tag $tag, currentJceHead=$currentJceHead")
-
-        fun readShort(tag: Int): Short =
-            readShortOrNull(tag) ?: error("cannot find tag $tag, currentJceHead=$currentJceHead")
-
-        fun readInt(tag: Int): Int = readIntOrNull(tag) ?: error("cannot find tag $tag, currentJceHead=$currentJceHead")
-        fun readLong(tag: Int): Long =
-            readLongOrNull(tag) ?: error("cannot find tag $tag, currentJceHead=$currentJceHead")
-
-        fun readFloat(tag: Int): Float =
-            readFloatOrNull(tag) ?: error("cannot find tag $tag, currentJceHead=$currentJceHead")
-
-        fun readDouble(tag: Int): Double =
-            readDoubleOrNull(tag) ?: error("cannot find tag $tag, currentJceHead=$currentJceHead")
-
-        fun readString(tag: Int): String =
-            readStringOrNull(tag) ?: error("cannot find tag $tag, currentJceHead=$currentJceHead")
-
-        fun readByteArray(tag: Int): ByteArray =
-            readByteArrayOrNull(tag) ?: error("cannot find tag $tag, currentJceHead=$currentJceHead")
-
-        fun readByteArrayOrNull(tag: Int): ByteArray? = skipToTagOrNull(tag) {
-            when (it.type) {
-                LIST -> ByteArray(readInt(0)) { readByte(0) }
-                SIMPLE_LIST -> {
-                    val head = readHead()
-                    readHead()
-                    check(head.type.toInt() == 0) { "type mismatch, expected=0(Byte), got=${head.type}" }
-                    input.readBytes(readInt(0))
-                }
-                else -> error("type mismatch, expected=9(List), got=${it.type}")
-            }
-        }
-
-        private fun readStringOrNull(tag: Int): String? = skipToTagOrNull(tag) { head ->
-            return when (head.type) {
-                STRING1 -> input.readString(input.readUByte().toInt(), charset = charset.kotlinCharset)
-                STRING4 -> input.readString(
-                    input.readUInt().toInt().also { require(it in 1 until 104857600) { "bad string length: $it" } },
-                    charset = charset.kotlinCharset
-                )
-                else -> error("type mismatch: ${head.type}, expecting 6 or 7 (for string)")
-            }
-        }
-
-        private fun readLongOrNull(tag: Int): Long? = skipToTagOrNull(tag) {
-            return when (it.type) {
-                ZERO_TYPE -> 0
-                BYTE -> input.readByte().toLong()
-                SHORT -> input.readShort().toLong()
-                INT -> input.readInt().toLong()
-                LONG -> input.readLong()
-                else -> error("type mismatch ${it.type} when reading tag $tag")
-            }
-        }
-
-        private fun readShortOrNull(tag: Int): Short? = skipToTagOrNull(tag) {
-            return when (it.type.toInt()) {
-                12 -> 0
-                0 -> input.readByte().toShort()
-                1 -> input.readShort()
-                else -> error("type mismatch: ${it.type}")
-            }
-        }
-
-        private fun readIntOrNull(tag: Int): Int? = skipToTagOrNull(tag) {
-            return when (it.type.toInt()) {
-                12 -> 0
-                0 -> input.readByte().toInt()
-                1 -> input.readShort().toInt()
-                2 -> input.readInt()
-                else -> error("type mismatch: ${it.type}")
-            }
-        }
-
-        private fun readByteOrNull(tag: Int): Byte? = skipToTagOrNull(tag) {
-            return when (it.type.toInt()) {
-                12 -> 0
-                0 -> input.readByte()
-                else -> error("type mismatch")
-            }
-        }
-
-        private fun readFloatOrNull(tag: Int): Float? = skipToTagOrNull(tag) {
-            return when (it.type.toInt()) {
-                12 -> 0f
-                4 -> input.readFloat()
-                else -> error("type mismatch: ${it.type}")
-            }
-        }
-
-        private fun readDoubleOrNull(tag: Int): Double? = skipToTagOrNull(tag) {
-            return when (it.type.toInt()) {
-                12 -> 0.0
-                4 -> input.readFloat().toDouble()
-                5 -> input.readDouble()
-                else -> error("type mismatch: ${it.type}")
-            }
-        }
-
-        private fun readBooleanOrNull(tag: Int): Boolean? = this.readByteOrNull(tag)?.let { it.toInt() != 0 }
-
-
-        private fun skipField() {
-            skipField(readHead().type)
-        }
-
-        private fun skipToStructEnd() {
-            var head: JceHead
-            do {
-                head = readHead()
-                skipField(head.type)
-            } while (head.type.toInt() != 11)
-        }
-
-        @OptIn(ExperimentalUnsignedTypes::class)
-        @PublishedApi
-        internal fun skipField(type: Byte) = when (type.toInt()) {
-            0 -> this.input.discardExact(1)
-            1 -> this.input.discardExact(2)
-            2 -> this.input.discardExact(4)
-            3 -> this.input.discardExact(8)
-            4 -> this.input.discardExact(4)
-            5 -> this.input.discardExact(8)
-            6 -> this.input.discardExact(this.input.readUByte().toInt())
-            7 -> this.input.discardExact(this.input.readInt())
-            8 -> { // map
-                repeat(this.readInt(0) * 2) {
-                    skipField()
-                }
-            }
-            9 -> { // list
-                repeat(this.readInt(0)) {
-                    skipField()
-                }
-            }
-            10 -> this.skipToStructEnd()
-            11, 12 -> {
-
-            }
-            13 -> {
-                val head = readHead()
-                check(head.type.toInt() == 0) { "skipField with invalid type, type value: " + type + ", " + head.type }
-                this.input.discardExact(this.readInt(0))
-            }
-            else -> error("invalid type: $type")
-        }
-
-    }
-
-    @Suppress("MemberVisibilityCanBePrivate")
-    companion object {
-        val UTF8 =
-            JceOld(JceCharset.UTF8)
-        val GBK =
-            JceOld(JceCharset.GBK)
-
-        fun byCharSet(c: JceCharset): JceOld {
-            return if (c == JceCharset.UTF8) {
-                UTF8
-            } else {
-                GBK
-            }
-        }
-
-        private fun Any?.getClassName(): String =
-            (if (this == null) Unit::class else this::class).qualifiedName?.split(".")?.takeLast(2)?.joinToString(".")
-                ?: "<unnamed class>"
-    }
-
-    fun <T> dumpAsPacket(serializer: SerializationStrategy<T>, obj: T): ByteReadPacket {
-        val encoder = BytePacketBuilder()
-        val dumper = JceEncoder(encoder)
-        dumper.encode(serializer, obj)
-        return encoder.build()
-    }
-
-    override fun <T> dump(serializer: SerializationStrategy<T>, value: T): ByteArray {
-        return dumpAsPacket(serializer, value).readBytes()
-    }
-
-    /**
-     * 注意 close [packet]!!
-     */
-    fun <T> load(
-        deserializer: DeserializationStrategy<T>,
-        packet: ByteReadPacket,
-        length: Int = packet.remaining.toInt()
-    ): T {
-        return JceDecoder(JceInput(packet, length.toLong())).decode(deserializer)
-    }
-
-    override fun <T> load(deserializer: DeserializationStrategy<T>, bytes: ByteArray): T {
-        return bytes.toReadPacket().use {
-            val decoder = JceDecoder(JceInput(it))
-            decoder.decode(deserializer)
-        }
-    }
-}
-
-internal inline fun <R> JceOld.JceInput.skipToTagOrNull(tag: Int, block: (JceHead) -> R): R? {
-    // println("skipping to $tag start")
-    while (true) {
-        if (isEndOfInput) { // 读不了了
-            currentJceHead = null
-            // println("skipping to $tag: endOfInput")
-            return null
-        }
-
-        var head = currentJceHead
-        if (head == null) { // 没有新的 head 了
-            head = readHeadOrNull() ?: return null
-        }
-
-        if (head.tag > tag) {
-            // println("skipping to $tag: head.tag > tag")
-            return null
-        }
-        // readHead()
-        if (head.tag == tag) {
-            // readHeadOrNull()
-            currentJceHead = null
-            // println("skipping to $tag: run block")
-            return block(head)
-        }
-
-        // println("skipping to $tag: tag not matching")
-        // println("skipping to $tag: skipField")
-        this.skipField(head.type)
-        currentJceHead = readHeadOrNull()
-    }
-}
\ No newline at end of file
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/ProtoBufWithNullableSupport.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/ProtoBufWithNullableSupport.kt
index 66d45fad8..565030251 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/ProtoBufWithNullableSupport.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/ProtoBufWithNullableSupport.kt
@@ -23,10 +23,13 @@ import kotlinx.serialization.modules.SerialModule
 import kotlinx.serialization.protobuf.ProtoBuf
 import kotlinx.serialization.protobuf.ProtoNumberType
 import kotlinx.serialization.protobuf.ProtoType
+import moe.him188.jcekt.JceId
 import net.mamoe.mirai.qqandroid.utils.io.serialization.ProtoBufWithNullableSupport.Varint.encodeVarint
 
 internal typealias ProtoDesc = Pair<Int, ProtoNumberType>
 
+internal fun getSerialId(desc: SerialDescriptor, index: Int): Int? = desc.findAnnotation<JceId>(index)?.id
+
 internal fun extractParameters(desc: SerialDescriptor, index: Int, zeroBasedDefault: Boolean = false): ProtoDesc {
     val idx = getSerialId(desc, index) ?: (if (zeroBasedDefault) index else index + 1)
     val format = desc.findAnnotation<ProtoType>(index)?.type
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/README.md b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/README.md
deleted file mode 100644
index c82a41a5c..000000000
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/README.md
+++ /dev/null
@@ -1,10 +0,0 @@
-# io.serialization
-
-**序列化支持**
-
-包含:
-- QQ 的 JceStruct 相关的全自动序列化和反序列化: [Jce.kt](jce/JceNew.kt)
-- Protocol Buffers 的 optional 支持: [ProtoBufWithNullableSupport.kt](ProtoBufWithNullableSupport.kt)
-
-其中, `ProtoBufWithNullableSupport` 的绝大部分源码来自 `kotlinx.serialization`. 原著权归该项目作者所有.  
-Mirai 所做的修改已经标记上了 `MIRAI MODIFY START`
\ No newline at end of file
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/jce/JceDecoder.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/jce/JceDecoder.kt
deleted file mode 100644
index 58c2b7dbd..000000000
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/jce/JceDecoder.kt
+++ /dev/null
@@ -1,330 +0,0 @@
-/*
- * Copyright 2020 Mamoe Technologies and contributors.
- *
- * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
- * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
- *
- * https://github.com/mamoe/mirai/blob/master/LICENSE
- */
-
-@file:Suppress("PrivatePropertyName")
-
-package net.mamoe.mirai.qqandroid.utils.io.serialization.jce
-
-import kotlinx.serialization.*
-import kotlinx.serialization.builtins.AbstractDecoder
-import kotlinx.serialization.internal.TaggedDecoder
-import kotlinx.serialization.modules.SerialModule
-
-
-@OptIn(InternalSerializationApi::class) // 将来 kotlinx 修改后再复制过来 mirai.
-internal class JceDecoder(
-    val jce: JceInput, override val context: SerialModule
-) : TaggedDecoder<JceTag>() {
-    override val updateMode: UpdateMode
-        get() = UpdateMode.BANNED
-
-    override fun SerialDescriptor.getTag(index: Int): JceTag {
-        val annotations = this.getElementAnnotations(index)
-
-        val id = annotations.filterIsInstance<JceId>().single().id
-        // ?: error("cannot find @JceId or @ProtoId for ${this.getElementName(index)} in ${this.serialName}")
-        //println("getTag: ${this.getElementName(index)}=$id")
-
-        return JceTagCommon(id)
-    }
-
-    private fun SerialDescriptor.getJceTagId(index: Int): Int {
-        // higher performance, don't use filterIsInstance
-        val annotation = getElementAnnotations(index).firstOrNull { it is JceId }
-            ?: error("missing @JceId for ${getElementName(index)} in ${this.serialName}")
-        return (annotation as JceId).id
-
-    }
-
-    private val SimpleByteArrayReader: SimpleByteArrayReaderImpl = SimpleByteArrayReaderImpl()
-
-    private inner class SimpleByteArrayReaderImpl : AbstractDecoder() {
-        override fun decodeSequentially(): Boolean = true
-
-        override fun endStructure(descriptor: SerialDescriptor) {
-            this@JceDecoder.endStructure(descriptor)
-        }
-
-        override fun beginStructure(descriptor: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder {
-            this@JceDecoder.pushTag(JceTagListElement)
-            return this@JceDecoder.beginStructure(descriptor, *typeParams)
-        }
-
-        override fun decodeByte(): Byte = jce.input.readByte()
-        override fun decodeShort(): Short = error("illegal access")
-        override fun decodeInt(): Int = error("illegal access")
-        override fun decodeLong(): Long = error("illegal access")
-        override fun decodeFloat(): Float = error("illegal access")
-        override fun decodeDouble(): Double = error("illegal access")
-        override fun decodeBoolean(): Boolean = error("illegal access")
-        override fun decodeChar(): Char = error("illegal access")
-        override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = error("illegal access")
-        override fun decodeString(): String = error("illegal access")
-
-        override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
-            error("should not be reached")
-        }
-
-        override fun decodeCollectionSize(descriptor: SerialDescriptor): Int {
-            // 不要读下一个 head
-            return jce.currentHead.let { jce.readJceIntValue(it) }
-        }
-    }
-
-    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) {
-            this@JceDecoder.endStructure(descriptor)
-        }
-
-        override fun beginStructure(descriptor: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder {
-            this@JceDecoder.pushTag(JceTagListElement)
-
-            return this@JceDecoder.beginStructure(descriptor, *typeParams)
-        }
-
-        override fun decodeByte(): Byte = jce.useHead { jce.readJceByteValue(it) }
-        override fun decodeShort(): Short = jce.useHead { jce.readJceShortValue(it) }
-        override fun decodeInt(): Int = jce.useHead { jce.readJceIntValue(it) }
-        override fun decodeLong(): Long = jce.useHead { jce.readJceLongValue(it) }
-        override fun decodeFloat(): Float = jce.useHead { jce.readJceFloatValue(it) }
-        override fun decodeDouble(): Double = jce.useHead { jce.readJceDoubleValue(it) }
-        override fun decodeBoolean(): Boolean = jce.useHead { jce.readJceBooleanValue(it) }
-        override fun decodeChar(): Char = decodeByte().toChar()
-        override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = decodeInt()
-        override fun decodeString(): String = jce.useHead { jce.readJceStringValue(it) }
-
-        override fun decodeCollectionSize(descriptor: SerialDescriptor): Int {
-            //println("decodeCollectionSize: ${descriptor.serialName}")
-            // 不读下一个 head
-            return jce.useHead { jce.readJceIntValue(it) }
-        }
-    }
-
-
-    private val MapReader: MapReaderImpl = MapReaderImpl()
-
-    private inner class MapReaderImpl : AbstractDecoder() {
-        override fun decodeSequentially(): Boolean = true
-        override fun decodeElementIndex(descriptor: SerialDescriptor): Int = error("stub")
-
-        override fun endStructure(descriptor: SerialDescriptor) {
-            this@JceDecoder.endStructure(descriptor)
-        }
-
-        override fun beginStructure(descriptor: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeDecoder {
-            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)
-        }
-
-        override fun decodeByte(): Byte = jce.useHead { jce.readJceByteValue(it) }
-        override fun decodeShort(): Short = jce.useHead { jce.readJceShortValue(it) }
-        override fun decodeInt(): Int = jce.useHead { jce.readJceIntValue(it) }
-        override fun decodeLong(): Long = jce.useHead { jce.readJceLongValue(it) }
-        override fun decodeFloat(): Float = jce.useHead { jce.readJceFloatValue(it) }
-        override fun decodeDouble(): Double = jce.useHead { jce.readJceDoubleValue(it) }
-
-        override fun decodeBoolean(): Boolean = jce.useHead { jce.readJceBooleanValue(it) }
-        override fun decodeChar(): Char = decodeByte().toChar()
-        override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = decodeInt()
-        override fun decodeString(): String = jce.useHead { jce.readJceStringValue(it) }
-
-        override fun decodeCollectionSize(descriptor: SerialDescriptor): Int {
-            println { "decodeCollectionSize in MapReader: ${descriptor.serialName}" }
-            // 不读下一个 head
-            return jce.useHead { jce.readJceIntValue(it) }
-        }
-    }
-
-
-    override fun endStructure(descriptor: SerialDescriptor) {
-        structureHierarchy--
-        println { "endStructure: ${descriptor.serialName}" }
-        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) {
-                    jce.prepareNextHead()
-                    //println("current end")
-                    break
-                }
-                //println("current $currentHead")
-                jce.skipField(currentHead.type)
-                jce.prepareNextHead()
-            }
-            // pushTag(JceTag(0, true))
-            // skip STRUCT_END
-            // popTag()
-        }
-    }
-
-
-    companion object {
-        @Suppress("MemberVisibilityCanBePrivate")
-        var debuggingMode: Boolean = false
-
-        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}" }
-        structureHierarchy++
-        return when (descriptor.kind) {
-            is PrimitiveKind -> this@JceDecoder
-
-            StructureKind.MAP -> {
-                //println("!! MAP")
-                val tag = popTag()
-                return jce.skipToHeadAndUseIfPossibleOrFail(tag.id) {
-                    it.checkType(Jce.MAP, "beginStructure", tag, descriptor)
-                    MapReader
-                }
-            }
-            StructureKind.LIST -> {
-                //println("!! ByteArray")
-                //println("decoderTag: $currentTagOrNull")
-                //println("jceHead: " + jce.currentHeadOrNull)
-                return jce.skipToHeadAndUseIfPossibleOrFail(currentTag.id) {
-                    // don't check type. it's polymorphic
-
-                    //println("listHead: $it")
-                    when (it.type) {
-                        Jce.SIMPLE_LIST -> {
-                            currentTag.isSimpleByteArray = true
-                            jce.nextHead() // 无用的元素类型
-                            SimpleByteArrayReader
-                        }
-                        Jce.LIST -> ListReader
-                        else -> error("type mismatch. Expected SIMPLE_LIST or LIST, got $it instead")
-                    }
-                }
-            }
-            StructureKind.CLASS -> {
-                currentTagOrNull ?: return this@JceDecoder // outermost
-
-                //println("!! CLASS")
-                //println("decoderTag: $currentTag")
-                //println("jceHead: " + jce.currentHeadOrNull)
-                val tag = popTag()
-                return jce.skipToHeadAndUseIfPossibleOrFail(tag.id) { jceHead ->
-                    jceHead.checkType(Jce.STRUCT_BEGIN, "beginStructure", tag, descriptor)
-
-                    repeat(descriptor.elementsCount) {
-                        pushTag(descriptor.getTag(descriptor.elementsCount - it - 1)) // better performance
-                    }
-                    this // independent tag stack
-                }
-            }
-
-            StructureKind.OBJECT -> error("unsupported StructureKind.OBJECT: ${descriptor.serialName}")
-            is UnionKind -> error("unsupported UnionKind: ${descriptor.serialName}")
-            is PolymorphicKind -> error("unsupported PolymorphicKind: ${descriptor.serialName}")
-        }
-    }
-
-    override fun decodeSequentially(): Boolean = false
-    override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
-        var jceHead = jce.currentHeadOrNull ?: kotlin.run {
-            println("decodeElementIndex: currentHead == null")
-            return CompositeDecoder.READ_DONE
-        }
-
-        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
-    }
-
-    override fun decodeTaggedInt(tag: JceTag): Int =
-        kotlin.runCatching { jce.skipToHeadAndUseIfPossibleOrFail(tag.id) { jce.readJceIntValue(it) } }.getOrElse {
-            throw IllegalStateException("$tag", it)
-        }
-
-    override fun decodeTaggedByte(tag: JceTag): Byte =
-        kotlin.runCatching { jce.skipToHeadAndUseIfPossibleOrFail(tag.id) { jce.readJceByteValue(it) } }.getOrElse {
-            throw IllegalStateException("$tag", it)
-        }
-
-    override fun decodeTaggedBoolean(tag: JceTag): Boolean =
-        jce.skipToHeadAndUseIfPossibleOrFail(tag.id) { jce.readJceBooleanValue(it) }
-
-    override fun decodeTaggedFloat(tag: JceTag): Float =
-        jce.skipToHeadAndUseIfPossibleOrFail(tag.id) { jce.readJceFloatValue(it) }
-
-    override fun decodeTaggedDouble(tag: JceTag): Double =
-        jce.skipToHeadAndUseIfPossibleOrFail(tag.id) { jce.readJceDoubleValue(it) }
-
-    override fun decodeTaggedShort(tag: JceTag): Short =
-        jce.skipToHeadAndUseIfPossibleOrFail(tag.id) { jce.readJceShortValue(it) }
-
-    override fun decodeTaggedLong(tag: JceTag): Long =
-        jce.skipToHeadAndUseIfPossibleOrFail(tag.id) { jce.readJceLongValue(it) }
-
-    override fun decodeTaggedString(tag: JceTag): String =
-        jce.skipToHeadAndUseIfPossibleOrFail(tag.id) { jce.readJceStringValue(it) }
-
-    override fun decodeTaggedNotNullMark(tag: JceTag): Boolean {
-        return jce.skipToHeadOrNull(tag.id) != null
-    }
-}
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/jce/JceInput.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/jce/JceInput.kt
deleted file mode 100644
index ffdd2e54a..000000000
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/jce/JceInput.kt
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * Copyright 2020 Mamoe Technologies and contributors.
- *
- * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
- * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
- *
- * https://github.com/mamoe/mirai/blob/master/LICENSE
- */
-
-package net.mamoe.mirai.qqandroid.utils.io.serialization.jce
-
-import kotlinx.io.core.*
-import net.mamoe.mirai.qqandroid.utils.io.readString
-import net.mamoe.mirai.qqandroid.utils.io.serialization.JceCharset
-import net.mamoe.mirai.qqandroid.utils.toUHexString
-
-
-/**
- * Jce Input. 需要手动管理 head.
- */
-internal class JceInput(
-    val input: Input, val charset: JceCharset
-) {
-    private var _head: JceHead? = null
-
-    val currentHead: JceHead get() = _head ?: throw EOFException("No current JceHead available")
-    val currentHeadOrNull: JceHead? get() = _head
-
-    init {
-        prepareNextHead()
-    }
-
-    /**
-     * 读取下一个 [JceHead] 并保存. 可通过 [currentHead] 获取这个 [JceHead].
-     *
-     * @return 是否成功读取. 返回 `false` 时代表 [Input.endOfInput]
-     */
-    fun prepareNextHead(): Boolean {
-        return readNextHeadButDoNotAssignTo_Head().also { _head = it; } != null
-    }
-
-    fun nextHead(): JceHead {
-        if (!prepareNextHead()) {
-            throw EOFException("No more JceHead available")
-        }
-        return currentHead
-    }
-
-    /**
-     * 直接读取下一个 [JceHead] 并返回.
-     * 返回 `null` 则代表 [Input.endOfInput]
-     */
-    @Suppress("FunctionName")
-    @OptIn(ExperimentalUnsignedTypes::class)
-    private fun readNextHeadButDoNotAssignTo_Head(): JceHead? {
-        if (input.endOfInput) {
-            return null
-        }
-        val var2 = input.readUByte()
-        val type = var2 and 15u
-        var tag = var2.toUInt() shr 4
-        if (tag == 15u) {
-            tag = input.readUByte().toUInt()
-        }
-        return JceHead(
-            tag = tag.toInt(),
-            type = type.toByte()
-        )
-    }
-
-    /**
-     * 使用这个 [JceHead].
-     * [block] 结束后将会 [准备下一个 [JceHead]][prepareNextHead]
-     */
-    inline fun <R> useHead(crossinline block: (JceHead) -> R): R {
-        return currentHead.let(block).also { prepareNextHead() }
-    }
-
-    /**
-     * 跳过 [JceHead] 和对应的数据值, 直到找到 [tag], 否则返回 `null`
-     */
-    inline fun <R> skipToHeadAndUseIfPossibleOrNull(tag: Int, crossinline block: (JceHead) -> R): R? {
-        return skipToHeadOrNull(tag)?.let(block).also { prepareNextHead() }
-    }
-
-    /**
-     * 跳过 [JceHead] 和对应的数据值, 直到找到 [tag], 否则抛出异常
-     */
-    inline fun <R : Any> skipToHeadAndUseIfPossibleOrFail(
-        tag: Int,
-        crossinline message: () -> String = { "tag not found: $tag" },
-        crossinline block: (JceHead) -> R
-    ): R {
-        return checkNotNull<R>(skipToHeadAndUseIfPossibleOrNull(tag, block), message)
-    }
-
-    tailrec fun skipToHeadOrNull(tag: Int): JceHead? {
-        val current: JceHead = currentHeadOrNull ?: return null // no backing field
-
-        return when {
-            current.tag > tag -> null // tag 大了,即找不到
-            current.tag == tag -> current // 满足需要.
-            else -> { // tag 小了
-                skipField(current.type)
-                check(prepareNextHead()) { "cannot skip to tag $tag, early EOF" }
-                skipToHeadOrNull(tag)
-            }
-        }
-    }
-
-    inline fun skipToHeadOrFail(
-        tag: Int,
-        message: () -> String = { "head not found: $tag" }
-    ): JceHead {
-        return checkNotNull(skipToHeadOrNull(tag), message)
-    }
-
-    @OptIn(ExperimentalUnsignedTypes::class)
-    @PublishedApi
-    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 -> 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
-                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.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.structureHierarchy++
-                var head: JceHead
-                do {
-                    head = nextHead()
-                    skipField(head.type)
-                } while (head.type != Jce.STRUCT_END)
-                JceDecoder.structureHierarchy--
-            }
-            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))
-                JceDecoder.structureHierarchy--
-            }
-            else -> error("invalid type: $type")
-        }
-    }
-
-    // region readers
-    fun readJceIntValue(head: JceHead): Int {
-        //println("readJceIntValue: $head")
-        return when (head.type) {
-            Jce.ZERO_TYPE -> 0
-            Jce.BYTE -> input.readByte().toInt()
-            Jce.SHORT -> input.readShort().toInt()
-            Jce.INT -> input.readInt()
-            else -> error("type mismatch: $head, remaining=${input.readBytes().toUHexString()}")
-        }
-    }
-
-    fun readJceShortValue(head: JceHead): Short {
-        return when (head.type) {
-            Jce.ZERO_TYPE -> 0
-            Jce.BYTE -> input.readByte().toShort()
-            Jce.SHORT -> input.readShort()
-            else -> error("type mismatch: $head")
-        }
-    }
-
-    fun readJceLongValue(head: JceHead): Long {
-        return when (head.type) {
-            Jce.ZERO_TYPE -> 0
-            Jce.BYTE -> input.readByte().toLong()
-            Jce.SHORT -> input.readShort().toLong()
-            Jce.INT -> input.readInt().toLong()
-            Jce.LONG -> input.readLong()
-            else -> error("type mismatch ${head.type}")
-        }
-    }
-
-    fun readJceByteValue(head: JceHead): Byte {
-        //println("readJceByteValue: $head")
-        return when (head.type) {
-            Jce.ZERO_TYPE -> 0
-            Jce.BYTE -> input.readByte()
-            else -> error("type mismatch: $head")
-        }
-    }
-
-    fun readJceFloatValue(head: JceHead): Float {
-        return when (head.type) {
-            Jce.ZERO_TYPE -> 0f
-            Jce.FLOAT -> input.readFloat()
-            else -> error("type mismatch: $head")
-        }
-    }
-
-    @OptIn(ExperimentalUnsignedTypes::class)
-    fun readJceStringValue(head: JceHead): String {
-        //println("readJceStringValue: $head")
-        return when (head.type) {
-            Jce.STRING1 -> input.readString(input.readUByte().toInt(), charset = charset.kotlinCharset)
-            Jce.STRING4 -> input.readString(
-                input.readUInt().toInt().also { require(it in 1 until 104857600) { "bad string length: $it" } },
-                charset = charset.kotlinCharset
-            )
-            else -> error("type mismatch: $head, expecting 6 or 7 (for string)")
-        }
-    }
-
-    fun readJceDoubleValue(head: JceHead): Double {
-        return when (head.type.toInt()) {
-            12 -> 0.0
-            4 -> input.readFloat().toDouble()
-            5 -> input.readDouble()
-            else -> error("type mismatch: $head")
-        }
-    }
-
-    fun readJceBooleanValue(head: JceHead): Boolean {
-        return readJceByteValue(head) == 1.toByte()
-    }
-}
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/jce/JceNew.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/jce/JceNew.kt
deleted file mode 100644
index 994a8c304..000000000
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/jce/JceNew.kt
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright 2020 Mamoe Technologies and contributors.
- *
- * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
- * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
- *
- * https://github.com/mamoe/mirai/blob/master/LICENSE
- */
-
-package net.mamoe.mirai.qqandroid.utils.io.serialization.jce
-
-import kotlinx.io.core.*
-import kotlinx.serialization.BinaryFormat
-import kotlinx.serialization.DeserializationStrategy
-import kotlinx.serialization.SerialFormat
-import kotlinx.serialization.SerializationStrategy
-import kotlinx.serialization.modules.EmptyModule
-import kotlinx.serialization.modules.SerialModule
-import net.mamoe.mirai.qqandroid.utils.io.serialization.IOFormat
-import net.mamoe.mirai.qqandroid.utils.io.serialization.JceCharset
-import net.mamoe.mirai.qqandroid.utils.io.serialization.JceOld
-import net.mamoe.mirai.qqandroid.utils.toReadPacket
-
-/**
- * Jce 数据结构序列化和反序列化器.
- *
- * @author Him188
- */
-internal class Jce(
-    override val context: SerialModule,
-    val charset: JceCharset
-) : SerialFormat, IOFormat, BinaryFormat {
-    override fun <T> dumpTo(serializer: SerializationStrategy<T>, ojb: T, output: Output) {
-        output.writePacket(JceOld.byCharSet(this.charset).dumpAsPacket(serializer, ojb))
-    }
-
-    override fun <T> load(deserializer: DeserializationStrategy<T>, input: Input): T {
-        return JceDecoder(
-            JceInput(
-                input,
-                charset
-            ), context
-        ).decodeSerializableValue(deserializer)
-    }
-
-    override fun <T> dump(serializer: SerializationStrategy<T>, value: T): ByteArray {
-        return buildPacket { dumpTo(serializer, value, this) }.readBytes()
-    }
-
-    override fun <T> load(deserializer: DeserializationStrategy<T>, bytes: ByteArray): T {
-        return load(deserializer, bytes.toReadPacket())
-    }
-
-    companion object {
-        val UTF_8 = Jce(
-            EmptyModule,
-            JceCharset.UTF8
-        )
-        val GBK = Jce(
-            EmptyModule,
-            JceCharset.GBK
-        )
-
-        fun byCharSet(c: JceCharset): Jce {
-            return if (c == JceCharset.UTF8) UTF_8 else GBK
-        }
-
-        internal const val BYTE: Byte = 0
-        internal const val DOUBLE: Byte = 5
-        internal const val FLOAT: Byte = 4
-        internal const val INT: Byte = 2
-        internal const val JCE_MAX_STRING_LENGTH = 104857600
-        internal const val LIST: Byte = 9
-        internal const val LONG: Byte = 3
-        internal const val MAP: Byte = 8
-        internal const val SHORT: Byte = 1
-        internal const val SIMPLE_LIST: Byte = 13
-        internal const val STRING1: Byte = 6
-        internal const val STRING4: Byte = 7
-        internal const val STRUCT_BEGIN: Byte = 10
-        internal const val STRUCT_END: Byte = 11
-        internal const val ZERO_TYPE: Byte = 12
-    }
-}
\ No newline at end of file
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/jce/common.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/jce/common.kt
deleted file mode 100644
index 86bfee2bd..000000000
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/jce/common.kt
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright 2020 Mamoe Technologies and contributors.
- *
- * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
- * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
- *
- * https://github.com/mamoe/mirai/blob/master/LICENSE
- */
-
-package net.mamoe.mirai.qqandroid.utils.io.serialization.jce
-
-import kotlinx.io.core.Output
-import kotlinx.serialization.SerialDescriptor
-import kotlinx.serialization.SerialInfo
-
-
-/**
- * 标注 JCE 序列化时使用的 ID
- */
-@SerialInfo
-@Target(AnnotationTarget.PROPERTY)
-internal annotation class JceId(val id: Int)
-
-/**
- * 类中元素的 tag
- *
- * 保留这个结构, 为将来增加功能的兼容性.
- */
-@PublishedApi
-internal abstract class JceTag {
-    abstract val id: Int
-
-    internal var isSimpleByteArray: Boolean = false
-}
-
-internal object JceTagListElement : JceTag() {
-    override val id: Int get() = 0
-    override fun toString(): String {
-        return "JceTagListElement"
-    }
-}
-
-internal object JceTagMapEntryKey : JceTag() {
-    override val id: Int get() = 0
-    override fun toString(): String {
-        return "JceTagMapEntryKey"
-    }
-}
-
-internal object JceTagMapEntryValue : JceTag() {
-    override val id: Int get() = 1
-    override fun toString(): String {
-        return "JceTagMapEntryValue"
-    }
-}
-
-internal data class JceTagCommon(
-    override val id: Int
-) : JceTag()
-
-internal fun JceHead.checkType(type: Byte, message: String, tag: JceTag, descriptor: SerialDescriptor) {
-    check(this.type == type) {
-        "type mismatch. " +
-                "Expected ${JceHead.findJceTypeName(type)}, " +
-                "actual ${JceHead.findJceTypeName(this.type)} for $message. " +
-                "Tag info: " +
-                "id=${tag.id}, " +
-                "name=${descriptor.getElementName(tag.id)} " +
-                "in ${descriptor.serialName}" }
-}
-
-@PublishedApi
-internal fun Output.writeJceHead(type: Byte, tag: Int) {
-    if (tag < 15) {
-        writeByte(((tag shl 4) or type.toInt()).toByte())
-        return
-    }
-    if (tag < 256) {
-        writeByte((type.toInt() or 0xF0).toByte())
-        writeByte(tag.toByte())
-        return
-    }
-    error("tag is too large: $tag")
-}
-
-@OptIn(ExperimentalUnsignedTypes::class)
-inline class JceHead(private val value: Long) {
-    constructor(tag: Int, type: Byte) : this(tag.toLong().shl(32) or type.toLong())
-
-    val tag: Int get() = (value ushr 32).toInt()
-    val type: Byte get() = value.toUInt().toByte()
-
-    override fun toString(): String {
-        return "JceHead(tag=$tag, type=$type(${findJceTypeName(type)}))"
-    }
-
-    companion object {
-        fun findJceTypeName(type: Byte): String {
-            return when (type) {
-                Jce.BYTE -> "Byte"
-                Jce.DOUBLE -> "Double"
-                Jce.FLOAT -> "Float"
-                Jce.INT -> "Int"
-                Jce.LIST -> "List"
-                Jce.LONG -> "Long"
-                Jce.MAP -> "Map"
-                Jce.SHORT -> "Short"
-                Jce.SIMPLE_LIST -> "SimpleList"
-                Jce.STRING1 -> "String1"
-                Jce.STRING4 -> "String4"
-                Jce.STRUCT_BEGIN -> "StructBegin"
-                Jce.STRUCT_END -> "StructEnd"
-                Jce.ZERO_TYPE -> "Zero"
-                else -> error("illegal jce type: $type")
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/utils.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/utils.kt
index ad04fd662..5ea7bf77c 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/utils.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/serialization/utils.kt
@@ -16,15 +16,14 @@ import kotlinx.io.core.*
 import kotlinx.serialization.DeserializationStrategy
 import kotlinx.serialization.SerialDescriptor
 import kotlinx.serialization.SerializationStrategy
+import moe.him188.jcekt.Jce
 import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestDataVersion2
 import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestDataVersion3
 import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestPacket
 import net.mamoe.mirai.qqandroid.utils.io.JceStruct
 import net.mamoe.mirai.qqandroid.utils.io.ProtoBuf
 import net.mamoe.mirai.qqandroid.utils.io.readPacketExact
-import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.Jce
 import net.mamoe.mirai.qqandroid.utils.read
-import net.mamoe.mirai.utils.MiraiInternalAPI
 import kotlin.jvm.JvmMultifileClass
 import kotlin.jvm.JvmName
 
@@ -34,25 +33,21 @@ internal fun <T : JceStruct> ByteArray.loadWithUniPacket(
 ): T = this.read { readUniPacket(deserializer, name) }
 
 internal fun <T : JceStruct> ByteArray.loadAs(
-    deserializer: DeserializationStrategy<T>,
-    c: JceCharset = JceCharset.UTF8
-): T = this.read { Jce.byCharSet(c).load(deserializer, this) }
+    deserializer: DeserializationStrategy<T>
+): T = this.read { Jce.UTF_8.load(deserializer, this) }
 
 internal fun <T : JceStruct> BytePacketBuilder.writeJceStruct(
     serializer: SerializationStrategy<T>,
-    struct: T,
-    charset: JceCharset = JceCharset.UTF8
+    struct: T
 ) {
-    Jce.byCharSet(charset).dumpTo(serializer, struct, this)
+    Jce.UTF_8.dumpTo(serializer, struct, this)
 }
 
 internal fun <T : JceStruct> ByteReadPacket.readJceStruct(
     serializer: DeserializationStrategy<T>,
-    charset: JceCharset = JceCharset.UTF8,
     length: Int = this.remaining.toInt()
 ): T {
-    @OptIn(MiraiInternalAPI::class)
-    return Jce.byCharSet(charset).load(serializer, this.readPacketExact(length))
+    return Jce.UTF_8.load(serializer, this.readPacketExact(length))
 }
 
 /**
@@ -103,9 +98,8 @@ private fun <R> ByteReadPacket.decodeUniRequestPacketAndDeserialize(name: String
 }
 
 internal fun <T : JceStruct> T.toByteArray(
-    serializer: SerializationStrategy<T>,
-    c: JceCharset = JceCharset.UTF8
-): ByteArray = Jce.byCharSet(c).dump(serializer, this)
+    serializer: SerializationStrategy<T>
+): ByteArray = Jce.UTF_8.dump(serializer, this)
 
 internal fun <T : ProtoBuf> BytePacketBuilder.writeProtoBuf(serializer: SerializationStrategy<T>, v: T) {
     this.writeFully(v.toByteArray(serializer))
@@ -140,19 +134,12 @@ internal fun <T : JceStruct> jceRequestSBuffer(
     name: String,
     serializer: SerializationStrategy<T>,
     jceStruct: T
-): ByteArray = jceRequestSBuffer(name, serializer, jceStruct, JceCharset.UTF8)
-
-internal fun <T : JceStruct> jceRequestSBuffer(
-    name: String,
-    serializer: SerializationStrategy<T>,
-    jceStruct: T,
-    charset: JceCharset
 ): ByteArray {
     return RequestDataVersion3(
         mapOf(
             name to JCE_STRUCT_HEAD_OF_TAG_0 + jceStruct.toByteArray(serializer) + JCE_STRUCT_TAIL_OF_TAG_0
         )
-    ).toByteArray(RequestDataVersion3.serializer(), charset)
+    ).toByteArray(RequestDataVersion3.serializer())
 }
 
 private val JCE_STRUCT_HEAD_OF_TAG_0 = byteArrayOf(0x0A)
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
deleted file mode 100644
index 78671f853..000000000
--- a/mirai-core-qqandroid/src/commonTest/kotlin/net.mamoe.mirai.qqandroid/io/serialization/JceInputTest.kt
+++ /dev/null
@@ -1,651 +0,0 @@
-@file:Suppress("unused", "DEPRECATION_ERROR")
-
-package net.mamoe.mirai.qqandroid.io.serialization
-
-import kotlinx.io.core.buildPacket
-import kotlinx.io.core.toByteArray
-import kotlinx.io.core.writeFully
-import kotlinx.serialization.MissingFieldException
-import kotlinx.serialization.Serializable
-import net.mamoe.mirai.qqandroid.network.protocol.data.jce.FileStoragePushFSSvcListFuckKotlin
-import net.mamoe.mirai.qqandroid.utils.autoHexToBytes
-import net.mamoe.mirai.qqandroid.utils.io.JceStruct
-import net.mamoe.mirai.qqandroid.utils.io.serialization.JceCharset
-import net.mamoe.mirai.qqandroid.utils.io.serialization.jce.*
-import net.mamoe.mirai.qqandroid.utils.io.serialization.loadAs
-import net.mamoe.mirai.qqandroid.utils.io.serialization.toByteArray
-import kotlin.test.Test
-import kotlin.test.assertEquals
-import kotlin.test.assertFailsWith
-
-
-internal const val BYTE: Byte = 0
-internal const val DOUBLE: Byte = 5
-internal const val FLOAT: Byte = 4
-internal const val INT: Byte = 2
-internal const val JCE_MAX_STRING_LENGTH = 104857600
-internal const val LIST: Byte = 9
-internal const val LONG: Byte = 3
-internal const val MAP: Byte = 8
-internal const val SHORT: Byte = 1
-internal const val SIMPLE_LIST: Byte = 13
-internal const val STRING1: Byte = 6
-internal const val STRING4: Byte = 7
-internal const val STRUCT_BEGIN: Byte = 10
-internal const val STRUCT_END: Byte = 11
-internal const val ZERO_TYPE: Byte = 12
-
-
-@Suppress("INVISIBLE_MEMBER") // bug
-internal class JceInputTest {
-    init {
-        JceDecoder.debuggingMode = true
-    }
-
-
-    @Test
-    fun testConfigPush() {
-        val data = """
-            9A 
-            09 00 0B 
-                0A 
-                    00 0F 
-                    19 00 01 
-                        0A 
-                            12 71 19 A3 B4 
-                            20 50 
-                        0B 
-                    29 0C 
-                0B 
-                0A 
-                    00 04 
-                    19 00 01 
-                        0A 
-                            12 0B 27 59 65 
-                            20 50
-                        0B 
-                    29 0C 
-                0B 
-                0A 
-                    00 0D 19 00 02 0A 12 55 31 BA DE 20 50 0B 0A 12 5B A0 6A 72 20 50 0B 29 0C 0B 0A 00 03 19 00 02 0A 12 C3 B9 D3 74 20 50 0B 0A 12 CC 43 E4 DD 20 50 0B 29 0C 0B 0A 00 07 19 00 01 0A 12 75 A2 E3 65 20 50 0B 29 0C 0B 0A 00 09 19 00 02 0A 12 BC 6C 24 B7 20 50 0B 0A 12 A6 6C 24 B7 20 50 0B 29 0C 0B 0A 00 0A 19 00 02 0A 12 11 B4 12 0E 20 50 0B 0A 12 15 8C D7 0E 20 50 0B 29 0C 0B 0A 00 05 19 00 01 0A 12 1D E2 03 B7 20 50 0B 29 0C 0B 0A 00 08 19 00 02 0A 12 DE 3F 5B 65 20 50 0B 0A 12 78 09 61 B4 20 50 0B 29 0C 0B 0A 00 06 19 00 02 0A 12 16 CF 97 3D 20 50 0B 0A 12 54 10 59 65 20 50 0B 29 0C 0B 0A 00 0E 19 00 02 0A 12 76 01 B1 6F 20 50 0B 0A 12 6B 89 31 3A 20 50 0B 29 0C 0B 0B 
-            AD 00 01 01 5B 08 01 10 A4 F6 AA 16 18 00 22 0A 31 39 39 34 37 30 31 30 32 31 28 AB E1 89 EF 0E 32 12 08 8E A4 D8 A5 09 10 50 18 89 D8 AC F0 08 20 50 28 64 32 12 08 8E A4 C4 DD 08 10 50 18 89 F4 DE E0 05 20 50 28 64 32 13 08 B4 C7 DA B0 02 10 50 18 8A EE D4 F2 0D 20 50 28 C8 01 32 13 08 B4 C7 DA A0 02 10 50 18 8A EC D0 86 0E 20 50 28 C8 01 32 13 08 8C 9D 9B 85 07 10 50 18 89 D6 AD 9C 09 20 50 28 AC 02 32 13 08 B7 81 97 F6 06 10 50 18 8A EC D4 96 02 20 50 28 AC 02 3A 1E 0A 10 24 0E 00 E1 A9 00 00 50 00 00 00 00 00 00 00 29 10 50 18 89 EC 8C B1 05 20 50 28 64 3A 1E 0A 10 24 0E 00 E1 A9 00 00 50 00 00 00 00 00 00 00 64 10 50 18 89 EC 8C D1 07 20 50 28 64 3A 1F 0A 10 24 0E 00 FF F1 01 00 10 00 00 00 00 00 00 01 6F 10 50 18 E4 E6 B1 F0 04 20 50 28 C8 01 3A 1F 0A 10 24 0E 00 FF F1 01 00 10 00 00 00 00 00 00 01 72 10 50 18 E4 E6 AD F0 0E 20 50 28 C8 01 3A 1F 0A 10 24 09 8C 1E 75 B0 00 13 00 00 00 00 00 00 00 36 10 50 18 89 EC 9C E8 0D 20 50 28 AC 02 3A 1F 0A 10 24 09 8C 54 10 03 00 10 00 00 00 00 00 00 00 55 10 50 18 89 CA 8C A0 01 20 50 28 AC 02
-
-                   """.trimIndent().autoHexToBytes()
-        /*
-        9A
-            09 00 0B
-                0A
-                    00 0F
-                    19 00 01
-                        0A
-                            12 71 19 A3 B4
-                            20 50
-                        0B
-                    29 0C
-                0B
-                0A
-                    00 04
-                    19 00 01
-                        0A
-                            12 0B 27 59 65
-                            20 50
-                        0B
-                    29 0C
-                0B
-                0A
-                    00 0D 19 00 02 0A 12 55 31 BA DE 20 50 0B 0A 12 5B A0 6A 72 20 50 0B 29 0C 0B 0A 00 03 19 00 02 0A 12 C3 B9 D3 74 20 50 0B 0A 12 CC 43 E4 DD 20 50 0B 29 0C 0B 0A 00 07 19 00 01 0A 12 75 A2 E3 65 20 50 0B 29 0C 0B 0A 00 09 19 00 02 0A 12 BC 6C 24 B7 20 50 0B 0A 12 A6 6C 24 B7 20 50 0B 29 0C 0B 0A 00 0A 19 00 02 0A 12 11 B4 12 0E 20 50 0B 0A 12 15 8C D7 0E 20 50 0B 29 0C 0B 0A 00 05 19 00 01 0A 12 1D E2 03 B7 20 50 0B 29 0C 0B 0A 00 08 19 00 02 0A 12 DE 3F 5B 65 20 50 0B 0A 12 78 09 61 B4 20 50 0B 29 0C 0B 0A 00 06 19 00 02 0A 12 16 CF 97 3D 20 50 0B 0A 12 54 10 59 65 20 50 0B 29 0C 0B 0A 00 0E 19 00 02 0A 12 76 01 B1 6F 20 50 0B 0A 12 6B 89 31 3A 20 50 0B 29 0C 0B 0B
-            AD 00 01 01 5B 08 01 10 A4 F6 AA 16 18 00 22 0A 31 39 39 34 37 30 31 30 32 31 28 AB E1 89 EF 0E 32 12 08 8E A4 D8 A5 09 10 50 18 89 D8 AC F0 08 20 50 28 64 32 12 08 8E A4 C4 DD 08 10 50 18 89 F4 DE E0 05 20 50 28 64 32 13 08 B4 C7 DA B0 02 10 50 18 8A EE D4 F2 0D 20 50 28 C8 01 32 13 08 B4 C7 DA A0 02 10 50 18 8A EC D0 86 0E 20 50 28 C8 01 32 13 08 8C 9D 9B 85 07 10 50 18 89 D6 AD 9C 09 20 50 28 AC 02 32 13 08 B7 81 97 F6 06 10 50 18 8A EC D4 96 02 20 50 28 AC 02 3A 1E 0A 10 24 0E 00 E1 A9 00 00 50 00 00 00 00 00 00 00 29 10 50 18 89 EC 8C B1 05 20 50 28 64 3A 1E 0A 10 24 0E 00 E1 A9 00 00 50 00 00 00 00 00 00 00 64 10 50 18 89 EC 8C D1 07 20 50 28 64 3A 1F 0A 10 24 0E 00 FF F1 01 00 10 00 00 00 00 00 00 01 6F 10 50 18 E4 E6 B1 F0 04 20 50 28 C8 01 3A 1F 0A 10 24 0E 00 FF F1 01 00 10 00 00 00 00 00 00 01 72 10 50 18 E4 E6 AD F0 0E 20 50 28 C8 01 3A 1F 0A 10 24 09 8C 1E 75 B0 00 13 00 00 00 00 00 00 00 36 10 50 18 89 EC 9C E8 0D 20 50 28 AC 02 3A 1F 0A 10 24 09 8C 54 10 03 00 10 00 00 00 00 00 00 00 55 10 50 18 89 CA 8C A0 01 20 50 28 AC 02
-
-         */
-        /*
-
-             39 00 06
-             0A
-                16 0E 31 31 39 2E 31 34 37 2E 31 39 2E 32 34 31
-                20 50
-             0B 0A 16 0E 31 31 39 2E 31 34 37 2E 31 39 2E 32 34 34 20 50 0B 0A 16 0E 31 31 39 2E 31 34 37 2E 31 39 2E 32 34 35 20 50 0B 0A 16 0E 31 31 39 2E 31 34 37 2E 31 39 2E 32 35 32 20 50 0B 0A 16 0E 31 31 39 2E 31 34 37 2E 31 39 2E 32 35 33 20 50 0B 0A 16 11 73 63 61 6E 6E 6F 6E 2E 33 67 2E 71 71 2E 63 6F 6D 20 50 0B 49 00 04 0A 16 0E 31 31 33 2E 39 36 2E 32 33 32 2E 31 30 38 20 50 0B 0A 16 0D 31 38 33 2E 33 2E 32 33 33 2E 32 32 35 21 1F 90 0B 0A 16 0E 31 32 33 2E 31 35 30 2E 37 36 2E 31 36 38 21 01 BB 0B 0A 16 0D 31 38 30 2E 31 36 33 2E 32 35 2E 33 38 20 50 0B 5A 09 00 03 0A 00 01 19 00 04 0A 00 01 16 0E 31 31 33 2E 39 36 2E 32 33 32 2E 31 30 38 20 50 0B 0A 00 01 16 0D 31 38 33 2E 33 2E 32 33 33 2E 32 32 35 21 1F 90 0B 0A 00 01 16 0E 31 32 33 2E 31 35 30 2E 37 36 2E 31 36 38 21 01 BB 0B 0A 00 01 16 0D 31 38 30 2E 31 36 33 2E 32 35 2E 33 38 20 50 0B 29 0C 3C 0B 0A 00 05 19 00 04 0A 00 01 16 0E 31 31 33 2E 39 36 2E 32 33 32 2E 31 30 38 20 50 0B 0A 00 01 16 0D 31 38 33 2E 33 2E 32 33 33 2E 32 32 35 21 1F 90 0B 0A 00 01 16 0E 31 32 33 2E 31 35 30 2E 37 36 2E 31 36 38 21 01 BB 0B 0A 00 01 16 0D 31 38 30 2E 31 36 33 2E 32 35 2E 33 38 20 50 0B 29 0C 3C 0B 0A 00 0A 19 00 04 0A 00 01 16 0E 31 31 33 2E 39 36 2E 32 33 32 2E 31 30 38 20 50 0B 0A 00 01 16 0D 31 38 33 2E 33 2E 32 33 33 2E 32 32 35 21 1F 90 0B 0A 00 01 16 0E 31 32 33 2E 31 35 30 2E 37 36 2E 31 36 38 21 01 BB 0B 0A 00 01 16 0D 31 38 30 2E 31 36 33 2E 32 35 2E 33 38 20 50 0B 29 00 05 0A 0C 11 20 00 20 10 30 01 0B 0A 00 01 11 20 00 20 08 30 02 0B 0A 00 02 11 20 00 20 08 30 01 0B 0A 00 03 12 00 01 00 00 20 08 30 02 0B 0A 00 04 11 20 00 20 08 30 02 0B 3C 0B 1D 00 00 68 CA 62 F1 01 C2 AF E6 CF 29 4B 18 71 B5 EE 6B 63 EB F0 0B AB EE A0 5C 20 B9 83 E2 52 F7 BF C7 46 80 BC C3 7F 22 6B 6E 23 42 D0 8F C8 6A C4 F4 49 AA E7 94 EF D4 80 0A E4 8B BF E2 C0 4F FC C5 3F 97 1A E8 0F 0F 7D 06 47 62 C3 C8 07 4F E6 F6 E9 DB CB 4C F5 95 6A AD EC FD D0 46 A5 16 8D 30 02 D5 8A 86 2E 5F E8 D6 8C 2D 00 00 10 33 6E 59 70 73 47 38 52 6E 48 6A 64 51 48 46 54 32 76 E4 B8 DD 40 01 5D 00 01 02 54 8A 50 D0 04 0A 68 CA 62 F1 01 C2 AF E6 CF 29 4B 18 71 B5 EE 6B 63 EB F0 0B AB EE A0 5C 20 B9 83 E2 52 F7 BF C7 46 80 BC C3 7F 22 6B 6E 23 42 D0 8F C8 6A C4 F4 49 AA E7 94 EF D4 80 0A E4 8B BF E2 C0 4F FC C5 3F 97 1A E8 0F 0F 7D 06 47 62 C3 C8 07 4F E6 F6 E9 DB CB 4C F5 95 6A AD EC FD D0 46 A5 16 8D 30 02 D5 8A 86 2E 5F E8 D6 8C 12 10 33 6E 59 70 73 47 38 52 6E 48 6A 64 51 48 46 54 1A 40 08 01 12 0D 08 01 15 71 60 E8 6C 18 50 20 01 28 01 12 0E 08 01 15 B7 03 E9 E1 18 90 3F 20 01 28 01 12 0E 08 01 15 7B 96 4C A8 18 BB 03 20 02 28 00 12 0D 08 01 15 B4 A3 19 26 18 50 20 04 28 00 1A 40 08 05 12 0D 08 01 15 71 60 E8 6C 18 50 20 01 28 01 12 0E 08 01 15 B7 03 E9 E1 18 90 3F 20 01 28 01 12 0E 08 01 15 7B 96 4C A8 18 BB 03 20 02 28 00 12 0D 08 01 15 B4 A3 19 26 18 50 20 04 28 00 1A 78 08 0A 12 0D 08 01 15 71 60 E8 6C 18 50 20 01 28 01 12 0E 08 01 15 B7 03 E9 E1 18 90 3F 20 01 28 01 12 0E 08 01 15 7B 96 4C A8 18 BB 03 20 02 28 00 12 0D 08 01 15 B4 A3 19 26 18 50 20 04 28 00 22 09 08 00 10 80 40 18 10 20 01 22 09 08 01 10 80 40 18 08 20 02 22 09 08 02 10 80 40 18 08 20 01 22 0A 08 03 10 80 80 04 18 08 20 02 22 09 08 04 10 80 40 18 08 20 02 20 01 32 04 08 00 10 01 3A 2A 08 10 10 10 18 09 20 09 28 0F 30 0F 38 05 40 05 48 5A 50 01 58 5A 60 5A 68 5A 70 5A 78 0A 80 01 0A 88 01 0A 90 01 0A 98 01 0A 42 0A 08 00 10 00 18 00 20 00 28 00 4A 06 08 01 10 01 18 03 52 42 08 01 12 0A 08 00 10 80 80 04 18 10 20 02 12 0A 08 01 10 80 80 04 18 08 20 02 12 0A 08 02 10 80 80 01 18 08 20 01 12 0A 08 03 10 80 80 04 18 08 20 02 12 0A 08 04 10 80 80 04 18 08 20 02 18 00 20 00 5A 40 08 01 12 0A 08 00 10 80 80 04 18 10 20 02 12 0A 08 01 10 80 80 04 18 08 20 02 12 0A 08 02 10 80 80 01 18 08 20 01 12 0A 08 03 10 80 80 04 18 08 20 02 12 0A 08 04 10 80 80 04 18 08 20 02 18 00 70 02 78 02 80 01 FA 01 0B 69 00 01 0A 16 26 69 6D 67 63 61 63 68 65 2E 71 71 2E 63 6F 6D 2E 73 63 68 65 64 2E 70 31 76 36 2E 74 64 6E 73 76 36 2E 63 6F 6D 2E 20 50 0B 79 00 02 0A 16 0E 31 30 31 2E 32 32 37 2E 31 33 31 2E 36 37 20 50 0B 0A 16 0D 36 31 2E 31 35 31 2E 31 38 33 2E 32 31 20 50 0B 8A 06 0F 31 37 31 2E 31 31 32 2E 32 32 36 2E 32 33 37 10 03 0B 9A 09 00 0B 0A 00 0F 19 00 01 0A 12 71 19 A3 B4 20 50 0B 29 0C 0B 0A 00 04 19 00 01 0A 12 0B 27 59 65 20 50 0B 29 0C 0B 0A 00 0D 19 00 02 0A 12 55 31 BA DE 20 50 0B 0A 12 5B A0 6A 72 20 50 0B 29 0C 0B 0A 00 03 19 00 02 0A 12 C3 B9 D3 74 20 50 0B 0A 12 CC 43 E4 DD 20 50 0B 29 0C 0B 0A 00 07 19 00 01 0A 12 75 A2 E3 65 20 50 0B 29 0C 0B 0A 00 09 19 00 02 0A 12 BC 6C 24 B7 20 50 0B 0A 12 A6 6C 24 B7 20 50 0B 29 0C 0B 0A 00 0A 19 00 02 0A 12 11 B4 12 0E 20 50 0B 0A 12 15 8C D7 0E 20 50 0B 29 0C 0B 0A 00 05 19 00 01 0A 12 1D E2 03 B7 20 50 0B 29 0C 0B 0A 00 08 19 00 02 0A 12 DE 3F 5B 65 20 50 0B 0A 12 78 09 61 B4 20 50 0B 29 0C 0B 0A 00 06 19 00 02 0A 12 16 CF 97 3D 20 50 0B 0A 12 54 10 59 65 20 50 0B 29 0C 0B 0A 00 0E 19 00 02 0A 12 76 01 B1 6F 20 50 0B 0A 12 6B 89 31 3A 20 50 0B 29 0C 0B 0B AD 00 01 01 5B 08 01 10 A4 F6 AA 16 18 00 22 0A 31 39 39 34 37 30 31 30 32 31 28 AB E1 89 EF 0E 32 12 08 8E A4 D8 A5 09 10 50 18 89 D8 AC F0 08 20 50 28 64 32 12 08 8E A4 C4 DD 08 10 50 18 89 F4 DE E0 05 20 50 28 64 32 13 08 B4 C7 DA B0 02 10 50 18 8A EE D4 F2 0D 20 50 28 C8 01 32 13 08 B4 C7 DA A0 02 10 50 18 8A EC D0 86 0E 20 50 28 C8 01 32 13 08 8C 9D 9B 85 07 10 50 18 89 D6 AD 9C 09 20 50 28 AC 02 32 13 08 B7 81 97 F6 06 10 50 18 8A EC D4 96 02 20 50 28 AC 02 3A 1E 0A 10 24 0E 00 E1 A9 00 00 50 00 00 00 00 00 00 00 29 10 50 18 89 EC 8C B1 05 20 50 28 64 3A 1E 0A 10 24 0E 00 E1 A9 00 00 50 00 00 00 00 00 00 00 64 10 50 18 89 EC 8C D1 07 20 50 28 64 3A 1F 0A 10 24 0E 00 FF F1 01 00 10 00 00 00 00 00 00 01 6F 10 50 18 E4 E6 B1 F0 04 20 50 28 C8 01 3A 1F 0A 10 24 0E 00 FF F1 01 00 10 00 00 00 00 00 00 01 72 10 50 18 E4 E6 AD F0 0E 20 50 28 C8 01 3A 1F 0A 10 24 09 8C 1E 75 B0 00 13 00 00 00 00 00 00 00 36 10 50 18 89 EC 9C E8 0D 20 50 28 AC 02 3A 1F 0A 10 24 09 8C 54 10 03 00 10 00 00 00 00 00 00 00 55 10 50 18 89 CA 8C A0 01 20 50 28 AC 02
-
-         */
-
-        data.loadAs(FileStoragePushFSSvcListFuckKotlin.serializer())
-    }
-
-    @Test
-    fun testIntToStructMap() {
-        @kotlinx.serialization.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
-        data class TestSerializableClassC(
-            @JceId(5) val value3: Int = 123123
-        )
-
-        @Serializable
-        data class TestSerializableClassB(
-            @JceId(0) val value: Int,
-            @JceId(123) val nested2: TestSerializableClassC,
-            @JceId(5) val value5: Int
-        )
-
-        @Serializable
-        data class TestSerializableClassA(
-            //@JceId(0) val map: Map<TestSerializableClassB, TestSerializableClassC>
-            @JceId(1) val optional: String = ""
-        )
-
-
-        val input = buildPacket {
-            writeJceHead(MAP, 0) // TestSerializableClassB
-            writeJceHead(BYTE, 0)
-            writeByte(1);
-
-            {
-                writeJceHead(STRUCT_BEGIN, 0);
-                {
-                    writeJceHead(INT, 0)
-                    writeInt(123)
-
-                    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(STRING1, 1)
-            writeByte(1)
-            writeStringUtf8("1")
-        }
-
-        assertEquals(
-            TestSerializableClassA(
-                /*mapOf(
-                    TestSerializableClassB(123, TestSerializableClassC(123123), 9)
-                            to TestSerializableClassC(123123)
-                ),*/
-
-                "1"
-            ),
-            Jce.UTF_8.load(TestSerializableClassA.serializer(), input)
-        )
-    }
-
-    @Test
-    fun testFuckingComprehensiveStruct() {
-        @Serializable
-        data class TestSerializableClassC(
-            @JceId(5) val value3: Int = 123123
-        )
-
-        @Serializable
-        data class TestSerializableClassB(
-            @JceId(0) val value: Int,
-            @JceId(123) val nested2: TestSerializableClassC,
-            @JceId(5) val value5: Int
-        )
-
-        @Serializable
-        data class TestSerializableClassA(
-            @JceId(0) val map: Map<TestSerializableClassB, TestSerializableClassC>
-        )
-
-
-        val input = buildPacket {
-            writeJceHead(MAP, 0) // TestSerializableClassB
-            writeJceHead(BYTE, 0)
-            writeByte(1)
-
-            writeJceHead(STRUCT_BEGIN, 0);
-            {
-                writeJceHead(INT, 0)
-                writeInt(123)
-
-                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)
-        }
-
-        assertEquals(
-            TestSerializableClassA(
-                mapOf(
-                    TestSerializableClassB(123, TestSerializableClassC(123123), 9)
-                            to TestSerializableClassC(123123)
-                )
-            ),
-            Jce.UTF_8.load(TestSerializableClassA.serializer(), input)
-        )
-    }
-
-    @Test
-    fun testNestedJceStruct() {
-        @Serializable
-        data class TestSerializableClassC(
-            @JceId(5) val value3: Int
-        )
-
-        @Serializable
-        data class TestSerializableClassB(
-            @JceId(0) val value: Int,
-            @JceId(123) val nested2: TestSerializableClassC,
-            @JceId(5) val value5: Int
-        )
-
-        @Serializable
-        data class TestSerializableClassA(
-            @JceId(0) val value1: Int,
-            @JceId(4) val notOptional: Int,
-            @JceId(1) val nestedStruct: TestSerializableClassB,
-            @JceId(2) val optional: Int = 3
-        )
-
-        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, 5)
-                writeInt(9)
-            }()
-            writeJceHead(STRUCT_END, 0)
-
-            writeJceHead(INT, 4)
-            writeInt(5)
-        }
-
-        assertEquals(
-            TestSerializableClassA(
-                444,
-                5,
-                TestSerializableClassB(123, TestSerializableClassC(123123), 9)
-            ),
-            Jce.UTF_8.load(TestSerializableClassA.serializer(), input)
-        )
-    }
-
-    @Test
-    fun testNestedList() {
-        @Serializable
-        data class TestSerializableClassA(
-            // @JceId(0) val byteArray: ByteArray = byteArrayOf(1, 2, 3),
-            @JceId(3) val byteArray2: List<List<Int>> = listOf(listOf(1, 2, 3, 4), listOf(1, 2, 3, 4))
-        )
-
-        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(LIST, 3)
-
-            writeJceHead(BYTE, 0)
-            writeByte(2)
-            listOf(listOf(1, 2, 3, 4), listOf(1, 2, 3, 4)).forEach { child ->
-                writeJceHead(LIST, 0)
-
-                writeJceHead(BYTE, 0)
-                writeByte(child.size.toByte())
-
-                child.forEach {
-                    writeJceHead(INT, 0)
-                    writeInt(it)
-                }
-            }
-        }
-
-        assertEquals(TestSerializableClassA(), Jce.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)),
-            Jce.UTF_8.load(TestSerializableClassA.serializer(), input)
-        )
-    }
-
-    @Test
-    fun testMapStringInt() {
-        @Serializable
-        data class TestSerializableClassA(
-            @JceId(0) val byteArray: Map<String, Int>
-        )
-
-        val input = buildPacket {
-            writeJceHead(MAP, 0)
-
-            mapOf("str1" to 2, "str2" to 44).let {
-                writeJceHead(BYTE, 0)
-                writeByte(it.size.toByte())
-
-                it.forEach { (key, value) ->
-                    writeJceHead(STRING1, 0)
-                    writeByte(key.length.toByte())
-                    writeStringUtf8(key)
-
-                    writeJceHead(INT, 1)
-                    writeInt(value)
-                }
-            }
-        }
-
-        assertEquals(
-            TestSerializableClassA(mapOf("str1" to 2, "str2" to 44)),
-            Jce.UTF_8.load(TestSerializableClassA.serializer(), input)
-        )
-    }
-
-    @Test
-    fun testMapStringByteArray() {
-        @Serializable
-        data class TestSerializableClassA(
-            @JceId(0) val map: Map<String, ByteArray>
-        ) {
-            override fun toString(): String {
-                @Suppress("EXPERIMENTAL_API_USAGE")
-                return map.entries.joinToString { "${it.key}=${it.value.contentToString()}" }
-            }
-        }
-
-        val input = buildPacket {
-            writeJceHead(MAP, 0)
-
-            mapOf("str1" to byteArrayOf(2, 3, 4), "str2" to byteArrayOf(2, 3, 4)).let {
-                writeJceHead(BYTE, 0)
-                writeByte(it.size.toByte())
-
-                it.forEach { (key, value) ->
-                    writeJceHead(STRING1, 0)
-                    writeByte(key.length.toByte())
-                    writeFully(key.toByteArray())
-
-                    writeJceHead(SIMPLE_LIST, 1)
-                    writeJceHead(BYTE, 0)
-                    writeJceHead(INT, 0)
-                    writeInt(value.size)
-                    writeFully(value)
-                }
-            }
-        }
-
-        assertEquals(
-            TestSerializableClassA(mapOf("str1" to byteArrayOf(2, 3, 4), "str2" to byteArrayOf(2, 3, 4))).toString(),
-            Jce.UTF_8.load(TestSerializableClassA.serializer(), input).toString()
-        )
-    }
-
-    @Test
-    fun testSimpleByteArray() {
-        @Serializable
-        data class TestSerializableClassA(
-            @JceId(0) val byteArray: ByteArray = byteArrayOf(1, 2, 3),
-            @JceId(3) val byteArray2: List<Byte> = listOf(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 != other.byteArray2) return false
-
-                return true
-            }
-
-            override fun hashCode(): Int {
-                var result = byteArray.contentHashCode()
-                result = 31 * result + byteArray2.hashCode()
-                return result
-            }
-        }
-
-        val input = buildPacket {
-            writeJceHead(SIMPLE_LIST, 0)
-            writeJceHead(BYTE, 0)
-
-            byteArrayOf(1, 2, 3).let {
-                writeJceHead(BYTE, 0)
-                writeByte(it.size.toByte())
-                writeFully(it)
-            }
-
-            writeJceHead(SIMPLE_LIST, 3)
-            writeJceHead(BYTE, 0)
-
-            byteArrayOf(1, 2, 3, 4).let {
-                writeJceHead(BYTE, 0)
-                writeByte(it.size.toByte())
-                writeFully(it)
-            }
-        }
-
-        assertEquals(TestSerializableClassA(), Jce.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)
-            writeJceHead(SHORT, 1)
-            writeShort(123)
-            writeJceHead(INT, 3)
-            writeInt(123456)
-            writeJceHead(FLOAT, 8)
-            writeFloat(123f)
-            writeJceHead(LONG, 15)
-            writeLong(123456789123456789L)
-            writeJceHead(DOUBLE, 16)
-            writeDouble(123456.0)
-            writeJceHead(BYTE, 17)
-            writeByte(1) // boolean
-        }
-
-        assertEquals(TestSerializableClassA(), Jce.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<MissingFieldException> { Jce.UTF_8.load(TestSerializableClassA.serializer(), input) }
-    }
-
-    @Test
-    fun testHeadSkip() {
-        val input = JceInput(buildPacket {
-            writeJceHead(BYTE, 0)
-            writeByte(66)
-            writeJceHead(SHORT, 1)
-            writeShort(123)
-            writeJceHead(INT, 3)
-            writeInt(123456)
-            writeJceHead(FLOAT, 8)
-            writeFloat(123f)
-            writeJceHead(LONG, 15)
-            writeLong(123456789123456789L)
-            writeJceHead(DOUBLE, 16)
-            writeDouble(123456.0)
-            writeJceHead(BYTE, 17)
-            writeByte(1) // boolean
-        }, JceCharset.UTF8)
-
-        assertEquals(123456, input.skipToHeadAndUseIfPossibleOrFail(3) { input.readJceIntValue(it) })
-
-        assertEquals(true, input.skipToHeadAndUseIfPossibleOrFail(17) { input.readJceBooleanValue(it) })
-
-        assertFailsWith<IllegalStateException> {
-            input.skipToHeadAndUseIfPossibleOrFail(18) {
-                error("test failed")
-            }
-        }
-    }
-
-    @Test
-    fun testReadPrimitive() {
-        val input = JceInput(buildPacket {
-            writeJceHead(BYTE, 0)
-            writeByte(66)
-            writeJceHead(SHORT, 1)
-            writeShort(123)
-            writeJceHead(INT, 3)
-            writeInt(123456)
-            writeJceHead(FLOAT, 8)
-            writeFloat(123f)
-            writeJceHead(LONG, 15)
-            writeLong(123456789123456789L)
-            writeJceHead(DOUBLE, 16)
-            writeDouble(123456.0)
-            writeJceHead(BYTE, 17)
-            writeByte(1) // boolean
-        }, JceCharset.UTF8)
-        assertEquals(66, input.useHead { input.readJceByteValue(it) })
-        assertEquals(123, input.useHead { input.readJceShortValue(it) })
-        assertEquals(123456, input.useHead { input.readJceIntValue(it) })
-        assertEquals(123f, input.useHead { input.readJceFloatValue(it) })
-        assertEquals(123456789123456789, input.useHead { input.readJceLongValue(it) })
-        assertEquals(123456.0, input.useHead { input.readJceDoubleValue(it) })
-        assertEquals(true, input.useHead { input.readJceBooleanValue(it) })
-    }
-}
\ No newline at end of file