Throws EOFException rather than IllegalStateException

This commit is contained in:
Him188 2020-03-06 15:14:51 +08:00
parent 115ccec9ed
commit 24983d8bbe
3 changed files with 38 additions and 20 deletions

View File

@ -45,28 +45,28 @@ private class JceDecoder(
}
override fun decodeTaggedInt(tag: JceTag): Int =
jce.skipToTagAndUseIfPossibleOrFail(tag.id) { jce.readJceIntValue(it) }
jce.skipToHeadAndUseIfPossibleOrFail(tag.id) { jce.readJceIntValue(it) }
override fun decodeTaggedByte(tag: JceTag): Byte =
jce.skipToTagAndUseIfPossibleOrFail(tag.id) { jce.readJceByteValue(it) }
jce.skipToHeadAndUseIfPossibleOrFail(tag.id) { jce.readJceByteValue(it) }
override fun decodeTaggedBoolean(tag: JceTag): Boolean =
jce.skipToTagAndUseIfPossibleOrFail(tag.id) { jce.readJceBooleanValue(it) }
jce.skipToHeadAndUseIfPossibleOrFail(tag.id) { jce.readJceBooleanValue(it) }
override fun decodeTaggedFloat(tag: JceTag): Float =
jce.skipToTagAndUseIfPossibleOrFail(tag.id) { jce.readJceFloatValue(it) }
jce.skipToHeadAndUseIfPossibleOrFail(tag.id) { jce.readJceFloatValue(it) }
override fun decodeTaggedDouble(tag: JceTag): Double =
jce.skipToTagAndUseIfPossibleOrFail(tag.id) { jce.readJceDoubleValue(it) }
jce.skipToHeadAndUseIfPossibleOrFail(tag.id) { jce.readJceDoubleValue(it) }
override fun decodeTaggedShort(tag: JceTag): Short =
jce.skipToTagAndUseIfPossibleOrFail(tag.id) { jce.readJceShortValue(it) }
jce.skipToHeadAndUseIfPossibleOrFail(tag.id) { jce.readJceShortValue(it) }
override fun decodeTaggedLong(tag: JceTag): Long =
jce.skipToTagAndUseIfPossibleOrFail(tag.id) { jce.readJceLongValue(it) }
jce.skipToHeadAndUseIfPossibleOrFail(tag.id) { jce.readJceLongValue(it) }
override fun decodeTaggedString(tag: JceTag): String =
jce.skipToTagAndUseIfPossibleOrFail(tag.id) { jce.readJceStringValue(it) }
jce.skipToHeadAndUseIfPossibleOrFail(tag.id) { jce.readJceStringValue(it) }
override fun decodeTaggedEnum(tag: JceTag, enumDescription: SerialDescriptor): Int {
return super.decodeTaggedEnum(tag, enumDescription)

View File

@ -23,7 +23,7 @@ class JceInput(
) {
private var _head: JceHead? = null
val currentHead: JceHead get() = _head ?: error("No current JceHead available")
val currentHead: JceHead get() = _head ?: throw EOFException("No current JceHead available")
val currentHeadOrNull: JceHead? get() = _head
init {
@ -40,7 +40,9 @@ class JceInput(
}
fun nextHead(): JceHead {
check(prepareNextHead()) { "No more JceHead available" }
if (!prepareNextHead()) {
throw EOFException("No more JceHead available")
}
return currentHead
}
@ -51,13 +53,13 @@ class JceInput(
@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) {
if (input.endOfInput) {
return null
}
tag = input.readUByte().toUInt()
}
return JceHead(
@ -77,19 +79,19 @@ class JceInput(
/**
* 跳过 [JceHead] 和对应的数据值, 直到找到 [tag], 否则返回 `null`
*/
inline fun <R> skipToTagAndUseIfPossibleOrNull(tag: Int, crossinline block: (JceHead) -> R): R? {
inline fun <R> skipToHeadAndUseIfPossibleOrNull(tag: Int, crossinline block: (JceHead) -> R): R? {
return skipToHeadOrNull(tag)?.let(block).also { prepareNextHead() }
}
/**
* 跳过 [JceHead] 和对应的数据值, 直到找到 [tag], 否则抛出异常
*/
inline fun <R : Any> skipToTagAndUseIfPossibleOrFail(
inline fun <R : Any> skipToHeadAndUseIfPossibleOrFail(
tag: Int,
crossinline message: () -> String = { "tag not found: $tag" },
crossinline block: (JceHead) -> R
): R {
return checkNotNull<R>(skipToTagAndUseIfPossibleOrNull(tag, block), message)
return checkNotNull<R>(skipToHeadAndUseIfPossibleOrNull(tag, block), message)
}
tailrec fun skipToHeadOrNull(tag: Int): JceHead? {
@ -125,14 +127,14 @@ class JceInput(
Jce.STRING1 -> this.input.discardExact(this.input.readUByte().toInt())
Jce.STRING4 -> this.input.discardExact(this.input.readInt())
Jce.MAP -> { // map
repeat(skipToTagAndUseIfPossibleOrFail(0) {
repeat(skipToHeadAndUseIfPossibleOrFail(0) {
readJceIntValue(it)
} * 2) {
useHead { skipField(it.type) }
}
}
Jce.LIST -> { // list
repeat(skipToTagAndUseIfPossibleOrFail(0) {
repeat(skipToHeadAndUseIfPossibleOrFail(0) {
readJceIntValue(it)
}) {
useHead { skipField(it.type) }
@ -155,7 +157,7 @@ class JceInput(
val head = nextHead()
check(head.type.toInt() == 0) { "skipField with invalid type, type value: " + type + ", " + head.type }
this.input.discardExact(
skipToTagAndUseIfPossibleOrFail(0) {
skipToHeadAndUseIfPossibleOrFail(0) {
readJceIntValue(it)
}
)
@ -232,6 +234,6 @@ class JceInput(
}
fun readJceBooleanValue(head: JceHead): Boolean {
return readJceByteValue(head) == 0.toByte()
return readJceByteValue(head) == 1.toByte()
}
}

View File

@ -9,6 +9,7 @@
package net.mamoe.mirai.qqandroid.io.serialization.jce
import io.ktor.utils.io.core.Output
import kotlinx.serialization.SerialInfo
import net.mamoe.mirai.qqandroid.io.serialization.Jce
@ -24,12 +25,27 @@ annotation class JceId(val id: Int)
*
* 保留这个结构, 为将来增加功能的兼容性.
*/
@PublishedApi
internal data class JceTag(
val id: Int,
val isNullable: Boolean
)
@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())