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
Expand all
Hide 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
This diff is collapsed.
Click to expand it.
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
())
}
@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
@Test
fun
testNestedArray
()
{
@Serializable
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