From 388e1a5b7fa40bd9481407271b85287f40281c74 Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Thu, 6 Feb 2020 13:58:57 +0800
Subject: [PATCH] Cleanup

---
 gradle/wrapper/gradle-wrapper.properties      |   2 +-
 .../network/QQAndroidBotNetworkHandler.kt     |  19 +-
 .../qqandroid/network/http/HttpRequest.kt     |  13 +-
 .../protocol/packet/OutgoingPacketAndroid.kt  |   3 +-
 .../qqandroid/network/protocol/packet/Tlv.kt  |   4 +-
 .../protocol/packet/chat/TroopManagement.kt   |  11 +-
 .../protocol/packet/login/LoginPacket.kt      |  31 ++--
 .../androidPacketTests/clientToServer.kt      |  12 +-
 .../androidPacketTests/serverToClient.kt      |   4 +
 .../mirai/utils/cryptor/contentToString.kt    |   7 +-
 .../commonMain/kotlin/net.mamoe.mirai/Bot.kt  |   2 -
 .../kotlin/net.mamoe.mirai/BotHelper.kt       |   3 -
 .../net.mamoe.mirai/utils/BotConfiguration.kt |   1 +
 .../net.mamoe.mirai/utils/Calculation.kt      |   8 -
 .../net.mamoe.mirai/utils/ExternalImage.kt    |   4 +-
 .../utils/LoginFailedException.kt             |   8 -
 .../net.mamoe.mirai/utils/MiraiLogger.kt      |   4 +-
 .../net.mamoe.mirai/utils/NumberUtils.kt      |   6 +
 .../utils/OverFileSizeMaxException.kt         |   6 +
 .../net.mamoe.mirai/utils/PlatformUtils.kt    |   7 +-
 .../net.mamoe.mirai/utils/SuspendLazy.kt      |   4 +-
 .../kotlin/net.mamoe.mirai/utils/Tested.kt    |   9 -
 .../kotlin/net.mamoe.mirai/utils/Time.kt      |   5 +
 .../utils/assertUnreachable.kt                |   7 -
 .../utils/cryptor/Decrypters.kt               |  66 -------
 .../net.mamoe.mirai/utils/cryptor/Proto.kt    |  15 +-
 .../net.mamoe.mirai/utils/io/ByteArrayUtil.kt |  18 +-
 .../net.mamoe.mirai/utils/io/DebugUtil.kt     |  23 ++-
 .../net.mamoe.mirai/utils/io/InputUtils.kt    | 170 ++++--------------
 .../net.mamoe.mirai/utils/io/OutputUtils.kt   | 108 +----------
 .../kotlin/net.mamoe.mirai/utils/io/Varint.kt |   8 +-
 .../kotlin/net.mamoe.mirai/utils/map.kt       |  16 +-
 .../mirai/utils/DefaultCaptchaSolverJvm.kt    |   2 +-
 .../mirai/utils/cryptor/contentToString.kt    |   2 +
 .../net/mamoe/mirai/demo/MiraiService.kt      |   3 +-
 .../net/mamoe/mirai/japt/BlockingBot.java     |   3 -
 36 files changed, 181 insertions(+), 433 deletions(-)
 delete mode 100644 mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Calculation.kt
 delete mode 100644 mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/LoginFailedException.kt
 delete mode 100644 mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Tested.kt
 delete mode 100644 mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/assertUnreachable.kt
 delete mode 100644 mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/Decrypters.kt

diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 46c88fb64..3e9038784 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
 #Thu Oct 03 14:28:43 CST 2019
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStorePath=wrapper/dists
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt
index 137dce05e..123b3911d 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt
@@ -41,7 +41,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
     override val supervisor: CompletableJob = SupervisorJob(bot.coroutineContext[Job])
 
     override val coroutineContext: CoroutineContext = bot.coroutineContext + CoroutineExceptionHandler { _, throwable ->
-        throwable.logStacktrace("Exception in NetworkHandler")
+        bot.logger.error("Exception in NetworkHandler", throwable)
     }
 
     private lateinit var channel: PlatformSocket
@@ -106,6 +106,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
         StatSvc.Register(bot.client).sendAndExpect<StatSvc.Register.Response>(6000) // it's slow
     }
 
+    @UseExperimental(MiraiExperimentalAPI::class)
     override suspend fun init() {
         this@QQAndroidBotNetworkHandler.subscribeAlways<ForceOfflineEvent> {
             if (this@QQAndroidBotNetworkHandler.bot == this.bot) {
@@ -113,6 +114,8 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
             }
         }
 
+        MessageSvc.PbGetMsg(bot.client, MsgSvc.SyncFlag.START, currentTimeSeconds).sendWithoutExpect()
+
         //val msg = MessageSvc.PbGetMsg(bot.client, MsgSvc.SyncFlag.START, currentTimeSeconds).sendAndExpect<MessageSvc.PbGetMsg.Response>()
         //println(msg.contentToString())
         bot.qqs.delegate.clear()
@@ -199,16 +202,14 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
                     } catch (e: Exception) {
                         groupInfo[it.groupCode] = -1
                         bot.logger.info("群${it.groupCode}的列表拉取失败, 将采用动态加入")
-                        println(e.message)
-                        println(e.logStacktrace())
+                        bot.logger.error(e)
                     }
                 }
             }
             bot.logger.info("群组列表与群成员加载完成, 共 ${troopListData.groups.size}个")
         } catch (e: Exception) {
             bot.logger.error("加载组信息失败|一般这是由于加载过于频繁导致/将以热加载方式加载群列表")
-            println(e.message)
-            println(e.logStacktrace())
+            bot.logger.error(e)
         }
 
         //===log===//
@@ -239,8 +240,8 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
             }
         }
         bot.logger.info("====================Mirai Bot List初始化完毕====================")
-        return
-        MessageSvc.PbGetMsg(bot.client, MsgSvc.SyncFlag.START, currentTimeSeconds).sendWithoutExpect()
+
+        bot.firstLoginSucceed = true
     }
 
 
@@ -483,7 +484,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
     private val packetReceiveLock: Mutex = Mutex()
 
     /**
-     * 发送一个包, 但不期待任何返回.-
+     * 发送一个包, 但不期待任何返回.
      */
     suspend fun OutgoingPacket.sendWithoutExpect() {
         bot.logger.info("Send: ${this.commandName}")
@@ -494,6 +495,8 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
 
     /**
      * 发送一个包, 并挂起直到接收到指定的返回包或超时(3000ms)
+     *
+     * @param retry 当不为 0 时将使用 [ByteArrayPool] 缓存. 因此若非必要, 请不要允许 retry
      */
     suspend fun <E : Packet> OutgoingPacket.sendAndExpect(timeoutMillis: Long = 3000, retry: Int = 0): E {
         require(timeoutMillis > 0) { "timeoutMillis must > 0" }
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/http/HttpRequest.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/http/HttpRequest.kt
index b74ac1501..3be0d0d90 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/http/HttpRequest.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/http/HttpRequest.kt
@@ -1,20 +1,17 @@
 package net.mamoe.mirai.qqandroid.network.http
 
 import io.ktor.client.HttpClient
-import io.ktor.client.request.*
+import io.ktor.client.request.get
+import io.ktor.client.request.headers
+import io.ktor.client.request.post
 import io.ktor.client.response.HttpResponse
-import io.ktor.http.ContentType
 import io.ktor.http.URLProtocol
 import io.ktor.http.setCookie
 import io.ktor.http.userAgent
-import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.io.readRemaining
-import kotlinx.coroutines.withContext
-import net.mamoe.mirai.qqandroid.QQAndroidBot
+import kotlinx.io.core.readBytes
 import net.mamoe.mirai.qqandroid.network.QQAndroidClient
-import net.mamoe.mirai.utils.cryptor.contentToString
 import net.mamoe.mirai.utils.currentTimeMillis
-import net.mamoe.mirai.utils.io.readRemainingBytes
 import net.mamoe.mirai.utils.io.toUHexString
 
 /**
@@ -49,7 +46,7 @@ internal suspend fun HttpClient.getPTLoginCookies(
 
     println(res.status)
     println(res.setCookie())
-    println(res.content.readRemaining().readRemainingBytes().toUHexString())
+    println(res.content.readRemaining().readBytes().toUHexString())
     return "done";
 }
 
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/OutgoingPacketAndroid.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/OutgoingPacketAndroid.kt
index 757956aad..6d93797b2 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/OutgoingPacketAndroid.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/OutgoingPacketAndroid.kt
@@ -10,7 +10,6 @@ import net.mamoe.mirai.utils.MiraiInternalAPI
 import net.mamoe.mirai.utils.io.encryptAndWrite
 import net.mamoe.mirai.utils.io.writeHex
 import net.mamoe.mirai.utils.io.writeIntLVPacket
-import net.mamoe.mirai.utils.io.writeQQ
 
 internal class OutgoingPacket constructor(
     name: String?,
@@ -250,7 +249,7 @@ internal fun BytePacketBuilder.writeOicqRequestPacket(
     writeShort(client.protocolVersion)
     writeShort(commandId.toShort())
     writeShort(1) // const??
-    writeQQ(client.uin)
+    writeInt(client.uin.toInt())
     writeByte(3) // originally const
     writeByte(encryptMethod.id.toByte())
     writeByte(0) // const8_always_0
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/Tlv.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/Tlv.kt
index b63d1365f..74cddb2d3 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/Tlv.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/Tlv.kt
@@ -23,7 +23,7 @@ fun BytePacketBuilder.t1(uin: Long, ip: ByteArray) {
         writeShort(1) // _ip_ver
         writeInt(Random.nextInt())
         writeInt(uin.toInt())
-        writeTime()
+        writeInt(currentTimeMillis.toInt())
         writeFully(ip)
         writeShort(0)
     } shouldEqualsTo 20
@@ -100,7 +100,7 @@ fun BytePacketBuilder.t106(
                 writeLong(uin)
             }
 
-            writeTime()
+            writeInt(currentTimeMillis.toInt())
             writeFully(ByteArray(4)) // ip // no need to write actual ip
             writeByte(n5_always_1.toByte())
             writeFully(passwordMd5)
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/TroopManagement.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/TroopManagement.kt
index 16f05b3c3..19d29799d 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/TroopManagement.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/TroopManagement.kt
@@ -1,15 +1,15 @@
 package net.mamoe.mirai.qqandroid.network.protocol.packet.chat
 
-import kotlinx.io.charsets.Charset
-import kotlinx.io.charsets.encode
-import kotlinx.io.core.*
+import kotlinx.io.core.ByteReadPacket
+import kotlinx.io.core.buildPacket
+import kotlinx.io.core.readBytes
+import kotlinx.io.core.toByteArray
 import kotlinx.serialization.toUtf8Bytes
 import net.mamoe.mirai.contact.Member
 import net.mamoe.mirai.data.Packet
 import net.mamoe.mirai.qqandroid.QQAndroidBot
 import net.mamoe.mirai.qqandroid.io.serialization.*
 import net.mamoe.mirai.qqandroid.network.QQAndroidClient
-import net.mamoe.mirai.qqandroid.network.protocol.data.jce.GetTroopListReqV2Simplify
 import net.mamoe.mirai.qqandroid.network.protocol.data.jce.ModifyGroupCardReq
 import net.mamoe.mirai.qqandroid.network.protocol.data.jce.RequestPacket
 import net.mamoe.mirai.qqandroid.network.protocol.data.jce.stUinInfo
@@ -22,7 +22,6 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
 import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory
 import net.mamoe.mirai.qqandroid.network.protocol.packet.buildOutgoingUniPacket
 import net.mamoe.mirai.utils.daysToSeconds
-import net.mamoe.mirai.utils.io.encodeToGBKString
 import net.mamoe.mirai.utils.io.encodeToString
 
 internal object TroopManagement {
@@ -355,7 +354,7 @@ internal object TroopManagement {
                                         gender = 0,
                                         dwuin = member.id,
                                         dwFlag = 31,
-                                        sName = newName.toUtf8Bytes().encodeToGBKString(),
+                                        sName = newName.toUtf8Bytes().encodeToString(charset = CharsetGBK),
                                         sPhone = "",
                                         sEmail = "",
                                         sRemark = ""
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt
index f244e509a..923aaae77 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/LoginPacket.kt
@@ -22,15 +22,15 @@ import net.mamoe.mirai.utils.md5
 /**
  * OicqRequest
  */
-@UseExperimental(ExperimentalUnsignedTypes::class)
+@Suppress("FunctionName")
+@UseExperimental(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class)
 internal object LoginPacket : OutgoingPacketFactory<LoginPacket.LoginPacketResponse>("wtlogin.login") {
+    private const val subAppId = 537062845L
+
     /**
      * 提交验证码
      */
     object SubCommand2 {
-        private const val appId = 16L
-        private const val subAppId = 537062845L
-
         fun SubmitSliderCaptcha(
             client: QQAndroidClient,
             ticket: String
@@ -66,10 +66,7 @@ internal object LoginPacket : OutgoingPacketFactory<LoginPacket.LoginPacketRespo
     }
 
     object SubCommand20 {
-        private const val appId = 16L
-        private const val subAppId = 537062845L
 
-        @UseExperimental(MiraiInternalAPI::class)
         operator fun invoke(
             client: QQAndroidClient,
             t402: ByteArray
@@ -91,9 +88,6 @@ internal object LoginPacket : OutgoingPacketFactory<LoginPacket.LoginPacketRespo
      * 提交 SMS
      */
     object SubCommand7 {
-        private const val appId = 16L
-        private const val subAppId = 537062845L
-        @UseExperimental(MiraiInternalAPI::class)
         operator fun invoke(
             client: QQAndroidClient
         ): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId ->
@@ -297,7 +291,7 @@ internal object LoginPacket : OutgoingPacketFactory<LoginPacket.LoginPacketRespo
             }
         }
 
-        class DeviceLockLogin(val t402: ByteArray, val t403: ByteArray) : LoginPacketResponse() {
+        class DeviceLockLogin(val t402: ByteArray) : LoginPacketResponse() {
             override fun toString(): String = "LoginPacket.LoginPacketResponse.DeviceLockLogin"
         }
     }
@@ -320,7 +314,7 @@ internal object LoginPacket : OutgoingPacketFactory<LoginPacket.LoginPacketRespo
             2 -> onSolveLoginCaptcha(tlvMap, bot)
             160 /*-96*/ -> onUnsafeDeviceLogin(tlvMap)
             204 /*-52*/ -> onSMSVerifyNeeded(tlvMap, bot)
-            else -> tlvMap[0x149]?.let { bot.client.analysisTlv149(it) } ?: error("unknown login result type: $type")
+            else -> tlvMap[0x149]?.let { analysisTlv149(it) } ?: error("unknown login result type: $type")
         }
     }
 
@@ -331,11 +325,11 @@ internal object LoginPacket : OutgoingPacketFactory<LoginPacket.LoginPacketRespo
     ): LoginPacketResponse.DeviceLockLogin {
         bot.client.t104 = tlvMap.getOrFail(0x104)
         // println("403: " + tlvMap[0x403]?.toUHexString())
-        return LoginPacketResponse.DeviceLockLogin(tlvMap[0x402]!!, tlvMap.getOrFail(0x403))
+        return LoginPacketResponse.DeviceLockLogin(tlvMap.getOrFail(0x402))
     }
 
     private fun onUnsafeDeviceLogin(tlvMap: TlvMap): LoginPacketResponse.UnsafeLogin {
-        return LoginPacketResponse.UnsafeLogin(tlvMap.getOrFail(0x204).toReadPacket().readRemainingBytes().encodeToString())
+        return LoginPacketResponse.UnsafeLogin(tlvMap.getOrFail(0x204).toReadPacket().readBytes().encodeToString())
     }
 
     private fun onErrorMessage(tlvMap: TlvMap): LoginPacketResponse.Error {
@@ -369,7 +363,7 @@ internal object LoginPacket : OutgoingPacketFactory<LoginPacket.LoginPacketRespo
                 imageData.discardExact(2)//image Length
                 val sign = imageData.readBytes(signInfoLength.toInt())
                 return LoginPacketResponse.Captcha.Picture(
-                    data = imageData.readRemainingBytes().toIoBuffer(),
+                    data = imageData.readBytes().toIoBuffer(),
                     sign = sign
                 )
             } else error("UNKNOWN CAPTCHA QUESTION: $question")
@@ -652,11 +646,6 @@ internal object LoginPacket : OutgoingPacketFactory<LoginPacket.LoginPacketRespo
         pwdFlag = readByte().toInt() == 1
     }
 
-    /**
-     */
-    private fun QQAndroidClient.analysisTlv528(t528: ByteArray) = t528.read {
-    }
-
     /**
      * 设置 [QQAndroidClient.uin]
      */
@@ -696,7 +685,7 @@ internal object LoginPacket : OutgoingPacketFactory<LoginPacket.LoginPacketRespo
     /**
      * 错误消息
      */
-    private fun QQAndroidClient.analysisTlv149(t149: ByteArray): LoginPacketResponse.Error {
+    private fun analysisTlv149(t149: ByteArray): LoginPacketResponse.Error {
 
         return t149.read {
             discardExact(2) //type
diff --git a/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/clientToServer.kt b/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/clientToServer.kt
index 58fd3faa4..ef2a81cf0 100644
--- a/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/clientToServer.kt
+++ b/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/clientToServer.kt
@@ -114,6 +114,16 @@ fun ByteReadPacket.decodeMultiClientToServerPackets() {
     println()
 }
 
+fun Map<Int, ByteArray>.printTLVMap(name: String = "", keyLength: Int = 2) =
+    debugPrintln("TLVMap $name= " + this.mapValues { (_, value) -> value.toUHexString() }.mapKeys {
+        when (keyLength) {
+            1 -> it.key.toUByte().contentToString()
+            2 -> it.key.toUShort().contentToString()
+            4 -> it.key.toUInt().contentToString()
+            else -> error("Expecting 1, 2 or 4 for keyLength")
+        }
+    }.entries.joinToString(prefix = "{", postfix = "}", separator = "\n"))
+
 fun ByteReadPacket.analysisOneFullPacket(): ByteReadPacket = debugIfFail("Failed", { buildPacket { writeInt(it.size + 4); writeFully(it) } }) {
     val flag1 = readInt()
     println("flag1=" + flag1.contentToString())
@@ -139,7 +149,7 @@ fun ByteReadPacket.analysisOneFullPacket(): ByteReadPacket = debugIfFail("Failed
     println("uin=" + readString(readInt() - 4))
 
     println("// 解密 body")
-    val encrypted = readRemainingBytes()
+    val encrypted = readBytes()
 
     val decrypted = encrypted.tryDecryptOrNull()
     if (decrypted == null) {
diff --git a/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/serverToClient.kt b/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/serverToClient.kt
index 638a7156d..98e12b425 100644
--- a/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/serverToClient.kt
+++ b/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/serverToClient.kt
@@ -198,6 +198,10 @@ private fun ByteReadPacket.parseOicqResponse(body: ByteReadPacket.() -> Unit) {
     }
 }
 
+fun ByteReadPacket.readIoBuffer(
+    n: Int = remaining.toInt()//not that safe but adequate
+): IoBuffer = IoBuffer.Pool.borrow().also { this.readFully(it, n) }
+
 /**
  * 解析 SSO 层包装
  */
diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt
index 12bcdde20..e50f9c0c2 100644
--- a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt
+++ b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt
@@ -1,17 +1,18 @@
 package net.mamoe.mirai.utils.cryptor
 
+import net.mamoe.mirai.utils.MiraiDebugAPI
 import java.lang.reflect.Field
 import kotlin.reflect.full.allSuperclasses
 
 
+@MiraiDebugAPI
 actual fun Any.contentToStringReflectively(prefix: String, filter: ((name: String, value: Any?) -> Boolean)?): String {
-    val newPrefix = prefix
     return (this::class.simpleName ?: "<UnnamedClass>") + "#" + this::class.hashCode() + " {\n" +
             this.allFieldsFromSuperClassesMatching { it.name.startsWith("net.mamoe.mirai") }
                 .distinctBy { it.name }
                 .filterNot { it.name.contains("$") || it.name == "Companion" || it.isSynthetic || it.name == "serialVersionUID" }
                 .joinToStringPrefixed(
-                    prefix = newPrefix
+                    prefix = prefix
                 ) {
                     it.isAccessible = true
                     if (filter != null) {
@@ -22,7 +23,7 @@ actual fun Any.contentToStringReflectively(prefix: String, filter: ((name: Strin
                     it.name + "=" + kotlin.runCatching {
                         val value = it.get(this)
                         if (value == this) "<this>"
-                        else value.contentToString(newPrefix)
+                        else value.contentToString(prefix)
                     }.getOrElse { "<!>" }
                 } + "\n$prefix}"
 }
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt
index 2c369a46b..fb8cb848f 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt
@@ -134,8 +134,6 @@ abstract class Bot : CoroutineScope {
      * 不建议调用这个函数.
      *
      * 最终调用 [net.mamoe.mirai.network.BotNetworkHandler.login]
-     *
-     * @throws LoginFailedException
      */
     abstract suspend fun login()
     // endregion
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotHelper.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotHelper.kt
index bab878901..a890f562f 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotHelper.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/BotHelper.kt
@@ -4,7 +4,6 @@
 
 package net.mamoe.mirai
 
-import net.mamoe.mirai.utils.LoginFailedException
 import kotlin.jvm.JvmMultifileClass
 import kotlin.jvm.JvmName
 
@@ -15,8 +14,6 @@ import kotlin.jvm.JvmName
 //Contacts
 /**
  * 登录, 返回 [this]
- *
- * @throws LoginFailedException
  */
 suspend inline fun <B: Bot> B.alsoLogin(): B = also { login() }
 
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.kt
index 913270bed..07e795fab 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/BotConfiguration.kt
@@ -7,6 +7,7 @@ import kotlin.coroutines.EmptyCoroutineContext
 import kotlin.jvm.JvmStatic
 
 /**
+ * 验证码, 设备锁解决器
  */
 abstract class LoginSolver {
     abstract suspend fun onSolvePicCaptcha(bot: Bot, data: IoBuffer): String?
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Calculation.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Calculation.kt
deleted file mode 100644
index 13f67f445..000000000
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Calculation.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package net.mamoe.mirai.utils
-
-import kotlinx.io.core.BytePacketBuilder
-import kotlinx.io.core.toByteArray
-import kotlinx.io.core.writeFully
-import net.mamoe.mirai.utils.io.getRandomByteArray
-
-fun md5(str: String): ByteArray = md5(str.toByteArray())
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt
index 171f232b9..011346d81 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt
@@ -80,7 +80,7 @@ class ExternalImage(
 suspend fun ExternalImage.sendTo(contact: Contact) = when (contact) {
     is Group -> contact.uploadImage(this).sendTo(contact)
     is QQ -> contact.uploadImage(this).sendTo(contact)
-    else -> assertUnreachable()
+    else -> error("unreachable")
 }
 
 /**
@@ -92,7 +92,7 @@ suspend fun ExternalImage.sendTo(contact: Contact) = when (contact) {
 suspend fun ExternalImage.upload(contact: Contact): Image = when (contact) {
     is Group -> contact.uploadImage(this)
     is QQ -> contact.uploadImage(this)
-    else -> assertUnreachable()
+    else -> error("unreachable")
 }
 
 /**
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/LoginFailedException.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/LoginFailedException.kt
deleted file mode 100644
index 85b2e68d1..000000000
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/LoginFailedException.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package net.mamoe.mirai.utils
-
-import net.mamoe.mirai.data.LoginResult
-
-class LoginFailedException(
-    val result: LoginResult,
-    message: String = "Login failed with reason $result"
-) : RuntimeException(message)
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/MiraiLogger.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/MiraiLogger.kt
index eb2d0891d..dfec87bd1 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/MiraiLogger.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/MiraiLogger.kt
@@ -1,3 +1,5 @@
+@file:Suppress("unused")
+
 package net.mamoe.mirai.utils
 
 import net.mamoe.mirai.Bot
@@ -139,7 +141,7 @@ interface MiraiLogger {
 /**
  * 当前平台的默认的日志记录器.
  * 在 _JVM 控制台_ 端的实现为 [println]
- * 在 _Android_ 端的实现为 [android.util.Log]
+ * 在 _Android_ 端的实现为 `android.util.Log`
  *
  * 不应该直接构造这个类的实例. 请使用 [DefaultLogger], 或使用默认的顶层日志记录 [MiraiLogger.Companion]
  */
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/NumberUtils.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/NumberUtils.kt
index 1db24b31f..061921504 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/NumberUtils.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/NumberUtils.kt
@@ -1,5 +1,11 @@
+@file:JvmMultifileClass
+@file:JvmName("Utils")
+
 package net.mamoe.mirai.utils
 
+import kotlin.jvm.JvmMultifileClass
+import kotlin.jvm.JvmName
+
 /**
  * 要求 [this] 最小为 [min].
  */
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/OverFileSizeMaxException.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/OverFileSizeMaxException.kt
index b6e33e620..c9183a133 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/OverFileSizeMaxException.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/OverFileSizeMaxException.kt
@@ -1,5 +1,11 @@
+@file:JvmMultifileClass
+@file:JvmName("Utils")
+
 package net.mamoe.mirai.utils
 
+import kotlin.jvm.JvmMultifileClass
+import kotlin.jvm.JvmName
+
 /**
  * 图片文件过大
  */
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformUtils.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformUtils.kt
index ea91627d7..a1b75697b 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformUtils.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/PlatformUtils.kt
@@ -1,10 +1,11 @@
-@file:Suppress("EXPERIMENTAL_API_USAGE")
+@file:Suppress("EXPERIMENTAL_API_USAGE", "NOTHING_TO_INLINE")
 
 package net.mamoe.mirai.utils
 
 import io.ktor.client.HttpClient
 import io.ktor.util.date.GMTDate
 import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.io.core.toByteArray
 
 /**
  * 时间戳
@@ -36,6 +37,8 @@ expect fun ByteArray.unzip(offset: Int = 0, length: Int = this.size - offset): B
  */
 expect fun md5(byteArray: ByteArray): ByteArray
 
+inline fun md5(str: String): ByteArray = md5(str.toByteArray())
+
 /**
  * hostname 解析 ipv4
  */
@@ -53,7 +56,7 @@ expect val Http: HttpClient
 
 expect fun newCoroutineDispatcher(threadCount: Int): CoroutineDispatcher
 
-internal fun ByteArray.checkOffsetAndLength(offset: Int, length: Int){
+internal fun ByteArray.checkOffsetAndLength(offset: Int, length: Int) {
     require(offset >= 0) { "offset shouldn't be negative: $offset" }
     require(length >= 0) { "length shouldn't be negative: $length" }
     require(offset + length <= this.size) { "offset ($offset) + length ($length) > array.size (${this.size})" }
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/SuspendLazy.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/SuspendLazy.kt
index d2287fb19..02b730187 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/SuspendLazy.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/SuspendLazy.kt
@@ -1,4 +1,4 @@
-@file:Suppress("unused", "FunctionName")
+@file:Suppress("unused", "FunctionName", "NOTHING_TO_INLINE")
 
 package net.mamoe.mirai.utils
 
@@ -16,7 +16,7 @@ import kotlin.coroutines.EmptyCoroutineContext
  * val image: Deferred<Image> by suspendLazy{  /* intializer */  }
  * ```
  */
-fun <R> CoroutineScope.suspendLazy(context: CoroutineContext = EmptyCoroutineContext, initializer: suspend () -> R): Lazy<Deferred<R>> =
+inline fun <R> CoroutineScope.suspendLazy(context: CoroutineContext = EmptyCoroutineContext, noinline initializer: suspend () -> R): Lazy<Deferred<R>> =
     SuspendLazy(this, context, initializer)
 
 /**
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Tested.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Tested.kt
deleted file mode 100644
index 1f898030c..000000000
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Tested.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package net.mamoe.mirai.utils
-
-/**
- * 仅用于测试时标记, 未来会删除
- *
- * @author Him188moe
- */
-@Suppress("unused")
-internal annotation class Tested(val date: String = "")
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Time.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Time.kt
index 2bf5aeb78..09d3380e7 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Time.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Time.kt
@@ -1,5 +1,10 @@
+@file:JvmMultifileClass
+@file:JvmName("Utils")
+
 package net.mamoe.mirai.utils
 
+import kotlin.jvm.JvmMultifileClass
+import kotlin.jvm.JvmName
 import kotlin.time.seconds
 
 // 临时使用, 待 Kotlin Duration 稳定后使用 Duration.
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/assertUnreachable.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/assertUnreachable.kt
deleted file mode 100644
index 49f3c7601..000000000
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/assertUnreachable.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package net.mamoe.mirai.utils
-
-/**
- * 表示这里是不可到达的位置.
- */
-@Suppress("NOTHING_TO_INLINE")
-internal inline fun assertUnreachable(): Nothing = error("This clause should not be reached")
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/Decrypters.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/Decrypters.kt
deleted file mode 100644
index 666fd6450..000000000
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/Decrypters.kt
+++ /dev/null
@@ -1,66 +0,0 @@
-package net.mamoe.mirai.utils.cryptor
-
-import kotlinx.io.core.BytePacketBuilder
-import kotlinx.io.core.ByteReadPacket
-import kotlinx.io.core.IoBuffer
-import kotlinx.io.pool.useInstance
-import net.mamoe.mirai.utils.io.ByteArrayPool
-import net.mamoe.mirai.utils.io.encryptAndWrite
-import net.mamoe.mirai.utils.io.toReadPacket
-
-
-/**
- * [ByteArray] 解密器
- */
-interface DecrypterByteArray : Decrypter {
-    val value: ByteArray
-    override fun decrypt(input: ByteReadPacket, offset: Int, length: Int): ByteReadPacket = input.decryptBy(value, offset, length)
-}
-
-/**
- * [IoBuffer] 解密器
- */
-interface DecrypterIoBuffer : Decrypter {
-    val value: IoBuffer
-    override fun decrypt(input: ByteReadPacket, offset: Int, length: Int): ByteReadPacket = input.decryptBy(value, offset, length)
-}
-
-/**
- * 连接在一起的解密器
- */
-inline class LinkedDecrypter(inline val block: (input: ByteReadPacket, offset: Int, length: Int) -> ByteReadPacket) : Decrypter {
-    override fun decrypt(input: ByteReadPacket, offset: Int, length: Int): ByteReadPacket = block(input, offset, length)
-}
-
-object NoDecrypter : Decrypter, DecrypterType<NoDecrypter> {
-    override fun decrypt(input: ByteReadPacket, offset: Int, length: Int): ByteReadPacket {
-        if (offset == 0 && length == input.remaining.toInt()) {
-            return input
-        }
-
-        ByteArrayPool.useInstance { buffer ->
-            input.readFully(buffer, offset, length)
-            return buffer.toReadPacket()
-        }
-    }
-}
-
-fun Decrypter.decrypt(input: ByteReadPacket): ByteReadPacket = this.decrypt(input, 0, input.remaining.toInt())
-
-/**
- * 解密器
- */
-interface Decrypter {
-    // do not write with default args. NoSuchMethodError when inline classes override this function
-    fun decrypt(input: ByteReadPacket, offset: Int, length: Int): ByteReadPacket
-    /**
-     * 连接后将会先用 this 解密, 再用 [another] 解密
-     */
-    operator fun plus(another: Decrypter): Decrypter =
-        LinkedDecrypter { input: ByteReadPacket, offset: Int, length: Int -> another.decrypt(this.decrypt(input, offset, length)) }
-}
-
-interface DecrypterType<D : Decrypter>
-
-inline fun BytePacketBuilder.encryptAndWrite(key: DecrypterByteArray, encoder: BytePacketBuilder.() -> Unit) =
-    this.encryptAndWrite(key.value, encoder)
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/Proto.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/Proto.kt
index 931789a6b..21604652a 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/Proto.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/Proto.kt
@@ -6,6 +6,9 @@ import kotlinx.io.core.ByteReadPacket
 import kotlinx.io.core.readBytes
 import kotlinx.io.core.readUInt
 import kotlinx.io.core.readULong
+import net.mamoe.mirai.utils.MiraiDebugAPI
+import net.mamoe.mirai.utils.MiraiExperimentalAPI
+import net.mamoe.mirai.utils.MiraiInternalAPI
 import net.mamoe.mirai.utils.io.*
 import kotlin.jvm.JvmStatic
 
@@ -24,12 +27,14 @@ import kotlin.jvm.JvmStatic
  *
  * https://www.jianshu.com/p/f888907adaeb
  */
+@MiraiDebugAPI
 fun ProtoFieldId(serializedId: UInt): ProtoFieldId =
     ProtoFieldId(
         protoFieldNumber(serializedId),
         protoType(serializedId)
     )
 
+@MiraiDebugAPI
 data class ProtoFieldId(
     val fieldNumber: Int,
     val type: ProtoType
@@ -38,6 +43,7 @@ data class ProtoFieldId(
 }
 
 @Suppress("SpellCheckingInspection")
+@MiraiDebugAPI
 enum class ProtoType(val value: Byte, private val typeName: String) {
     /**
      * int32, int64, uint32, uint64, sint32, sint64, bool, enum
@@ -82,6 +88,7 @@ enum class ProtoType(val value: Byte, private val typeName: String) {
  *
  * serializedId = (fieldNumber << 3) | wireType
  */
+@MiraiDebugAPI
 fun protoType(number: UInt): ProtoType =
     ProtoType.valueOf(number.toInt().shl(29).ushr(29).toByte())
 
@@ -90,9 +97,10 @@ fun protoType(number: UInt): ProtoType =
  *
  * serializedId = (fieldNumber << 3) | wireType
  */
+@MiraiDebugAPI
 fun protoFieldNumber(number: UInt): Int = number.toInt().ushr(3)
 
-
+@MiraiDebugAPI
 class ProtoMap(map: MutableMap<ProtoFieldId, Any>) : MutableMap<ProtoFieldId, Any> by map {
     companion object {
         @JvmStatic
@@ -120,6 +128,7 @@ class ProtoMap(map: MutableMap<ProtoFieldId, Any>) : MutableMap<ProtoFieldId, An
 /**
  * 将所有元素加入转换为多行的字符串表示.
  */
+@MiraiDebugAPI
 fun <T> Sequence<T>.joinToStringPrefixed(prefix: String, transform: (T) -> CharSequence): String {
     return this.joinToString(prefix = "$prefix${ProtoMap.indent}", separator = "\n$prefix${ProtoMap.indent}", transform = transform)
 }
@@ -135,6 +144,7 @@ fun <T> Sequence<T>.joinToStringPrefixed(prefix: String, transform: (T) -> CharS
  * `data class`: 调用其 [toString]
  * 其他类型: 反射获取它和它的所有来自 Mirai 的 super 类型的所有自有属性并递归调用 [contentToString]. 嵌套结构将会以缩进表示
  */
+@MiraiDebugAPI("Extremely slow")
 fun Any?.contentToString(prefix: String = ""): String = when (this) {
     is Unit -> "Unit"
     is UInt -> "0x" + this.toUHexString("") + "($this)"
@@ -222,8 +232,11 @@ fun Any?.contentToString(prefix: String = ""): String = when (this) {
     }
 }
 
+@MiraiExperimentalAPI("Extremely slow")
+@MiraiDebugAPI("Extremely slow")
 expect fun Any.contentToStringReflectively(prefix: String = "", filter: ((String, Any?) -> Boolean)? = null): String
 
+@MiraiDebugAPI
 @Suppress("UNCHECKED_CAST")
 fun ByteReadPacket.readProtoMap(length: Long = this.remaining): ProtoMap {
     val map = ProtoMap(mutableMapOf())
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/ByteArrayUtil.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/ByteArrayUtil.kt
index e304fccfd..7e63fa014 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/ByteArrayUtil.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/ByteArrayUtil.kt
@@ -1,8 +1,11 @@
-@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS")
+@file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS", "NOTHING_TO_INLINE")
+@file:JvmMultifileClass
+@file:JvmName("Utils")
 
 package net.mamoe.mirai.utils.io
 
 import kotlinx.io.charsets.Charset
+import kotlinx.io.charsets.Charsets
 import kotlinx.io.core.ByteReadPacket
 import kotlinx.io.core.String
 import kotlinx.io.core.use
@@ -10,6 +13,8 @@ import net.mamoe.mirai.utils.checkOffsetAndLength
 import kotlin.contracts.ExperimentalContracts
 import kotlin.contracts.InvocationKind
 import kotlin.contracts.contract
+import kotlin.jvm.JvmMultifileClass
+import kotlin.jvm.JvmName
 import kotlin.jvm.JvmOverloads
 import kotlin.jvm.JvmSynthetic
 
@@ -80,12 +85,9 @@ fun UByteArray.toUHexString(separator: String = " ", offset: Int = 0, length: In
 }
 
 @Suppress("NOTHING_TO_INLINE")
-inline fun ByteArray.encodeToString(): String = String(this)
+inline fun ByteArray.encodeToString(charset: Charset = Charsets.UTF_8): String = String(this, charset = charset)
 
-fun ByteArray.encodeToGBKString(): String = String(this, 0, this.size, Charset.forName("GBK"))
-
-
-fun ByteArray.toReadPacket(offset: Int = 0, length: Int = this.size - offset) =
+inline fun ByteArray.toReadPacket(offset: Int = 0, length: Int = this.size - offset) =
     ByteReadPacket(this, offset = offset, length = length)
 
 @UseExperimental(ExperimentalContracts::class)
@@ -94,6 +96,4 @@ inline fun <R> ByteArray.read(t: ByteReadPacket.() -> R): R {
         callsInPlace(t, InvocationKind.EXACTLY_ONCE)
     }
     return this.toReadPacket().use(t)
-}
-
-fun ByteArray.cutTail(length: Int): ByteArray = this.copyOfRange(0, this.size - length)
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt
index 36a9b56cf..dba0f3820 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt
@@ -1,3 +1,7 @@
+@file:Suppress("NOTHING_TO_INLINE")
+@file:JvmMultifileClass
+@file:JvmName("Utils")
+
 package net.mamoe.mirai.utils.io
 
 import kotlinx.io.core.*
@@ -9,29 +13,34 @@ import net.mamoe.mirai.utils.withSwitch
 import kotlin.contracts.ExperimentalContracts
 import kotlin.contracts.InvocationKind
 import kotlin.contracts.contract
+import kotlin.js.JsName
+import kotlin.jvm.JvmMultifileClass
+import kotlin.jvm.JvmName
 
 
+@MiraiDebugAPI("Unsatble")
 object DebugLogger : MiraiLogger by DefaultLogger("Packet Debug").withSwitch()
 
-fun Throwable.logStacktrace(message: String? = null) = DebugLogger.error(message, this)
+@MiraiDebugAPI("Unstable")
+inline fun Throwable.logStacktrace(message: String? = null) = DebugLogger.error(message, this)
 
 @MiraiDebugAPI("Low efficiency.")
-fun debugPrintln(any: Any?) = DebugLogger.debug(any)
+inline fun debugPrintln(any: Any?) = DebugLogger.debug(any)
 
 @MiraiDebugAPI("Low efficiency.")
-fun String.debugPrintThis(name: String): String {
+inline fun String.debugPrintThis(name: String): String {
     DebugLogger.debug("$name=$this")
     return this
 }
 
 @MiraiDebugAPI("Low efficiency.")
-fun ByteArray.debugPrintThis(name: String): ByteArray {
+inline fun ByteArray.debugPrintThis(name: String): ByteArray {
     DebugLogger.debug(name + "=" + this.toUHexString())
     return this
 }
 
 @MiraiDebugAPI("Low efficiency.")
-fun IoBuffer.debugPrintThis(name: String): IoBuffer {
+inline fun IoBuffer.debugPrintThis(name: String): IoBuffer {
     ByteArrayPool.useInstance {
         val count = this.readAvailable(it)
         DebugLogger.debug(name + "=" + it.toUHexString(offset = 0, length = count))
@@ -49,12 +58,12 @@ inline fun IoBuffer.debugCopyUse(block: IoBuffer.() -> Unit): IoBuffer {
 }
 
 @MiraiDebugAPI("Low efficiency.")
-fun Input.debugDiscardExact(n: Number, name: String = "") {
+inline fun Input.debugDiscardExact(n: Number, name: String = "") {
     DebugLogger.debug("Discarded($n) $name=" + this.readBytes(n.toInt()).toUHexString())
 }
 
 @MiraiDebugAPI("Low efficiency.")
-fun ByteReadPacket.debugPrintThis(name: String = ""): ByteReadPacket {
+inline fun ByteReadPacket.debugPrintThis(name: String = ""): ByteReadPacket {
     ByteArrayPool.useInstance {
         val count = this.readAvailable(it)
         DebugLogger.debug("ByteReadPacket $name=" + it.toUHexString(offset = 0, length = count))
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/InputUtils.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/InputUtils.kt
index 4cde854c4..4059e3169 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/InputUtils.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/InputUtils.kt
@@ -1,4 +1,6 @@
 @file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS", "NOTHING_TO_INLINE")
+@file:JvmMultifileClass
+@file:JvmName("Utils")
 
 package net.mamoe.mirai.utils.io
 
@@ -7,8 +9,10 @@ import kotlinx.io.charsets.Charset
 import kotlinx.io.charsets.Charsets
 import kotlinx.io.core.*
 import kotlinx.io.pool.useInstance
-import net.mamoe.mirai.utils.assertUnreachable
+import net.mamoe.mirai.utils.MiraiDebugAPI
+import net.mamoe.mirai.utils.MiraiInternalAPI
 import net.mamoe.mirai.utils.cryptor.contentToString
+import kotlin.jvm.JvmMultifileClass
 import kotlin.jvm.JvmName
 import kotlin.jvm.JvmSynthetic
 
@@ -34,6 +38,7 @@ fun ByteReadPacket.transferTo(outputStream: OutputStream) {
     }
 }
 
+@MiraiInternalAPI
 inline fun <R> ByteReadPacket.useBytes(
     n: Int = remaining.toInt(),//not that safe but adequate
     block: (data: ByteArray, length: Int) -> R
@@ -42,51 +47,24 @@ inline fun <R> ByteReadPacket.useBytes(
     block(it, n)
 }
 
-fun ByteReadPacket.readRemainingBytes(
-    n: Int = remaining.toInt()//not that safe but adequate
-): ByteArray = ByteArray(n).also { readAvailable(it, 0, n) }
-
-fun ByteReadPacket.readIoBuffer(
-    n: Int = remaining.toInt()//not that safe but adequate
-): IoBuffer = IoBuffer.Pool.borrow().also { this.readFully(it, n) }
-
-fun ByteReadPacket.readPacket(
+inline fun ByteReadPacket.readPacket(
     n: Int = remaining.toInt()//not that safe but adequate
 ): ByteReadPacket = this.readBytes(n).toReadPacket()
 
-fun ByteReadPacket.readIoBuffer(n: Short) = this.readIoBuffer(n.toInt())
+inline fun Input.readUByteLVString(): String = String(this.readUByteLVByteArray())
 
-fun Input.readIP(): String = buildString(4 + 3) {
-    repeat(4) {
-        val byte = readUByte()
-        this.append(byte.toString())
-        if (it != 3) this.append(".")
-    }
-}
+inline fun Input.readUShortLVString(): String = String(this.readUShortLVByteArray())
 
-fun Input.readQQ(): Long = this.readUInt().toLong()
-fun Input.readGroup(): Long = this.readUInt().toLong()
-fun Input.readGroupId(): Long = this.readUInt().toLong()
-fun Input.readGroupCode(): Long = this.readUInt().toLong()
+inline fun Input.readUByteLVByteArray(): ByteArray = this.readBytes(this.readUByte().toInt())
 
-fun Input.readUVarIntLVString(): String = String(this.readUVarIntByteArray())
-
-fun Input.readUByteLVString(): String = String(this.readUByteLVByteArray())
-
-fun Input.readUShortLVString(): String = String(this.readUShortLVByteArray())
-
-fun Input.readUVarIntByteArray(): ByteArray = this.readBytes(this.readUVarInt().toInt())
-
-fun Input.readUByteLVByteArray(): ByteArray = this.readBytes(this.readUByte().toInt())
-
-fun Input.readUShortLVByteArray(): ByteArray = this.readBytes(this.readUShort().toInt())
+inline fun Input.readUShortLVByteArray(): ByteArray = this.readBytes(this.readUShort().toInt())
 
 private inline fun <R> inline(block: () -> R): R = block()
 
 
 typealias TlvMap = MutableMap<Int, ByteArray>
 
-fun TlvMap.getOrFail(tag: Int): ByteArray {
+inline fun TlvMap.getOrFail(tag: Int): ByteArray {
     return this[tag] ?: error("cannot find tlv 0x${tag.toUHexString("")}($tag)")
 }
 
@@ -94,10 +72,12 @@ inline fun TlvMap.getOrFail(tag: Int, lazyMessage: (tag: Int) -> String): ByteAr
     return this[tag] ?: error(lazyMessage(tag))
 }
 
-fun Input.readTLVMap(tagSize: Int = 2): TlvMap = readTLVMap(true, tagSize)
+@MiraiDebugAPI
+inline fun Input.readTLVMap(tagSize: Int = 2, suppressDuplication: Boolean = true): TlvMap = readTLVMap(true, tagSize, suppressDuplication)
 
+@MiraiDebugAPI
 @Suppress("DuplicatedCode")
-fun Input.readTLVMap(expectingEOF: Boolean = true, tagSize: Int): TlvMap {
+fun Input.readTLVMap(expectingEOF: Boolean = true, tagSize: Int, suppressDuplication: Boolean = true): TlvMap {
     val map = mutableMapOf<Int, ByteArray>()
     var key = 0
 
@@ -119,20 +99,22 @@ fun Input.readTLVMap(expectingEOF: Boolean = true, tagSize: Int): TlvMap {
         }.toUByte() != UByte.MAX_VALUE) {
 
         if (map.containsKey(key)) {
-            DebugLogger.error(
-                @Suppress("IMPLICIT_CAST_TO_ANY")
-                """
+            if (!suppressDuplication) {
+                DebugLogger.error(
+                    @Suppress("IMPLICIT_CAST_TO_ANY")
+                    """
                 Error readTLVMap: 
                 duplicated key ${when (tagSize) {
-                    1 -> key.toByte()
-                    2 -> key.toShort()
-                    4 -> key
-                    else -> assertUnreachable()
-                }.contentToString()}
+                        1 -> key.toByte()
+                        2 -> key.toShort()
+                        4 -> key
+                        else -> error("unreachable")
+                    }.contentToString()}
                 map=${map.contentToString()}
                 duplicating value=${this.readUShortLVByteArray().toUHexString()}
                 """.trimIndent()
-            )
+                )
+            }
         } else {
             try {
                 map[key] = this.readUShortLVByteArray()
@@ -147,98 +129,10 @@ fun Input.readTLVMap(expectingEOF: Boolean = true, tagSize: Int): TlvMap {
     return map
 }
 
-/**
- * 读扁平的 tag-UVarInt map. 重复的 tag 将只保留最后一个
- *
- * tag: UByte
- * value: UVarint
- */
-@Suppress("DuplicatedCode")
-fun Input.readFlatTUVarIntMap(expectingEOF: Boolean = false, tagSize: Int = 1): MutableMap<UInt, UInt> {
-    val map = mutableMapOf<UInt, UInt>()
-    var type: UShort = 0u
-
-    while (inline {
-            try {
-                type = when (tagSize) {
-                    1 -> readUByte().toUShort()
-                    2 -> readUShort()
-                    else -> error("Unsupported tag size: $tagSize")
-                }
-            } catch (e: EOFException) {
-                if (expectingEOF) {
-                    return map
-                }
-                throw e
-            }
-            type
-        }.toUByte() != UByte.MAX_VALUE) {
-
-        if (map.containsKey(type.toUInt())) {
-            map[type.toUInt()] = this.readUVarInt()
-        } else {
-            map[type.toUInt()] = this.readUVarInt()
-        }
-    }
-    return map
-}
-
-fun Map<Int, ByteArray>.printTLVMap(name: String = "", keyLength: Int = 2) =
-    debugPrintln("TLVMap $name= " + this.mapValues { (_, value) -> value.toUHexString() }.mapKeys {
-        when (keyLength) {
-            1 -> it.key.toUByte().contentToString()
-            2 -> it.key.toUShort().contentToString()
-            4 -> it.key.toUInt().contentToString()
-            else -> illegalArgument("Expecting 1, 2 or 4 for keyLength")
-        }
-    }.entries.joinToString(prefix = "{", postfix = "}", separator = "\n"))
-
-internal inline fun unsupported(message: String? = null): Nothing = error(message ?: "Unsupported")
-
-internal inline fun illegalArgument(message: String? = null): Nothing = error(message ?: "Illegal argument passed")
-
-@JvmName("printTLVStringMap")
-fun Map<Int, String>.printTLVMap(name: String = "") =
-    debugPrintln("TLVMap $name= " + this.mapKeys { it.key.toInt().toUShort().toUHexString() })
-
-fun Input.readString(length: Int, charset: Charset = Charsets.UTF_8): String = String(this.readBytes(length), charset = charset)
-fun Input.readString(length: Long, charset: Charset = Charsets.UTF_8): String = String(this.readBytes(length.toInt()), charset = charset)
-fun Input.readString(length: Short, charset: Charset = Charsets.UTF_8): String = String(this.readBytes(length.toInt()), charset = charset)
+inline fun Input.readString(length: Int, charset: Charset = Charsets.UTF_8): String = String(this.readBytes(length), charset = charset)
+inline fun Input.readString(length: Long, charset: Charset = Charsets.UTF_8): String = String(this.readBytes(length.toInt()), charset = charset)
+inline fun Input.readString(length: Short, charset: Charset = Charsets.UTF_8): String = String(this.readBytes(length.toInt()), charset = charset)
 @JvmSynthetic
-fun Input.readString(length: UShort, charset: Charset = Charsets.UTF_8): String = String(this.readBytes(length.toInt()), charset = charset)
+inline fun Input.readString(length: UShort, charset: Charset = Charsets.UTF_8): String = String(this.readBytes(length.toInt()), charset = charset)
 
-fun Input.readString(length: Byte, charset: Charset = Charsets.UTF_8): String = String(this.readBytes(length.toInt()), charset = charset)
-
-@JvmSynthetic
-fun Input.readStringUntil(stopSignalExclude: UByte, expectingEOF: Boolean = false): String = readStringUntil(stopSignalExclude.toByte(), expectingEOF)
-
-@JvmName("readStringUntil0")
-fun Input.readStringUntil(stopSignalExclude: Byte, expectingEOF: Boolean = false): String {
-    ByteArrayPool.useInstance {
-        var count = 0
-
-        val buffer = byteArrayOf(1)
-        while (readAvailable(buffer, 1) == 1) {
-            if (buffer[0] == stopSignalExclude) {
-                return buffer.encodeToString()
-            }
-            it[count++] = buffer[0]
-        }
-        if (!expectingEOF) {
-            throw EOFException("Early EOF")
-        }
-        return buffer.encodeToString()
-    }
-}
-
-private const val TRUE_BYTE_VALUE: Byte = 1
-fun Input.readBoolean(): Boolean = this.readByte() == TRUE_BYTE_VALUE
-fun Input.readLVNumber(): Number {
-    return when (this.readShort().toInt()) {
-        1 -> this.readByte()
-        2 -> this.readShort()
-        4 -> this.readInt()
-        8 -> this.readLong()
-        else -> throw UnsupportedOperationException()
-    }
-}
\ No newline at end of file
+inline fun Input.readString(length: Byte, charset: Charset = Charsets.UTF_8): String = String(this.readBytes(length.toInt()), charset = charset)
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/OutputUtils.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/OutputUtils.kt
index 6f940b792..24e612c00 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/OutputUtils.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/OutputUtils.kt
@@ -1,27 +1,14 @@
-@file:Suppress("EXPERIMENTAL_API_USAGE")
+@file:Suppress("EXPERIMENTAL_API_USAGE", "NOTHING_TO_INLINE")
+@file:JvmMultifileClass
+@file:JvmName("Utils")
 
 package net.mamoe.mirai.utils.io
 
 import kotlinx.io.core.*
-import kotlinx.io.pool.useInstance
-import net.mamoe.mirai.utils.Tested
 import net.mamoe.mirai.utils.coerceAtMostOrFail
 import net.mamoe.mirai.utils.cryptor.encryptBy
-import net.mamoe.mirai.utils.currentTimeMillis
-import net.mamoe.mirai.utils.deviceName
-import kotlin.random.Random
-import kotlin.random.nextInt
-
-fun BytePacketBuilder.writeZero(count: Int) {
-    require(count != 0) { "Trying to write zero with count 0, you made a mistake?" }
-    require(count > 0) { "writeZero: count must > 0" }
-    repeat(count) { this.writeByte(0) }
-}
-
-fun BytePacketBuilder.writeRandom(length: Int) = repeat(length) { this.writeByte(Random.Default.nextInt(255).toByte()) }
-
-fun BytePacketBuilder.writeQQ(qq: Long) = this.writeUInt(qq.toUInt()) // same bit rep.
-fun BytePacketBuilder.writeGroup(groupId: Long) = this.writeUInt(groupId.toUInt())
+import kotlin.jvm.JvmMultifileClass
+import kotlin.jvm.JvmName
 
 fun BytePacketBuilder.writeShortLVByteArrayLimitedLength(array: ByteArray, maxLength: Int) {
     if (array.size <= maxLength) {
@@ -35,7 +22,7 @@ fun BytePacketBuilder.writeShortLVByteArrayLimitedLength(array: ByteArray, maxLe
     }
 }
 
-fun BytePacketBuilder.writeShortLVByteArray(byteArray: ByteArray): Int {
+inline fun BytePacketBuilder.writeShortLVByteArray(byteArray: ByteArray): Int {
     this.writeShort(byteArray.size.toShort())
     this.writeFully(byteArray)
     return byteArray.size
@@ -59,18 +46,7 @@ inline fun BytePacketBuilder.writeShortLVPacket(tag: UByte? = null, lengthOffset
         return length.toInt()
     }
 
-inline fun BytePacketBuilder.writeUVarIntLVPacket(tag: UByte? = null, lengthOffset: ((Long) -> Long) = {it}, builder: BytePacketBuilder.() -> Unit) =
-    BytePacketBuilder().apply(builder).build().use {
-        if (tag != null) writeUByte(tag)
-        writeUVarInt(lengthOffset.invoke(it.remaining).coerceAtMostOrFail(0xFFFFL))
-        writePacket(it)
-    }
-
-fun BytePacketBuilder.writeShortLVString(str: String) = writeShortLVByteArray(str.toByteArray())
-
-fun BytePacketBuilder.writeIP(ip: String) = writeFully(ip.trim().split(".").map { it.toUByte() }.toUByteArray())
-
-fun BytePacketBuilder.writeTime() = this.writeInt(currentTimeMillis.toInt())
+inline fun BytePacketBuilder.writeShortLVString(str: String) = writeShortLVByteArray(str.toByteArray())
 
 fun BytePacketBuilder.writeHex(uHex: String) {
     uHex.split(" ").forEach {
@@ -79,76 +55,8 @@ fun BytePacketBuilder.writeHex(uHex: String) {
         }
     }
 }
-
-
-fun BytePacketBuilder.writeTLV(tag: UByte, values: UByteArray) {
-    writeUByte(tag)
-    writeUVarInt(values.size.toUInt())
-    writeFully(values)
-}
-
-fun BytePacketBuilder.writeTLV(tag: UByte, values: ByteArray) {
-    writeUByte(tag)
-    writeUVarInt(values.size.toUInt())
-    writeFully(values)
-}
-
-fun BytePacketBuilder.writeTHex(tag: UByte, uHex: String) {
-    this.writeUByte(tag)
-    this.writeFully(uHex.hexToUBytes())
-}
-
-fun BytePacketBuilder.writeTV(tagValue: UShort) = writeUShort(tagValue)
-
-fun BytePacketBuilder.writeTV(tag: UByte, value: UByte) {
-    writeUByte(tag)
-    writeUByte(value)
-}
-
-fun BytePacketBuilder.writeTUbyte(tag: UByte, value: UByte) {
-    this.writeUByte(tag)
-    this.writeUByte(value)
-}
-
-fun BytePacketBuilder.writeTUVarint(tag: UByte, value: UInt) {
-    this.writeUByte(tag)
-    this.writeUVarInt(value)
-}
-
-fun BytePacketBuilder.writeTByteArray(tag: UByte, value: ByteArray) {
-    this.writeUByte(tag)
-    this.writeFully(value)
-}
-
-fun BytePacketBuilder.writeTByteArray(tag: UByte, value: UByteArray) {
-    this.writeUByte(tag)
-    this.writeFully(value)
-}
-
 /**
  * 会使用 [ByteArrayPool] 缓存
  */
 inline fun BytePacketBuilder.encryptAndWrite(key: ByteArray, encoder: BytePacketBuilder.() -> Unit) =
-    BytePacketBuilder().apply(encoder).build().encryptBy(key) { decrypted -> writeFully(decrypted) }
-
-inline fun BytePacketBuilder.encryptAndWrite(key: IoBuffer, encoder: BytePacketBuilder.() -> Unit) = ByteArrayPool.useInstance {
-    key.readFully(it, 0, key.readRemaining)
-    encryptAndWrite(it, encoder)
-}
-
-inline fun BytePacketBuilder.encryptAndWrite(keyHex: String, encoder: BytePacketBuilder.() -> Unit) = encryptAndWrite(keyHex.hexToBytes(), encoder)
-
-@Tested
-fun BytePacketBuilder.writeDeviceName(random: Boolean) {
-    val deviceName: String = if (random) {
-        "DESKTOP-" + String(ByteArray(7) {
-            (if (Random.nextBoolean()) Random.nextInt('A'.toInt()..'Z'.toInt())
-            else Random.nextInt('1'.toInt()..'9'.toInt())).toByte()
-        })
-    } else {
-        deviceName
-    }
-    this.writeShort((deviceName.length + 2).toShort())
-    this.writeShort(deviceName.length.toShort())
-    this.writeStringUtf8(deviceName)
-}
\ No newline at end of file
+    BytePacketBuilder().apply(encoder).build().encryptBy(key) { decrypted -> writeFully(decrypted) }
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/Varint.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/Varint.kt
index f3c656ca1..e8c8d850d 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/Varint.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/Varint.kt
@@ -16,8 +16,8 @@ import kotlin.jvm.JvmSynthetic
  *
  * Source project: [Nukkit](http://github.com/nukkit/nukkit)
  *
- * @author MagicDroidX of Nukkit Project
- * @author lmlstarqaq of Nukkit Project
+ * @author MagicDroidX from Nukkit Project
+ * @author lmlstarqaq from Nukkit Project
  */
 
 internal fun encodeZigZag32(signedInt: Int): Long {
@@ -42,10 +42,6 @@ internal fun decodeZigZag64(signedLong: Long): Long {
 }
 
 
-fun Input.readVarInt(): Int {
-    return decodeZigZag32(this.readUVarInt())
-}
-
 inline class UVarInt(
     val data: UInt
 )
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/map.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/map.kt
index 0e9710358..f0506092e 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/map.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/map.kt
@@ -1,9 +1,17 @@
+@file:Suppress("NOTHING_TO_INLINE")
+@file:JvmMultifileClass
+@file:JvmName("Utils")
+
+
 package net.mamoe.mirai.utils
 
-fun <K, V> Map<K, V>.firstValue(): V = this.entries.first().value
+import kotlin.jvm.JvmMultifileClass
+import kotlin.jvm.JvmName
 
-fun <K, V> Map<K, V>.firstValueOrNull(): V? = this.entries.firstOrNull()?.value
+inline fun <K, V> Map<K, V>.firstValue(): V = this.entries.first().value
 
-fun <K, V> Map<K, V>.firstKey(): K = this.entries.first().key
+inline fun <K, V> Map<K, V>.firstValueOrNull(): V? = this.entries.firstOrNull()?.value
 
-fun <K, V> Map<K, V>.firstKeyOrNull(): K? = this.entries.firstOrNull()?.key
\ No newline at end of file
+inline fun <K, V> Map<K, V>.firstKey(): K = this.entries.first().key
+
+inline fun <K, V> Map<K, V>.firstKeyOrNull(): K? = this.entries.firstOrNull()?.key
\ No newline at end of file
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/DefaultCaptchaSolverJvm.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/DefaultCaptchaSolverJvm.kt
index 6bcf26598..c17f8f74b 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/DefaultCaptchaSolverJvm.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/DefaultCaptchaSolverJvm.kt
@@ -28,7 +28,7 @@ import kotlin.coroutines.CoroutineContext
 actual var defaultLoginSolver: LoginSolver = DefaultLoginSolver()
 
 
-class DefaultLoginSolver : LoginSolver() {
+internal class DefaultLoginSolver : LoginSolver() {
     override suspend fun onSolvePicCaptcha(bot: Bot, data: IoBuffer): String? = loginSolverLock.withLock {
         val tempFile: File = createTempFile(suffix = ".png").apply { deleteOnExit() }
         withContext(Dispatchers.IO) {
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt
index b6aa1ab05..a59a5b0ca 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/cryptor/contentToString.kt
@@ -1,11 +1,13 @@
 package net.mamoe.mirai.utils.cryptor
 
+import net.mamoe.mirai.utils.MiraiDebugAPI
 import java.lang.reflect.Field
 import kotlin.reflect.full.allSuperclasses
 
 
 val FIELD_TRY_SET_ACCESSIBLE = Field::class.java.declaredMethods.firstOrNull { it.name == "trySetAccessible" }
 
+@MiraiDebugAPI
 actual fun Any.contentToStringReflectively(prefix: String, filter: ((name: String, value: Any?) -> Boolean)?): String {
     val newPrefix = prefix + ProtoMap.indent
     return (this::class.simpleName ?: "<UnnamedClass>") + "#" + this::class.hashCode() + " {\n" +
diff --git a/mirai-demos/mirai-demo-android/src/main/kotlin/net/mamoe/mirai/demo/MiraiService.kt b/mirai-demos/mirai-demo-android/src/main/kotlin/net/mamoe/mirai/demo/MiraiService.kt
index f35414fb6..36992d2f3 100644
--- a/mirai-demos/mirai-demo-android/src/main/kotlin/net/mamoe/mirai/demo/MiraiService.kt
+++ b/mirai-demos/mirai-demo-android/src/main/kotlin/net/mamoe/mirai/demo/MiraiService.kt
@@ -16,7 +16,6 @@ import kotlinx.io.core.readBytes
 import net.mamoe.mirai.Bot
 import net.mamoe.mirai.event.subscribeMessages
 import net.mamoe.mirai.qqandroid.QQAndroid
-import net.mamoe.mirai.utils.LoginFailedException
 import net.mamoe.mirai.utils.LoginSolver
 import java.lang.ref.WeakReference
 
@@ -67,7 +66,7 @@ class MiraiService : Service() {
                 try {
                     login()
                     mCallback?.get()?.onSuccess()
-                } catch (e: LoginFailedException) {
+                } catch (e: Exception) {
                     mCallback?.get()?.onFailed()
                 }
             }
diff --git a/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingBot.java b/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingBot.java
index 7deeefd30..49996572a 100644
--- a/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingBot.java
+++ b/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingBot.java
@@ -77,10 +77,7 @@ public interface BlockingBot {
      * 登录.
      * <p>
      * 最终调用 [net.mamoe.mirai.network.BotNetworkHandler.login]
-     *
-     * @throws net.mamoe.mirai.utils.LoginFailedException
      */
-    @SuppressWarnings("JavaDoc")
     void login();
 
     // endregion