Merge remote-tracking branch 'origin/master'

This commit is contained in:
jiahua.liu 2020-01-28 00:05:31 +08:00
commit 92f7ca4dbd
2 changed files with 62 additions and 36 deletions

View File

@ -12,8 +12,7 @@ import kotlinx.serialization.modules.EmptyModule
import kotlinx.serialization.modules.SerialModule import kotlinx.serialization.modules.SerialModule
import net.mamoe.mirai.qqandroid.io.CharsetUTF8 import net.mamoe.mirai.qqandroid.io.CharsetUTF8
import net.mamoe.mirai.qqandroid.io.JceEncodeException import net.mamoe.mirai.qqandroid.io.JceEncodeException
import net.mamoe.mirai.qqandroid.io.JceOutput import net.mamoe.mirai.qqandroid.io.JceStruct
import net.mamoe.mirai.utils.io.toUHexString
import kotlin.reflect.KClass import kotlin.reflect.KClass
fun <T> ByteArray.loadAs(deserializer: DeserializationStrategy<T>, c: Charset): T { fun <T> ByteArray.loadAs(deserializer: DeserializationStrategy<T>, c: Charset): T {
@ -68,17 +67,16 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
defaultStringCharset: JceCharset, defaultStringCharset: JceCharset,
private val count: Int, private val count: Int,
private val tag: JceDesc, private val tag: JceDesc,
private val parentEncoder: JceEncoder, private val parentEncoder: JceEncoder
private val stream: ByteArrayOutputStream = ByteArrayOutputStream() ) : JceEncoder(defaultStringCharset, ByteArrayOutputStream()) {
) : JceEncoder(defaultStringCharset, stream) {
override fun SerialDescriptor.getTag(index: Int): JceDesc { override fun SerialDescriptor.getTag(index: Int): JceDesc {
return JceDesc(0, getCharset(index)) return JceDesc(0, getCharset(index))
} }
override fun endEncode(desc: SerialDescriptor) { override fun endEncode(desc: SerialDescriptor) {
parentEncoder.writeHead(JceOutput.LIST, this.tag.id) parentEncoder.writeHead(LIST, this.tag.id)
parentEncoder.encodeTaggedInt(JceDesc.STUB_FOR_PRIMITIVE_NUMBERS_GBK, count) parentEncoder.encodeTaggedInt(JceDesc.STUB_FOR_PRIMITIVE_NUMBERS_GBK, count)
parentEncoder.output.write(stream.toByteArray()) parentEncoder.output.write(this.output.toByteArray())
} }
} }
@ -89,28 +87,35 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
private val stream: ByteArrayOutputStream = ByteArrayOutputStream() private val stream: ByteArrayOutputStream = ByteArrayOutputStream()
) : JceEncoder(defaultStringCharset, stream) { ) : JceEncoder(defaultStringCharset, stream) {
override fun endEncode(desc: SerialDescriptor) { override fun endEncode(desc: SerialDescriptor) {
parentEncoder.writeHead(JceOutput.STRUCT_BEGIN, this.tag.id) parentEncoder.writeHead(STRUCT_BEGIN, this.tag.id)
parentEncoder.output.write(stream.toByteArray()) parentEncoder.output.write(stream.toByteArray())
parentEncoder.writeHead(JceOutput.STRUCT_END, 0) parentEncoder.writeHead(STRUCT_END, 0)
} }
} }
private inner class JceMapWriter( private inner class JceMapWriter(
defaultStringCharset: JceCharset, defaultStringCharset: JceCharset,
private val count: Int, output: ByteArrayOutputStream
private val tag: JceDesc, ) : JceEncoder(defaultStringCharset, output) {
private val parentEncoder: JceEncoder
) : JceEncoder(defaultStringCharset, ByteArrayOutputStream()) {
override fun SerialDescriptor.getTag(index: Int): JceDesc { override fun SerialDescriptor.getTag(index: Int): JceDesc {
return if (index % 2 == 0) JceDesc(0, getCharset(index)) return if (index % 2 == 0) JceDesc(0, getCharset(index))
else JceDesc(1, getCharset(index)) else JceDesc(1, getCharset(index))
} }
/*
override fun endEncode(desc: SerialDescriptor) { override fun endEncode(desc: SerialDescriptor) {
parentEncoder.writeHead(JceOutput.MAP, this.tag.id) parentEncoder.writeHead(MAP, this.tag.id)
parentEncoder.encodeTaggedInt(JceDesc.STUB_FOR_PRIMITIVE_NUMBERS_GBK, count) parentEncoder.encodeTaggedInt(JceDesc.STUB_FOR_PRIMITIVE_NUMBERS_GBK, count)
println(this.output.toByteArray().toUHexString()) println(this.output.toByteArray().toUHexString())
parentEncoder.output.write(this.output.toByteArray()) parentEncoder.output.write(this.output.toByteArray())
}*/
override fun beginCollection(desc: SerialDescriptor, collectionSize: Int, vararg typeParams: KSerializer<*>): CompositeEncoder {
return this
}
override fun beginStructure(desc: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeEncoder {
return this
} }
} }
@ -142,31 +147,27 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
override fun beginStructure(desc: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeEncoder = when (desc.kind) { override fun beginStructure(desc: SerialDescriptor, vararg typeParams: KSerializer<*>): CompositeEncoder = when (desc.kind) {
StructureKind.LIST -> this StructureKind.LIST -> this
StructureKind.MAP -> this StructureKind.MAP -> this
StructureKind.CLASS, UnionKind.OBJECT -> { StructureKind.CLASS, UnionKind.OBJECT -> this
val currentTag = currentTagOrNull is PolymorphicKind -> this
if (currentTag == null) {
this
} else {
JceStructWriter(defaultStringCharset, currentTag, this)
}
}
is PolymorphicKind -> error("unsupported: PolymorphicKind")
else -> throw SerializationException("Primitives are not supported at top-level") else -> throw SerializationException("Primitives are not supported at top-level")
} }
@Suppress("UNCHECKED_CAST", "NAME_SHADOWING") @Suppress("UNCHECKED_CAST", "NAME_SHADOWING")
override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) = when (serializer.descriptor) { override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) = when (serializer.descriptor) {
// encode maps as collection of map entries, not merged collection of key-values
is MapLikeDescriptor -> { is MapLikeDescriptor -> {
println("hello")
val entries = (value as Map<*, *>).entries val entries = (value as Map<*, *>).entries
val serializer = (serializer as MapLikeSerializer<Any?, Any?, T, *>) val serializer = (serializer as MapLikeSerializer<Any?, Any?, T, *>)
val mapEntrySerial = MapEntrySerializer(serializer.keySerializer, serializer.valueSerializer) val mapEntrySerial = MapEntrySerializer(serializer.keySerializer, serializer.valueSerializer)
HashSetSerializer(mapEntrySerial).serialize(JceMapWriter(charset, entries.size, popTag(), this), entries)
this.writeHead(MAP, currentTag.id)
this.encodeTaggedInt(JceDesc.STUB_FOR_PRIMITIVE_NUMBERS_GBK, entries.count())
HashSetSerializer(mapEntrySerial).serialize(JceMapWriter(charset, this.output), entries)
} }
ByteArraySerializer.descriptor -> encodeTaggedByteArray(popTag(), value as ByteArray) ByteArraySerializer.descriptor -> encodeTaggedByteArray(popTag(), value as ByteArray)
is PrimitiveArrayDescriptor -> { is PrimitiveArrayDescriptor -> {
if (value is ByteArray) { if (value is ByteArray) {
this.encodeTaggedByteArray(currentTag, value) this.encodeTaggedByteArray( popTag(), value)
} else{ } else{
serializer.serialize( serializer.serialize(
ListWriter(charset, when(value){ ListWriter(charset, when(value){
@ -177,24 +178,34 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
is DoubleArray -> value.size is DoubleArray -> value.size
is CharArray -> value.size is CharArray -> value.size
else -> error("unknown array type: ${value.getClassName()}") else -> error("unknown array type: ${value.getClassName()}")
}, currentTag, this), }, popTag(), this),
value value
) )
} }
} }
is ArrayClassDesc-> { is ArrayClassDesc-> {
serializer.serialize( serializer.serialize(
ListWriter(charset, (value as Array<*>).size, currentTag, this), ListWriter(charset, (value as Array<*>).size, popTag(), this),
value value
) )
} }
is ListLikeDescriptor -> { is ListLikeDescriptor -> {
serializer.serialize( serializer.serialize(
ListWriter(charset, (value as Collection<*>).size, currentTag, this), ListWriter(charset, (value as Collection<*>).size, popTag(), this),
value value
) )
} }
else -> serializer.serialize(this, value) else -> {
if (value is JceStruct) {
if (currentTagOrNull == null) {
serializer.serialize(this, value)
} else {
this.writeHead(STRUCT_BEGIN, currentTag.id)
serializer.serialize(this, value)
this.writeHead(STRUCT_END, 0)
}
} else serializer.serialize(this, value)
}
} }
override fun encodeTaggedByte(tag: JceDesc, value: Byte) { override fun encodeTaggedByte(tag: JceDesc, value: Byte) {
@ -263,8 +274,8 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
} }
fun encodeTaggedByteArray(tag: JceDesc, bytes: ByteArray) { fun encodeTaggedByteArray(tag: JceDesc, bytes: ByteArray) {
writeHead(JceOutput.SIMPLE_LIST, tag.id) writeHead(SIMPLE_LIST, tag.id)
writeHead(JceOutput.BYTE, 0) writeHead(BYTE, 0)
encodeTaggedInt(JceDesc.STUB_FOR_PRIMITIVE_NUMBERS_GBK, bytes.size) encodeTaggedInt(JceDesc.STUB_FOR_PRIMITIVE_NUMBERS_GBK, bytes.size)
output.write(bytes) output.write(bytes)
} }

View File

@ -3,6 +3,9 @@ package net.mamoe.mirai.qqandroid.io.serialization
import kotlinx.io.core.readBytes import kotlinx.io.core.readBytes
import kotlinx.serialization.SerialId import kotlinx.serialization.SerialId
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import net.mamoe.mirai.qqandroid.io.CharsetUTF8
import net.mamoe.mirai.qqandroid.io.JceOutput
import net.mamoe.mirai.qqandroid.io.JceStruct
import net.mamoe.mirai.qqandroid.io.buildJcePacket import net.mamoe.mirai.qqandroid.io.buildJcePacket
import net.mamoe.mirai.utils.io.toUHexString import net.mamoe.mirai.utils.io.toUHexString
import kotlin.test.Test import kotlin.test.Test
@ -20,7 +23,17 @@ class JceEncoderTest {
@SerialId(4) val long: Long = 123, @SerialId(4) val long: Long = 123,
@SerialId(5) val float: Float = 123f, @SerialId(5) val float: Float = 123f,
@SerialId(6) val double: Double = 123.0 @SerialId(6) val double: Double = 123.0
) ) : JceStruct() {
override fun writeTo(builder: JceOutput) = builder.run {
writeString("123", 0)
writeByte(123, 1)
writeShort(123, 2)
writeInt(123, 3)
writeLong(123, 4)
writeFloat(123f, 5)
writeDouble(123.0, 6)
}
}
@Test @Test
fun testEncoder() { fun testEncoder() {
@ -44,12 +57,13 @@ class JceEncoderTest {
@Test @Test
fun testEncoder2() { fun testEncoder2() {
assertEquals( assertEquals(
buildJcePacket { buildJcePacket(stringCharset = CharsetUTF8) {
writeFully(byteArrayOf(1, 2, 3), 7) writeFully(byteArrayOf(1, 2, 3), 7)
writeCollection(listOf(1, 2, 3), 8) writeCollection(listOf(1, 2, 3), 8)
writeMap(mapOf("哈哈" to "嘿嘿"), 9) writeMap(mapOf("哈哈" to "嘿嘿"), 9)
writeJceStruct(TestSimpleJceStruct(), 10)
}.readBytes().toUHexString(), }.readBytes().toUHexString(),
Jce.GBK.dump( Jce.UTF8.dump(
TestComplexJceStruct.serializer(), TestComplexJceStruct.serializer(),
TestComplexJceStruct() TestComplexJceStruct()
).toUHexString() ).toUHexString()
@ -60,6 +74,7 @@ class JceEncoderTest {
class TestComplexJceStruct( class TestComplexJceStruct(
@SerialId(7) val byteArray: ByteArray = byteArrayOf(1, 2, 3), @SerialId(7) val byteArray: ByteArray = byteArrayOf(1, 2, 3),
@SerialId(8) val byteList: List<Byte> = listOf(1, 2, 3), @SerialId(8) val byteList: List<Byte> = listOf(1, 2, 3),
@SerialId(9) val map: Map<String, String> = mapOf("哈哈" to "嘿嘿") @SerialId(9) val map: Map<String, String> = mapOf("哈哈" to "嘿嘿"),
@SerialId(10) val nestedJceStruct: TestSimpleJceStruct = TestSimpleJceStruct()
) )
} }