2020-12-26 17:36:00 +08:00
|
|
|
/*
|
|
|
|
* Copyright 2019-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
|
|
|
|
*/
|
|
|
|
|
|
|
|
@file:JvmMultifileClass
|
|
|
|
@file:JvmName("MiraiUtils")
|
2020-12-26 20:05:10 +08:00
|
|
|
@file:Suppress("NOTHING_TO_INLINE")
|
2020-12-26 17:36:00 +08:00
|
|
|
|
|
|
|
package net.mamoe.mirai.utils
|
|
|
|
|
2020-12-26 20:05:10 +08:00
|
|
|
import kotlinx.io.charsets.Charset
|
|
|
|
import kotlinx.io.core.ByteReadPacket
|
|
|
|
import java.util.*
|
|
|
|
import kotlin.contracts.InvocationKind
|
|
|
|
import kotlin.contracts.contract
|
|
|
|
|
2020-12-26 17:36:00 +08:00
|
|
|
|
|
|
|
@JvmOverloads
|
|
|
|
public fun generateImageId(md5: ByteArray, format: String = "mirai"): String {
|
|
|
|
return """{${generateUUID(md5)}}.$format"""
|
|
|
|
}
|
|
|
|
|
|
|
|
public fun generateUUID(md5: ByteArray): String {
|
|
|
|
return "${md5[0, 3]}-${md5[4, 5]}-${md5[6, 7]}-${md5[8, 9]}-${md5[10, 15]}"
|
|
|
|
}
|
|
|
|
|
|
|
|
@JvmSynthetic
|
2020-12-26 20:05:10 +08:00
|
|
|
public operator fun ByteArray.get(rangeStart: Int, rangeEnd: Int): String = buildString {
|
2020-12-26 17:36:00 +08:00
|
|
|
for (it in rangeStart..rangeEnd) {
|
|
|
|
append(this@get[it].fixToString())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun Byte.fixToString(): String {
|
|
|
|
return when (val b = this.toInt() and 0xff) {
|
|
|
|
in 0..15 -> "0${this.toString(16).toUpperCase()}"
|
|
|
|
else -> b.toString(16).toUpperCase()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@OptIn(ExperimentalUnsignedTypes::class)
|
|
|
|
@JvmOverloads
|
|
|
|
@Suppress("DuplicatedCode") // false positive. foreach is not common to UByteArray and ByteArray
|
|
|
|
public fun ByteArray.toUHexString(
|
|
|
|
separator: String = " ",
|
|
|
|
offset: Int = 0,
|
|
|
|
length: Int = this.size - offset
|
|
|
|
): String {
|
|
|
|
this.checkOffsetAndLength(offset, length)
|
|
|
|
if (length == 0) {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
val lastIndex = offset + length
|
|
|
|
return buildString(length * 2) {
|
|
|
|
this@toUHexString.forEachIndexed { index, it ->
|
|
|
|
if (index in offset until lastIndex) {
|
|
|
|
var ret = it.toUByte().toString(16).toUpperCase()
|
|
|
|
if (ret.length == 1) ret = "0$ret"
|
|
|
|
append(ret)
|
|
|
|
if (index < lastIndex - 1) append(separator)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public fun ByteArray.checkOffsetAndLength(offset: Int, length: Int) {
|
|
|
|
require(offset >= 0) { "offset shouldn't be negative: $offset" }
|
|
|
|
require(length >= 0) { "length shouldn't be negative: $length" }
|
|
|
|
require(offset + length <= this.size) { "offset ($offset) + length ($length) > array.size (${this.size})" }
|
2020-12-26 20:05:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@JvmOverloads
|
|
|
|
@Suppress("DuplicatedCode") // false positive. foreach is not common to UByteArray and ByteArray
|
|
|
|
public fun List<Byte>.toUHexString(separator: String = " ", offset: Int = 0, length: Int = this.size - offset): String {
|
|
|
|
require(offset >= 0) { "offset shouldn't be negative: $offset" }
|
|
|
|
require(length >= 0) { "length shouldn't be negative: $length" }
|
|
|
|
require(offset + length <= this.size) { "offset ($offset) + length ($length) > array.size (${this.size})" }
|
|
|
|
|
|
|
|
if (length == 0) {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
val lastIndex = offset + length
|
|
|
|
return buildString(length * 2) {
|
|
|
|
this@toUHexString.forEachIndexed { index, it ->
|
|
|
|
if (index in offset until lastIndex) {
|
|
|
|
var ret = it.toUByte().toString(16).toUpperCase()
|
|
|
|
if (ret.length == 1) ret = "0$ret"
|
|
|
|
append(ret)
|
|
|
|
if (index < lastIndex - 1) append(separator)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@JvmSynthetic
|
|
|
|
@Suppress("DuplicatedCode") // false positive. foreach is not common to UByteArray and ByteArray
|
|
|
|
@ExperimentalUnsignedTypes
|
|
|
|
public fun UByteArray.toUHexString(separator: String = " ", offset: Int = 0, length: Int = this.size - offset): String {
|
|
|
|
if (length == 0) {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
val lastIndex = offset + length
|
|
|
|
return buildString(length * 2) {
|
|
|
|
this@toUHexString.forEachIndexed { index, it ->
|
|
|
|
if (index in offset until lastIndex) {
|
|
|
|
var ret = it.toByte().toUByte().toString(16).toUpperCase()
|
|
|
|
if (ret.length == 1) ret = "0$ret"
|
|
|
|
append(ret)
|
|
|
|
if (index < lastIndex - 1) append(separator)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public inline fun ByteArray.encodeToString(offset: Int = 0, charset: Charset = Charsets.UTF_8): String =
|
|
|
|
kotlinx.io.core.String(this, charset = charset, offset = offset, length = this.size - offset)
|
|
|
|
|
|
|
|
public inline fun ByteArray.encodeToBase64(): String =
|
|
|
|
Base64.getEncoder().encodeToString(this)
|
|
|
|
|
|
|
|
public inline fun ByteArray.toReadPacket(offset: Int = 0, length: Int = this.size - offset): ByteReadPacket =
|
|
|
|
ByteReadPacket(this, offset = offset, length = length)
|
|
|
|
|
|
|
|
public inline fun <R> ByteArray.read(t: ByteReadPacket.() -> R): R {
|
|
|
|
contract {
|
|
|
|
callsInPlace(t, InvocationKind.EXACTLY_ONCE)
|
|
|
|
}
|
|
|
|
return this.toReadPacket().withUse(t)
|
2020-12-26 17:36:00 +08:00
|
|
|
}
|