Redesign MessageChain:

Make MessageChain immutable
Introduce CombinedMessage, a left-biased list
Introduce MessageChainBuilder
This commit is contained in:
Him188 2020-02-23 16:47:21 +08:00
parent 125bfd3245
commit ccc6826e3a
17 changed files with 344 additions and 191 deletions

View File

@ -97,7 +97,7 @@ fun MessageChain.toDTOChain() = mutableListOf(this[MessageSource].toDTO()).apply
}
fun MessageChainDTO.toMessageChain(contact: Contact) =
MessageChain().apply { this@toMessageChain.forEach { add(it.toMessage(contact)) } }
buildMessageChain { this@toMessageChain.forEach { add(it.toMessage(contact)) } }
@UseExperimental(ExperimentalUnsignedTypes::class)
fun Message.toDTO() = when (this) {

View File

@ -147,6 +147,7 @@ internal class QQImpl(
}
} finally {
(image.input as? Closeable)?.close()
(image.input as? io.ktor.utils.io.core.Closeable)?.close()
}
@MiraiExperimentalAPI
@ -644,7 +645,8 @@ internal class GroupImpl(
}
}
} finally {
(image.input as Closeable)?.close()
(image.input as? Closeable)?.close()
(image.input as? io.ktor.utils.io.core.Closeable)?.close()
}
override fun toString(): String {

View File

@ -326,10 +326,10 @@ internal class NotOnlineImageFromServer(
internal fun MsgComm.Msg.toMessageChain(): MessageChain {
val elements = this.msgBody.richText.elems
val message = MessageChain(initialCapacity = elements.size + 1)
val message = ArrayList<Message>(elements.size + 1)
message.add(MessageSourceFromMsg(delegate = this))
elements.joinToMessageChain(message)
return message
return message.asMessageChain()
}
// These two functions are not the same.
@ -338,15 +338,15 @@ internal fun MsgComm.Msg.toMessageChain(): MessageChain {
internal fun ImMsgBody.SourceMsg.toMessageChain(): MessageChain {
val elements = this.elems!!
val message = MessageChain(initialCapacity = elements.size + 1)
val message = ArrayList<Message>(elements.size + 1)
message.add(MessageSourceFromServer(delegate = this))
elements.joinToMessageChain(message)
return message
return message.asMessageChain()
}
@UseExperimental(MiraiInternalAPI::class, ExperimentalUnsignedTypes::class, MiraiDebugAPI::class)
internal fun List<ImMsgBody.Elem>.joinToMessageChain(message: MessageChain) {
internal fun List<ImMsgBody.Elem>.joinToMessageChain(message: MutableList<Message>) {
this.forEach {
when {
it.srcMsg != null -> message.add(QuoteReply(MessageSourceFromServer(it.srcMsg)))

View File

@ -86,25 +86,32 @@ object Highway {
writeFully(head)
when (body) {
is ByteReadPacket -> writePacket(body)
is Input -> ByteArrayPool.useInstance { buffer ->
is Input -> body.use {
ByteArrayPool.useInstance { buffer ->
var size: Int
while (body.readAvailable(buffer).also { size = it } != 0) {
this@buildPacket.writeFully(buffer, 0, size)
}
}
}
is ByteReadChannel -> ByteArrayPool.useInstance { buffer ->
var size: Int
while (body.readAvailable(buffer, 0, buffer.size).also { size = it } != 0) {
this@buildPacket.writeFully(buffer, 0, size)
}
}
is InputStream -> ByteArrayPool.useInstance { buffer ->
is InputStream -> try {
ByteArrayPool.useInstance { buffer ->
var size: Int
while (body.read(buffer).also { size = it } != 0) {
this@buildPacket.writeFully(buffer, 0, size)
}
}
} finally {
body.close()
}
}
writeByte(41)
}
}

View File

@ -26,7 +26,6 @@ import net.mamoe.mirai.event.subscribingGetAsync
import net.mamoe.mirai.message.FriendMessage
import net.mamoe.mirai.message.data.MessageChain
import net.mamoe.mirai.message.data.MessageSource
import net.mamoe.mirai.message.data.addOrRemove
import net.mamoe.mirai.qqandroid.GroupImpl
import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.io.serialization.decodeUniPacket
@ -398,8 +397,6 @@ internal class MessageSvc {
msgVia = 1
)
)
message.addOrRemove(source)
}
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response {

View File

@ -139,3 +139,8 @@ suspend inline fun <C : Contact> C.sendMessage(message: Message): MessageReceipt
* @see Contact.sendMessage
*/
suspend inline fun <C : Contact> C.sendMessage(plain: String): MessageReceipt<C> = sendMessage(plain.toMessage())
/**
* @see Contact.sendMessage
*/
suspend inline fun <C : Contact> C.sendMessage(plain: CombinedMessage): MessageReceipt<C> = sendMessage(MessageChain(plain as Message)) as MessageReceipt<C>

View File

@ -15,6 +15,7 @@ import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.QQ
import net.mamoe.mirai.contact.sendMessage
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.recallIn
import net.mamoe.mirai.utils.MiraiExperimentalAPI

View File

@ -36,7 +36,7 @@ class At @MiraiInternalAPI constructor(val target: Long, val display: String) :
// 自动为消息补充 " "
override fun followedBy(tail: Message): MessageChain {
override fun followedBy(tail: Message): CombinedMessage {
if (tail is PlainText && tail.stringValue.startsWith(' ')) {
return super.followedBy(tail)
}

View File

@ -27,7 +27,7 @@ object AtAll : Message, Message.Key<AtAll> {
// 自动为消息补充 " "
override fun followedBy(tail: Message): MessageChain {
override fun followedBy(tail: Message): CombinedMessage {
if (tail is PlainText && tail.stringValue.startsWith(' ')) {
return super.followedBy(tail)
}

View File

@ -0,0 +1,41 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.message.data
/**
* Left-biased list
*/
class CombinedMessage(
val left: Message,
val element: Message
) : Iterable<Message>, Message {
private suspend fun SequenceScope<Message>.yieldCombinedOrElements(message: Message) {
when (message) {
is CombinedMessage -> {
yieldCombinedOrElements(message.element)
yieldCombinedOrElements(message.left)
}
is MessageChain -> message.forEach { yieldCombinedOrElements(it) }
else -> yield(message)
}
}
fun asSequence(): Sequence<Message> = sequence {
yieldCombinedOrElements(this@CombinedMessage)
}
override fun iterator(): Iterator<Message> {
return asSequence().iterator()
}
override fun toString(): String {
return left.toString() + element.toString()
}
}

View File

@ -85,20 +85,19 @@ interface Message {
* ```
*/
@JvmSynthetic // in java they should use `plus` instead
fun followedBy(tail: Message): MessageChain {
fun followedBy(tail: Message): CombinedMessage {
require(tail !is SingleOnly) { "SingleOnly Message cannot follow another message" }
require(this !is SingleOnly) { "SingleOnly Message cannot be followed" }
return if (tail is MessageChain) tail.followedBy(this)/*MessageChainImpl(this).also { tail.forEach { child -> it.concat(child) } }*/
else MessageChainImpl(this, tail)
return CombinedMessage(tail, this)
}
override fun toString(): String
operator fun plus(another: Message): MessageChain = this.followedBy(another)
operator fun plus(another: Message): CombinedMessage = this.followedBy(another)
operator fun plus(another: String): MessageChain = this.followedBy(another.toMessage())
operator fun plus(another: String): CombinedMessage = this.followedBy(another.toMessage())
// `+ ""` will be resolved to `plus(String)` instead of `plus(CharSeq)`
operator fun plus(another: CharSequence): MessageChain = this.followedBy(another.toString().toMessage())
operator fun plus(another: CharSequence): CombinedMessage = this.followedBy(another.toString().toMessage())
}
/**

View File

@ -16,8 +16,6 @@ import net.mamoe.mirai.message.data.NullMessageChain.toString
import kotlin.js.JsName
import kotlin.jvm.JvmMultifileClass
import kotlin.jvm.JvmName
import kotlin.jvm.JvmSynthetic
import kotlin.jvm.Volatile
import kotlin.reflect.KProperty
/**
@ -31,24 +29,14 @@ import kotlin.reflect.KProperty
* - 若一个 [Message] 与一个 [MessageChain] 连接, [Message] 将会被添加入 [MessageChain].
*
* 要获取更多信息, 请查看 [Message]
*
* @see buildMessageChain
*/
interface MessageChain : Message, MutableList<Message> {
interface MessageChain : Message, List<Message> {
// region Message override
override operator fun contains(sub: String): Boolean
override fun followedBy(tail: Message): MessageChain
// endregion
@JvmSynthetic
operator fun plusAssign(message: Message) {
this.followedBy(message)
}
@JvmSynthetic // make java user happier
operator fun plusAssign(plain: String) {
this.plusAssign(plain.toMessage())
}
override fun toString(): String
/**
@ -59,15 +47,6 @@ interface MessageChain : Message, MutableList<Message> {
operator fun <M : Message> get(key: Message.Key<M>): M = first(key)
}
/**
* 先删除同类型的消息, 再添加 [message]
*/
fun <T : Message> MessageChain.addOrRemove(message: T) {
val clazz = message::class
this.removeAll { clazz.isInstance(it) }
this.add(message)
}
/**
* 遍历每一个有内容的消息, [At], [AtAll], [PlainText], [Image], [Face], [XMLMessage]
*/
@ -109,17 +88,7 @@ inline operator fun <reified T : Message> MessageChain.getValue(thisRef: Any?, p
@JvmName("newChain")
@JsName("newChain")
@Suppress("FunctionName")
fun MessageChain(): MessageChain = EmptyMessageChain()
/**
* 构造无初始元素的可修改的 [MessageChain]. 初始大小将会被设定为 [initialCapacity]
*/
@JvmName("newChain")
@JsName("newChain")
@Suppress("FunctionName")
fun MessageChain(initialCapacity: Int): MessageChain =
if (initialCapacity == 0) EmptyMessageChain()
else MessageChainImpl(ArrayList(initialCapacity))
fun MessageChain(): MessageChain = EmptyMessageChain
/**
* 构造 [MessageChain]
@ -129,7 +98,7 @@ fun MessageChain(initialCapacity: Int): MessageChain =
@JsName("newChain")
@Suppress("FunctionName")
fun MessageChain(vararg messages: Message): MessageChain =
if (messages.isEmpty()) EmptyMessageChain()
if (messages.isEmpty()) EmptyMessageChain
else MessageChainImpl(messages.toMutableList())
/**
@ -140,7 +109,7 @@ fun MessageChain(vararg messages: Message): MessageChain =
@JsName("newChain")
@Suppress("FunctionName")
fun MessageChain(message: Message): MessageChain =
MessageChainImpl(mutableListOf(message))
MessageChainImpl(listOf(message))
/**
* 构造 [MessageChain]
@ -149,7 +118,16 @@ fun MessageChain(message: Message): MessageChain =
@JsName("newChain")
@Suppress("FunctionName")
fun MessageChain(messages: Iterable<Message>): MessageChain =
MessageChainImpl(messages.toMutableList())
MessageChainImpl(messages.toList())
/**
* 构造 [MessageChain]
*/
@JvmName("newChain")
@JsName("newChain")
@Suppress("FunctionName")
fun MessageChain(messages: List<Message>): MessageChain =
MessageChainImpl(messages)
/**
@ -164,7 +142,7 @@ inline fun Message.toChain(): MessageChain = if (this is MessageChain) this else
* 构造 [MessageChain]
*/
@Suppress("unused", "NOTHING_TO_INLINE")
inline fun List<Message>.toMessageChain(): MessageChain = MessageChain(this)
inline fun List<Message>.asMessageChain(): MessageChain = MessageChain(this)
/**
* 获取第一个 [M] 类型的 [Message] 实例
@ -211,71 +189,9 @@ fun <M : Message> MessageChain.first(key: Message.Key<M>): M = firstOrNull(key)
@Suppress("UNCHECKED_CAST")
fun <M : Message> MessageChain.any(key: Message.Key<M>): Boolean = firstOrNull(key) != null
/**
* 空的 [Message].
*
* 它不包含任何元素, 但维护一个 'lazy' [MessageChainImpl].
*
* 只有在必要的时候(如迭代([iterator]), 插入([add]), 连接([followedBy], [plus], [plusAssign]))才会创建这个对象代表的 list
*
* 它是一个正常的 [Message] [MessageChain]. 可以做所有 [Message] 能做的事.
*/
class EmptyMessageChain : MessageChain {
private val delegate: MessageChain by lazy {
MessageChainImpl().also { initialized = true }
}
@Volatile
private var initialized: Boolean = false
override fun subList(fromIndex: Int, toIndex: Int): MutableList<Message> =
if (initialized) delegate.subList(
fromIndex,
toIndex
) else throw IndexOutOfBoundsException("given args that from $fromIndex to $toIndex, but the list is empty")
override fun toString(): String = if (initialized) delegate.toString() else ""
override fun contains(sub: String): Boolean = if (initialized) delegate.contains(sub) else false
override fun contains(element: Message): Boolean = if (initialized) delegate.contains(element) else false
override fun followedBy(tail: Message): MessageChain = delegate.followedBy(tail)
override val size: Int = if (initialized) delegate.size else 0
override fun containsAll(elements: Collection<Message>): Boolean =
if (initialized) delegate.containsAll(elements) else false
override fun get(index: Int): Message =
if (initialized) delegate[index] else throw IndexOutOfBoundsException(index.toString())
override fun indexOf(element: Message): Int = if (initialized) delegate.indexOf(element) else -1
override fun isEmpty(): Boolean = if (initialized) delegate.isEmpty() else true
override fun iterator(): MutableIterator<Message> = delegate.iterator()
override fun lastIndexOf(element: Message): Int = if (initialized) delegate.lastIndexOf(element) else -1
override fun add(element: Message): Boolean = delegate.add(element)
override fun add(index: Int, element: Message) = delegate.add(index, element)
override fun addAll(index: Int, elements: Collection<Message>): Boolean = delegate.addAll(elements)
override fun addAll(elements: Collection<Message>): Boolean = delegate.addAll(elements)
override fun clear() {
if (initialized) delegate.clear()
}
override fun listIterator(): MutableListIterator<Message> = delegate.listIterator()
override fun listIterator(index: Int): MutableListIterator<Message> = delegate.listIterator()
override fun remove(element: Message): Boolean = if (initialized) delegate.remove(element) else false
override fun removeAll(elements: Collection<Message>): Boolean =
if (initialized) delegate.removeAll(elements) else false
override fun removeAt(index: Int): Message =
if (initialized) delegate.removeAt(index) else throw IndexOutOfBoundsException(index.toString())
override fun retainAll(elements: Collection<Message>): Boolean =
if (initialized) delegate.retainAll(elements) else false
override fun set(index: Int, element: Message): Message =
if (initialized) delegate.set(index, element) else throw IndexOutOfBoundsException(index.toString())
}
object EmptyMessageChain : MessageChain by {
MessageChainImpl(emptyList())
}()
/**
* Null [MessageChain].
@ -290,7 +206,7 @@ object NullMessageChain : MessageChain {
override fun contains(sub: String): Boolean = error("accessing NullMessageChain")
override fun contains(element: Message): Boolean = error("accessing NullMessageChain")
override fun followedBy(tail: Message): MessageChain = tail.toChain()
override fun followedBy(tail: Message): CombinedMessage = CombinedMessage(left = EmptyMessageChain, element = tail)
override val size: Int get() = error("accessing NullMessageChain")
override fun containsAll(elements: Collection<Message>): Boolean = error("accessing NullMessageChain")
@ -300,76 +216,29 @@ object NullMessageChain : MessageChain {
override fun iterator(): MutableIterator<Message> = error("accessing NullMessageChain")
override fun lastIndexOf(element: Message): Int = error("accessing NullMessageChain")
override fun add(element: Message): Boolean = error("accessing NullMessageChain")
override fun add(index: Int, element: Message) = error("accessing NullMessageChain")
override fun addAll(index: Int, elements: Collection<Message>): Boolean = error("accessing NullMessageChain")
override fun addAll(elements: Collection<Message>): Boolean = error("accessing NullMessageChain")
override fun clear() {
error("accessing NullMessageChain")
}
override fun listIterator(): MutableListIterator<Message> = error("accessing NullMessageChain")
override fun listIterator(index: Int): MutableListIterator<Message> = error("accessing NullMessageChain")
override fun remove(element: Message): Boolean = error("accessing NullMessageChain")
override fun removeAll(elements: Collection<Message>): Boolean = error("accessing NullMessageChain")
override fun removeAt(index: Int): Message = error("accessing NullMessageChain")
override fun retainAll(elements: Collection<Message>): Boolean = error("accessing NullMessageChain")
override fun set(index: Int, element: Message): Message = error("accessing NullMessageChain")
}
/**
* [MessageChain] 实现
* 它是一个特殊的 [Message], 实现 [MutableList] 接口, 但将所有的接口调用都转到内部维护的另一个 [MutableList].
*/
internal inline class MessageChainImpl constructor(
internal class MessageChainImpl constructor(
/**
* Elements will not be instances of [MessageChain]
*/
private val delegate: MutableList<Message>
) : Message, MutableList<Message>, // do not `by delegate`, bcz Inline class cannot implement an interface by delegation
MessageChain {
constructor(vararg messages: Message) : this(messages.toMutableList())
// region Message override
private val delegate: List<Message>
) : Message, List<Message> by delegate, MessageChain {
override fun toString(): String = this.delegate.joinToString("") { it.toString() }
override operator fun contains(sub: String): Boolean = delegate.any { it.contains(sub) }
override fun followedBy(tail: Message): MessageChain {
override fun followedBy(tail: Message): CombinedMessage {
require(tail !is SingleOnly) { "SingleOnly Message cannot follow another message" }
if (tail is MessageChain) tail.forEach { child -> this.followedBy(child) }
else this.delegate.add(tail)
return this
}
// endregion
// region MutableList override
override fun containsAll(elements: Collection<Message>): Boolean = delegate.containsAll(elements)
override operator fun get(index: Int): Message = delegate[index]
override fun indexOf(element: Message): Int = delegate.indexOf(element)
override fun isEmpty(): Boolean = delegate.isEmpty()
override fun lastIndexOf(element: Message): Int = delegate.lastIndexOf(element)
override fun add(element: Message): Boolean = delegate.add(element)
override fun add(index: Int, element: Message) = delegate.add(index, element)
override fun addAll(index: Int, elements: Collection<Message>): Boolean = delegate.addAll(index, elements)
override fun addAll(elements: Collection<Message>): Boolean = delegate.addAll(elements)
override fun clear() = delegate.clear()
override fun listIterator(): MutableListIterator<Message> = delegate.listIterator()
override fun listIterator(index: Int): MutableListIterator<Message> = delegate.listIterator(index)
override fun remove(element: Message): Boolean = delegate.remove(element)
override fun removeAll(elements: Collection<Message>): Boolean = delegate.removeAll(elements)
override fun removeAt(index: Int): Message = delegate.removeAt(index)
override fun retainAll(elements: Collection<Message>): Boolean = delegate.retainAll(elements)
override fun set(index: Int, element: Message): Message = delegate.set(index, element)
override fun subList(fromIndex: Int, toIndex: Int): MutableList<Message> = delegate.subList(fromIndex, toIndex)
override operator fun iterator(): MutableIterator<Message> = delegate.iterator()
override operator fun contains(element: Message): Boolean = delegate.contains(element)
override val size: Int get() = delegate.size
// endregion
// if (tail is MessageChain) tail.forEach { child -> this.followedBy(child) }
// else this.delegate.add(tail)
return CombinedMessage(tail, this)
}
}

View File

@ -14,6 +14,7 @@ package net.mamoe.mirai.message.data
import kotlin.jvm.JvmMultifileClass
import kotlin.jvm.JvmName
import kotlin.jvm.JvmStatic
/**
* 纯文本. 可含 emoji 表情.
@ -21,10 +22,17 @@ import kotlin.jvm.JvmName
* 一般不需要主动构造 [PlainText], [Message] 可直接与 [String] 相加. Java 用户请使用 [MessageChain.plus]
*/
inline class PlainText(val stringValue: String) : Message {
constructor(charSequence: CharSequence) : this(charSequence.toString())
override operator fun contains(sub: String): Boolean = sub in stringValue
override fun toString(): String = stringValue
companion object Key : Message.Key<PlainText>
companion object Key : Message.Key<PlainText> {
@JvmStatic
val Empty = PlainText("")
@JvmStatic
val Null = PlainText("null")
}
}
/**

View File

@ -0,0 +1,70 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.message.data
import kotlin.jvm.JvmOverloads
/**
* 构造一个 [MessageChain]
*
* @see MessageChainBuilder
*/
inline fun buildMessageChain(block: MessageChainBuilder.() -> Unit): MessageChain {
return MessageChainBuilder().apply(block).asMessageChain()
}
class MessageChainBuilder @JvmOverloads constructor(
private val container: MutableList<Message> = mutableListOf()
) : MutableList<Message> by container, Appendable {
operator fun Message.unaryPlus() {
add(this)
}
operator fun String.unaryPlus() {
add(this.toMessage())
}
operator fun plusAssign(plain: String) {
this.add(plain.toMessage())
}
operator fun plusAssign(message: Message) {
this.add(message)
}
fun add(plain: String) {
this.add(plain.toMessage())
}
operator fun plusAssign(charSequence: CharSequence) {
this.add(PlainText(charSequence))
}
override fun append(c: Char): Appendable = apply {
this.add(PlainText(c.toString()))
}
override fun append(csq: CharSequence?): Appendable = apply {
when {
csq == null -> this.add(PlainText.Null)
csq.isEmpty() -> this.add(PlainText.Empty)
else -> this.add(PlainText(csq))
}
}
override fun append(csq: CharSequence?, start: Int, end: Int): Appendable = apply {
when {
csq == null -> this.add(PlainText.Null)
csq.isEmpty() -> this.add(PlainText.Empty)
else -> this.add(PlainText(csq.substring(start, end)))
}
}
}

View File

@ -0,0 +1,155 @@
package net.mamoe.mirai.message.data
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.time.ExperimentalTime
import kotlin.time.measureTime
internal class CombinedMessageTest {
@Test
fun testAsSequence() {
var message: Message = "Hello ".toMessage()
message += "World"
assertEquals(
"Hello World",
(message as CombinedMessage).asSequence().joinToString(separator = "")
)
}
@Test
fun testAsSequence2() {
var message: Message = "Hello ".toMessage()
message += MessageChain(
PlainText("W"),
PlainText("o"),
PlainText("r") + PlainText("ld")
)
assertEquals(
"Hello World",
(message as CombinedMessage).asSequence().joinToString(separator = "")
)
}
private val toAdd = "1".toMessage()
@UseExperimental(ExperimentalTime::class)
@Test
fun speedTest() = repeat(100) {
var count = 1L
repeat(Int.MAX_VALUE) {
count++
}
var combineMessage: Message = toAdd
println(
"init combine ok " + measureTime {
repeat(1000) {
combineMessage += toAdd
}
}.inMilliseconds
)
val list = mutableListOf<Message>()
println(
"init messageChain ok " + measureTime {
repeat(1000) {
list += toAdd
}
}.inMilliseconds
)
measureTime {
list.joinToString(separator = "")
}.let { time ->
println("list foreach: ${time.inMilliseconds} ms")
}
measureTime {
(combineMessage as CombinedMessage).iterator().joinToString(separator = "")
}.let { time ->
println("combined iterate: ${time.inMilliseconds} ms")
}
measureTime {
(combineMessage as CombinedMessage).asSequence().joinToString(separator = "")
}.let { time ->
println("combined sequence: ${time.inMilliseconds} ms")
}
repeat(5) {
println()
}
}
@UseExperimental(ExperimentalTime::class)
@Test
fun testFastIteration() {
println("start!")
println("start!")
println("start!")
println("start!")
var combineMessage: Message = toAdd
println(
"init combine ok " + measureTime {
repeat(1000) {
combineMessage += toAdd
}
}.inMilliseconds
)
measureTime {
(combineMessage as CombinedMessage).iterator().joinToString(separator = "")
}.let { time ->
println("combine: ${time.inMilliseconds} ms")
}
}
}
public fun <T> Iterator<T>.joinToString(
separator: CharSequence = ", ",
prefix: CharSequence = "",
postfix: CharSequence = "",
limit: Int = -1,
truncated: CharSequence = "...",
transform: ((T) -> CharSequence)? = null
): String {
return joinTo(StringBuilder(), separator, prefix, postfix, limit, truncated, transform).toString()
}
public fun <T, A : Appendable> Iterator<T>.joinTo(
buffer: A,
separator: CharSequence = ", ",
prefix: CharSequence = "",
postfix: CharSequence = "",
limit: Int = -1,
truncated: CharSequence = "...",
transform: ((T) -> CharSequence)? = null
): A {
buffer.append(prefix)
var count = 0
for (element in this) {
if (++count > 1) buffer.append(separator)
if (limit < 0 || count <= limit) {
buffer.appendElement(element, transform)
} else break
}
if (limit >= 0 && count > limit) buffer.append(truncated)
buffer.append(postfix)
return buffer
}
internal fun <T> Appendable.appendElement(element: T, transform: ((T) -> CharSequence)?) {
when {
transform != null -> append(transform(element))
element is CharSequence? -> append(element)
element is Char -> append(element)
else -> append(element.toString())
}
}

View File

@ -68,14 +68,12 @@ fun File.toExternalImage(): ExternalImage {
?: error("Unable to read file(path=${this.path}), no ImageReader found")
image.input = input
val inputStream = this.inputStream()
return ExternalImage(
width = image.getWidth(0),
height = image.getHeight(0),
md5 = this.inputStream().md5(), // dont change
imageFormat = image.formatName,
input = inputStream.asInput(),
inputSize = inputStream.available().toLong(),
input = this.inputStream(),
filename = this.name
)
}

View File

@ -58,6 +58,7 @@ internal class LockFreeLinkedListTest {
@Test
fun `so many concurrent add remove and foreach`() = runBlocking {
return@runBlocking // 测试通过了, 加快速度. 因为 kotlin 一些其他 bug
val list = LockFreeLinkedList<Int>()
val addJob = async { list.concurrentDo(2, 30000) { addLast(1) } }