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
6b5cbc3d
Commit
6b5cbc3d
authored
May 02, 2020
by
Him188
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implement new ExternalImage and image chunk strategy
parent
b14bfde6
Changes
13
Show whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
143 additions
and
172 deletions
+143
-172
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.common.kt
...n/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.common.kt
+2
-3
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/HighwayHelper.kt
...et/mamoe/mirai/qqandroid/network/highway/HighwayHelper.kt
+30
-47
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/highway.kt
...tlin/net/mamoe/mirai/qqandroid/network/highway/highway.kt
+9
-21
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt
.../mirai/qqandroid/network/protocol/packet/PacketFactory.kt
+2
-0
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/ConfigPushSvc.kt
.../qqandroid/network/protocol/packet/login/ConfigPushSvc.kt
+1
-0
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/ByteArrayPool.kt
...n/kotlin/net/mamoe/mirai/qqandroid/utils/ByteArrayPool.kt
+2
-34
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/input.kt
...onMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/input.kt
+1
-0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt
.../commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt
+7
-33
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/internal/asReusableInput.common.kt
.../net.mamoe.mirai/utils/internal/asReusableInput.common.kt
+2
-8
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/internal/chunked.kt
...mmonMain/kotlin/net.mamoe.mirai/utils/internal/chunked.kt
+22
-13
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/internal/md5.common.kt
...nMain/kotlin/net.mamoe.mirai/utils/internal/md5.common.kt
+2
-2
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/ExternalImageJvm.kt
.../jvmMain/kotlin/net/mamoe/mirai/utils/ExternalImageJvm.kt
+3
-3
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/internal/asReusableInput.jvm.kt
...lin/net/mamoe/mirai/utils/internal/asReusableInput.jvm.kt
+60
-8
No files found.
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.common.kt
View file @
6b5cbc3d
...
@@ -50,7 +50,6 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList
...
@@ -50,7 +50,6 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList
import
net.mamoe.mirai.qqandroid.utils.MiraiPlatformUtils
import
net.mamoe.mirai.qqandroid.utils.MiraiPlatformUtils
import
net.mamoe.mirai.qqandroid.utils.encodeToString
import
net.mamoe.mirai.qqandroid.utils.encodeToString
import
net.mamoe.mirai.qqandroid.utils.io.serialization.toByteArray
import
net.mamoe.mirai.qqandroid.utils.io.serialization.toByteArray
import
net.mamoe.mirai.qqandroid.utils.toReadPacket
import
net.mamoe.mirai.utils.*
import
net.mamoe.mirai.utils.*
import
kotlin.collections.asSequence
import
kotlin.collections.asSequence
import
kotlin.contracts.ExperimentalContracts
import
kotlin.contracts.ExperimentalContracts
...
@@ -678,8 +677,8 @@ internal abstract class QQAndroidBotBase constructor(
...
@@ -678,8 +677,8 @@ internal abstract class QQAndroidBotBase constructor(
response
.
proto
.
uint32UpIp
.
zip
(
response
.
proto
.
uint32UpPort
),
response
.
proto
.
uint32UpIp
.
zip
(
response
.
proto
.
uint32UpPort
),
response
.
proto
.
msgSig
,
response
.
proto
.
msgSig
,
MiraiPlatformUtils
.
md5
(
body
),
MiraiPlatformUtils
.
md5
(
body
),
body
.
toReadPacket
(),
@Suppress
(
"INVISIBLE_REFERENCE"
)
body
.
size
.
toLong
().
and
(
0
xFFFF_FFFF
),
// don't use toLongUnsigned: Overload resolution ambiguity
net
.
mamoe
.
mirai
.
utils
.
internal
.
asReusableInput0
(
body
),
// don't use toLongUnsigned: Overload resolution ambiguity
"group long message"
,
"group long message"
,
27
27
)
)
...
...
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/HighwayHelper.kt
View file @
6b5cbc3d
...
@@ -20,22 +20,24 @@ import io.ktor.utils.io.ByteWriteChannel
...
@@ -20,22 +20,24 @@ import io.ktor.utils.io.ByteWriteChannel
import
kotlinx.coroutines.InternalCoroutinesApi
import
kotlinx.coroutines.InternalCoroutinesApi
import
kotlinx.coroutines.delay
import
kotlinx.coroutines.delay
import
kotlinx.coroutines.flow.collect
import
kotlinx.coroutines.flow.collect
import
kotlinx.coroutines.io.ByteReadChannel
import
kotlinx.coroutines.isActive
import
kotlinx.coroutines.isActive
import
kotlinx.coroutines.withTimeoutOrNull
import
kotlinx.coroutines.withTimeoutOrNull
import
kotlinx.io.InputStream
import
kotlinx.io.core.Input
import
kotlinx.io.core.discardExact
import
kotlinx.io.core.discardExact
import
kotlinx.io.core.readAvailable
import
kotlinx.io.core.use
import
kotlinx.io.core.use
import
kotlinx.serialization.InternalSerializationApi
import
kotlinx.serialization.InternalSerializationApi
import
net.mamoe.mirai.qqandroid.QQAndroidBot
import
net.mamoe.mirai.qqandroid.QQAndroidBot
import
net.mamoe.mirai.qqandroid.network.QQAndroidClient
import
net.mamoe.mirai.qqandroid.network.QQAndroidClient
import
net.mamoe.mirai.qqandroid.network.protocol.data.proto.CSDataHighwayHead
import
net.mamoe.mirai.qqandroid.network.protocol.data.proto.CSDataHighwayHead
import
net.mamoe.mirai.qqandroid.utils.*
import
net.mamoe.mirai.qqandroid.utils.PlatformSocket
import
net.mamoe.mirai.qqandroid.utils.SocketException
import
net.mamoe.mirai.qqandroid.utils.addSuppressedMirai
import
net.mamoe.mirai.qqandroid.utils.io.serialization.readProtoBuf
import
net.mamoe.mirai.qqandroid.utils.io.serialization.readProtoBuf
import
net.mamoe.mirai.qqandroid.utils.io.withUse
import
net.mamoe.mirai.qqandroid.utils.io.withUse
import
net.mamoe.mirai.utils.*
import
net.mamoe.mirai.qqandroid.utils.toIpV4AddressString
import
net.mamoe.mirai.utils.ExternalImage
import
net.mamoe.mirai.utils.MiraiExperimentalAPI
import
net.mamoe.mirai.utils.MiraiInternalAPI
import
net.mamoe.mirai.utils.verbose
import
kotlin.coroutines.EmptyCoroutineContext
import
kotlin.coroutines.EmptyCoroutineContext
import
kotlin.math.roundToInt
import
kotlin.math.roundToInt
import
kotlin.time.ExperimentalTime
import
kotlin.time.ExperimentalTime
...
@@ -75,26 +77,8 @@ internal suspend fun HttpClient.postImage(
...
@@ -75,26 +77,8 @@ internal suspend fun HttpClient.postImage(
@OptIn
(
MiraiExperimentalAPI
::
class
)
@OptIn
(
MiraiExperimentalAPI
::
class
)
override
suspend
fun
writeTo
(
channel
:
ByteWriteChannel
)
{
override
suspend
fun
writeTo
(
channel
:
ByteWriteChannel
)
{
ByteArrayPool
.
useInstance
{
buffer
:
ByteArray
->
imageInput
.
writeTo
(
channel
)
when
(
imageInput
)
{
is
Input
->
{
var
size
:
Int
while
(
imageInput
.
readAvailable
(
buffer
).
also
{
size
=
it
}
>
0
)
{
channel
.
writeFully
(
buffer
,
0
,
size
)
channel
.
flush
()
}
}
is
ByteReadChannel
->
imageInput
.
copyAndClose
(
channel
)
is
InputStream
->
{
var
size
:
Int
while
(
imageInput
.
read
(
buffer
).
also
{
size
=
it
}
>
0
)
{
channel
.
writeFully
(
buffer
,
0
,
size
)
channel
.
flush
()
}
}
else
->
error
(
"unsupported imageInput: ${imageInput::class.simpleName}"
)
}
}
}
}
}
}
}
==
HttpStatusCode
.
OK
}
==
HttpStatusCode
.
OK
...
@@ -109,26 +93,26 @@ internal object HighwayHelper {
...
@@ -109,26 +93,26 @@ internal object HighwayHelper {
image
:
ExternalImage
.
ReusableInput
,
image
:
ExternalImage
.
ReusableInput
,
kind
:
String
,
kind
:
String
,
commandId
:
Int
commandId
:
Int
)
=
uploadImageToServers
(
bot
,
servers
,
uKey
,
image
.
md5
,
image
.
input
(),
image
.
size
,
kind
,
commandId
)
)
=
uploadImageToServers
(
bot
,
servers
,
uKey
,
image
.
md5
,
image
,
kind
,
commandId
)
@Suppress
(
"INVISIBLE_REFERENCE"
,
"INVISIBLE_MEMBER"
)
@OptIn
(
ExperimentalTime
::
class
)
@OptIn
(
ExperimentalTime
::
class
)
suspend
fun
uploadImageToServers
(
suspend
fun
uploadImageToServers
(
bot
:
QQAndroidBot
,
bot
:
QQAndroidBot
,
servers
:
List
<
Pair
<
Int
,
Int
>>,
servers
:
List
<
Pair
<
Int
,
Int
>>,
uKey
:
ByteArray
,
uKey
:
ByteArray
,
md5
:
ByteArray
,
md5
:
ByteArray
,
input
:
Any
,
input
:
ExternalImage
.
ReusableInput
,
inputSize
:
Long
,
kind
:
String
,
kind
:
String
,
commandId
:
Int
commandId
:
Int
)
=
servers
.
retryWithServers
(
)
=
servers
.
retryWithServers
(
(
input
S
ize
*
1000
/
1024
/
10
).
coerceAtLeast
(
5000
),
(
input
.
s
ize
*
1000
/
1024
/
10
).
coerceAtLeast
(
5000
),
onFail
=
{
onFail
=
{
throw
IllegalStateException
(
"cannot upload $kind, failed on all servers."
,
it
)
throw
IllegalStateException
(
"cannot upload $kind, failed on all servers."
,
it
)
}
}
)
{
ip
,
port
->
)
{
ip
,
port
->
bot
.
network
.
logger
.
verbose
{
bot
.
network
.
logger
.
verbose
{
"[Highway] Uploading $kind to ${ip}:$port, size=${input
S
ize.sizeToString()}"
"[Highway] Uploading $kind to ${ip}:$port, size=${input
.s
ize.sizeToString()}"
}
}
val
time
=
measureTime
{
val
time
=
measureTime
{
...
@@ -137,7 +121,6 @@ internal object HighwayHelper {
...
@@ -137,7 +121,6 @@ internal object HighwayHelper {
serverIp
=
ip
,
serverIp
=
ip
,
serverPort
=
port
,
serverPort
=
port
,
imageInput
=
input
,
imageInput
=
input
,
inputSize
=
inputSize
,
fileMd5
=
md5
,
fileMd5
=
md5
,
ticket
=
uKey
,
ticket
=
uKey
,
commandId
=
commandId
commandId
=
commandId
...
@@ -145,22 +128,21 @@ internal object HighwayHelper {
...
@@ -145,22 +128,21 @@ internal object HighwayHelper {
}
}
bot
.
network
.
logger
.
verbose
{
bot
.
network
.
logger
.
verbose
{
"[Highway] Uploading $kind: succeed at ${(input
S
ize.toDouble() / 1024 / time.inSeconds).roundToInt()} KiB/s"
"[Highway] Uploading $kind: succeed at ${(input
.s
ize.toDouble() / 1024 / time.inSeconds).roundToInt()} KiB/s"
}
}
}
}
@Suppress
(
"INVISIBLE_REFERENCE"
,
"INVISIBLE_MEMBER"
)
@OptIn
(
InternalCoroutinesApi
::
class
)
@OptIn
(
InternalCoroutinesApi
::
class
)
internal
suspend
fun
uploadImage
(
internal
suspend
fun
uploadImage
(
client
:
QQAndroidClient
,
client
:
QQAndroidClient
,
serverIp
:
String
,
serverIp
:
String
,
serverPort
:
Int
,
serverPort
:
Int
,
ticket
:
ByteArray
,
ticket
:
ByteArray
,
imageInput
:
Any
,
imageInput
:
ExternalImage
.
ReusableInput
,
inputSize
:
Long
,
fileMd5
:
ByteArray
,
fileMd5
:
ByteArray
,
commandId
:
Int
// group=2, friend=1
commandId
:
Int
// group=2, friend=1
)
{
)
{
require
(
imageInput
is
Input
||
imageInput
is
InputStream
||
imageInput
is
ByteReadChannel
)
{
"unsupported imageInput: ${imageInput::class.simpleName}"
}
require
(
fileMd5
.
size
==
16
)
{
"bad md5. Required size=16, got ${fileMd5.size}"
}
require
(
fileMd5
.
size
==
16
)
{
"bad md5. Required size=16, got ${fileMd5.size}"
}
// require(ticket.size == 128) { "bad uKey. Required size=128, got ${ticket.size}" }
// require(ticket.size == 128) { "bad uKey. Required size=128, got ${ticket.size}" }
// require(commandId == 2 || commandId == 1) { "bad commandId. Must be 1 or 2" }
// require(commandId == 2 || commandId == 1) { "bad commandId. Must be 1 or 2" }
...
@@ -181,9 +163,9 @@ internal object HighwayHelper {
...
@@ -181,9 +163,9 @@ internal object HighwayHelper {
commandId
=
commandId
,
commandId
=
commandId
,
ticket
=
ticket
,
ticket
=
ticket
,
data
=
imageInput
,
data
=
imageInput
,
dataSize
=
inputSize
,
fileMd5
=
fileMd5
fileMd5
=
fileMd5
).
collect
{
).
withUse
{
flow
.
collect
{
socket
.
send
(
it
)
socket
.
send
(
it
)
//0A 3C 08 01 12 0A 31 39 39 34 37 30 31 30 32 31 1A 0C 50 69 63 55 70 2E 44 61 74 61 55 70 20 E9 A7 05 28 00 30 BD DB 8B 80 02 38 80 20 40 02 4A 0A 38 2E 32 2E 30 2E 31 32 39 36 50 84 10 12 3D 08 00 10 FD 08 18 00 20 FD 08 28 C6 01 38 00 42 10 D4 1D 8C D9 8F 00 B2 04 E9 80 09 98 EC F8 42 7E 4A 10 D4 1D 8C D9 8F 00 B2 04 E9 80 09 98 EC F8 42 7E 50 89 92 A2 FB 06 58 00 60 00 18 53 20 01 28 00 30 04 3A 00 40 E6 B7 F7 D9 80 2E 48 00 50 00
//0A 3C 08 01 12 0A 31 39 39 34 37 30 31 30 32 31 1A 0C 50 69 63 55 70 2E 44 61 74 61 55 70 20 E9 A7 05 28 00 30 BD DB 8B 80 02 38 80 20 40 02 4A 0A 38 2E 32 2E 30 2E 31 32 39 36 50 84 10 12 3D 08 00 10 FD 08 18 00 20 FD 08 28 C6 01 38 00 42 10 D4 1D 8C D9 8F 00 B2 04 E9 80 09 98 EC F8 42 7E 4A 10 D4 1D 8C D9 8F 00 B2 04 E9 80 09 98 EC F8 42 7E 50 89 92 A2 FB 06 58 00 60 00 18 53 20 01 28 00 30 04 3A 00 40 E6 B7 F7 D9 80 2E 48 00 50 00
...
@@ -197,6 +179,7 @@ internal object HighwayHelper {
...
@@ -197,6 +179,7 @@ internal object HighwayHelper {
}
}
}
}
}
}
}
}
}
...
...
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/highway/highway.kt
View file @
6b5cbc3d
...
@@ -11,12 +11,7 @@
...
@@ -11,12 +11,7 @@
package
net.mamoe.mirai.qqandroid.network.highway
package
net.mamoe.mirai.qqandroid.network.highway
import
kotlinx.coroutines.flow.Flow
import
kotlinx.coroutines.flow.map
import
kotlinx.coroutines.io.ByteReadChannel
import
kotlinx.io.InputStream
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.Input
import
kotlinx.io.core.buildPacket
import
kotlinx.io.core.buildPacket
import
kotlinx.io.core.writeFully
import
kotlinx.io.core.writeFully
import
kotlinx.serialization.InternalSerializationApi
import
kotlinx.serialization.InternalSerializationApi
...
@@ -25,9 +20,12 @@ import net.mamoe.mirai.qqandroid.network.protocol.data.proto.CSDataHighwayHead
...
@@ -25,9 +20,12 @@ import net.mamoe.mirai.qqandroid.network.protocol.data.proto.CSDataHighwayHead
import
net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
import
net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
import
net.mamoe.mirai.qqandroid.utils.ByteArrayPool
import
net.mamoe.mirai.qqandroid.utils.ByteArrayPool
import
net.mamoe.mirai.qqandroid.utils.MiraiPlatformUtils
import
net.mamoe.mirai.qqandroid.utils.MiraiPlatformUtils
import
net.mamoe.mirai.qqandroid.utils.io.chunkedFlow
import
net.mamoe.mirai.qqandroid.utils.io.serialization.toByteArray
import
net.mamoe.mirai.qqandroid.utils.io.serialization.toByteArray
import
net.mamoe.mirai.utils.ExternalImage
import
net.mamoe.mirai.utils.MiraiInternalAPI
import
net.mamoe.mirai.utils.MiraiInternalAPI
import
net.mamoe.mirai.utils.internal.ChunkedFlowSession
import
net.mamoe.mirai.utils.internal.ChunkedInput
import
net.mamoe.mirai.utils.internal.map
@OptIn
(
MiraiInternalAPI
::
class
,
InternalSerializationApi
::
class
)
@OptIn
(
MiraiInternalAPI
::
class
,
InternalSerializationApi
::
class
)
internal
fun
createImageDataPacketSequence
(
internal
fun
createImageDataPacketSequence
(
...
@@ -39,27 +37,17 @@ internal fun createImageDataPacketSequence(
...
@@ -39,27 +37,17 @@ internal fun createImageDataPacketSequence(
commandId
:
Int
,
commandId
:
Int
,
localId
:
Int
=
2052
,
localId
:
Int
=
2052
,
ticket
:
ByteArray
,
ticket
:
ByteArray
,
data
:
ExternalImage
.
ReusableInput
,
data
:
Any
,
dataSize
:
Long
,
fileMd5
:
ByteArray
,
fileMd5
:
ByteArray
,
sizePerPacket
:
Int
=
ByteArrayPool
.
BUFFER_SIZE
sizePerPacket
:
Int
=
ByteArrayPool
.
BUFFER_SIZE
):
Flow
<
ByteReadPacket
>
{
):
ChunkedFlowSession
<
ByteReadPacket
>
{
ByteArrayPool
.
checkBufferSize
(
sizePerPacket
)
ByteArrayPool
.
checkBufferSize
(
sizePerPacket
)
require
(
data
is
Input
||
data
is
InputStream
||
data
is
ByteReadChannel
)
{
"unsupported data: ${data::class.simpleName}"
}
// require(ticket.size == 128) { "bad uKey. Required size=128, got ${ticket.size}" }
// require(ticket.size == 128) { "bad uKey. Required size=128, got ${ticket.size}" }
require
(
data
!
is
ByteReadPacket
)
{
"bad input. given dataSize=$dataSize, but actual readRemaining=${(data as ByteReadPacket).remaining}"
}
val
flow
=
when
(
data
)
{
val
session
:
ChunkedFlowSession
<
ChunkedInput
>
=
data
.
chunkedFlow
(
sizePerPacket
)
is
ByteReadPacket
->
data
.
chunkedFlow
(
sizePerPacket
)
is
Input
->
data
.
chunkedFlow
(
sizePerPacket
)
is
ByteReadChannel
->
data
.
chunkedFlow
(
sizePerPacket
)
is
InputStream
->
data
.
chunkedFlow
(
sizePerPacket
)
else
->
error
(
"unreachable code"
)
}
var
offset
=
0L
var
offset
=
0L
return
flow
.
map
{
chunkedInput
->
return
session
.
map
{
chunkedInput
->
buildPacket
{
buildPacket
{
val
head
=
CSDataHighwayHead
.
ReqDataHighwayHead
(
val
head
=
CSDataHighwayHead
.
ReqDataHighwayHead
(
msgBasehead
=
CSDataHighwayHead
.
DataHighwayHead
(
msgBasehead
=
CSDataHighwayHead
.
DataHighwayHead
(
...
@@ -82,7 +70,7 @@ internal fun createImageDataPacketSequence(
...
@@ -82,7 +70,7 @@ internal fun createImageDataPacketSequence(
// cacheAddr = 812157193,
// cacheAddr = 812157193,
datalength
=
chunkedInput
.
bufferSize
,
datalength
=
chunkedInput
.
bufferSize
,
dataoffset
=
offset
,
dataoffset
=
offset
,
filesize
=
data
S
ize
,
filesize
=
data
.
s
ize
,
serviceticket
=
ticket
,
serviceticket
=
ticket
,
md5
=
MiraiPlatformUtils
.
md5
(
chunkedInput
.
buffer
,
0
,
chunkedInput
.
bufferSize
),
md5
=
MiraiPlatformUtils
.
md5
(
chunkedInput
.
buffer
,
0
,
chunkedInput
.
bufferSize
),
fileMd5
=
fileMd5
,
fileMd5
=
fileMd5
,
...
...
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/PacketFactory.kt
View file @
6b5cbc3d
...
@@ -197,6 +197,7 @@ internal object KnownPacketFactories {
...
@@ -197,6 +197,7 @@ internal object KnownPacketFactories {
readString
(
readInt
()
-
4
)
// uinAccount
readString
(
readInt
()
-
4
)
// uinAccount
@Suppress
(
"INVISIBLE_MEMBER"
,
"INVISIBLE_REFERENCE"
)
ByteArrayPool
.
useInstance
(
this
.
remaining
.
toInt
())
{
data
->
ByteArrayPool
.
useInstance
(
this
.
remaining
.
toInt
())
{
data
->
val
size
=
this
.
readAvailable
(
data
)
val
size
=
this
.
readAvailable
(
data
)
...
@@ -385,6 +386,7 @@ internal object KnownPacketFactories {
...
@@ -385,6 +386,7 @@ internal object KnownPacketFactories {
}
}
0
->
{
0
->
{
val
data
=
if
(
bot
.
client
.
loginState
==
0
)
{
val
data
=
if
(
bot
.
client
.
loginState
==
0
)
{
@Suppress
(
"INVISIBLE_MEMBER"
,
"INVISIBLE_REFERENCE"
)
ByteArrayPool
.
useInstance
(
this
.
remaining
.
toInt
())
{
byteArrayBuffer
->
ByteArrayPool
.
useInstance
(
this
.
remaining
.
toInt
())
{
byteArrayBuffer
->
val
size
=
(
this
.
remaining
-
1
).
toInt
()
val
size
=
(
this
.
remaining
-
1
).
toInt
()
this
.
readFully
(
byteArrayBuffer
,
0
,
size
)
this
.
readFully
(
byteArrayBuffer
,
0
,
size
)
...
...
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/ConfigPushSvc.kt
View file @
6b5cbc3d
...
@@ -70,6 +70,7 @@ internal class ConfigPushSvc {
...
@@ -70,6 +70,7 @@ internal class ConfigPushSvc {
override
suspend
fun
ByteReadPacket
.
decode
(
bot
:
QQAndroidBot
,
sequenceId
:
Int
):
PushReqResponse
?
{
override
suspend
fun
ByteReadPacket
.
decode
(
bot
:
QQAndroidBot
,
sequenceId
:
Int
):
PushReqResponse
?
{
@Suppress
(
"INVISIBLE_MEMBER"
,
"INVISIBLE_REFERENCE"
)
ByteArrayPool
.
useInstance
(
this
.
remaining
.
toInt
())
{
buffer
->
ByteArrayPool
.
useInstance
(
this
.
remaining
.
toInt
())
{
buffer
->
val
length
=
this
.
readAvailable
(
buffer
)
val
length
=
this
.
readAvailable
(
buffer
)
...
...
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/ByteArrayPool.kt
View file @
6b5cbc3d
package
net.mamoe.mirai.qqandroid.utils
package
net.mamoe.mirai.qqandroid.utils
import
kotlinx.io.pool.DefaultPool
import
kotlinx.io.pool.ObjectPool
import
kotlinx.io.pool.ObjectPool
/**
/**
* 缓存 [ByteArray] 实例的 [ObjectPool]
* 缓存 [ByteArray] 实例的 [ObjectPool]
*/
*/
internal
object
ByteArrayPool
:
DefaultPool
<
ByteArray
>(
256
)
{
@Suppress
(
"INVISIBLE_MEMBER"
,
"INVISIBLE_REFERENCE"
)
/**
internal
typealias
ByteArrayPool
=
net
.
mamoe
.
mirai
.
utils
.
internal
.
ByteArrayPool
* 每一个 [ByteArray] 的大小
\ No newline at end of file
*/
const
val
BUFFER_SIZE
:
Int
=
8192
*
8
override
fun
produceInstance
():
ByteArray
=
ByteArray
(
BUFFER_SIZE
)
override
fun
clearInstance
(
instance
:
ByteArray
):
ByteArray
=
instance
fun
checkBufferSize
(
size
:
Int
)
{
require
(
size
<=
BUFFER_SIZE
)
{
"sizePerPacket is too large. Maximum buffer size=$BUFFER_SIZE"
}
}
fun
checkBufferSize
(
size
:
Long
)
{
require
(
size
<=
BUFFER_SIZE
)
{
"sizePerPacket is too large. Maximum buffer size=$BUFFER_SIZE"
}
}
/**
* 请求一个大小至少为 [requestedSize] 的 [ByteArray] 实例.
*/
// 不要写为扩展函数. 它需要优先于 kotlinx.io 的扩展函数 resolve
inline
fun
<
R
>
useInstance
(
requestedSize
:
Int
=
0
,
block
:
(
ByteArray
)
->
R
):
R
{
if
(
requestedSize
>
BUFFER_SIZE
)
{
return
ByteArray
(
requestedSize
).
run
(
block
)
}
val
instance
=
borrow
()
try
{
return
block
(
instance
)
}
finally
{
recycle
(
instance
)
}
}
}
\ No newline at end of file
mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io/input.kt
View file @
6b5cbc3d
...
@@ -27,6 +27,7 @@ import kotlin.jvm.JvmMultifileClass
...
@@ -27,6 +27,7 @@ import kotlin.jvm.JvmMultifileClass
import
kotlin.jvm.JvmName
import
kotlin.jvm.JvmName
import
kotlin.jvm.JvmSynthetic
import
kotlin.jvm.JvmSynthetic
@Suppress
(
"INVISIBLE_MEMBER"
,
"INVISIBLE_REFERENCE"
)
internal
inline
fun
<
R
>
ByteReadPacket
.
useBytes
(
internal
inline
fun
<
R
>
ByteReadPacket
.
useBytes
(
n
:
Int
=
remaining
.
toInt
(),
//not that safe but adequate
n
:
Int
=
remaining
.
toInt
(),
//not that safe but adequate
block
:
(
data
:
ByteArray
,
length
:
Int
)
->
R
block
:
(
data
:
ByteArray
,
length
:
Int
)
->
R
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt
View file @
6b5cbc3d
...
@@ -11,18 +11,15 @@
...
@@ -11,18 +11,15 @@
package
net.mamoe.mirai.utils
package
net.mamoe.mirai.utils
import
kotlinx.coroutines.io.ByteReadChannel
import
io.ktor.utils.io.ByteWriteChannel
import
kotlinx.io.InputStream
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.Input
import
kotlinx.serialization.InternalSerializationApi
import
net.mamoe.mirai.contact.Contact
import
net.mamoe.mirai.contact.Contact
import
net.mamoe.mirai.contact.Group
import
net.mamoe.mirai.contact.Group
import
net.mamoe.mirai.contact.User
import
net.mamoe.mirai.contact.User
import
net.mamoe.mirai.message.MessageReceipt
import
net.mamoe.mirai.message.MessageReceipt
import
net.mamoe.mirai.message.data.Image
import
net.mamoe.mirai.message.data.Image
import
net.mamoe.mirai.message.data.sendTo
import
net.mamoe.mirai.message.data.sendTo
import
net.mamoe.mirai.utils.internal.asReusableInput
import
net.mamoe.mirai.utils.internal.ChunkedFlowSession
import
net.mamoe.mirai.utils.internal.ChunkedInput
import
kotlin.jvm.JvmField
import
kotlin.jvm.JvmField
import
kotlin.jvm.JvmSynthetic
import
kotlin.jvm.JvmSynthetic
...
@@ -36,43 +33,20 @@ import kotlin.jvm.JvmSynthetic
...
@@ -36,43 +33,20 @@ import kotlin.jvm.JvmSynthetic
*/
*/
@OptIn
(
MiraiInternalAPI
::
class
)
@OptIn
(
MiraiInternalAPI
::
class
)
class
ExternalImage
internal
constructor
(
class
ExternalImage
internal
constructor
(
val
md5
:
ByteArray
,
@JvmField
@JvmField
internal
val
input
:
ReusableInput
// Input from kotlinx.io, InputStream from kotlinx.io MPP, ByteReadChannel from ktor
internal
val
input
:
ReusableInput
// Input from kotlinx.io, InputStream from kotlinx.io MPP, ByteReadChannel from ktor
)
{
)
{
val
md5
:
ByteArray
get
()
=
this
.
input
.
md5
@SinceMirai
(
"1.0.0"
)
@SinceMirai
(
"1.0.0"
)
internal
interface
ReusableInput
{
internal
interface
ReusableInput
{
val
md5
:
ByteArray
val
md5
:
ByteArray
val
size
:
Long
val
size
:
Long
/**
fun
chunkedFlow
(
sizePerPacket
:
Int
):
ChunkedFlowSession
<
ChunkedInput
>
* Create a input for once usage
suspend
fun
writeTo
(
out
:
ByteWriteChannel
):
Long
*/
fun
input
():
Input
}
}
constructor
(
md5
:
ByteArray
,
input
:
ByteReadChannel
)
:
this
(
md5
,
input
.
asReusableInput
())
constructor
(
md5
:
ByteArray
,
input
:
Input
)
:
this
(
md5
,
input
.
asReusableInput
())
constructor
(
md5
:
ByteArray
,
input
:
ByteReadPacket
)
:
this
(
md5
,
input
.
asReusableInput
())
@OptIn
(
InternalSerializationApi
::
class
)
constructor
(
md5
:
ByteArray
,
input
:
InputStream
)
:
this
(
md5
,
input
.
asReusableInput
())
init
{
init
{
require
(
input
.
size
<
30L
*
1024
*
1024
)
{
"Image file is too big. Maximum is 30 MiB, but recommended to be 20 MiB"
}
require
(
input
.
size
<
30L
*
1024
*
1024
)
{
"Image file is too big. Maximum is 30 MiB, but recommended to be 20 MiB"
}
}
}
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/internal/asReusableInput.common.kt
View file @
6b5cbc3d
...
@@ -9,15 +9,9 @@
...
@@ -9,15 +9,9 @@
package
net.mamoe.mirai.utils.internal
package
net.mamoe.mirai.utils.internal
import
kotlinx.coroutines.io.ByteReadChannel
import
kotlinx.io.InputStream
import
kotlinx.io.core.Input
import
kotlinx.serialization.InternalSerializationApi
import
net.mamoe.mirai.utils.ExternalImage
import
net.mamoe.mirai.utils.ExternalImage
internal
expect
fun
ByteReadChannel
.
asReusableInput
():
ExternalImage
.
ReusableInput
internal
expect
fun
ByteArray
.
asReusableInput
():
ExternalImage
.
ReusableInput
internal
expect
fun
Input
.
asReusableInput
():
ExternalImage
.
ReusableInput
@OptIn
(
InternalSerializationApi
::
class
)
internal
fun
asReusableInput0
(
input
:
ByteArray
):
ExternalImage
.
ReusableInput
=
input
.
asReusableInput
()
internal
expect
fun
InputStream
.
asReusableInput
():
ExternalImage
.
ReusableInput
\ No newline at end of file
mirai-core
-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/io
/chunked.kt
→
mirai-core
/src/commonMain/kotlin/net.mamoe.mirai/utils/internal
/chunked.kt
View file @
6b5cbc3d
...
@@ -7,20 +7,33 @@
...
@@ -7,20 +7,33 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
*/
package
net.mamoe.mirai.
qqandroid.utils.io
package
net.mamoe.mirai.
utils.internal
import
kotlinx.coroutines.ExperimentalCoroutinesApi
import
kotlinx.coroutines.ExperimentalCoroutinesApi
import
kotlinx.coroutines.flow.Flow
import
kotlinx.coroutines.flow.Flow
import
kotlinx.coroutines.flow.flow
import
kotlinx.coroutines.flow.flow
import
kotlinx.coroutines.flow.flowOf
import
kotlinx.coroutines.flow.flowOf
import
kotlinx.coroutines.flow.map
import
kotlinx.coroutines.io.ByteReadChannel
import
kotlinx.coroutines.io.ByteReadChannel
import
kotlinx.io.InputStream
import
kotlinx.io.InputStream
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.ByteReadPacket
import
kotlinx.io.core.Closeable
import
kotlinx.io.core.Input
import
kotlinx.io.core.Input
import
kotlinx.io.pool.useInstance
import
net.mamoe.mirai.utils.MiraiInternalAPI
import
kotlinx.serialization.InternalSerializationApi
import
kotlinx.serialization.InternalSerializationApi
import
net.mamoe.mirai.qqandroid.utils.ByteArrayPool
import
kotlin.jvm.JvmField
internal
interface
ChunkedFlowSession
<
T
>
:
Closeable
{
val
flow
:
Flow
<
T
>
override
fun
close
()
}
internal
inline
fun
<
T
,
R
>
ChunkedFlowSession
<
T
>.
map
(
crossinline
mapper
:
suspend
ChunkedFlowSession
<
T
>.(
T
)
->
R
):
ChunkedFlowSession
<
R
>
{
return
object
:
ChunkedFlowSession
<
R
>
{
override
val
flow
:
Flow
<
R
>
=
this
@
map
.
flow
.
map
{
this
@
map
.
mapper
(
it
)
}
override
fun
close
()
=
this
@
map
.
close
()
}
}
/**
/**
...
@@ -34,13 +47,13 @@ internal class ChunkedInput(
...
@@ -34,13 +47,13 @@ internal class ChunkedInput(
*
*
* **注意**: 不要将他带出 [Flow.collect] 作用域, 否则将造成内存泄露
* **注意**: 不要将他带出 [Flow.collect] 作用域, 否则将造成内存泄露
*/
*/
val
buffer
:
ByteArray
,
@JvmField
val
buffer
:
ByteArray
,
internal
var
size
:
Int
@JvmField
internal
var
size
:
Int
)
{
)
{
/**
/**
* [buffer] 的有效大小
* [buffer] 的有效大小
*/
*/
val
bufferSize
:
Int
get
()
=
size
inline
val
bufferSize
:
Int
get
()
=
size
}
}
/**
/**
...
@@ -51,7 +64,6 @@ internal class ChunkedInput(
...
@@ -51,7 +64,6 @@ internal class ChunkedInput(
*
*
* 若 [ByteReadPacket.remaining] 小于 [sizePerPacket], 将会返回唯一元素 [this] 的 [Sequence]
* 若 [ByteReadPacket.remaining] 小于 [sizePerPacket], 将会返回唯一元素 [this] 的 [Sequence]
*/
*/
@OptIn
(
MiraiInternalAPI
::
class
)
internal
fun
ByteReadPacket
.
chunkedFlow
(
sizePerPacket
:
Int
):
Flow
<
ChunkedInput
>
{
internal
fun
ByteReadPacket
.
chunkedFlow
(
sizePerPacket
:
Int
):
Flow
<
ChunkedInput
>
{
ByteArrayPool
.
checkBufferSize
(
sizePerPacket
)
ByteArrayPool
.
checkBufferSize
(
sizePerPacket
)
if
(
this
.
remaining
<=
sizePerPacket
.
toLong
())
{
if
(
this
.
remaining
<=
sizePerPacket
.
toLong
())
{
...
@@ -81,7 +93,6 @@ internal fun ByteReadPacket.chunkedFlow(sizePerPacket: Int): Flow<ChunkedInput>
...
@@ -81,7 +93,6 @@ internal fun ByteReadPacket.chunkedFlow(sizePerPacket: Int): Flow<ChunkedInput>
* 对于一个 1000 长度的 [ByteReadChannel] 和参数 [sizePerPacket] = 300, 将会产生含四个元素的 [Sequence],
* 对于一个 1000 长度的 [ByteReadChannel] 和参数 [sizePerPacket] = 300, 将会产生含四个元素的 [Sequence],
* 其长度分别为: 300, 300, 300, 100.
* 其长度分别为: 300, 300, 300, 100.
*/
*/
@OptIn
(
MiraiInternalAPI
::
class
)
internal
fun
ByteReadChannel
.
chunkedFlow
(
sizePerPacket
:
Int
):
Flow
<
ChunkedInput
>
{
internal
fun
ByteReadChannel
.
chunkedFlow
(
sizePerPacket
:
Int
):
Flow
<
ChunkedInput
>
{
ByteArrayPool
.
checkBufferSize
(
sizePerPacket
)
ByteArrayPool
.
checkBufferSize
(
sizePerPacket
)
if
(
this
.
isClosedForRead
)
{
if
(
this
.
isClosedForRead
)
{
...
@@ -105,7 +116,7 @@ internal fun ByteReadChannel.chunkedFlow(sizePerPacket: Int): Flow<ChunkedInput>
...
@@ -105,7 +116,7 @@ internal fun ByteReadChannel.chunkedFlow(sizePerPacket: Int): Flow<ChunkedInput>
* 对于一个 1000 长度的 [Input] 和参数 [sizePerPacket] = 300, 将会产生含四个元素的 [Sequence],
* 对于一个 1000 长度的 [Input] 和参数 [sizePerPacket] = 300, 将会产生含四个元素的 [Sequence],
* 其长度分别为: 300, 300, 300, 100.
* 其长度分别为: 300, 300, 300, 100.
*/
*/
@OptIn
(
MiraiInternalAPI
::
class
,
ExperimentalCoroutinesApi
::
class
)
@OptIn
(
ExperimentalCoroutinesApi
::
class
)
internal
fun
Input
.
chunkedFlow
(
sizePerPacket
:
Int
):
Flow
<
ChunkedInput
>
{
internal
fun
Input
.
chunkedFlow
(
sizePerPacket
:
Int
):
Flow
<
ChunkedInput
>
{
ByteArrayPool
.
checkBufferSize
(
sizePerPacket
)
ByteArrayPool
.
checkBufferSize
(
sizePerPacket
)
...
@@ -132,9 +143,7 @@ internal fun Input.chunkedFlow(sizePerPacket: Int): Flow<ChunkedInput> {
...
@@ -132,9 +143,7 @@ internal fun Input.chunkedFlow(sizePerPacket: Int): Flow<ChunkedInput> {
*
*
* 若 [ByteReadPacket.remaining] 小于 [sizePerPacket], 将会返回唯一元素 [this] 的 [Sequence]
* 若 [ByteReadPacket.remaining] 小于 [sizePerPacket], 将会返回唯一元素 [this] 的 [Sequence]
*/
*/
@OptIn
(
@OptIn
(
ExperimentalCoroutinesApi
::
class
,
InternalSerializationApi
::
class
)
MiraiInternalAPI
::
class
,
ExperimentalCoroutinesApi
::
class
,
InternalSerializationApi
::
class
)
internal
fun
InputStream
.
chunkedFlow
(
sizePerPacket
:
Int
):
Flow
<
ChunkedInput
>
{
internal
fun
InputStream
.
chunkedFlow
(
sizePerPacket
:
Int
):
Flow
<
ChunkedInput
>
{
ByteArrayPool
.
checkBufferSize
(
sizePerPacket
)
ByteArrayPool
.
checkBufferSize
(
sizePerPacket
)
...
...
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/internal/md5.common.kt
View file @
6b5cbc3d
...
@@ -38,11 +38,11 @@ internal inline fun InputStream.readInSequence(block: (ByteArray, len: Int) -> U
...
@@ -38,11 +38,11 @@ internal inline fun InputStream.readInSequence(block: (ByteArray, len: Int) -> U
/**
/**
* 缓存 [ByteArray] 实例的 [ObjectPool]
* 缓存 [ByteArray] 实例的 [ObjectPool]
*/
*/
internal
object
ByteArrayPool
:
DefaultPool
<
ByteArray
>(
32
)
{
internal
object
ByteArrayPool
:
DefaultPool
<
ByteArray
>(
256
)
{
/**
/**
* 每一个 [ByteArray] 的大小
* 每一个 [ByteArray] 的大小
*/
*/
const
val
BUFFER_SIZE
:
Int
=
8192
const
val
BUFFER_SIZE
:
Int
=
8192
*
8
override
fun
produceInstance
():
ByteArray
=
ByteArray
(
BUFFER_SIZE
)
override
fun
produceInstance
():
ByteArray
=
ByteArray
(
BUFFER_SIZE
)
...
...
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/ExternalImageJvm.kt
View file @
6b5cbc3d
...
@@ -18,6 +18,7 @@ import kotlinx.io.core.Input
...
@@ -18,6 +18,7 @@ import kotlinx.io.core.Input
import
kotlinx.io.core.copyTo
import
kotlinx.io.core.copyTo
import
kotlinx.io.errors.IOException
import
kotlinx.io.errors.IOException
import
kotlinx.io.streams.asOutput
import
kotlinx.io.streams.asOutput
import
net.mamoe.mirai.utils.internal.asReusableInput
import
net.mamoe.mirai.utils.internal.md5
import
net.mamoe.mirai.utils.internal.md5
import
java.awt.image.BufferedImage
import
java.awt.image.BufferedImage
import
java.io.File
import
java.io.File
...
@@ -63,7 +64,7 @@ fun BufferedImage.toExternalImage(formatName: String = "png"): ExternalImage {
...
@@ -63,7 +64,7 @@ fun BufferedImage.toExternalImage(formatName: String = "png"): ExternalImage {
}
}
@Suppress
(
"DEPRECATION_ERROR"
)
@Suppress
(
"DEPRECATION_ERROR"
)
return
ExternalImage
(
digest
.
digest
(),
file
.
inputStream
())
return
ExternalImage
(
file
.
asReusableInput
())
}
}
suspend
inline
fun
BufferedImage
.
suspendToExternalImage
():
ExternalImage
=
withContext
(
IO
)
{
toExternalImage
()
}
suspend
inline
fun
BufferedImage
.
suspendToExternalImage
():
ExternalImage
=
withContext
(
IO
)
{
toExternalImage
()
}
...
@@ -76,8 +77,7 @@ suspend inline fun BufferedImage.suspendToExternalImage(): ExternalImage = withC
...
@@ -76,8 +77,7 @@ suspend inline fun BufferedImage.suspendToExternalImage(): ExternalImage = withC
fun
File
.
toExternalImage
():
ExternalImage
{
fun
File
.
toExternalImage
():
ExternalImage
{
@Suppress
(
"DEPRECATION_ERROR"
)
@Suppress
(
"DEPRECATION_ERROR"
)
return
ExternalImage
(
return
ExternalImage
(
md5
=
this
.
inputStream
().
md5
(),
// dont change
input
=
this
.
asReusableInput
()
input
=
this
.
inputStream
()
)
)
}
}
...
...
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/utils/internal/asReusableInput.jvm.kt
View file @
6b5cbc3d
package
net.mamoe.mirai.utils.internal
package
net.mamoe.mirai.utils.internal
import
kotlinx.coroutines.io.ByteReadChannel
import
io.ktor.utils.io.ByteWriteChannel
import
kotlinx.io.core.Input
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.flow.Flow
import
kotlinx.coroutines.withContext
import
net.mamoe.mirai.message.data.toLongUnsigned
import
net.mamoe.mirai.utils.ExternalImage
import
net.mamoe.mirai.utils.ExternalImage
import
java.io.File
import
java.io.InputStream
import
java.io.InputStream
internal
actual
fun
ByteReadChannel
.
asReusableInput
():
ExternalImage
.
ReusableInput
{
internal
actual
fun
ByteArray
.
asReusableInput
():
ExternalImage
.
ReusableInput
{
TODO
(
"Not yet implemented"
)
return
object
:
ExternalImage
.
ReusableInput
{
override
val
md5
:
ByteArray
=
md5
()
override
val
size
:
Long
get
()
=
this
@
asReusableInput
.
size
.
toLongUnsigned
()
override
fun
chunkedFlow
(
sizePerPacket
:
Int
):
ChunkedFlowSession
<
ChunkedInput
>
{
return
object
:
ChunkedFlowSession
<
ChunkedInput
>
{
override
val
flow
:
Flow
<
ChunkedInput
>
=
inputStream
().
chunkedFlow
(
sizePerPacket
)
override
fun
close
()
{
// nothing to do
}
}
}
override
suspend
fun
writeTo
(
out
:
ByteWriteChannel
):
Long
{
out
.
writeFully
(
this
@
asReusableInput
,
0
,
this
@
asReusableInput
.
size
)
out
.
flush
()
return
this
@
asReusableInput
.
size
.
toLongUnsigned
()
}
}
}
}
internal
actual
fun
Input
.
asReusableInput
():
ExternalImage
.
ReusableInput
{
internal
fun
File
.
asReusableInput
():
ExternalImage
.
ReusableInput
{
TODO
(
"Not yet implemented"
)
return
object
:
ExternalImage
.
ReusableInput
{
override
val
md5
:
ByteArray
=
inputStream
().
use
{
it
.
md5
()
}
override
val
size
:
Long
get
()
=
length
()
override
fun
chunkedFlow
(
sizePerPacket
:
Int
):
ChunkedFlowSession
<
ChunkedInput
>
{
val
stream
=
inputStream
()
return
object
:
ChunkedFlowSession
<
ChunkedInput
>
{
override
val
flow
:
Flow
<
ChunkedInput
>
=
stream
.
chunkedFlow
(
sizePerPacket
)
override
fun
close
()
=
stream
.
close
()
}
}
override
suspend
fun
writeTo
(
out
:
ByteWriteChannel
):
Long
{
return
inputStream
().
use
{
it
.
copyTo
(
out
)
}
}
}
}
}
internal
actual
fun
InputStream
.
asReusableInput
():
ExternalImage
.
ReusableInput
{
TODO
(
"Not yet implemented"
)
private
suspend
fun
InputStream
.
copyTo
(
out
:
ByteWriteChannel
):
Long
=
withContext
(
Dispatchers
.
IO
)
{
var
bytesCopied
:
Long
=
0
ByteArrayPool
.
useInstance
{
buffer
->
var
bytes
=
read
(
buffer
)
while
(
bytes
>=
0
)
{
out
.
writeFully
(
buffer
,
0
,
bytes
)
bytesCopied
+=
bytes
bytes
=
read
(
buffer
)
}
}
out
.
flush
()
return
@
withContext
bytesCopied
}
}
\ 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