Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
M
Mirai
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
MyCard
Mirai
Commits
1c96bb08
Commit
1c96bb08
authored
Jan 28, 2020
by
jiahua.liu
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'origin/master'
parents
e71cb765
94502d44
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
596 additions
and
104 deletions
+596
-104
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/JceOutput.kt
...mmonMain/kotlin/net/mamoe/mirai/qqandroid/io/JceOutput.kt
+292
-0
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/JceStruct.kt
...mmonMain/kotlin/net/mamoe/mirai/qqandroid/io/JceStruct.kt
+3
-1
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/serialization/Jce.kt
.../kotlin/net/mamoe/mirai/qqandroid/io/serialization/Jce.kt
+195
-95
mirai-core-qqandroid/src/jvmTest/kotlin/net.mamoe.mirai.qqandroid.io.serialization/JceDecoderTest.kt
....mamoe.mirai.qqandroid.io.serialization/JceDecoderTest.kt
+106
-8
No files found.
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/JceOutput.kt
0 → 100644
View file @
1c96bb08
package
net.mamoe.mirai.qqandroid.io
import
kotlinx.io.charsets.Charset
import
kotlinx.io.core.*
import
kotlin.experimental.or
import
kotlin.reflect.KClass
@PublishedApi
internal
val
CharsetGBK
=
Charset
.
forName
(
"GBK"
)
@PublishedApi
internal
val
CharsetUTF8
=
Charset
.
forName
(
"UTF8"
)
inline
fun
buildJcePacket
(
stringCharset
:
Charset
=
CharsetGBK
,
block
:
JceOutput
.()
->
Unit
):
ByteReadPacket
{
return
JceOutput
(
stringCharset
).
apply
(
block
).
build
()
}
inline
fun
BytePacketBuilder
.
writeJcePacket
(
stringCharset
:
Charset
=
CharsetGBK
,
block
:
JceOutput
.()
->
Unit
)
{
return
this
.
writePacket
(
buildJcePacket
(
stringCharset
,
block
))
}
fun
jceStruct
(
tag
:
Int
,
struct
:
JceStruct
):
ByteArray
{
return
buildJcePacket
{
writeJceStruct
(
struct
,
tag
)
}.
readBytes
()
}
fun
<
K
,
V
>
jceMap
(
tag
:
Int
,
vararg
entries
:
Pair
<
K
,
V
>):
ByteArray
{
return
buildJcePacket
{
writeMap
(
mapOf
(*
entries
),
tag
)
}.
readBytes
()
}
/**
*
* From: com.qq.taf.jce.JceOutputStream
*/
@Suppress
(
"unused"
,
"MemberVisibilityCanBePrivate"
)
@UseExperimental
(
ExperimentalIoApi
::
class
)
class
JceOutput
(
private
val
stringCharset
:
Charset
=
CharsetGBK
)
{
private
val
output
:
BytePacketBuilder
=
BytePacketBuilder
()
fun
build
():
ByteReadPacket
=
output
.
build
()
fun
close
()
=
output
.
close
()
fun
flush
()
=
output
.
flush
()
fun
writeByte
(
v
:
Byte
,
tag
:
Int
)
{
if
(
v
.
toInt
()
==
0
)
{
writeHead
(
ZERO_TYPE
,
tag
)
}
else
{
writeHead
(
BYTE
,
tag
)
output
.
writeByte
(
v
)
}
}
fun
writeDouble
(
v
:
Double
,
tag
:
Int
)
{
writeHead
(
DOUBLE
,
tag
)
output
.
writeDouble
(
v
)
}
fun
writeFloat
(
v
:
Float
,
tag
:
Int
)
{
writeHead
(
FLOAT
,
tag
)
output
.
writeFloat
(
v
)
}
fun
writeFully
(
src
:
ByteArray
,
tag
:
Int
)
{
writeHead
(
SIMPLE_LIST
,
tag
)
writeHead
(
BYTE
,
0
)
writeInt
(
src
.
size
,
0
)
output
.
writeFully
(
src
)
}
fun
writeFully
(
src
:
DoubleArray
,
tag
:
Int
)
{
writeHead
(
LIST
,
tag
)
writeInt
(
src
.
size
,
0
)
src
.
forEach
{
writeDouble
(
it
,
0
)
}
}
fun
writeFully
(
src
:
FloatArray
,
tag
:
Int
)
{
writeHead
(
LIST
,
tag
)
writeInt
(
src
.
size
,
0
)
src
.
forEach
{
writeFloat
(
it
,
0
)
}
}
fun
writeFully
(
src
:
IntArray
,
tag
:
Int
)
{
writeHead
(
LIST
,
tag
)
writeInt
(
src
.
size
,
0
)
src
.
forEach
{
writeInt
(
it
,
0
)
}
}
fun
writeFully
(
src
:
LongArray
,
tag
:
Int
)
{
writeHead
(
LIST
,
tag
)
writeInt
(
src
.
size
,
0
)
src
.
forEach
{
writeLong
(
it
,
0
)
}
}
fun
writeFully
(
src
:
ShortArray
,
tag
:
Int
)
{
writeHead
(
LIST
,
tag
)
writeInt
(
src
.
size
,
0
)
src
.
forEach
{
writeShort
(
it
,
0
)
}
}
fun
writeFully
(
src
:
BooleanArray
,
tag
:
Int
)
{
writeHead
(
LIST
,
tag
)
writeInt
(
src
.
size
,
0
)
src
.
forEach
{
writeBoolean
(
it
,
0
)
}
}
fun
<
T
>
writeFully
(
src
:
Array
<
T
>,
tag
:
Int
)
{
writeHead
(
LIST
,
tag
)
writeInt
(
src
.
size
,
0
)
src
.
forEach
{
writeObject
(
it
,
0
)
}
}
fun
writeInt
(
v
:
Int
,
tag
:
Int
)
{
if
(
v
in
Short
.
MIN_VALUE
..
Short
.
MAX_VALUE
)
{
writeShort
(
v
.
toShort
(),
tag
)
}
else
{
writeHead
(
INT
,
tag
)
output
.
writeInt
(
v
)
}
}
fun
writeLong
(
v
:
Long
,
tag
:
Int
)
{
if
(
v
in
Int
.
MIN_VALUE
..
Int
.
MAX_VALUE
)
{
writeInt
(
v
.
toInt
(),
tag
)
}
else
{
writeHead
(
LONG
,
tag
)
output
.
writeLong
(
v
)
}
}
fun
writeShort
(
v
:
Short
,
tag
:
Int
)
{
if
(
v
in
Byte
.
MIN_VALUE
..
Byte
.
MAX_VALUE
)
{
writeByte
(
v
.
toByte
(),
tag
)
}
else
{
writeHead
(
SHORT
,
tag
)
output
.
writeShort
(
v
)
}
}
fun
writeBoolean
(
v
:
Boolean
,
tag
:
Int
)
{
this
.
writeByte
(
if
(
v
)
1
else
0
,
tag
)
}
fun
writeString
(
v
:
String
,
tag
:
Int
)
{
val
array
=
v
.
toByteArray
(
stringCharset
)
if
(
array
.
size
>
255
)
{
writeHead
(
STRING4
,
tag
)
output
.
writeInt
(
array
.
size
)
output
.
writeFully
(
array
)
}
else
{
writeHead
(
STRING1
,
tag
)
output
.
writeByte
(
array
.
size
.
toByte
())
output
.
writeFully
(
array
)
}
}
fun
<
K
,
V
>
writeMap
(
map
:
Map
<
K
,
V
>,
tag
:
Int
)
{
writeHead
(
MAP
,
tag
)
if
(
map
.
isEmpty
())
{
writeInt
(
0
,
0
)
}
else
{
writeInt
(
map
.
size
,
0
)
map
.
forEach
{
(
key
,
value
)
->
writeObject
(
key
,
0
)
writeObject
(
value
,
1
)
}
}
}
fun
writeCollection
(
collection
:
Collection
<
*
>?,
tag
:
Int
)
{
writeHead
(
LIST
,
tag
)
if
(
collection
==
null
||
collection
.
isEmpty
())
{
writeInt
(
0
,
0
)
}
else
{
writeInt
(
collection
.
size
,
0
)
collection
.
forEach
{
writeObject
(
it
,
0
)
}
}
}
fun
writeJceStruct
(
v
:
JceStruct
,
tag
:
Int
)
{
writeHead
(
STRUCT_BEGIN
,
tag
)
v
.
writeTo
(
this
)
writeHead
(
STRUCT_END
,
0
)
}
fun
<
T
>
writeObject
(
v
:
T
,
tag
:
Int
)
{
when
(
v
)
{
is
Byte
->
writeByte
(
v
,
tag
)
is
Short
->
writeShort
(
v
,
tag
)
is
Int
->
writeInt
(
v
,
tag
)
is
Long
->
writeLong
(
v
,
tag
)
is
Float
->
writeFloat
(
v
,
tag
)
is
Double
->
writeDouble
(
v
,
tag
)
is
JceStruct
->
writeJceStruct
(
v
,
tag
)
is
ByteArray
->
writeFully
(
v
,
tag
)
is
Collection
<
*
>
->
writeCollection
(
v
,
tag
)
is
Boolean
->
writeBoolean
(
v
,
tag
)
is
Map
<
*
,
*
>
->
writeMap
(
v
,
tag
)
is
IntArray
->
writeFully
(
v
,
tag
)
is
ShortArray
->
writeFully
(
v
,
tag
)
is
BooleanArray
->
writeFully
(
v
,
tag
)
is
LongArray
->
writeFully
(
v
,
tag
)
is
FloatArray
->
writeFully
(
v
,
tag
)
is
DoubleArray
->
writeFully
(
v
,
tag
)
is
Array
<
*
>
->
writeFully
(
v
,
tag
)
is
String
->
writeString
(
v
,
tag
)
//
// is ByteReadPacket -> ByteArrayPool.useInstance {
// v.readAvailable(it)
// writeFully(it, tag)
// }
else
->
error
(
"unsupported type: ${v.getClassName()}"
)
}
}
fun
write
(
v
:
Int
,
tag
:
Int
)
=
writeInt
(
v
,
tag
)
fun
write
(
v
:
Byte
,
tag
:
Int
)
=
writeByte
(
v
,
tag
)
fun
write
(
v
:
Short
,
tag
:
Int
)
=
writeShort
(
v
,
tag
)
fun
write
(
v
:
Long
,
tag
:
Int
)
=
writeLong
(
v
,
tag
)
fun
write
(
v
:
Float
,
tag
:
Int
)
=
writeFloat
(
v
,
tag
)
fun
write
(
v
:
Double
,
tag
:
Int
)
=
writeDouble
(
v
,
tag
)
fun
write
(
v
:
String
,
tag
:
Int
)
=
writeString
(
v
,
tag
)
fun
write
(
v
:
Boolean
,
tag
:
Int
)
=
writeBoolean
(
v
,
tag
)
fun
write
(
v
:
Collection
<
*
>,
tag
:
Int
)
=
writeCollection
(
v
,
tag
)
fun
write
(
v
:
Map
<
*
,
*
>,
tag
:
Int
)
=
writeMap
(
v
,
tag
)
fun
write
(
v
:
ByteArray
,
tag
:
Int
)
=
writeFully
(
v
,
tag
)
fun
write
(
v
:
IntArray
,
tag
:
Int
)
=
writeFully
(
v
,
tag
)
fun
write
(
v
:
BooleanArray
,
tag
:
Int
)
=
writeFully
(
v
,
tag
)
fun
write
(
v
:
LongArray
,
tag
:
Int
)
=
writeFully
(
v
,
tag
)
fun
write
(
v
:
ShortArray
,
tag
:
Int
)
=
writeFully
(
v
,
tag
)
fun
write
(
v
:
Array
<
*
>,
tag
:
Int
)
=
writeFully
(
v
,
tag
)
fun
write
(
v
:
FloatArray
,
tag
:
Int
)
=
writeFully
(
v
,
tag
)
fun
write
(
v
:
DoubleArray
,
tag
:
Int
)
=
writeFully
(
v
,
tag
)
@PublishedApi
internal
companion
object
{
const
val
BYTE
:
Int
=
0
const
val
DOUBLE
:
Int
=
5
const
val
FLOAT
:
Int
=
4
const
val
INT
:
Int
=
2
const
val
JCE_MAX_STRING_LENGTH
=
104857600
const
val
LIST
:
Int
=
9
const
val
LONG
:
Int
=
3
const
val
MAP
:
Int
=
8
const
val
SHORT
:
Int
=
1
const
val
SIMPLE_LIST
:
Int
=
13
const
val
STRING1
:
Int
=
6
const
val
STRING4
:
Int
=
7
const
val
STRUCT_BEGIN
:
Int
=
10
const
val
STRUCT_END
:
Int
=
11
const
val
ZERO_TYPE
:
Int
=
12
private
fun
Any
?.
getClassName
():
KClass
<
out
Any
>
=
if
(
this
==
null
)
Unit
::
class
else this::class
}
@
PublishedApi
internal
fun
writeHead
(
type
:
Int
,
tag
:
Int
)
{
if
(
tag
<
15
)
{
this
.
output
.
writeByte
(((
tag
shl
4
)
or
type
).
toByte
())
return
}
if
(
tag
<
256
)
{
this
.
output
.
writeByte
((
type
.
toByte
()
or
0
xF0
.
toByte
()))
this
.
output
.
writeByte
(
tag
.
toByte
())
return
}
throw
JceEncodeException
(
"tag is too large: $tag"
)
}
}
class
JceEncodeException
(
message
:
String
)
:
RuntimeException
(
message
)
\ No newline at end of file
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/JceStruct.kt
View file @
1c96bb08
package
net.mamoe.mirai.qqandroid.io
interface
JceStruct
\ No newline at end of file
interface
JceStruct
{
fun
writeTo
(
output
:
JceOutput
)
=
Unit
}
\ No newline at end of file
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/io/serialization/Jce.kt
View file @
1c96bb08
...
...
@@ -13,7 +13,6 @@ import net.mamoe.mirai.qqandroid.io.JceStruct
import
net.mamoe.mirai.qqandroid.network.protocol.packet.withUse
import
net.mamoe.mirai.utils.io.readString
import
net.mamoe.mirai.utils.io.toIoBuffer
import
kotlin.reflect.KClass
@PublishedApi
internal
val
CharsetGBK
=
Charset
.
forName
(
"GBK"
)
...
...
@@ -24,7 +23,7 @@ fun <T> ByteArray.loadAs(deserializer: DeserializationStrategy<T>, c: JceCharset
return
Jce
.
byCharSet
(
c
).
load
(
deserializer
,
this
)
}
fun
<
T
:
JceStruct
>
T
.
toByteArray
(
serializer
:
SerializationStrategy
<
T
>,
c
:
JceCharset
=
JceCharset
.
UTF8
):
ByteArray
=
Jce
.
byCharSet
(
c
).
dump
(
serializer
,
this
)
fun
<
T
:
JceStruct
>
T
.
toByteArray
(
serializer
:
SerializationStrategy
<
T
>,
c
:
JceCharset
=
JceCharset
.
GBK
):
ByteArray
=
Jce
.
byCharSet
(
c
).
dump
(
serializer
,
this
)
enum
class
JceCharset
(
val
kotlinCharset
:
Charset
)
{
GBK
(
Charset
.
forName
(
"GBK"
)),
...
...
@@ -104,7 +103,6 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
@Suppress
(
"UNCHECKED_CAST"
,
"NAME_SHADOWING"
)
override
fun
<
T
>
encodeSerializableValue
(
serializer
:
SerializationStrategy
<
T
>,
value
:
T
)
=
when
(
serializer
.
descriptor
)
{
is
MapLikeDescriptor
->
{
println
(
"hello"
)
val
entries
=
(
value
as
Map
<
*
,
*
>).
entries
val
serializer
=
(
serializer
as
MapLikeSerializer
<
Any
?
,
Any
?
,
T
,
*
>)
val
mapEntrySerial
=
MapEntrySerializer
(
serializer
.
keySerializer
,
serializer
.
valueSerializer
)
...
...
@@ -115,9 +113,9 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
}
ByteArraySerializer
.
descriptor
->
encodeTaggedByteArray
(
popTag
(),
value
as
ByteArray
)
is
PrimitiveArrayDescriptor
->
{
if
(
value
is
ByteArray
)
{
this
.
encodeTaggedByteArray
(
popTag
(),
value
)
}
else
{
//
if (value is ByteArray) {
//
this.encodeTaggedByteArray(popTag(), value)
//
} else {
serializer
.
serialize
(
ListWriter
(
when
(
value
)
{
...
...
@@ -127,12 +125,14 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
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
)
}
//
}
}
is
ArrayClassDesc
->
{
serializer
.
serialize
(
...
...
@@ -260,13 +260,13 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
}
@PublishedApi
internal
fun
writeHead
(
type
:
Int
,
tag
:
Int
)
{
internal
fun
writeHead
(
type
:
Byte
,
tag
:
Int
)
{
if
(
tag
<
15
)
{
this
.
output
.
write
((
tag
shl
4
)
or
type
)
this
.
output
.
write
((
tag
shl
4
)
or
type
.
toInt
()
)
return
}
if
(
tag
<
256
)
{
this
.
output
.
write
(
type
or
0
xF0
)
this
.
output
.
write
(
type
.
toInt
()
or
0
xF0
)
this
.
output
.
write
(
tag
)
return
}
...
...
@@ -274,6 +274,45 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
}
}
private
open
inner
class
JceMapReader
(
val
size
:
Int
,
input
:
JceInput
)
:
JceDecoder
(
input
)
{
override
fun
decodeCollectionSize
(
desc
:
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
(
desc
:
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
(
desc
:
SerialDescriptor
)
{
input
.
readHead
()
// STRUCT_END
}
}
private
open
inner
class
NullReader
(
input
:
JceInput
)
:
JceDecoder
(
input
)
private
open
inner
class
JceDecoder
(
internal
val
input
:
JceInput
)
:
TaggedDecoder
<
Int
>()
{
...
...
@@ -291,73 +330,134 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
override
fun
decodeTaggedString
(
tag
:
Int
):
String
=
input
.
readString
(
tag
)
override
fun
decodeTaggedBoolean
(
tag
:
Int
):
Boolean
=
input
.
readBoolean
(
tag
)
@Suppress
(
"UNCHECKED_CAST"
)
override
fun
<
T
>
decodeSerializableValue
(
deserializer
:
DeserializationStrategy
<
T
>):
T
=
when
(
deserializer
.
descriptor
)
{
override
fun
decodeTaggedEnum
(
tag
:
Int
,
enumDescription
:
SerialDescriptor
):
Int
=
TODO
()
/**
* 在 [KSerializer.serialize] 前
*/
override
fun
beginStructure
(
desc
:
SerialDescriptor
,
vararg
typeParams
:
KSerializer
<
*
>):
CompositeDecoder
{
println
(
"beginStructure: desc=${desc.getClassName()}, typeParams: ${typeParams.contentToString()}"
)
when
(
desc
)
{
// 由于 Byte 的数组有两种方式写入, 需特定读取器
ByteArraySerializer
.
descriptor
->
{
// ByteArray, 交给 decodeSerializableValue 进行处理
return
this
}
is
ListLikeDescriptor
->
{
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
&&
desc
.
isNullable
)
{
return
NullReader
(
this
.
input
)
}
}
is
MapLikeDescriptor
->
{
deserializer
as
MapLikeSerializer
<
Any
?
,
Any
?
,
T
,
*
>
val
tag
=
popTag
()
this
.
input
.
skipToTagOrNull
(
tag
)
{
check
(
it
.
type
.
toInt
()
==
8
)
{
"type mismatch: ${it.type}"
}
val
size
=
this
.
input
.
readInt
(
0
)
val
map
=
HashMap
<
Any
?
,
Any
?
>(
size
)
repeat
(
size
)
{
pushTag
(
0
)
val
key
=
deserializer
.
keySerializer
.
deserialize
(
this
)
pushTag
(
1
)
val
value
=
deserializer
.
valueSerializer
.
deserialize
(
this
)
map
[
key
]
=
value
}
return
map
as
T
}
?:
error
(
"cannot find tag $tag"
)
}
ByteArraySerializer
.
descriptor
->
input
.
readByteArray
(
popTag
())
as
T
ShortArraySerializer
.
descriptor
->
input
.
readShortArray
(
popTag
())
as
T
IntArraySerializer
.
descriptor
->
input
.
readIntArray
(
popTag
())
as
T
LongArraySerializer
.
descriptor
->
input
.
readLongArray
(
popTag
())
as
T
FloatArraySerializer
.
descriptor
->
input
.
readFloatArray
(
popTag
())
as
T
DoubleArraySerializer
.
descriptor
->
input
.
readDoubleArray
(
popTag
())
as
T
CharArraySerializer
.
descriptor
->
input
.
readByteArray
(
popTag
()).
map
{
it
.
toChar
()
}.
toCharArray
()
as
T
BooleanArraySerializer
.
descriptor
->
input
.
readBooleanArray
(
popTag
())
as
T
val
tag
=
currentTagOrNull
is
ArrayClassDesc
->
{
deserializer
as
ArrayListSerializer
<
Any
?
>
if
(
tag
!=
null
&&
input
.
skipToTagOrNull
(
tag
)
{
popTag
()
}
==
null
&&
desc
.
isNullable
)
{
return
NullReader
(
this
.
input
)
}
val
tag
=
popTag
()
input
.
skipToTagOrNull
(
tag
)
{
head
->
return
Array
(
input
.
readInt
(
0
))
{
input
.
readHead
()
deserializer
.
deserialize
(
this
)
}
as
T
}
?:
error
(
"cannot find tag $tag"
)
if
(
tag
!=
null
)
{
popTag
()
}
return
JceMapReader
(
input
.
readInt
(
0
),
this
.
input
)
}
}
is
ListLikeDescriptor
->
{
deserializer
as
ListLikeSerializer
<
Any
?
,
T
,
*
>
val
tag
=
currentTag
input
.
skipToTagOrNull
(
tag
)
{
head
->
val
size
=
input
.
readInt
(
0
)
val
list
=
ArrayList
<
Any
?
>(
size
)
if
(!
input
.
input
.
endOfInput
)
{
val
tag
=
currentTagOrNull
if
(
tag
!=
null
&&
input
.
peakHead
().
tag
>
tag
)
{
return
NullReader
(
this
.
input
)
}
}
repeat
(
size
)
{
//input.readHead()
this
.
pushTag
(
0
)
list
.
add
(
deserializer
.
typeParams
[
0
].
also
{
println
(
it
.
getClassName
())
}.
deserialize
(
this
))
if
(
desc
.
kind
==
StructureKind
.
CLASS
||
desc
.
kind
==
UnionKind
.
OBJECT
)
{
val
tag
=
currentTagOrNull
if
(
tag
!=
null
)
{
@Suppress
(
"SENSELESS_COMPARISON"
)
// 推断 bug
if
(
input
.
skipToTagOrNull
(
tag
)
{
popTag
()
return
JceStructReader
(
input
)
}
==
null
&&
desc
.
isNullable
)
{
return
NullReader
(
this
.
input
)
}
}
return
list
as
T
}
?:
error
(
"cannot find tag $tag"
)
return
this
// top-level
}
else
->
{
if
(
input
.
peakHead
().
type
.
toInt
()
==
STRUCT_BEGIN
)
{
input
.
readHead
()
deserializer
.
deserialize
(
this
).
also
{
input
.
readHead
()
}
}
else
deserializer
.
deserialize
(
this
)
return
super
.
beginStructure
(
desc
,
*
typeParams
)
}
override
fun
decodeTaggedNull
(
tag
:
Int
):
Nothing
?
{
return
null
}
override
fun
decodeTaggedEnum
(
tag
:
Int
,
enumDescription
:
SerialDescriptor
):
Int
=
TODO
()
override
fun
decodeTaggedNotNullMark
(
tag
:
Int
):
Boolean
{
return
!
input
.
input
.
endOfInput
&&
input
.
peakHead
().
tag
<=
tag
}
@Suppress
(
"UNCHECKED_CAST"
)
override
fun
<
T
:
Any
>
decodeNullableSerializableValue
(
deserializer
:
DeserializationStrategy
<
T
?
>):
T
?
{
println
(
"decodeNullableSerializableValue: ${deserializer.getClassName()}"
)
if
(
deserializer
is
NullReader
)
{
return
null
}
when
(
deserializer
.
descriptor
)
{
ByteArraySerializer
.
descriptor
->
{
return
input
.
readByteArray
(
popTag
())
as
T
}
is
ListLikeDescriptor
->
{
if
(
deserializer
is
ReferenceArraySerializer
<
*
,
*
>
&&
(
deserializer
as
ListLikeSerializer
<
Any
?
,
T
,
Any
?
>).
typeParams
.
isNotEmpty
()
&&
(
deserializer
as
ListLikeSerializer
<
Any
?
,
T
,
Any
?
>).
typeParams
[
0
]
is
ByteSerializer
)
{
return
input
.
readByteArray
(
popTag
()).
toTypedArray
()
as
T
}
else
if
(
deserializer
is
ArrayListSerializer
<
*
>
&&
(
deserializer
as
ArrayListSerializer
<
*
>).
typeParams
.
isNotEmpty
()
&&
(
deserializer
as
ArrayListSerializer
<
*
>).
typeParams
[
0
]
is
ByteSerializer
)
{
return
input
.
readByteArray
(
popTag
()).
toMutableList
()
as
T
}
return
super
.
decodeSerializableValue
(
deserializer
)
}
is
MapLikeDescriptor
->
{
// 将 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
=
HashSetSerializer
(
mapEntrySerial
).
deserialize
(
this
)
return
setOfEntries
.
associateBy
({
it
.
key
},
{
it
.
value
})
as
T
}
}
val
tag
=
currentTagOrNull
?:
return
deserializer
.
deserialize
(
this
)
return
if
(
this
.
decodeTaggedNotNullMark
(
tag
)){
deserializer
.
deserialize
(
this
)
}
else
{
null
}
}
@Suppress
(
"UNCHECKED_CAST"
)
override
fun
<
T
>
decodeSerializableValue
(
deserializer
:
DeserializationStrategy
<
T
>):
T
{
return
decodeNullableSerializableValue
(
deserializer
as
DeserializationStrategy
<
Any
?
>)
as
?
T
?:
error
(
"value is not optional but cannot find"
)
}
}
...
...
@@ -606,23 +706,23 @@ class Jce private constructor(private val charset: JceCharset, context: SerialMo
}
}
internal
const
val
BYTE
:
Int
=
0
internal
const
val
DOUBLE
:
Int
=
5
internal
const
val
FLOAT
:
Int
=
4
internal
const
val
INT
:
Int
=
2
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
:
Int
=
9
internal
const
val
LONG
:
Int
=
3
internal
const
val
MAP
:
Int
=
8
internal
const
val
SHORT
:
Int
=
1
internal
const
val
SIMPLE_LIST
:
Int
=
13
internal
const
val
STRING1
:
Int
=
6
internal
const
val
STRING4
:
Int
=
7
internal
const
val
STRUCT_BEGIN
:
Int
=
10
internal
const
val
STRUCT_END
:
Int
=
11
internal
const
val
ZERO_TYPE
:
Int
=
12
private
fun
Any
?.
getClassName
():
KClass
<
out
Any
>
=
if
(
this
==
null
)
Unit
::
class
else this::class
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
private
fun
Any
?.
getClassName
():
String
=
(
if
(
this
==
null
)
Unit
::
class
else this::class).simple
Name
?:
"<unnamed class>"
}
override
fun
<
T
>
dump
(
serializer
:
SerializationStrategy
<
T
>,
obj
:
T
):
ByteArray
{
...
...
mirai-core-qqandroid/src/jvmTest/kotlin/net.mamoe.mirai.qqandroid.io.serialization/JceDecoderTest.kt
View file @
1c96bb08
package
net.mamoe.mirai.qqandroid.io.serialization
import
kotlinx.io.core.readBytes
import
kotlinx.serialization.SerialId
import
kotlinx.serialization.Serializable
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.utils.cryptor.contentToString
import
net.mamoe.mirai.utils.io.toUHexString
import
kotlin.test.Test
import
kotlin.test.assertEquals
fun
main
()
{
JceDecoderTest
().
testSimpleMap
()
}
class
JceDecoderTest
{
...
...
@@ -18,23 +26,113 @@ class JceDecoderTest {
@SerialId
(
4
)
val
long
:
Long
=
123
,
@SerialId
(
5
)
val
float
:
Float
=
123f
,
@SerialId
(
6
)
val
double
:
Double
=
123.0
)
:
JceStruct
{
override
fun
writeTo
(
output
:
JceOutput
)
=
output
.
run
{
writeString
(
string
,
0
)
writeByte
(
byte
,
1
)
writeShort
(
short
,
2
)
writeInt
(
int
,
3
)
writeLong
(
long
,
4
)
writeFloat
(
float
,
5
)
writeDouble
(
double
,
6
)
}
}
@Serializable
class
TestComplexJceStruct
(
@SerialId
(
7
)
val
byteArray
:
ByteArray
=
byteArrayOf
(
1
,
2
,
3
),
@SerialId
(
8
)
val
byteList
:
List
<
Byte
>
=
listOf
(
1
,
2
,
3
),
// error here
@SerialId
(
9
)
val
map
:
Map
<
String
,
Map
<
String
,
ByteArray
>>
=
mapOf
(
"哈哈"
to
mapOf
(
"哈哈"
to
byteArrayOf
(
1
,
2
,
3
))),
// @SerialId(10) val nestedJceStruct: TestSimpleJceStruct = TestSimpleJceStruct(),
@SerialId
(
11
)
val
byteList2
:
List
<
List
<
Int
>>
=
listOf
(
listOf
(
1
,
2
,
3
),
listOf
(
1
,
2
,
3
))
)
:
JceStruct
@Serializable
class
TestComplexNullableJceStruct
(
@SerialId
(
7
)
val
byteArray
:
ByteArray
?
=
byteArrayOf
(
1
,
2
,
3
),
@SerialId
(
8
)
val
byteList
:
List
<
Byte
>?
=
listOf
(
1
,
2
,
3
),
// error here
@SerialId
(
9
)
val
map
:
Map
<
String
,
Map
<
String
,
ByteArray
>>?
=
mapOf
(
"哈哈"
to
mapOf
(
"哈哈"
to
byteArrayOf
(
1
,
2
,
3
))),
@SerialId
(
10
)
val
nestedJceStruct
:
TestSimpleJceStruct
?
=
TestSimpleJceStruct
(),
@SerialId
(
11
)
val
byteList2
:
List
<
List
<
Int
>>?
=
listOf
(
listOf
(
1
,
2
,
3
),
listOf
(
1
,
2
,
3
))
)
:
JceStruct
@Test
fun
testEncoder
()
{
println
(
TestComplexJceStruct
().
toByteArray
(
TestComplexJceStruct
.
serializer
()).
loadAs
(
TestComplexJceStruct
.
serializer
()).
contentToString
())
println
(
TestComplexJceStruct
().
toByteArray
(
TestComplexJceStruct
.
serializer
()).
loadAs
(
TestComplex
Nullable
JceStruct
.
serializer
()).
contentToString
())
}
@Test
fun
testEncoder2
()
{
assertEquals
(
buildJcePacket
{
writeFully
(
byteArrayOf
(
1
,
2
,
3
),
7
)
writeCollection
(
listOf
(
1
,
2
,
3
),
8
)
writeMap
(
mapOf
(
"哈哈"
to
mapOf
(
"哈哈"
to
byteArrayOf
(
1
,
2
,
3
))),
9
)
writeJceStruct
(
TestSimpleJceStruct
(),
10
)
writeCollection
(
listOf
(
listOf
(
1
,
2
,
3
),
listOf
(
1
,
2
,
3
)),
11
)
}.
readBytes
().
toUHexString
(),
TestComplexNullableJceStruct
().
toByteArray
(
TestComplexNullableJceStruct
.
serializer
()).
toUHexString
()
)
}
@Test
fun
testNestedList
()
{
@Serializable
class
TestNestedList
(
@SerialId
(
7
)
val
array
:
List
<
List
<
Int
>>
=
listOf
(
listOf
(
1
,
2
,
3
),
listOf
(
1
,
2
,
3
),
listOf
(
1
,
2
,
3
))
)
println
(
buildJcePacket
{
writeCollection
(
listOf
(
listOf
(
1
,
2
,
3
),
listOf
(
1
,
2
,
3
),
listOf
(
1
,
2
,
3
)),
7
)
}.
readBytes
().
loadAs
(
TestNestedList
.
serializer
()).
contentToString
())
}
@Test
fun
testNestedArray
()
{
@Serializable
class
TestComplexJceStruct
(
@SerialId
(
7
)
val
byteArray
:
ByteArray
=
byteArrayOf
(
1
,
2
,
3
),
@SerialId
(
8
)
val
byteList
:
List
<
Byte
>
=
listOf
(
1
,
2
,
3
),
@SerialId
(
9
)
val
map
:
Map
<
String
,
String
>
=
mapOf
(
"哈哈"
to
"嘿嘿"
),
@SerialId
(
10
)
val
nestedJceStruct
:
TestSimpleJceStruct
=
TestSimpleJceStruct
()
)
:
JceStruct
class
TestNestedArray
(
@SerialId
(
7
)
val
array
:
Array
<
Array
<
Int
>>
=
arrayOf
(
arrayOf
(
1
,
2
,
3
),
arrayOf
(
1
,
2
,
3
),
arrayOf
(
1
,
2
,
3
))
)
println
(
buildJcePacket
{
writeFully
(
arrayOf
(
arrayOf
(
1
,
2
,
3
),
arrayOf
(
1
,
2
,
3
),
arrayOf
(
1
,
2
,
3
)),
7
)
}.
readBytes
().
loadAs
(
TestNestedArray
.
serializer
()).
contentToString
())
}
@Test
fun
testSimpleMap
()
{
@Serializable
class
TestSimpleMap
(
@SerialId
(
7
)
val
map
:
Map
<
String
,
Long
>
=
mapOf
(
"byteArrayOf(1)"
to
2222L
)
)
println
(
buildJcePacket
{
writeMap
(
mapOf
(
"byteArrayOf(1)"
to
2222
),
7
)
}.
readBytes
().
loadAs
(
TestSimpleMap
.
serializer
()).
contentToString
())
}
@Test
fun
testSimpleList
()
{
@Serializable
class
TestSimpleList
(
@SerialId
(
7
)
val
list
:
List
<
String
>
=
listOf
(
"asd"
,
"asdasdasd"
)
)
println
(
buildJcePacket
{
writeCollection
(
listOf
(
"asd"
,
"asdasdasd"
),
7
)
}.
readBytes
().
loadAs
(
TestSimpleList
.
serializer
()).
contentToString
())
}
@Test
fun
testNestedMap
()
{
@Serializable
class
TestNestedMap
(
@SerialId
(
7
)
val
map
:
Map
<
ByteArray
,
Map
<
ByteArray
,
ShortArray
>>
=
mapOf
(
byteArrayOf
(
1
)
to
mapOf
(
byteArrayOf
(
1
)
to
shortArrayOf
(
2
)))
)
println
(
buildJcePacket
{
writeMap
(
mapOf
(
byteArrayOf
(
1
)
to
mapOf
(
byteArrayOf
(
1
)
to
shortArrayOf
(
2
))),
7
)
}.
readBytes
().
loadAs
(
TestNestedMap
.
serializer
()).
map
.
entries
.
first
().
value
!!
.
contentToString
())
}
}
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment