Introduce HexFormat for formatting and parsing hexadecimals #KT-57762
Merge-request: KT-MR-9460 Merged-by: Abduqodiri Qurbonzoda <abduqodiri.qurbonzoda@jetbrains.com>
This commit is contained in:
committed by
Space Team
parent
5ef0b52ac5
commit
63a5a74613
@@ -8,6 +8,11 @@ public val kotlin.CharSequence.indices: kotlin.ranges.IntRange { get; }
|
||||
|
||||
public val kotlin.CharSequence.lastIndex: kotlin.Int { get; }
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun HexFormat(builderAction: kotlin.text.HexFormat.Builder.() -> kotlin.Unit): kotlin.text.HexFormat
|
||||
|
||||
@kotlin.SinceKotlin(version = "1.2")
|
||||
@kotlin.Deprecated(message = "Use CharArray.concatToString() instead", replaceWith = kotlin.ReplaceWith(expression = "chars.concatToString()", imports = {}))
|
||||
@kotlin.DeprecatedSinceKotlin(errorSince = "1.5", warningSince = "1.4")
|
||||
@@ -355,6 +360,52 @@ public inline fun <K> kotlin.CharSequence.groupingBy(crossinline keySelector: (k
|
||||
|
||||
public fun kotlin.CharSequence.hasSurrogatePairAt(index: kotlin.Int): kotlin.Boolean
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
public fun kotlin.String.hexToByte(format: kotlin.text.HexFormat = ...): kotlin.Byte
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
public fun kotlin.String.hexToByteArray(format: kotlin.text.HexFormat = ...): kotlin.ByteArray
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
public fun kotlin.String.hexToInt(format: kotlin.text.HexFormat = ...): kotlin.Int
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
public fun kotlin.String.hexToLong(format: kotlin.text.HexFormat = ...): kotlin.Long
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
public fun kotlin.String.hexToShort(format: kotlin.text.HexFormat = ...): kotlin.Short
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun kotlin.String.hexToUByte(format: kotlin.text.HexFormat = ...): kotlin.UByte
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
@kotlin.ExperimentalUnsignedTypes
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun kotlin.String.hexToUByteArray(format: kotlin.text.HexFormat = ...): kotlin.UByteArray
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun kotlin.String.hexToUInt(format: kotlin.text.HexFormat = ...): kotlin.UInt
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun kotlin.String.hexToULong(format: kotlin.text.HexFormat = ...): kotlin.ULong
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun kotlin.String.hexToUShort(format: kotlin.text.HexFormat = ...): kotlin.UShort
|
||||
|
||||
@kotlin.SinceKotlin(version = "1.3")
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun <C : kotlin.CharSequence, R> C.ifBlank(defaultValue: () -> R): R where C : R
|
||||
@@ -1004,6 +1055,62 @@ public inline fun kotlin.String.toFloatOrNull(): kotlin.Float?
|
||||
|
||||
public fun kotlin.CharSequence.toHashSet(): kotlin.collections.HashSet<kotlin.Char>
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
public fun kotlin.Byte.toHexString(format: kotlin.text.HexFormat = ...): kotlin.String
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
public fun kotlin.ByteArray.toHexString(startIndex: kotlin.Int = ..., endIndex: kotlin.Int = ..., format: kotlin.text.HexFormat = ...): kotlin.String
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
public fun kotlin.ByteArray.toHexString(format: kotlin.text.HexFormat = ...): kotlin.String
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
public fun kotlin.Int.toHexString(format: kotlin.text.HexFormat = ...): kotlin.String
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
public fun kotlin.Long.toHexString(format: kotlin.text.HexFormat = ...): kotlin.String
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
public fun kotlin.Short.toHexString(format: kotlin.text.HexFormat = ...): kotlin.String
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun kotlin.UByte.toHexString(format: kotlin.text.HexFormat = ...): kotlin.String
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
@kotlin.ExperimentalUnsignedTypes
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun kotlin.UByteArray.toHexString(startIndex: kotlin.Int = ..., endIndex: kotlin.Int = ..., format: kotlin.text.HexFormat = ...): kotlin.String
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
@kotlin.ExperimentalUnsignedTypes
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun kotlin.UByteArray.toHexString(format: kotlin.text.HexFormat = ...): kotlin.String
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun kotlin.UInt.toHexString(format: kotlin.text.HexFormat = ...): kotlin.String
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun kotlin.ULong.toHexString(format: kotlin.text.HexFormat = ...): kotlin.String
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun kotlin.UShort.toHexString(format: kotlin.text.HexFormat = ...): kotlin.String
|
||||
|
||||
public fun kotlin.String.toInt(): kotlin.Int
|
||||
|
||||
public fun kotlin.String.toInt(radix: kotlin.Int): kotlin.Int
|
||||
@@ -1332,6 +1439,86 @@ public open class CharacterCodingException : kotlin.Exception {
|
||||
public constructor CharacterCodingException(message: kotlin.String?)
|
||||
}
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
public final class HexFormat {
|
||||
public final val bytes: kotlin.text.HexFormat.BytesHexFormat { get; }
|
||||
|
||||
public final val number: kotlin.text.HexFormat.NumberHexFormat { get; }
|
||||
|
||||
public final val upperCase: kotlin.Boolean { get; }
|
||||
|
||||
public open override fun toString(): kotlin.String
|
||||
|
||||
public final class Builder {
|
||||
public final val bytes: kotlin.text.HexFormat.BytesHexFormat.Builder { get; }
|
||||
|
||||
public final val number: kotlin.text.HexFormat.NumberHexFormat.Builder { get; }
|
||||
|
||||
public final var upperCase: kotlin.Boolean { get; set; }
|
||||
|
||||
@kotlin.internal.InlineOnly
|
||||
public final inline fun bytes(builderAction: kotlin.text.HexFormat.BytesHexFormat.Builder.() -> kotlin.Unit): kotlin.Unit
|
||||
|
||||
@kotlin.internal.InlineOnly
|
||||
public final inline fun number(builderAction: kotlin.text.HexFormat.NumberHexFormat.Builder.() -> kotlin.Unit): kotlin.Unit
|
||||
}
|
||||
|
||||
public final class BytesHexFormat {
|
||||
public final val bytePrefix: kotlin.String { get; }
|
||||
|
||||
public final val byteSeparator: kotlin.String { get; }
|
||||
|
||||
public final val byteSuffix: kotlin.String { get; }
|
||||
|
||||
public final val bytesPerGroup: kotlin.Int { get; }
|
||||
|
||||
public final val bytesPerLine: kotlin.Int { get; }
|
||||
|
||||
public final val groupSeparator: kotlin.String { get; }
|
||||
|
||||
public open override fun toString(): kotlin.String
|
||||
|
||||
public final class Builder {
|
||||
public final var bytePrefix: kotlin.String { get; set; }
|
||||
|
||||
public final var byteSeparator: kotlin.String { get; set; }
|
||||
|
||||
public final var byteSuffix: kotlin.String { get; set; }
|
||||
|
||||
public final var bytesPerGroup: kotlin.Int { get; set; }
|
||||
|
||||
public final var bytesPerLine: kotlin.Int { get; set; }
|
||||
|
||||
public final var groupSeparator: kotlin.String { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
public companion object of HexFormat {
|
||||
public final val Default: kotlin.text.HexFormat { get; }
|
||||
|
||||
public final val UpperCase: kotlin.text.HexFormat { get; }
|
||||
}
|
||||
|
||||
public final class NumberHexFormat {
|
||||
public final val prefix: kotlin.String { get; }
|
||||
|
||||
public final val removeLeadingZeros: kotlin.Boolean { get; }
|
||||
|
||||
public final val suffix: kotlin.String { get; }
|
||||
|
||||
public open override fun toString(): kotlin.String
|
||||
|
||||
public final class Builder {
|
||||
public final var prefix: kotlin.String { get; set; }
|
||||
|
||||
public final var removeLeadingZeros: kotlin.Boolean { get; set; }
|
||||
|
||||
public final var suffix: kotlin.String { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final data class MatchGroup {
|
||||
public constructor MatchGroup(value: kotlin.String)
|
||||
|
||||
|
||||
@@ -8,6 +8,11 @@ public val kotlin.CharSequence.indices: kotlin.ranges.IntRange { get; }
|
||||
|
||||
public val kotlin.CharSequence.lastIndex: kotlin.Int { get; }
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun HexFormat(builderAction: kotlin.text.HexFormat.Builder.() -> kotlin.Unit): kotlin.text.HexFormat
|
||||
|
||||
@kotlin.SinceKotlin(version = "1.2")
|
||||
@kotlin.Deprecated(message = "Use CharArray.concatToString() instead", replaceWith = kotlin.ReplaceWith(expression = "chars.concatToString()", imports = {}))
|
||||
@kotlin.DeprecatedSinceKotlin(errorSince = "1.5", warningSince = "1.4")
|
||||
@@ -355,6 +360,52 @@ public inline fun <K> kotlin.CharSequence.groupingBy(crossinline keySelector: (k
|
||||
|
||||
public fun kotlin.CharSequence.hasSurrogatePairAt(index: kotlin.Int): kotlin.Boolean
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
public fun kotlin.String.hexToByte(format: kotlin.text.HexFormat = ...): kotlin.Byte
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
public fun kotlin.String.hexToByteArray(format: kotlin.text.HexFormat = ...): kotlin.ByteArray
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
public fun kotlin.String.hexToInt(format: kotlin.text.HexFormat = ...): kotlin.Int
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
public fun kotlin.String.hexToLong(format: kotlin.text.HexFormat = ...): kotlin.Long
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
public fun kotlin.String.hexToShort(format: kotlin.text.HexFormat = ...): kotlin.Short
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun kotlin.String.hexToUByte(format: kotlin.text.HexFormat = ...): kotlin.UByte
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
@kotlin.ExperimentalUnsignedTypes
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun kotlin.String.hexToUByteArray(format: kotlin.text.HexFormat = ...): kotlin.UByteArray
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun kotlin.String.hexToUInt(format: kotlin.text.HexFormat = ...): kotlin.UInt
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun kotlin.String.hexToULong(format: kotlin.text.HexFormat = ...): kotlin.ULong
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun kotlin.String.hexToUShort(format: kotlin.text.HexFormat = ...): kotlin.UShort
|
||||
|
||||
@kotlin.SinceKotlin(version = "1.3")
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun <C : kotlin.CharSequence, R> C.ifBlank(defaultValue: () -> R): R where C : R
|
||||
@@ -1004,6 +1055,62 @@ public inline fun kotlin.String.toFloatOrNull(): kotlin.Float?
|
||||
|
||||
public fun kotlin.CharSequence.toHashSet(): kotlin.collections.HashSet<kotlin.Char>
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
public fun kotlin.Byte.toHexString(format: kotlin.text.HexFormat = ...): kotlin.String
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
public fun kotlin.ByteArray.toHexString(startIndex: kotlin.Int = ..., endIndex: kotlin.Int = ..., format: kotlin.text.HexFormat = ...): kotlin.String
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
public fun kotlin.ByteArray.toHexString(format: kotlin.text.HexFormat = ...): kotlin.String
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
public fun kotlin.Int.toHexString(format: kotlin.text.HexFormat = ...): kotlin.String
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
public fun kotlin.Long.toHexString(format: kotlin.text.HexFormat = ...): kotlin.String
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
public fun kotlin.Short.toHexString(format: kotlin.text.HexFormat = ...): kotlin.String
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun kotlin.UByte.toHexString(format: kotlin.text.HexFormat = ...): kotlin.String
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
@kotlin.ExperimentalUnsignedTypes
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun kotlin.UByteArray.toHexString(startIndex: kotlin.Int = ..., endIndex: kotlin.Int = ..., format: kotlin.text.HexFormat = ...): kotlin.String
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
@kotlin.ExperimentalUnsignedTypes
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun kotlin.UByteArray.toHexString(format: kotlin.text.HexFormat = ...): kotlin.String
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun kotlin.UInt.toHexString(format: kotlin.text.HexFormat = ...): kotlin.String
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun kotlin.ULong.toHexString(format: kotlin.text.HexFormat = ...): kotlin.String
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
@kotlin.internal.InlineOnly
|
||||
public inline fun kotlin.UShort.toHexString(format: kotlin.text.HexFormat = ...): kotlin.String
|
||||
|
||||
public fun kotlin.String.toInt(): kotlin.Int
|
||||
|
||||
public fun kotlin.String.toInt(radix: kotlin.Int): kotlin.Int
|
||||
@@ -1332,6 +1439,86 @@ public open class CharacterCodingException : kotlin.Exception {
|
||||
public constructor CharacterCodingException(message: kotlin.String?)
|
||||
}
|
||||
|
||||
@kotlin.ExperimentalStdlibApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
public final class HexFormat {
|
||||
public final val bytes: kotlin.text.HexFormat.BytesHexFormat { get; }
|
||||
|
||||
public final val number: kotlin.text.HexFormat.NumberHexFormat { get; }
|
||||
|
||||
public final val upperCase: kotlin.Boolean { get; }
|
||||
|
||||
public open override fun toString(): kotlin.String
|
||||
|
||||
public final class Builder {
|
||||
public final val bytes: kotlin.text.HexFormat.BytesHexFormat.Builder { get; }
|
||||
|
||||
public final val number: kotlin.text.HexFormat.NumberHexFormat.Builder { get; }
|
||||
|
||||
public final var upperCase: kotlin.Boolean { get; set; }
|
||||
|
||||
@kotlin.internal.InlineOnly
|
||||
public final inline fun bytes(builderAction: kotlin.text.HexFormat.BytesHexFormat.Builder.() -> kotlin.Unit): kotlin.Unit
|
||||
|
||||
@kotlin.internal.InlineOnly
|
||||
public final inline fun number(builderAction: kotlin.text.HexFormat.NumberHexFormat.Builder.() -> kotlin.Unit): kotlin.Unit
|
||||
}
|
||||
|
||||
public final class BytesHexFormat {
|
||||
public final val bytePrefix: kotlin.String { get; }
|
||||
|
||||
public final val byteSeparator: kotlin.String { get; }
|
||||
|
||||
public final val byteSuffix: kotlin.String { get; }
|
||||
|
||||
public final val bytesPerGroup: kotlin.Int { get; }
|
||||
|
||||
public final val bytesPerLine: kotlin.Int { get; }
|
||||
|
||||
public final val groupSeparator: kotlin.String { get; }
|
||||
|
||||
public open override fun toString(): kotlin.String
|
||||
|
||||
public final class Builder {
|
||||
public final var bytePrefix: kotlin.String { get; set; }
|
||||
|
||||
public final var byteSeparator: kotlin.String { get; set; }
|
||||
|
||||
public final var byteSuffix: kotlin.String { get; set; }
|
||||
|
||||
public final var bytesPerGroup: kotlin.Int { get; set; }
|
||||
|
||||
public final var bytesPerLine: kotlin.Int { get; set; }
|
||||
|
||||
public final var groupSeparator: kotlin.String { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
public companion object of HexFormat {
|
||||
public final val Default: kotlin.text.HexFormat { get; }
|
||||
|
||||
public final val UpperCase: kotlin.text.HexFormat { get; }
|
||||
}
|
||||
|
||||
public final class NumberHexFormat {
|
||||
public final val prefix: kotlin.String { get; }
|
||||
|
||||
public final val removeLeadingZeros: kotlin.Boolean { get; }
|
||||
|
||||
public final val suffix: kotlin.String { get; }
|
||||
|
||||
public open override fun toString(): kotlin.String
|
||||
|
||||
public final class Builder {
|
||||
public final var prefix: kotlin.String { get; set; }
|
||||
|
||||
public final var removeLeadingZeros: kotlin.Boolean { get; set; }
|
||||
|
||||
public final var suffix: kotlin.String { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final data class MatchGroup {
|
||||
public constructor MatchGroup(value: kotlin.String)
|
||||
|
||||
|
||||
@@ -0,0 +1,593 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package kotlin.text
|
||||
|
||||
import kotlin.native.concurrent.SharedImmutable
|
||||
|
||||
private const val LOWER_CASE_HEX_DIGITS = "0123456789abcdef"
|
||||
private const val UPPER_CASE_HEX_DIGITS = "0123456789ABCDEF"
|
||||
|
||||
// case-insensitive parsing
|
||||
@SharedImmutable
|
||||
private val HEX_DIGITS_TO_DECIMAL = IntArray(128) { -1 }.apply {
|
||||
LOWER_CASE_HEX_DIGITS.forEachIndexed { index, char -> this[char.code] = index }
|
||||
UPPER_CASE_HEX_DIGITS.forEachIndexed { index, char -> this[char.code] = index }
|
||||
}
|
||||
|
||||
// -------------------------- format and parse ByteArray --------------------------
|
||||
|
||||
/**
|
||||
* Formats bytes in this array using the specified [format].
|
||||
*
|
||||
* Note that only [HexFormat.upperCase] and [HexFormat.BytesHexFormat] affect formatting.
|
||||
*
|
||||
* @param format the [HexFormat] to use for formatting, [HexFormat.Default] by default.
|
||||
*
|
||||
* @throws IllegalArgumentException if the result length is more than [String] maximum capacity.
|
||||
*/
|
||||
@ExperimentalStdlibApi
|
||||
@SinceKotlin("1.9")
|
||||
public fun ByteArray.toHexString(format: HexFormat = HexFormat.Default): String = toHexString(0, size, format)
|
||||
|
||||
/**
|
||||
* Formats bytes in this array using the specified [HexFormat].
|
||||
*
|
||||
* Note that only [HexFormat.upperCase] and [HexFormat.BytesHexFormat] affect formatting.
|
||||
*
|
||||
* @param startIndex the beginning (inclusive) of the subrange to format, 0 by default.
|
||||
* @param endIndex the end (exclusive) of the subrange to format, size of this array by default.
|
||||
* @param format the [HexFormat] to use for formatting, [HexFormat.Default] by default.
|
||||
*
|
||||
* @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of this array indices.
|
||||
* @throws IllegalArgumentException when `startIndex > endIndex`.
|
||||
* @throws IllegalArgumentException if the result length is more than [String] maximum capacity.
|
||||
*/
|
||||
@ExperimentalStdlibApi
|
||||
@SinceKotlin("1.9")
|
||||
public fun ByteArray.toHexString(
|
||||
startIndex: Int = 0,
|
||||
endIndex: Int = size,
|
||||
format: HexFormat = HexFormat.Default
|
||||
): String {
|
||||
AbstractList.checkBoundsIndexes(startIndex, endIndex, size)
|
||||
|
||||
if (startIndex == endIndex) {
|
||||
return ""
|
||||
}
|
||||
|
||||
val digits = if (format.upperCase) UPPER_CASE_HEX_DIGITS else LOWER_CASE_HEX_DIGITS
|
||||
|
||||
val bytesFormat = format.bytes
|
||||
val bytesPerLine = bytesFormat.bytesPerLine
|
||||
val bytesPerGroup = bytesFormat.bytesPerGroup
|
||||
val bytePrefix = bytesFormat.bytePrefix
|
||||
val byteSuffix = bytesFormat.byteSuffix
|
||||
val byteSeparator = bytesFormat.byteSeparator
|
||||
val groupSeparator = bytesFormat.groupSeparator
|
||||
|
||||
val formatLength = formattedStringLength(
|
||||
totalBytes = endIndex - startIndex,
|
||||
bytesPerLine,
|
||||
bytesPerGroup,
|
||||
groupSeparator.length,
|
||||
byteSeparator.length,
|
||||
bytePrefix.length,
|
||||
byteSuffix.length
|
||||
)
|
||||
|
||||
var indexInLine = 0
|
||||
var indexInGroup = 0
|
||||
|
||||
return buildString(formatLength) {
|
||||
for (i in startIndex until endIndex) {
|
||||
val byte = this@toHexString[i].toInt() and 0xFF
|
||||
|
||||
if (indexInLine == bytesPerLine) {
|
||||
append('\n')
|
||||
indexInLine = 0
|
||||
indexInGroup = 0
|
||||
} else if (indexInGroup == bytesPerGroup) {
|
||||
append(groupSeparator)
|
||||
indexInGroup = 0
|
||||
}
|
||||
if (indexInGroup != 0) {
|
||||
append(byteSeparator)
|
||||
}
|
||||
|
||||
append(bytePrefix)
|
||||
append(digits[byte shr 4])
|
||||
append(digits[byte and 0xF])
|
||||
append(byteSuffix)
|
||||
|
||||
indexInGroup += 1
|
||||
indexInLine += 1
|
||||
}
|
||||
|
||||
check(formatLength == length)
|
||||
}
|
||||
}
|
||||
|
||||
// Declared internal for testing
|
||||
internal fun formattedStringLength(
|
||||
totalBytes: Int,
|
||||
bytesPerLine: Int,
|
||||
bytesPerGroup: Int,
|
||||
groupSeparatorLength: Int,
|
||||
byteSeparatorLength: Int,
|
||||
bytePrefixLength: Int,
|
||||
byteSuffixLength: Int
|
||||
): Int {
|
||||
require(totalBytes > 0)
|
||||
// By contract bytesPerLine and bytesPerGroup are > 0
|
||||
|
||||
val lineSeparators = (totalBytes - 1) / bytesPerLine
|
||||
val groupSeparators = run {
|
||||
val groupSeparatorsPerLine = (bytesPerLine - 1) / bytesPerGroup
|
||||
val bytesInLastLine = (totalBytes % bytesPerLine).let { if (it == 0) bytesPerLine else it }
|
||||
val groupSeparatorsInLastLine = (bytesInLastLine - 1) / bytesPerGroup
|
||||
lineSeparators * groupSeparatorsPerLine + groupSeparatorsInLastLine
|
||||
}
|
||||
val byteSeparators = totalBytes - 1 - lineSeparators - groupSeparators
|
||||
|
||||
// The max totalLength is achieved when
|
||||
// totalBytes, bytePrefix/Suffix/Separator.length = Int.MAX_VALUE.
|
||||
// The result is 3 * Int.MAX_VALUE * Int.MAX_VALUE + Int.MAX_VALUE,
|
||||
// which is > Long.MAX_VALUE, but < ULong.MAX_VALUE.
|
||||
|
||||
val totalLength: Long = lineSeparators.toLong() /* * lineSeparator.length = 1 */ +
|
||||
groupSeparators.toLong() * groupSeparatorLength.toLong() +
|
||||
byteSeparators.toLong() * byteSeparatorLength.toLong() +
|
||||
totalBytes.toLong() * (bytePrefixLength.toLong() + 2L + byteSuffixLength.toLong())
|
||||
|
||||
if (totalLength !in 0..Int.MAX_VALUE) {
|
||||
// TODO: Common OutOfMemoryError?
|
||||
throw IllegalArgumentException("The resulting string length is too big: ${totalLength.toULong()}")
|
||||
}
|
||||
|
||||
return totalLength.toInt()
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses bytes from this string using the specified [HexFormat].
|
||||
*
|
||||
* Note that only [HexFormat.BytesHexFormat] affects parsing,
|
||||
* and parsing is performed in case-insensitive manner.
|
||||
* Also, any of the char sequences CRLF, LF and CR is considered a valid line separator.
|
||||
*
|
||||
* @param format the [HexFormat] to use for parsing, [HexFormat.Default] by default.
|
||||
*
|
||||
* @throws IllegalArgumentException if this string does not comply with the specified [format].
|
||||
*/
|
||||
@ExperimentalStdlibApi
|
||||
@SinceKotlin("1.9")
|
||||
public fun String.hexToByteArray(format: HexFormat = HexFormat.Default): ByteArray = hexToByteArray(0, length, format)
|
||||
|
||||
/**
|
||||
* Parses bytes from this string using the specified [HexFormat].
|
||||
*
|
||||
* Note that only [HexFormat.BytesHexFormat] affects parsing,
|
||||
* and parsing is performed in case-insensitive manner.
|
||||
* Also, any of the char sequences CRLF, LF and CR is considered a valid line separator.
|
||||
*
|
||||
* @param startIndex the beginning (inclusive) of the substring to parse, 0 by default.
|
||||
* @param endIndex the end (exclusive) of the substring to parse, length of this string by default.
|
||||
* @param format the [HexFormat] to use for parsing, [HexFormat.Default] by default.
|
||||
*
|
||||
* @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of this string indices.
|
||||
* @throws IllegalArgumentException when `startIndex > endIndex`.
|
||||
* @throws IllegalArgumentException if the substring does not comply with the specified [format].
|
||||
*/
|
||||
@ExperimentalStdlibApi
|
||||
//@SinceKotlin("1.9")
|
||||
private fun String.hexToByteArray(
|
||||
startIndex: Int = 0,
|
||||
endIndex: Int = length,
|
||||
format: HexFormat = HexFormat.Default
|
||||
): ByteArray {
|
||||
AbstractList.checkBoundsIndexes(startIndex, endIndex, length)
|
||||
|
||||
if (startIndex == endIndex) {
|
||||
return byteArrayOf()
|
||||
}
|
||||
|
||||
val bytesFormat = format.bytes
|
||||
val bytesPerLine = bytesFormat.bytesPerLine
|
||||
val bytesPerGroup = bytesFormat.bytesPerGroup
|
||||
val bytePrefix = bytesFormat.bytePrefix
|
||||
val byteSuffix = bytesFormat.byteSuffix
|
||||
val byteSeparator = bytesFormat.byteSeparator
|
||||
val groupSeparator = bytesFormat.groupSeparator
|
||||
|
||||
val resultCapacity = parsedByteArrayMaxSize(
|
||||
stringLength = endIndex - startIndex,
|
||||
bytesPerLine,
|
||||
bytesPerGroup,
|
||||
groupSeparator.length,
|
||||
byteSeparator.length,
|
||||
bytePrefix.length,
|
||||
byteSuffix.length
|
||||
)
|
||||
val result = ByteArray(resultCapacity)
|
||||
|
||||
var i = startIndex
|
||||
var byteIndex = 0
|
||||
var indexInLine = 0
|
||||
var indexInGroup = 0
|
||||
|
||||
while (i < endIndex) {
|
||||
if (indexInLine == bytesPerLine) {
|
||||
i = checkNewLineAt(i, endIndex)
|
||||
indexInLine = 0
|
||||
indexInGroup = 0
|
||||
} else if (indexInGroup == bytesPerGroup) {
|
||||
i = checkContainsAt(groupSeparator, i, endIndex, "group separator")
|
||||
indexInGroup = 0
|
||||
} else if (indexInGroup != 0) {
|
||||
i = checkContainsAt(byteSeparator, i, endIndex, "byte separator")
|
||||
}
|
||||
indexInLine += 1
|
||||
indexInGroup += 1
|
||||
|
||||
i = checkContainsAt(bytePrefix, i, endIndex, "byte prefix")
|
||||
|
||||
checkHexLength(i, (i + 2).coerceAtMost(endIndex), maxDigits = 2, requireMaxLength = true)
|
||||
|
||||
result[byteIndex++] = ((decimalFromHexDigitAt(i++) shl 4) or decimalFromHexDigitAt(i++)).toByte()
|
||||
|
||||
i = checkContainsAt(byteSuffix, i, endIndex, "byte suffix")
|
||||
}
|
||||
|
||||
return if (byteIndex == result.size) result else result.copyOf(byteIndex)
|
||||
}
|
||||
|
||||
// Declared internal for testing
|
||||
internal fun parsedByteArrayMaxSize(
|
||||
stringLength: Int,
|
||||
bytesPerLine: Int,
|
||||
bytesPerGroup: Int,
|
||||
groupSeparatorLength: Int,
|
||||
byteSeparatorLength: Int,
|
||||
bytePrefixLength: Int,
|
||||
byteSuffixLength: Int
|
||||
): Int {
|
||||
require(stringLength > 0)
|
||||
// By contract bytesPerLine and bytesPerGroup are > 0
|
||||
|
||||
// The max charsPerSet is achieved when
|
||||
// bytesPerLine/Group, bytePrefix/Suffix/SeparatorLength = Int.MAX_VALUE.
|
||||
// The result is 3 * Int.MAX_VALUE * Int.MAX_VALUE + Int.MAX_VALUE,
|
||||
// which is > Long.MAX_VALUE, but < ULong.MAX_VALUE.
|
||||
|
||||
val charsPerByte = bytePrefixLength + 2L + byteSuffixLength
|
||||
|
||||
val charsPerGroup = charsPerSet(charsPerByte, bytesPerGroup, byteSeparatorLength)
|
||||
|
||||
val charsPerLine = if (bytesPerLine <= bytesPerGroup) {
|
||||
charsPerSet(charsPerByte, bytesPerLine, byteSeparatorLength)
|
||||
} else {
|
||||
val groupsPerLine = bytesPerLine / bytesPerGroup
|
||||
var result = charsPerSet(charsPerGroup, groupsPerLine, groupSeparatorLength)
|
||||
val bytesPerLastGroupInLine = bytesPerLine % bytesPerGroup
|
||||
if (bytesPerLastGroupInLine != 0) {
|
||||
result += groupSeparatorLength
|
||||
result += charsPerSet(charsPerByte, bytesPerLastGroupInLine, byteSeparatorLength)
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
var numberOfChars = stringLength.toLong()
|
||||
|
||||
// assume one-character line separator to maximize size
|
||||
val wholeLines = wholeElementsPerSet(numberOfChars, charsPerLine, 1)
|
||||
numberOfChars -= wholeLines * (charsPerLine + 1)
|
||||
|
||||
val wholeGroupsInLastLine = wholeElementsPerSet(numberOfChars, charsPerGroup, groupSeparatorLength)
|
||||
numberOfChars -= wholeGroupsInLastLine * (charsPerGroup + groupSeparatorLength)
|
||||
|
||||
val wholeBytesInLastGroup = wholeElementsPerSet(numberOfChars, charsPerByte, byteSeparatorLength)
|
||||
numberOfChars -= wholeBytesInLastGroup * (charsPerByte + byteSeparatorLength)
|
||||
|
||||
// If numberOfChars is bigger than zero here:
|
||||
// * CRLF might have been used as line separator
|
||||
// * or there are dangling characters at the end of string
|
||||
// Anyhow, have a spare capacity to let parsing continue.
|
||||
// In case of dangling characters it will throw later on with a correct message.
|
||||
val spare = if (numberOfChars > 0L) 1 else 0
|
||||
|
||||
// The number of parsed bytes will always fit into Int, each parsed byte consumes at least 2 chars of the input string.
|
||||
return ((wholeLines * bytesPerLine) + (wholeGroupsInLastLine * bytesPerGroup) + wholeBytesInLastGroup + spare).toInt()
|
||||
}
|
||||
|
||||
private fun charsPerSet(charsPerElement: Long, elementsPerSet: Int, elementSeparatorLength: Int): Long {
|
||||
require(elementsPerSet > 0)
|
||||
return (charsPerElement * elementsPerSet) + (elementSeparatorLength * (elementsPerSet - 1L))
|
||||
}
|
||||
|
||||
private fun wholeElementsPerSet(charsPerSet: Long, charsPerElement: Long, elementSeparatorLength: Int): Long {
|
||||
return if (charsPerSet <= 0 || charsPerElement <= 0) 0
|
||||
else (charsPerSet + elementSeparatorLength) / (charsPerElement + elementSeparatorLength)
|
||||
}
|
||||
|
||||
private fun String.checkNewLineAt(index: Int, endIndex: Int): Int {
|
||||
return if (this[index] == '\r') {
|
||||
if (index + 1 < endIndex && this[index + 1] == '\n') index + 2 else index + 1
|
||||
} else if (this[index] == '\n') {
|
||||
index + 1
|
||||
} else {
|
||||
throw NumberFormatException("Expected a new line at index $index, but was ${this[index]}")
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------- format and parse Byte --------------------------
|
||||
|
||||
/**
|
||||
* Formats this `Byte` value using the specified [format].
|
||||
*
|
||||
* Note that only [HexFormat.upperCase] and [HexFormat.NumberHexFormat] affect formatting.
|
||||
*
|
||||
* @param format the [HexFormat] to use for formatting, [HexFormat.Default] by default.
|
||||
*/
|
||||
@ExperimentalStdlibApi
|
||||
@SinceKotlin("1.9")
|
||||
public fun Byte.toHexString(format: HexFormat = HexFormat.Default): String = toLong().toHexStringImpl(format, bits = 8)
|
||||
|
||||
/**
|
||||
* Parses a `Byte` value from this string using the specified [format].
|
||||
*
|
||||
* Note that only [HexFormat.NumberHexFormat] affects parsing,
|
||||
* and parsing is performed in case-insensitive manner.
|
||||
*
|
||||
* @param format the [HexFormat] to use for parsing, [HexFormat.Default] by default.
|
||||
*
|
||||
* @throws IllegalArgumentException if this string does not comply with the specified [format].
|
||||
*/
|
||||
@ExperimentalStdlibApi
|
||||
@SinceKotlin("1.9")
|
||||
public fun String.hexToByte(format: HexFormat = HexFormat.Default): Byte = hexToByte(0, length, format)
|
||||
|
||||
/**
|
||||
* Parses a `Byte` value from this string using the specified [format].
|
||||
*
|
||||
* Note that only [HexFormat.NumberHexFormat] affects parsing,
|
||||
* and parsing is performed in case-insensitive manner.
|
||||
*
|
||||
* @param startIndex the beginning (inclusive) of the substring to parse, 0 by default.
|
||||
* @param endIndex the end (exclusive) of the substring to parse, length of this string by default.
|
||||
* @param format the [HexFormat] to use for parsing, [HexFormat.Default] by default.
|
||||
*
|
||||
* @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of this string indices.
|
||||
* @throws IllegalArgumentException when `startIndex > endIndex`.
|
||||
* @throws IllegalArgumentException if the substring does not comply with the specified [format].
|
||||
*/
|
||||
@ExperimentalStdlibApi
|
||||
//@SinceKotlin("1.9")
|
||||
private fun String.hexToByte(startIndex: Int = 0, endIndex: Int = length, format: HexFormat = HexFormat.Default): Byte =
|
||||
hexToLongImpl(startIndex, endIndex, format, maxDigits = 2).toByte()
|
||||
|
||||
// -------------------------- format and parse Short --------------------------
|
||||
|
||||
/**
|
||||
* Formats this `Short` value using the specified [format].
|
||||
*
|
||||
* Note that only [HexFormat.upperCase] and [HexFormat.NumberHexFormat] affect formatting.
|
||||
*
|
||||
* @param format the [HexFormat] to use for formatting, [HexFormat.Default] by default.
|
||||
*/
|
||||
@ExperimentalStdlibApi
|
||||
@SinceKotlin("1.9")
|
||||
public fun Short.toHexString(format: HexFormat = HexFormat.Default): String = toLong().toHexStringImpl(format, bits = 16)
|
||||
|
||||
/**
|
||||
* Parses a `Short` value from this string using the specified [format].
|
||||
*
|
||||
* Note that only [HexFormat.NumberHexFormat] affects parsing,
|
||||
* and parsing is performed in case-insensitive manner.
|
||||
*
|
||||
* @param format the [HexFormat] to use for parsing, [HexFormat.Default] by default.
|
||||
*
|
||||
* @throws IllegalArgumentException if this string does not comply with the specified [format].
|
||||
*/
|
||||
@ExperimentalStdlibApi
|
||||
@SinceKotlin("1.9")
|
||||
public fun String.hexToShort(format: HexFormat = HexFormat.Default): Short = hexToShort(0, length, format)
|
||||
|
||||
/**
|
||||
* Parses a `Short` value from this string using the specified [format].
|
||||
*
|
||||
* Note that only [HexFormat.NumberHexFormat] affects parsing,
|
||||
* and parsing is performed in case-insensitive manner.
|
||||
*
|
||||
* @param startIndex the beginning (inclusive) of the substring to parse, 0 by default.
|
||||
* @param endIndex the end (exclusive) of the substring to parse, length of this string by default.
|
||||
* @param format the [HexFormat] to use for parsing, [HexFormat.Default] by default.
|
||||
*
|
||||
* @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of this string indices.
|
||||
* @throws IllegalArgumentException when `startIndex > endIndex`.
|
||||
* @throws IllegalArgumentException if the substring does not comply with the specified [format].
|
||||
*/
|
||||
@ExperimentalStdlibApi
|
||||
//@SinceKotlin("1.9")
|
||||
private fun String.hexToShort(startIndex: Int = 0, endIndex: Int = length, format: HexFormat = HexFormat.Default): Short =
|
||||
hexToLongImpl(startIndex, endIndex, format, maxDigits = 4).toShort()
|
||||
|
||||
// -------------------------- format and parse Int --------------------------
|
||||
|
||||
/**
|
||||
* Formats this `Int` value using the specified [format].
|
||||
*
|
||||
* Note that only [HexFormat.upperCase] and [HexFormat.NumberHexFormat] affect formatting.
|
||||
*
|
||||
* @param format the [HexFormat] to use for formatting, [HexFormat.Default] by default.
|
||||
*/
|
||||
@ExperimentalStdlibApi
|
||||
@SinceKotlin("1.9")
|
||||
public fun Int.toHexString(format: HexFormat = HexFormat.Default): String = toLong().toHexStringImpl(format, bits = 32)
|
||||
|
||||
/**
|
||||
* Parses an `Int` value from this string using the specified [format].
|
||||
*
|
||||
* Note that only [HexFormat.NumberHexFormat] affects parsing,
|
||||
* and parsing is performed in case-insensitive manner.
|
||||
*
|
||||
* @param format the [HexFormat] to use for parsing, [HexFormat.Default] by default.
|
||||
*
|
||||
* @throws IllegalArgumentException if this string does not comply with the specified [format].
|
||||
*/
|
||||
@ExperimentalStdlibApi
|
||||
@SinceKotlin("1.9")
|
||||
public fun String.hexToInt(format: HexFormat = HexFormat.Default): Int = hexToInt(0, length, format)
|
||||
|
||||
/**
|
||||
* Parses an `Int` value from this string using the specified [format].
|
||||
*
|
||||
* Note that only [HexFormat.NumberHexFormat] affects parsing,
|
||||
* and parsing is performed in case-insensitive manner.
|
||||
*
|
||||
* @param startIndex the beginning (inclusive) of the substring to parse, 0 by default.
|
||||
* @param endIndex the end (exclusive) of the substring to parse, length of this string by default.
|
||||
* @param format the [HexFormat] to use for parsing, [HexFormat.Default] by default.
|
||||
*
|
||||
* @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of this string indices.
|
||||
* @throws IllegalArgumentException when `startIndex > endIndex`.
|
||||
* @throws IllegalArgumentException if the substring does not comply with the specified [format].
|
||||
*/
|
||||
@ExperimentalStdlibApi
|
||||
//@SinceKotlin("1.9")
|
||||
private fun String.hexToInt(startIndex: Int = 0, endIndex: Int = length, format: HexFormat = HexFormat.Default): Int =
|
||||
hexToLongImpl(startIndex, endIndex, format, maxDigits = 8).toInt()
|
||||
|
||||
// -------------------------- format and parse Long --------------------------
|
||||
|
||||
/**
|
||||
* Formats this `Long` value using the specified [format].
|
||||
*
|
||||
* Note that only [HexFormat.upperCase] and [HexFormat.NumberHexFormat] affect formatting.
|
||||
*
|
||||
* @param format the [HexFormat] to use for formatting, [HexFormat.Default] by default.
|
||||
*/
|
||||
@ExperimentalStdlibApi
|
||||
@SinceKotlin("1.9")
|
||||
public fun Long.toHexString(format: HexFormat = HexFormat.Default): String = toHexStringImpl(format, bits = 64)
|
||||
|
||||
/**
|
||||
* Parses a `Long` value from this string using the specified [format].
|
||||
*
|
||||
* Note that only [HexFormat.NumberHexFormat] affects parsing,
|
||||
* and parsing is performed in case-insensitive manner.
|
||||
*
|
||||
* @param format the [HexFormat] to use for parsing, [HexFormat.Default] by default.
|
||||
*
|
||||
* @throws IllegalArgumentException if this string does not comply with the specified [format].
|
||||
*/
|
||||
@ExperimentalStdlibApi
|
||||
@SinceKotlin("1.9")
|
||||
public fun String.hexToLong(format: HexFormat = HexFormat.Default): Long = hexToLong(0, length, format)
|
||||
|
||||
/**
|
||||
* Parses a `Long` value from this string using the specified [format].
|
||||
*
|
||||
* Note that only [HexFormat.NumberHexFormat] affects parsing,
|
||||
* and parsing is performed in case-insensitive manner.
|
||||
*
|
||||
* @param startIndex the beginning (inclusive) of the substring to parse, 0 by default.
|
||||
* @param endIndex the end (exclusive) of the substring to parse, length of this string by default.
|
||||
* @param format the [HexFormat] to use for parsing, [HexFormat.Default] by default.
|
||||
*
|
||||
* @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of this string indices.
|
||||
* @throws IllegalArgumentException when `startIndex > endIndex`.
|
||||
* @throws IllegalArgumentException if the substring does not comply with the specified [format].
|
||||
*/
|
||||
@ExperimentalStdlibApi
|
||||
//@SinceKotlin("1.9")
|
||||
private fun String.hexToLong(startIndex: Int = 0, endIndex: Int = length, format: HexFormat = HexFormat.Default): Long =
|
||||
hexToLongImpl(startIndex, endIndex, format, maxDigits = 16)
|
||||
|
||||
// -------------------------- private format and parse functions --------------------------
|
||||
|
||||
@ExperimentalStdlibApi
|
||||
private fun Long.toHexStringImpl(format: HexFormat, bits: Int): String {
|
||||
require(bits and 0x3 == 0)
|
||||
|
||||
val digits = if (format.upperCase) UPPER_CASE_HEX_DIGITS else LOWER_CASE_HEX_DIGITS
|
||||
val value = this
|
||||
|
||||
val prefix = format.number.prefix
|
||||
val suffix = format.number.suffix
|
||||
val formatLength = prefix.length + (bits shr 2) + suffix.length
|
||||
var removeZeros = format.number.removeLeadingZeros
|
||||
|
||||
return buildString(formatLength) {
|
||||
append(prefix)
|
||||
|
||||
var shift = bits
|
||||
while (shift > 0) {
|
||||
shift -= 4
|
||||
val decimal = ((value shr shift) and 0xF).toInt()
|
||||
removeZeros = removeZeros && decimal == 0 && shift > 0
|
||||
if (!removeZeros) {
|
||||
append(digits[decimal])
|
||||
}
|
||||
}
|
||||
|
||||
append(suffix)
|
||||
}
|
||||
}
|
||||
|
||||
@ExperimentalStdlibApi
|
||||
private fun String.hexToLongImpl(startIndex: Int = 0, endIndex: Int = length, format: HexFormat, maxDigits: Int): Long {
|
||||
AbstractList.checkBoundsIndexes(startIndex, endIndex, length)
|
||||
|
||||
val prefix = format.number.prefix
|
||||
val suffix = format.number.suffix
|
||||
|
||||
if (prefix.length + suffix.length >= endIndex - startIndex) {
|
||||
throw NumberFormatException(
|
||||
"Expected a hexadecimal number with prefix \"$prefix\" and suffix \"$suffix\", but was ${substring(startIndex, endIndex)}"
|
||||
)
|
||||
}
|
||||
|
||||
val digitsStartIndex = checkContainsAt(prefix, startIndex, endIndex, "prefix")
|
||||
val digitsEndIndex = endIndex - suffix.length
|
||||
checkContainsAt(suffix, digitsEndIndex, endIndex, "suffix")
|
||||
|
||||
checkHexLength(digitsStartIndex, digitsEndIndex, maxDigits, requireMaxLength = false)
|
||||
|
||||
var result = 0L
|
||||
for (i in digitsStartIndex until digitsEndIndex) {
|
||||
result = (result shl 4) or decimalFromHexDigitAt(i).toLong()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun String.checkContainsAt(part: String, index: Int, endIndex: Int, partName: String): Int {
|
||||
val end = index + part.length
|
||||
if (end > endIndex || !regionMatches(index, part, 0, part.length, ignoreCase = true)) {
|
||||
throw NumberFormatException(
|
||||
"Expected $partName \"$part\" at index $index, but was ${this.substring(index, end.coerceAtMost(endIndex))}"
|
||||
)
|
||||
}
|
||||
return end
|
||||
}
|
||||
|
||||
private fun String.checkHexLength(startIndex: Int, endIndex: Int, maxDigits: Int, requireMaxLength: Boolean) {
|
||||
val digitsLength = endIndex - startIndex
|
||||
val isCorrectLength = if (requireMaxLength) digitsLength == maxDigits else digitsLength <= maxDigits
|
||||
if (!isCorrectLength) {
|
||||
val specifier = if (requireMaxLength) "exactly" else "at most"
|
||||
val substring = substring(startIndex, endIndex)
|
||||
throw NumberFormatException(
|
||||
"Expected $specifier $maxDigits hexadecimal digits at index $startIndex, but was $substring of length $digitsLength"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.decimalFromHexDigitAt(index: Int): Int {
|
||||
val code = this[index].code
|
||||
if (code > 127 || HEX_DIGITS_TO_DECIMAL[code] < 0) {
|
||||
throw NumberFormatException("Expected a hexadecimal digit at index $index, but was ${this[index]}")
|
||||
}
|
||||
return HEX_DIGITS_TO_DECIMAL[code]
|
||||
}
|
||||
@@ -0,0 +1,411 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package kotlin.text
|
||||
|
||||
import kotlin.internal.InlineOnly
|
||||
|
||||
/**
|
||||
* Represents hexadecimal format options.
|
||||
*
|
||||
* To create a new [HexFormat] use `HexFormat` function.
|
||||
*/
|
||||
@ExperimentalStdlibApi
|
||||
@SinceKotlin("1.9")
|
||||
public class HexFormat internal constructor(
|
||||
/**
|
||||
* Specifies whether upper case hexadecimal digits `0-9`, `A-F` should be used for formatting.
|
||||
* If `false`, lower case hexadecimal digits `0-9`, `a-f` will be used.
|
||||
*
|
||||
* Affects both `ByteArray` and numeric value formatting.
|
||||
*/
|
||||
val upperCase: Boolean,
|
||||
/**
|
||||
* Specifies hexadecimal format used for formatting and parsing `ByteArray`.
|
||||
*/
|
||||
val bytes: BytesHexFormat,
|
||||
/**
|
||||
* Specifies hexadecimal format used for formatting and parsing a numeric value.
|
||||
*/
|
||||
val number: NumberHexFormat
|
||||
) {
|
||||
|
||||
override fun toString(): String = buildString {
|
||||
append("HexFormat(").appendLine()
|
||||
append(" upperCase = ").append(upperCase).appendLine(",")
|
||||
append(" bytes = BytesHexFormat(").appendLine()
|
||||
bytes.appendOptionsTo(this, indent = " ").appendLine()
|
||||
append(" ),").appendLine()
|
||||
append(" number = NumberHexFormat(").appendLine()
|
||||
number.appendOptionsTo(this, indent = " ").appendLine()
|
||||
append(" )").appendLine()
|
||||
append(")")
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents hexadecimal format options for formatting and parsing `ByteArray`.
|
||||
*
|
||||
* When formatting one can assume that bytes are firstly separated using LF character (`'\n'`) into lines
|
||||
* with [bytesPerLine] bytes in each line. The last line may have fewer bytes.
|
||||
* Then each line is separated into groups using [groupSeparator] with [bytesPerGroup] bytes in each group,
|
||||
* except the last group in the line, which may have fewer bytes.
|
||||
* All bytes in a group are separated using [byteSeparator].
|
||||
* Each byte is converted to its two-digit hexadecimal representation,
|
||||
* immediately preceded by [bytePrefix] and immediately succeeded by [byteSuffix].
|
||||
*
|
||||
* When parsing the input string is required to be in the format described above.
|
||||
* However, any of the char sequences CRLF, LF and CR is considered a valid line separator,
|
||||
* and parsing is performed in case-insensitive manner.
|
||||
*
|
||||
* See [BytesHexFormat.Builder] to find out how the options are configured,
|
||||
* and what is the default value of each option.
|
||||
*/
|
||||
public class BytesHexFormat internal constructor(
|
||||
/** The maximum number of bytes per line. */
|
||||
val bytesPerLine: Int,
|
||||
|
||||
/** The maximum number of bytes per group. */
|
||||
val bytesPerGroup: Int,
|
||||
/** The string used to separate adjacent groups in a line. */
|
||||
val groupSeparator: String,
|
||||
|
||||
/** The string used to separate adjacent bytes in a group. */
|
||||
val byteSeparator: String,
|
||||
/** The string that immediately precedes two-digit hexadecimal representation of each byte. */
|
||||
val bytePrefix: String,
|
||||
/** The string that immediately succeeds two-digit hexadecimal representation of each byte. */
|
||||
val byteSuffix: String
|
||||
) {
|
||||
|
||||
override fun toString(): String = buildString {
|
||||
append("BytesHexFormat(").appendLine()
|
||||
appendOptionsTo(this, indent = " ").appendLine()
|
||||
append(")")
|
||||
}
|
||||
|
||||
internal fun appendOptionsTo(sb: StringBuilder, indent: String): StringBuilder {
|
||||
sb.append(indent).append("bytesPerLine = ").append(bytesPerLine).appendLine(",")
|
||||
sb.append(indent).append("bytesPerGroup = ").append(bytesPerGroup).appendLine(",")
|
||||
sb.append(indent).append("groupSeparator = \"").append(groupSeparator).appendLine("\",")
|
||||
sb.append(indent).append("byteSeparator = \"").append(byteSeparator).appendLine("\",")
|
||||
sb.append(indent).append("bytePrefix = \"").append(bytePrefix).appendLine("\",")
|
||||
sb.append(indent).append("byteSuffix = \"").append(byteSuffix).append("\"")
|
||||
return sb
|
||||
}
|
||||
|
||||
/**
|
||||
* A context for building a [BytesHexFormat]. Provides API for configuring format options.
|
||||
*/
|
||||
class Builder internal constructor() {
|
||||
/**
|
||||
* Defines [BytesHexFormat.bytesPerLine] of the format being built, [Int.MAX_VALUE] by default.
|
||||
*
|
||||
* The value must be positive.
|
||||
*
|
||||
* @throws IllegalArgumentException if a non-positive value is assigned to this property.
|
||||
*/
|
||||
var bytesPerLine: Int = Default.bytesPerLine
|
||||
set(value) {
|
||||
if (value <= 0)
|
||||
throw IllegalArgumentException("Non-positive values are prohibited for bytesPerLine, but was $value")
|
||||
field = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines [BytesHexFormat.bytesPerGroup] of the format being built, [Int.MAX_VALUE] by default.
|
||||
*
|
||||
* The value must be positive.
|
||||
*
|
||||
* @throws IllegalArgumentException if a non-positive value is assigned to this property.
|
||||
*/
|
||||
var bytesPerGroup: Int = Default.bytesPerGroup
|
||||
set(value) {
|
||||
if (value <= 0)
|
||||
throw IllegalArgumentException("Non-positive values are prohibited for bytesPerGroup, but was $value")
|
||||
field = value
|
||||
}
|
||||
|
||||
/** Defines [BytesHexFormat.groupSeparator] of the format being built, two space characters (`" "`) by default. */
|
||||
var groupSeparator: String = Default.groupSeparator
|
||||
|
||||
/**
|
||||
* Defines [BytesHexFormat.byteSeparator] of the format being built, empty string by default.
|
||||
*
|
||||
* The string must not contain LF and CR characters.
|
||||
*
|
||||
* @throws IllegalArgumentException if a string containing LF or CR character is assigned to this property.
|
||||
*/
|
||||
var byteSeparator: String = Default.byteSeparator
|
||||
set(value) {
|
||||
if (value.contains('\n') || value.contains('\r'))
|
||||
throw IllegalArgumentException("LF and CR characters are prohibited in byteSeparator, but was $value")
|
||||
field = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines [BytesHexFormat.bytePrefix] of the format being built, empty string by default.
|
||||
*
|
||||
* The string must not contain LF and CR characters.
|
||||
*
|
||||
* @throws IllegalArgumentException if a string containing LF or CR character is assigned to this property.
|
||||
*/
|
||||
var bytePrefix: String = Default.bytePrefix
|
||||
set(value) {
|
||||
if (value.contains('\n') || value.contains('\r'))
|
||||
throw IllegalArgumentException("LF and CR characters are prohibited in bytePrefix, but was $value")
|
||||
field = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines [BytesHexFormat.byteSuffix] of the format being built, empty string by default.
|
||||
*
|
||||
* The string must not contain LF and CR characters.
|
||||
*
|
||||
* @throws IllegalArgumentException if a string containing LF or CR character is assigned to this property.
|
||||
*/
|
||||
var byteSuffix: String = Default.byteSuffix
|
||||
set(value) {
|
||||
if (value.contains('\n') || value.contains('\r'))
|
||||
throw IllegalArgumentException("LF and CR characters are prohibited in byteSuffix, but was $value")
|
||||
field = value
|
||||
}
|
||||
|
||||
internal fun build(): BytesHexFormat {
|
||||
return BytesHexFormat(bytesPerLine, bytesPerGroup, groupSeparator, byteSeparator, bytePrefix, byteSuffix)
|
||||
}
|
||||
}
|
||||
|
||||
internal companion object {
|
||||
internal val Default = BytesHexFormat(
|
||||
bytesPerLine = Int.MAX_VALUE,
|
||||
bytesPerGroup = Int.MAX_VALUE,
|
||||
groupSeparator = " ",
|
||||
byteSeparator = "",
|
||||
bytePrefix = "",
|
||||
byteSuffix = ""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents hexadecimal format options for formatting and parsing a numeric value.
|
||||
*
|
||||
* The formatting result consist of [prefix] string, hexadecimal representation of the value being formatted, and [suffix] string.
|
||||
* Hexadecimal representation of a value is calculated by mapping each four-bit chunk
|
||||
* of its binary representation to the corresponding hexadecimal digit, starting with the most significant bits.
|
||||
* [upperCase] determines whether upper case `0-9`, `A-F` or lower case `0-9`, `a-f` hexadecimal digits are used.
|
||||
* If [removeLeadingZeros] it `true`, leading zeros in the hexadecimal representation are removed.
|
||||
*
|
||||
* For example, the binary representation of the `Byte` value `58` is the 8-bit long `00111010`,
|
||||
* which converts to a hexadecimal representation of `3a` or `3A` depending on [upperCase].
|
||||
* Whereas, the binary representation of the `Int` value `58` is the 32-bit long `00000000000000000000000000111010`,
|
||||
* which converts to a hexadecimal representation of `0000003a` or `0000003A` depending on [upperCase].
|
||||
* If [removeLeadingZeros] it `true`, leading zeros in `0000003a` are removed, resulting `3a`.
|
||||
*
|
||||
* To convert a value to hexadecimal string of a particular length,
|
||||
* first convert the value to a type with the corresponding bit size.
|
||||
* For example, to convert an `Int` value to 4-digit hexadecimal string,
|
||||
* convert the value `toShort()` before hexadecimal formatting.
|
||||
* To convert it to hexadecimal string of at most 4 digits
|
||||
* without leading zeros, set [removeLeadingZeros] to `true` in addition.
|
||||
*
|
||||
* Parsing requires [prefix] and [suffix] to be present in the input string,
|
||||
* and the amount of hexadecimal digits to be at least one and at most the value bit size divided by four.
|
||||
* Parsing is performed in case-insensitive manner, and [removeLeadingZeros] is ignored as well.
|
||||
*
|
||||
* See [NumberHexFormat.Builder] to find out how the options are configured,
|
||||
* and what is the default value of each option.
|
||||
*/
|
||||
public class NumberHexFormat internal constructor(
|
||||
/** The string that immediately precedes hexadecimal representation of a numeric value. */
|
||||
val prefix: String,
|
||||
/** The string that immediately succeeds hexadecimal representation of a numeric value. */
|
||||
val suffix: String,
|
||||
/** Specifies whether to remove leading zeros in the hexadecimal representation of a numeric value. */
|
||||
val removeLeadingZeros: Boolean
|
||||
) {
|
||||
|
||||
override fun toString(): String = buildString {
|
||||
append("NumberHexFormat(").appendLine()
|
||||
appendOptionsTo(this, indent = " ").appendLine()
|
||||
append(")")
|
||||
}
|
||||
|
||||
internal fun appendOptionsTo(sb: StringBuilder, indent: String): StringBuilder {
|
||||
sb.append(indent).append("prefix = \"").append(prefix).appendLine("\",")
|
||||
sb.append(indent).append("suffix = \"").append(suffix).appendLine("\",")
|
||||
sb.append(indent).append("removeLeadingZeros = ").append(removeLeadingZeros)
|
||||
return sb
|
||||
}
|
||||
|
||||
/**
|
||||
* A context for building a [NumberHexFormat]. Provides API for configuring format options.
|
||||
*/
|
||||
class Builder internal constructor() {
|
||||
/**
|
||||
* Defines [NumberHexFormat.prefix] of the format being built, empty string by default.
|
||||
*
|
||||
* The string must not contain LF and CR characters.
|
||||
*
|
||||
* @throws IllegalArgumentException if a string containing LF or CR character is assigned to this property.
|
||||
*/
|
||||
var prefix: String = Default.prefix
|
||||
set(value) {
|
||||
if (value.contains('\n') || value.contains('\r'))
|
||||
throw IllegalArgumentException("LF and CR characters are prohibited in prefix, but was $value")
|
||||
field = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines [NumberHexFormat.suffix] of the format being built, empty string by default.
|
||||
*
|
||||
* The string must not contain LF and CR characters.
|
||||
*
|
||||
* @throws IllegalArgumentException if a string containing LF or CR character is assigned to this property.
|
||||
*/
|
||||
var suffix: String = Default.suffix
|
||||
set(value) {
|
||||
if (value.contains('\n') || value.contains('\r'))
|
||||
throw IllegalArgumentException("LF and CR characters are prohibited in suffix, but was $value")
|
||||
field = value
|
||||
}
|
||||
|
||||
/** Defines [NumberHexFormat.removeLeadingZeros] of the format being built, empty string by default. */
|
||||
var removeLeadingZeros: Boolean = Default.removeLeadingZeros
|
||||
|
||||
internal fun build(): NumberHexFormat {
|
||||
return NumberHexFormat(prefix, suffix, removeLeadingZeros)
|
||||
}
|
||||
}
|
||||
|
||||
internal companion object {
|
||||
internal val Default = NumberHexFormat(
|
||||
prefix = "",
|
||||
suffix = "",
|
||||
removeLeadingZeros = false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A context for building a [HexFormat]. Provides API for configuring format options.
|
||||
*/
|
||||
public class Builder @PublishedApi internal constructor() {
|
||||
/** Defines [HexFormat.upperCase] of the format being built, `false` by default. */
|
||||
var upperCase: Boolean = Default.upperCase
|
||||
|
||||
/**
|
||||
* Defines [HexFormat.bytes] of the format being built.
|
||||
*
|
||||
* See [BytesHexFormat.Builder] for default values of the options.
|
||||
*/
|
||||
val bytes: BytesHexFormat.Builder
|
||||
get() {
|
||||
if (_bytes == null) {
|
||||
_bytes = BytesHexFormat.Builder()
|
||||
}
|
||||
return _bytes!!
|
||||
}
|
||||
|
||||
private var _bytes: BytesHexFormat.Builder? = null
|
||||
|
||||
/**
|
||||
* Defines [HexFormat.number] of the format being built.
|
||||
*
|
||||
* See [NumberHexFormat.Builder] for default values of the options.
|
||||
*/
|
||||
val number: NumberHexFormat.Builder
|
||||
get() {
|
||||
if (_number == null) {
|
||||
_number = NumberHexFormat.Builder()
|
||||
}
|
||||
return _number!!
|
||||
}
|
||||
|
||||
private var _number: NumberHexFormat.Builder? = null
|
||||
|
||||
/**
|
||||
* Provides a scope for configuring the [HexFormat.bytes] format options.
|
||||
*
|
||||
* See [BytesHexFormat.Builder] for default values of the options.
|
||||
*/
|
||||
@InlineOnly
|
||||
inline fun bytes(builderAction: BytesHexFormat.Builder.() -> Unit) {
|
||||
bytes.builderAction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a scope for configuring the [HexFormat.number] format options.
|
||||
*
|
||||
* See [NumberHexFormat.Builder] for default values of the options.
|
||||
*/
|
||||
@InlineOnly
|
||||
inline fun number(builderAction: NumberHexFormat.Builder.() -> Unit) {
|
||||
number.builderAction()
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
internal fun build(): HexFormat {
|
||||
return HexFormat(
|
||||
upperCase,
|
||||
_bytes?.build() ?: BytesHexFormat.Default,
|
||||
_number?.build() ?: NumberHexFormat.Default
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* The default hexadecimal format options.
|
||||
*
|
||||
* Uses lower case hexadecimal digits `0-9`, `a-f` when formatting
|
||||
* both `ByteArray` and numeric values. That is [upperCase] is `false`.
|
||||
*
|
||||
* No line separator, group separator, byte separator, byte prefix or byte suffix is used
|
||||
* when formatting or parsing `ByteArray`. That is:
|
||||
* * [BytesHexFormat.bytesPerLine] is `Int.MAX_VALUE`.
|
||||
* * [BytesHexFormat.bytesPerGroup] is `Int.MAX_VALUE`.
|
||||
* * [BytesHexFormat.byteSeparator], [BytesHexFormat.bytePrefix] and [BytesHexFormat.byteSuffix] are empty strings.
|
||||
*
|
||||
* No prefix or suffix is used, and no leading zeros in hexadecimal representation are removed
|
||||
* when formatting or parsing a numeric value. That is:
|
||||
* * [NumberHexFormat.prefix] and [NumberHexFormat.suffix] are empty strings.
|
||||
* * [NumberHexFormat.removeLeadingZeros] is `false`.
|
||||
*/
|
||||
public val Default: HexFormat = HexFormat(
|
||||
upperCase = false,
|
||||
bytes = BytesHexFormat.Default,
|
||||
number = NumberHexFormat.Default,
|
||||
)
|
||||
|
||||
/**
|
||||
* Uses upper case hexadecimal digits `0-9`, `A-F` when formatting
|
||||
* both `ByteArray` and numeric values. That is [upperCase] is `true`.
|
||||
*
|
||||
* The same as [Default] format in other aspects.
|
||||
*/
|
||||
public val UpperCase: HexFormat = HexFormat(
|
||||
upperCase = true,
|
||||
bytes = BytesHexFormat.Default,
|
||||
number = NumberHexFormat.Default,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a new [HexFormat] by configuring its format options using the specified [builderAction],
|
||||
* and returns the resulting format.
|
||||
*
|
||||
* The builder passed as a receiver to the [builderAction] is valid only inside that function.
|
||||
* Using it outside of the function produces an unspecified behavior.
|
||||
*/
|
||||
@ExperimentalStdlibApi
|
||||
@SinceKotlin("1.9")
|
||||
@InlineOnly
|
||||
public inline fun HexFormat(builderAction: HexFormat.Builder.() -> Unit): HexFormat {
|
||||
return HexFormat.Builder().apply(builderAction).build()
|
||||
}
|
||||
@@ -0,0 +1,287 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package kotlin.text
|
||||
|
||||
import kotlin.internal.InlineOnly
|
||||
|
||||
// -------------------------- format and parse UByteArray --------------------------
|
||||
|
||||
/**
|
||||
* Formats bytes in this array using the specified [format].
|
||||
*
|
||||
* Note that only [HexFormat.upperCase] and [HexFormat.BytesHexFormat] affect formatting.
|
||||
*
|
||||
* @param format the [HexFormat] to use for formatting, [HexFormat.Default] by default.
|
||||
*
|
||||
* @throws IllegalArgumentException if the result length is more than [String] maximum capacity.
|
||||
*/
|
||||
@ExperimentalStdlibApi
|
||||
@SinceKotlin("1.9")
|
||||
@ExperimentalUnsignedTypes
|
||||
@InlineOnly
|
||||
public inline fun UByteArray.toHexString(format: HexFormat = HexFormat.Default): String = storage.toHexString(format)
|
||||
|
||||
/**
|
||||
* Formats bytes in this array using the specified [HexFormat].
|
||||
*
|
||||
* Note that only [HexFormat.upperCase] and [HexFormat.BytesHexFormat] affect formatting.
|
||||
*
|
||||
* @param startIndex the beginning (inclusive) of the subrange to format, 0 by default.
|
||||
* @param endIndex the end (exclusive) of the subrange to format, size of this array by default.
|
||||
* @param format the [HexFormat] to use for formatting, [HexFormat.Default] by default.
|
||||
*
|
||||
* @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of this array indices.
|
||||
* @throws IllegalArgumentException when `startIndex > endIndex`.
|
||||
* @throws IllegalArgumentException if the result length is more than [String] maximum capacity.
|
||||
*/
|
||||
@ExperimentalStdlibApi
|
||||
@SinceKotlin("1.9")
|
||||
@ExperimentalUnsignedTypes
|
||||
@InlineOnly
|
||||
public inline fun UByteArray.toHexString(
|
||||
startIndex: Int = 0,
|
||||
endIndex: Int = size,
|
||||
format: HexFormat = HexFormat.Default
|
||||
): String = storage.toHexString(startIndex, endIndex, format)
|
||||
|
||||
/**
|
||||
* Parses bytes from this string using the specified [HexFormat].
|
||||
*
|
||||
* Note that only [HexFormat.BytesHexFormat] affects parsing,
|
||||
* and parsing is performed in case-insensitive manner.
|
||||
* Also, any of the char sequences CRLF, LF and CR is considered a valid line separator.
|
||||
*
|
||||
* @param format the [HexFormat] to use for parsing, [HexFormat.Default] by default.
|
||||
*
|
||||
* @throws IllegalArgumentException if this string does not comply with the specified [format].
|
||||
*/
|
||||
@ExperimentalStdlibApi
|
||||
@SinceKotlin("1.9")
|
||||
@ExperimentalUnsignedTypes
|
||||
@InlineOnly
|
||||
public inline fun String.hexToUByteArray(format: HexFormat = HexFormat.Default): UByteArray =
|
||||
hexToByteArray(format).asUByteArray()
|
||||
|
||||
///**
|
||||
// * Parses bytes from this string using the specified [HexFormat].
|
||||
// *
|
||||
// * Note that only [HexFormat.BytesHexFormat] affects parsing,
|
||||
// * and parsing is performed in case-insensitive manner.
|
||||
// * Also, any of the char sequences CRLF, LF and CR is considered a valid line separator.
|
||||
// *
|
||||
// * @param startIndex the beginning (inclusive) of the substring to parse, 0 by default.
|
||||
// * @param endIndex the end (exclusive) of the substring to parse, length of this string by default.
|
||||
// * @param format the [HexFormat] to use for parsing, [HexFormat.Default] by default.
|
||||
// *
|
||||
// * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of this string indices.
|
||||
// * @throws IllegalArgumentException when `startIndex > endIndex`.
|
||||
// * @throws IllegalArgumentException if the substring does not comply with the specified [format].
|
||||
// */
|
||||
//@ExperimentalStdlibApi
|
||||
//@SinceKotlin("1.9")
|
||||
//@ExperimentalUnsignedTypes
|
||||
//@InlineOnly
|
||||
//public inline fun String.hexToUByteArray(
|
||||
// startIndex: Int = 0,
|
||||
// endIndex: Int = length,
|
||||
// format: HexFormat = HexFormat.Default
|
||||
//): UByteArray = hexToByteArray(startIndex, endIndex, format).asUByteArray()
|
||||
|
||||
// -------------------------- format and parse UByte --------------------------
|
||||
|
||||
/**
|
||||
* Formats this `UByte` value using the specified [format].
|
||||
*
|
||||
* Note that only [HexFormat.upperCase] and [HexFormat.NumberHexFormat] affect formatting.
|
||||
*
|
||||
* @param format the [HexFormat] to use for formatting, [HexFormat.Default] by default.
|
||||
*/
|
||||
@ExperimentalStdlibApi
|
||||
@SinceKotlin("1.9")
|
||||
@InlineOnly
|
||||
public inline fun UByte.toHexString(format: HexFormat = HexFormat.Default): String = data.toHexString(format)
|
||||
|
||||
/**
|
||||
* Parses an `UByte` value from this string using the specified [format].
|
||||
*
|
||||
* Note that only [HexFormat.NumberHexFormat] affects parsing,
|
||||
* and parsing is performed in case-insensitive manner.
|
||||
*
|
||||
* @param format the [HexFormat] to use for parsing, [HexFormat.Default] by default.
|
||||
*
|
||||
* @throws IllegalArgumentException if this string does not comply with the specified [format].
|
||||
*/
|
||||
@ExperimentalStdlibApi
|
||||
@SinceKotlin("1.9")
|
||||
@InlineOnly
|
||||
public inline fun String.hexToUByte(format: HexFormat = HexFormat.Default): UByte = hexToByte(format).toUByte()
|
||||
|
||||
///**
|
||||
// * Parses an `UByte` value from this string using the specified [format].
|
||||
// *
|
||||
// * Note that only [HexFormat.NumberHexFormat] affects parsing,
|
||||
// * and parsing is performed in case-insensitive manner.
|
||||
// *
|
||||
// * @param startIndex the beginning (inclusive) of the substring to parse, 0 by default.
|
||||
// * @param endIndex the end (exclusive) of the substring to parse, length of this string by default.
|
||||
// * @param format the [HexFormat] to use for parsing, [HexFormat.Default] by default.
|
||||
// *
|
||||
// * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of this string indices.
|
||||
// * @throws IllegalArgumentException when `startIndex > endIndex`.
|
||||
// * @throws IllegalArgumentException if the substring does not comply with the specified [format].
|
||||
// */
|
||||
//@ExperimentalStdlibApi
|
||||
//@SinceKotlin("1.9")
|
||||
//@InlineOnly
|
||||
//public inline fun String.hexToUByte(startIndex: Int = 0, endIndex: Int = length, format: HexFormat = HexFormat.Default): UByte =
|
||||
// hexToByte(startIndex, endIndex, format).toUByte()
|
||||
|
||||
// -------------------------- format and parse UShort --------------------------
|
||||
|
||||
/**
|
||||
* Formats this `UShort` value using the specified [format].
|
||||
*
|
||||
* Note that only [HexFormat.upperCase] and [HexFormat.NumberHexFormat] affect formatting.
|
||||
*
|
||||
* @param format the [HexFormat] to use for formatting, [HexFormat.Default] by default.
|
||||
*/
|
||||
@ExperimentalStdlibApi
|
||||
@SinceKotlin("1.9")
|
||||
@InlineOnly
|
||||
public inline fun UShort.toHexString(format: HexFormat = HexFormat.Default): String = data.toHexString(format)
|
||||
|
||||
/**
|
||||
* Parses an `UShort` value from this string using the specified [format].
|
||||
*
|
||||
* Note that only [HexFormat.NumberHexFormat] affects parsing,
|
||||
* and parsing is performed in case-insensitive manner.
|
||||
*
|
||||
* @param format the [HexFormat] to use for parsing, [HexFormat.Default] by default.
|
||||
*
|
||||
* @throws IllegalArgumentException if this string does not comply with the specified [format].
|
||||
*/
|
||||
@ExperimentalStdlibApi
|
||||
@SinceKotlin("1.9")
|
||||
@InlineOnly
|
||||
public inline fun String.hexToUShort(format: HexFormat = HexFormat.Default): UShort = hexToShort(format).toUShort()
|
||||
|
||||
///**
|
||||
// * Parses an `UShort` value from this string using the specified [format].
|
||||
// *
|
||||
// * Note that only [HexFormat.NumberHexFormat] affects parsing,
|
||||
// * and parsing is performed in case-insensitive manner.
|
||||
// *
|
||||
// * @param startIndex the beginning (inclusive) of the substring to parse, 0 by default.
|
||||
// * @param endIndex the end (exclusive) of the substring to parse, length of this string by default.
|
||||
// * @param format the [HexFormat] to use for parsing, [HexFormat.Default] by default.
|
||||
// *
|
||||
// * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of this string indices.
|
||||
// * @throws IllegalArgumentException when `startIndex > endIndex`.
|
||||
// * @throws IllegalArgumentException if the substring does not comply with the specified [format].
|
||||
// */
|
||||
//@ExperimentalStdlibApi
|
||||
//@SinceKotlin("1.9")
|
||||
//@InlineOnly
|
||||
//public inline fun String.hexToUShort(startIndex: Int = 0, endIndex: Int = length, format: HexFormat = HexFormat.Default): UShort =
|
||||
// hexToShort(startIndex, endIndex, format).toUShort()
|
||||
|
||||
// -------------------------- format and parse UInt --------------------------
|
||||
|
||||
/**
|
||||
* Formats this `UInt` value using the specified [format].
|
||||
*
|
||||
* Note that only [HexFormat.upperCase] and [HexFormat.NumberHexFormat] affect formatting.
|
||||
*
|
||||
* @param format the [HexFormat] to use for formatting, [HexFormat.Default] by default.
|
||||
*/
|
||||
@ExperimentalStdlibApi
|
||||
@SinceKotlin("1.9")
|
||||
@InlineOnly
|
||||
public inline fun UInt.toHexString(format: HexFormat = HexFormat.Default): String = data.toHexString(format)
|
||||
|
||||
/**
|
||||
* Parses an `UInt` value from this string using the specified [format].
|
||||
*
|
||||
* Note that only [HexFormat.NumberHexFormat] affects parsing,
|
||||
* and parsing is performed in case-insensitive manner.
|
||||
*
|
||||
* @param format the [HexFormat] to use for parsing, [HexFormat.Default] by default.
|
||||
*
|
||||
* @throws IllegalArgumentException if this string does not comply with the specified [format].
|
||||
*/
|
||||
@ExperimentalStdlibApi
|
||||
@SinceKotlin("1.9")
|
||||
@InlineOnly
|
||||
public inline fun String.hexToUInt(format: HexFormat = HexFormat.Default): UInt = hexToInt(format).toUInt()
|
||||
|
||||
///**
|
||||
// * Parses an `UInt` value from this string using the specified [format].
|
||||
// *
|
||||
// * Note that only [HexFormat.NumberHexFormat] affects parsing,
|
||||
// * and parsing is performed in case-insensitive manner.
|
||||
// *
|
||||
// * @param startIndex the beginning (inclusive) of the substring to parse, 0 by default.
|
||||
// * @param endIndex the end (exclusive) of the substring to parse, length of this string by default.
|
||||
// * @param format the [HexFormat] to use for parsing, [HexFormat.Default] by default.
|
||||
// *
|
||||
// * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of this string indices.
|
||||
// * @throws IllegalArgumentException when `startIndex > endIndex`.
|
||||
// * @throws IllegalArgumentException if the substring does not comply with the specified [format].
|
||||
// */
|
||||
//@ExperimentalStdlibApi
|
||||
//@SinceKotlin("1.9")
|
||||
//@InlineOnly
|
||||
//public inline fun String.hexToUInt(startIndex: Int = 0, endIndex: Int = length, format: HexFormat = HexFormat.Default): UInt =
|
||||
// hexToInt(startIndex, endIndex, format).toUInt()
|
||||
|
||||
// -------------------------- format and parse ULong --------------------------
|
||||
|
||||
/**
|
||||
* Formats this `ULong` value using the specified [format].
|
||||
*
|
||||
* Note that only [HexFormat.upperCase] and [HexFormat.NumberHexFormat] affect formatting.
|
||||
*
|
||||
* @param format the [HexFormat] to use for formatting, [HexFormat.Default] by default.
|
||||
*/
|
||||
@ExperimentalStdlibApi
|
||||
@SinceKotlin("1.9")
|
||||
@InlineOnly
|
||||
public inline fun ULong.toHexString(format: HexFormat = HexFormat.Default): String = data.toHexString(format)
|
||||
|
||||
/**
|
||||
* Parses an `ULong` value from this string using the specified [format].
|
||||
*
|
||||
* Note that only [HexFormat.NumberHexFormat] affects parsing,
|
||||
* and parsing is performed in case-insensitive manner.
|
||||
*
|
||||
* @param format the [HexFormat] to use for parsing, [HexFormat.Default] by default.
|
||||
*
|
||||
* @throws IllegalArgumentException if this string does not comply with the specified [format].
|
||||
*/
|
||||
@ExperimentalStdlibApi
|
||||
@SinceKotlin("1.9")
|
||||
@InlineOnly
|
||||
public inline fun String.hexToULong(format: HexFormat = HexFormat.Default): ULong = hexToLong(format).toULong()
|
||||
|
||||
///**
|
||||
// * Parses an `ULong` value from this string using the specified [format].
|
||||
// *
|
||||
// * Note that only [HexFormat.NumberHexFormat] affects parsing,
|
||||
// * and parsing is performed in case-insensitive manner.
|
||||
// *
|
||||
// * @param startIndex the beginning (inclusive) of the substring to parse, 0 by default.
|
||||
// * @param endIndex the end (exclusive) of the substring to parse, length of this string by default.
|
||||
// * @param format the [HexFormat] to use for parsing, [HexFormat.Default] by default.
|
||||
// *
|
||||
// * @throws IndexOutOfBoundsException when [startIndex] or [endIndex] is out of range of this string indices.
|
||||
// * @throws IllegalArgumentException when `startIndex > endIndex`.
|
||||
// * @throws IllegalArgumentException if the substring does not comply with the specified [format].
|
||||
// */
|
||||
//@ExperimentalStdlibApi
|
||||
//@SinceKotlin("1.9")
|
||||
//@InlineOnly
|
||||
//public inline fun String.hexToULong(startIndex: Int = 0, endIndex: Int = length, format: HexFormat = HexFormat.Default): ULong =
|
||||
// hexToLong(startIndex, endIndex, format).toULong()
|
||||
@@ -0,0 +1,569 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package test.text
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
class BytesHexFormatTest {
|
||||
companion object {
|
||||
val fourBytes = ByteArray(4) { (10 + it).toByte() }
|
||||
val twentyBytes = ByteArray(20) { it.toByte() }
|
||||
}
|
||||
|
||||
private fun testFormatAndParse(bytes: ByteArray, expected: String, format: HexFormat) {
|
||||
assertEquals(expected, bytes.toHexString(format))
|
||||
assertContentEquals(bytes, expected.hexToByteArray(format))
|
||||
|
||||
assertEquals(expected, bytes.asUByteArray().toHexString(format))
|
||||
assertContentEquals(bytes.asUByteArray(), expected.hexToUByteArray(format))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun ignoreNumberFormat() {
|
||||
val format = HexFormat {
|
||||
number {
|
||||
prefix = "0x"
|
||||
suffix = "h"
|
||||
removeLeadingZeros = true
|
||||
}
|
||||
}
|
||||
|
||||
testFormatAndParse(fourBytes, "0a0b0c0d", format)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun upperCase() {
|
||||
testFormatAndParse(fourBytes, "0A0B0C0D", HexFormat { upperCase = true })
|
||||
testFormatAndParse(fourBytes, "0A0B0C0D", HexFormat.UpperCase)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun lowerCase() {
|
||||
testFormatAndParse(fourBytes, "0a0b0c0d", HexFormat { upperCase = false })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun defaultCase() {
|
||||
testFormatAndParse(fourBytes, "0a0b0c0d", HexFormat.Default)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun byteSeparatorPrefixSuffix() {
|
||||
// byteSeparator
|
||||
testFormatAndParse(fourBytes, "0a 0b 0c 0d", HexFormat { bytes.byteSeparator = " " })
|
||||
// bytePrefix
|
||||
testFormatAndParse(fourBytes, "0x0a0x0b0x0c0x0d", HexFormat { bytes.bytePrefix = "0x" })
|
||||
// bytePrefix
|
||||
testFormatAndParse(fourBytes, "0a;0b;0c;0d;", HexFormat { bytes.byteSuffix = ";" })
|
||||
// all together
|
||||
testFormatAndParse(fourBytes, "0x0a; 0x0b; 0x0c; 0x0d;", HexFormat {
|
||||
bytes {
|
||||
byteSeparator = " "
|
||||
bytePrefix = "0x"
|
||||
byteSuffix = ";"
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@Test
|
||||
fun bytesPerLine() {
|
||||
// Fewer bytes in the last line
|
||||
run {
|
||||
val format = HexFormat { bytes.bytesPerLine = 8 }
|
||||
val expected = "0001020304050607\n08090a0b0c0d0e0f\n10111213"
|
||||
testFormatAndParse(twentyBytes, expected, format)
|
||||
}
|
||||
|
||||
// The last line is not ended by the line separator
|
||||
run {
|
||||
val format = HexFormat { bytes.bytesPerLine = 4 }
|
||||
val expected = "00010203\n04050607\n08090a0b\n0c0d0e0f\n10111213"
|
||||
testFormatAndParse(twentyBytes, expected, format)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun bytesPerGroup() {
|
||||
// The default group separator, and fewer bytes in the last group
|
||||
run {
|
||||
val format = HexFormat { bytes.bytesPerGroup = 8 }
|
||||
val expected = "0001020304050607 08090a0b0c0d0e0f 10111213"
|
||||
testFormatAndParse(twentyBytes, expected, format)
|
||||
}
|
||||
|
||||
// The specified group separator
|
||||
run {
|
||||
val format = HexFormat {
|
||||
bytes {
|
||||
bytesPerGroup = 8
|
||||
groupSeparator = "---"
|
||||
}
|
||||
}
|
||||
val expected = "0001020304050607---08090a0b0c0d0e0f---10111213"
|
||||
testFormatAndParse(twentyBytes, expected, format)
|
||||
}
|
||||
|
||||
// The last group is not ended by the group separator
|
||||
run {
|
||||
val format = HexFormat { bytes.bytesPerGroup = 4 }
|
||||
val expected = "00010203 04050607 08090a0b 0c0d0e0f 10111213"
|
||||
testFormatAndParse(twentyBytes, expected, format)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun bytesPerLineAndBytesPerGroup() {
|
||||
val format = HexFormat {
|
||||
upperCase = true
|
||||
bytes {
|
||||
bytesPerLine = 10
|
||||
bytesPerGroup = 4
|
||||
groupSeparator = "---"
|
||||
byteSeparator = " "
|
||||
bytePrefix = "#"
|
||||
byteSuffix = ";"
|
||||
}
|
||||
}
|
||||
|
||||
val byteArray = ByteArray(31) { it.toByte() }
|
||||
|
||||
val expected = """
|
||||
#00; #01; #02; #03;---#04; #05; #06; #07;---#08; #09;
|
||||
#0A; #0B; #0C; #0D;---#0E; #0F; #10; #11;---#12; #13;
|
||||
#14; #15; #16; #17;---#18; #19; #1A; #1B;---#1C; #1D;
|
||||
#1E;
|
||||
""".trimIndent()
|
||||
|
||||
testFormatAndParse(byteArray, expected, format)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun macAddress() {
|
||||
val address = byteArrayOf(0x00, 0x1b, 0x63, 0x84.toByte(), 0x45, 0xe6.toByte())
|
||||
|
||||
testFormatAndParse(
|
||||
address,
|
||||
"00:1b:63:84:45:e6",
|
||||
HexFormat { bytes.byteSeparator = ":" }
|
||||
)
|
||||
testFormatAndParse(
|
||||
address,
|
||||
"00-1B-63-84-45-E6",
|
||||
HexFormat { upperCase = true; bytes.byteSeparator = "-" }
|
||||
)
|
||||
testFormatAndParse(
|
||||
address,
|
||||
"001B.6384.45E6",
|
||||
HexFormat { upperCase = true; bytes.bytesPerGroup = 2; bytes.groupSeparator = "." }
|
||||
)
|
||||
}
|
||||
|
||||
// Parsing strictness
|
||||
|
||||
@Test
|
||||
fun parseRequiresTwoDigitsPerByte() {
|
||||
assertContentEquals(byteArrayOf(58), "3a".hexToByteArray())
|
||||
assertFailsWith<NumberFormatException> {
|
||||
"a".hexToByteArray()
|
||||
}
|
||||
assertFailsWith<NumberFormatException> {
|
||||
"03a".hexToByteArray()
|
||||
}
|
||||
|
||||
val format = HexFormat { bytes.bytePrefix = "#"; bytes.byteSuffix = ";" }
|
||||
assertContentEquals(byteArrayOf(58), "#3a;".hexToByteArray(format))
|
||||
assertFailsWith<NumberFormatException> {
|
||||
"#a;".hexToByteArray(format)
|
||||
}
|
||||
assertFailsWith<NumberFormatException> {
|
||||
"#03a;".hexToByteArray(format)
|
||||
}
|
||||
|
||||
assertFailsWith<NumberFormatException> {
|
||||
"0a0b0c0".hexToByteArray()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseIgnoresCase() {
|
||||
assertContentEquals(
|
||||
twentyBytes,
|
||||
"000102030405060708090A0B0C0D0E0F10111213".hexToByteArray()
|
||||
)
|
||||
val hexString = "0x00H bs 0x01H bS 0x02H Bs 0x03H gs " +
|
||||
"0x04h BS 0x05h bs 0x06h Bs 0x07h Gs " +
|
||||
"0X08H bS 0X09H Bs 0X0aH bs 0X0bH gS " +
|
||||
"0X0Ch bs 0X0Dh BS 0X0Eh Bs 0X0Fh GS " +
|
||||
"0x10H Bs 0x11H bS 0x12H BS 0x13H"
|
||||
val format = HexFormat {
|
||||
upperCase = true
|
||||
bytes {
|
||||
bytesPerGroup = 4
|
||||
groupSeparator = " gs "
|
||||
byteSeparator = " bs "
|
||||
bytePrefix = "0x"
|
||||
byteSuffix = "h"
|
||||
}
|
||||
}
|
||||
assertContentEquals(
|
||||
twentyBytes,
|
||||
hexString.hexToByteArray(format)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseAcceptsAllNewLineSequences() {
|
||||
assertContentEquals(
|
||||
twentyBytes,
|
||||
"00010203\n04050607\r\n08090a0b\r0c0d0e0f\r\n10111213".hexToByteArray(HexFormat { bytes.bytesPerLine = 4 })
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseMultipleNewLines() {
|
||||
assertFailsWith<NumberFormatException> {
|
||||
"00010203\n\r04050607\r\n08090a0b\r0c0d0e0f\r\n10111213".hexToByteArray(HexFormat { bytes.bytesPerLine = 4 })
|
||||
}
|
||||
assertFailsWith<NumberFormatException> {
|
||||
"00010203\n\n04050607\r\n08090a0b\r0c0d0e0f\r\n10111213".hexToByteArray(HexFormat { bytes.bytesPerLine = 4 })
|
||||
}
|
||||
assertFailsWith<NumberFormatException> {
|
||||
"00010203\n04050607\r\n\n08090a0b\r0c0d0e0f\r\n10111213".hexToByteArray(HexFormat { bytes.bytesPerLine = 4 })
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseNewLineAtEnd() {
|
||||
assertFailsWith<NumberFormatException> {
|
||||
"00010203\n04050607\r\n08090a0b\r0c0d0e0f\r\n10111213\n".hexToByteArray(HexFormat { bytes.bytesPerLine = 4 })
|
||||
}
|
||||
}
|
||||
|
||||
// HexFormat corner-case options configuration
|
||||
|
||||
@Test
|
||||
fun emptyGroupSeparator() {
|
||||
testFormatAndParse(
|
||||
twentyBytes,
|
||||
"000102030405060708090a0b0c0d0e0f10111213",
|
||||
HexFormat { bytes.bytesPerGroup = 8; bytes.groupSeparator = "" }
|
||||
)
|
||||
testFormatAndParse(
|
||||
twentyBytes,
|
||||
"00 01 02 03 04 05 06 0708 09 0a 0b 0c 0d 0e 0f10 11 12 13",
|
||||
HexFormat { bytes.bytesPerGroup = 8; bytes.groupSeparator = ""; bytes.byteSeparator = " " }
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun bytesPerGroupBiggerThanBytesPerLine() {
|
||||
val format = HexFormat { bytes.bytesPerLine = 8; bytes.bytesPerGroup = 10 }
|
||||
val expected = "0001020304050607\n08090a0b0c0d0e0f\n10111213"
|
||||
testFormatAndParse(twentyBytes, expected, format)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun groupSeparatorWithNewLine() {
|
||||
val format = HexFormat { bytes.bytesPerLine = 8; bytes.bytesPerGroup = 3; bytes.groupSeparator = "\n" }
|
||||
val expected = "000102\n030405\n0607\n08090a\n0b0c0d\n0e0f\n101112\n13"
|
||||
testFormatAndParse(twentyBytes, expected, format)
|
||||
}
|
||||
|
||||
// Invalid HexFormat configuration
|
||||
|
||||
@Test
|
||||
fun nonPositiveBytesPerLine() {
|
||||
assertFailsWith<IllegalArgumentException> { HexFormat { bytes.bytesPerLine = 0 } }
|
||||
assertFailsWith<IllegalArgumentException> { HexFormat { bytes.bytesPerLine = -1 } }
|
||||
assertFailsWith<IllegalArgumentException> { HexFormat { bytes.bytesPerLine = Int.MIN_VALUE } }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun nonPositiveBytesPerGroup() {
|
||||
assertFailsWith<IllegalArgumentException> { HexFormat { bytes.bytesPerGroup = 0 } }
|
||||
assertFailsWith<IllegalArgumentException> { HexFormat { bytes.bytesPerGroup = -1 } }
|
||||
assertFailsWith<IllegalArgumentException> { HexFormat { bytes.bytesPerGroup = Int.MIN_VALUE } }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun byteSeparatorWithNewLine() {
|
||||
assertFailsWith<IllegalArgumentException> { HexFormat { bytes.byteSeparator = "ab\n" } }
|
||||
assertFailsWith<IllegalArgumentException> { HexFormat { bytes.byteSeparator = "\rcd" } }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun bytePrefixWithNewLine() {
|
||||
assertFailsWith<IllegalArgumentException> { HexFormat { bytes.bytePrefix = "\n" } }
|
||||
assertFailsWith<IllegalArgumentException> { HexFormat { bytes.bytePrefix = "\r" } }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun byteSuffixWithNewLine() {
|
||||
assertFailsWith<IllegalArgumentException> { HexFormat { bytes.byteSuffix = "ab\n\rcd" } }
|
||||
assertFailsWith<IllegalArgumentException> { HexFormat { bytes.byteSuffix = "ab\r\ncd" } }
|
||||
}
|
||||
|
||||
// format.toString()
|
||||
|
||||
@Test
|
||||
fun formatToString() {
|
||||
val format = HexFormat {
|
||||
upperCase = true
|
||||
bytes {
|
||||
bytesPerLine = 10
|
||||
bytesPerGroup = 4
|
||||
groupSeparator = "---"
|
||||
byteSeparator = " "
|
||||
bytePrefix = "#"
|
||||
byteSuffix = ";"
|
||||
}
|
||||
number {
|
||||
prefix = "0x"
|
||||
suffix = "h"
|
||||
removeLeadingZeros = true
|
||||
}
|
||||
}
|
||||
|
||||
val expectedHexFormat = """
|
||||
HexFormat(
|
||||
upperCase = true,
|
||||
bytes = BytesHexFormat(
|
||||
bytesPerLine = 10,
|
||||
bytesPerGroup = 4,
|
||||
groupSeparator = "---",
|
||||
byteSeparator = " ",
|
||||
bytePrefix = "#",
|
||||
byteSuffix = ";"
|
||||
),
|
||||
number = NumberHexFormat(
|
||||
prefix = "0x",
|
||||
suffix = "h",
|
||||
removeLeadingZeros = true
|
||||
)
|
||||
)
|
||||
""".trimIndent()
|
||||
assertEquals(expectedHexFormat, format.toString())
|
||||
|
||||
val expectedBytesHexFormat = """
|
||||
BytesHexFormat(
|
||||
bytesPerLine = 10,
|
||||
bytesPerGroup = 4,
|
||||
groupSeparator = "---",
|
||||
byteSeparator = " ",
|
||||
bytePrefix = "#",
|
||||
byteSuffix = ";"
|
||||
)
|
||||
""".trimIndent()
|
||||
assertEquals(expectedBytesHexFormat, format.bytes.toString())
|
||||
|
||||
val expectedNumberHexFormat = """
|
||||
NumberHexFormat(
|
||||
prefix = "0x",
|
||||
suffix = "h",
|
||||
removeLeadingZeros = true
|
||||
)
|
||||
""".trimIndent()
|
||||
assertEquals(expectedNumberHexFormat, format.number.toString())
|
||||
}
|
||||
|
||||
// Create format objects on demand
|
||||
|
||||
@Test
|
||||
fun createOnDemand() {
|
||||
assertSame(HexFormat.Default.bytes, HexFormat.UpperCase.bytes)
|
||||
assertSame(HexFormat.Default.number, HexFormat.UpperCase.number)
|
||||
|
||||
val emptyFormat = HexFormat {}
|
||||
assertNotSame(HexFormat.Default, emptyFormat)
|
||||
assertEquals(HexFormat.Default.upperCase, emptyFormat.upperCase)
|
||||
assertSame(HexFormat.Default.bytes, emptyFormat.bytes)
|
||||
assertSame(HexFormat.Default.number, emptyFormat.number)
|
||||
|
||||
val bytesFormat = HexFormat { bytes }
|
||||
assertNotSame(HexFormat.Default.bytes, bytesFormat.bytes)
|
||||
assertSame(HexFormat.Default.number, bytesFormat.number)
|
||||
|
||||
val numberFormat = HexFormat { number {} }
|
||||
assertSame(HexFormat.Default.bytes, numberFormat.bytes)
|
||||
assertNotSame(HexFormat.Default.number, numberFormat.number)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun formattedStringLength() {
|
||||
run {
|
||||
// 00010203\n04050607\n08090a0b\n0c0d0e0f\n10111213
|
||||
val length = formattedStringLength(
|
||||
totalBytes = 20,
|
||||
bytesPerLine = 4,
|
||||
bytesPerGroup = Int.MAX_VALUE,
|
||||
groupSeparatorLength = 2,
|
||||
byteSeparatorLength = 0,
|
||||
bytePrefixLength = 0,
|
||||
byteSuffixLength = 0
|
||||
)
|
||||
assertEquals(44, length)
|
||||
}
|
||||
run {
|
||||
// 0001020304050607---08090a0b0c0d0e0f---10111213
|
||||
val length = formattedStringLength(
|
||||
totalBytes = 20,
|
||||
bytesPerLine = Int.MAX_VALUE,
|
||||
bytesPerGroup = 8,
|
||||
groupSeparatorLength = 3,
|
||||
byteSeparatorLength = 0,
|
||||
bytePrefixLength = 0,
|
||||
byteSuffixLength = 0
|
||||
)
|
||||
assertEquals(46, length)
|
||||
}
|
||||
run {
|
||||
// #00; #01; #02; #03;---#04; #05; #06; #07;---#08; #09;
|
||||
// #0A; #0B; #0C; #0D;---#0E; #0F; #10; #11;---#12; #13;
|
||||
// #14; #15; #16; #17;---#18; #19; #1A; #1B;---#1C; #1D;
|
||||
// #1E;
|
||||
val length = formattedStringLength(
|
||||
totalBytes = 31,
|
||||
bytesPerLine = 10,
|
||||
bytesPerGroup = 4,
|
||||
groupSeparatorLength = 3,
|
||||
byteSeparatorLength = 1,
|
||||
bytePrefixLength = 1,
|
||||
byteSuffixLength = 1
|
||||
)
|
||||
assertEquals(166, length)
|
||||
}
|
||||
run {
|
||||
val length = formattedStringLength(
|
||||
totalBytes = Int.MAX_VALUE / 2,
|
||||
bytesPerLine = Int.MAX_VALUE,
|
||||
bytesPerGroup = Int.MAX_VALUE,
|
||||
groupSeparatorLength = 0,
|
||||
byteSeparatorLength = 0,
|
||||
bytePrefixLength = 0,
|
||||
byteSuffixLength = 0
|
||||
)
|
||||
assertEquals(Int.MAX_VALUE / 2 * 2, length)
|
||||
}
|
||||
assertFailsWith<IllegalArgumentException> {
|
||||
formattedStringLength(
|
||||
totalBytes = Int.MAX_VALUE,
|
||||
bytesPerLine = Int.MAX_VALUE,
|
||||
bytesPerGroup = Int.MAX_VALUE,
|
||||
groupSeparatorLength = Int.MAX_VALUE,
|
||||
byteSeparatorLength = Int.MAX_VALUE,
|
||||
bytePrefixLength = Int.MAX_VALUE,
|
||||
byteSuffixLength = Int.MAX_VALUE
|
||||
)
|
||||
}
|
||||
assertFailsWith<IllegalArgumentException> {
|
||||
formattedStringLength(
|
||||
totalBytes = Int.MAX_VALUE / 2 + 1,
|
||||
bytesPerLine = Int.MAX_VALUE,
|
||||
bytesPerGroup = Int.MAX_VALUE,
|
||||
groupSeparatorLength = 0,
|
||||
byteSeparatorLength = 0,
|
||||
bytePrefixLength = 0,
|
||||
byteSuffixLength = 0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parsedByteArrayMaxSize() {
|
||||
run {
|
||||
// 00010203\n04050607\n08090a0b\n0c0d0e0f\n10111213
|
||||
val maxSize = parsedByteArrayMaxSize(
|
||||
stringLength = 44,
|
||||
bytesPerLine = 4,
|
||||
bytesPerGroup = Int.MAX_VALUE,
|
||||
groupSeparatorLength = 2,
|
||||
byteSeparatorLength = 0,
|
||||
bytePrefixLength = 0,
|
||||
byteSuffixLength = 0
|
||||
)
|
||||
assertEquals(20, maxSize)
|
||||
}
|
||||
run {
|
||||
// 0001020304050607---08090a0b0c0d0e0f---10111213
|
||||
val maxSize = parsedByteArrayMaxSize(
|
||||
stringLength = 46,
|
||||
bytesPerLine = Int.MAX_VALUE,
|
||||
bytesPerGroup = 8,
|
||||
groupSeparatorLength = 3,
|
||||
byteSeparatorLength = 0,
|
||||
bytePrefixLength = 0,
|
||||
byteSuffixLength = 0
|
||||
)
|
||||
assertEquals(20, maxSize)
|
||||
}
|
||||
run {
|
||||
// #00; #01; #02; #03;---#04; #05; #06; #07;---#08; #09;
|
||||
// #0A; #0B; #0C; #0D;---#0E; #0F; #10; #11;---#12; #13;
|
||||
// #14; #15; #16; #17;---#18; #19; #1A; #1B;---#1C; #1D;
|
||||
// #1E;
|
||||
val maxSize = parsedByteArrayMaxSize(
|
||||
stringLength = 166,
|
||||
bytesPerLine = 10,
|
||||
bytesPerGroup = 4,
|
||||
groupSeparatorLength = 3,
|
||||
byteSeparatorLength = 1,
|
||||
bytePrefixLength = 1,
|
||||
byteSuffixLength = 1
|
||||
)
|
||||
assertEquals(31, maxSize)
|
||||
}
|
||||
run {
|
||||
// 0001020304050607\n08090a0b0c0d0e0f\n10111213
|
||||
// bytesPerGroup > bytesPerLine
|
||||
val maxSize = parsedByteArrayMaxSize(
|
||||
stringLength = 42,
|
||||
bytesPerLine = 8,
|
||||
bytesPerGroup = 10,
|
||||
groupSeparatorLength = 3,
|
||||
byteSeparatorLength = 0,
|
||||
bytePrefixLength = 0,
|
||||
byteSuffixLength = 0
|
||||
)
|
||||
assertEquals(20, maxSize)
|
||||
}
|
||||
run {
|
||||
val maxSize = parsedByteArrayMaxSize(
|
||||
stringLength = Int.MAX_VALUE,
|
||||
bytesPerLine = Int.MAX_VALUE,
|
||||
bytesPerGroup = Int.MAX_VALUE,
|
||||
groupSeparatorLength = 0,
|
||||
byteSeparatorLength = 0,
|
||||
bytePrefixLength = 0,
|
||||
byteSuffixLength = 0
|
||||
)
|
||||
assertEquals(Int.MAX_VALUE / 2 + 1, maxSize)
|
||||
}
|
||||
run {
|
||||
val maxSize = parsedByteArrayMaxSize(
|
||||
stringLength = Int.MAX_VALUE,
|
||||
bytesPerLine = Int.MAX_VALUE,
|
||||
bytesPerGroup = Int.MAX_VALUE,
|
||||
groupSeparatorLength = Int.MAX_VALUE,
|
||||
byteSeparatorLength = Int.MAX_VALUE,
|
||||
bytePrefixLength = Int.MAX_VALUE,
|
||||
byteSuffixLength = Int.MAX_VALUE
|
||||
)
|
||||
assertEquals(1, maxSize)
|
||||
}
|
||||
run {
|
||||
val maxSize = parsedByteArrayMaxSize(
|
||||
stringLength = Int.MAX_VALUE,
|
||||
bytesPerLine = Int.MAX_VALUE,
|
||||
bytesPerGroup = Int.MAX_VALUE / 2,
|
||||
groupSeparatorLength = Int.MAX_VALUE,
|
||||
byteSeparatorLength = Int.MAX_VALUE,
|
||||
bytePrefixLength = Int.MAX_VALUE,
|
||||
byteSuffixLength = Int.MAX_VALUE
|
||||
)
|
||||
assertEquals(1, maxSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package test.text
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
|
||||
class NumberHexFormatTest {
|
||||
|
||||
companion object {
|
||||
private const val longHex3a: String = "000000000000003a" // 16 hex digits
|
||||
private const val longHex3A: String = "000000000000003A" // 16 hex digits
|
||||
}
|
||||
|
||||
private fun testFormatAndParse(number: Long, string: String, format: HexFormat) {
|
||||
testFormat(number, string, format)
|
||||
testParse(string, number, format)
|
||||
}
|
||||
|
||||
private fun testFormat(number: Long, string: String, format: HexFormat) {
|
||||
require(format.number.prefix.isEmpty() && format.number.suffix.isEmpty())
|
||||
|
||||
assertEquals(string.takeLast(2), number.toByte().toHexString(format))
|
||||
assertEquals(string.takeLast(2), number.toUByte().toHexString(format))
|
||||
assertEquals(string.takeLast(4), number.toShort().toHexString(format))
|
||||
assertEquals(string.takeLast(4), number.toUShort().toHexString(format))
|
||||
assertEquals(string.takeLast(8), number.toInt().toHexString(format))
|
||||
assertEquals(string.takeLast(8), number.toUInt().toHexString(format))
|
||||
assertEquals(string, number.toHexString(format))
|
||||
assertEquals(string, number.toULong().toHexString(format))
|
||||
}
|
||||
|
||||
private fun testParse(string: String, number: Long, format: HexFormat) {
|
||||
require(format.number.prefix.isEmpty() && format.number.suffix.isEmpty())
|
||||
|
||||
assertEquals(number.toByte(), string.takeLast(2).hexToByte(format))
|
||||
assertEquals(number.toUByte(), string.takeLast(2).hexToUByte(format))
|
||||
assertEquals(number.toShort(), string.takeLast(4).hexToShort(format))
|
||||
assertEquals(number.toUShort(), string.takeLast(4).hexToUShort(format))
|
||||
assertEquals(number.toInt(), string.takeLast(8).hexToInt(format))
|
||||
assertEquals(number.toUInt(), string.takeLast(8).hexToUInt(format))
|
||||
assertEquals(number, string.hexToLong(format))
|
||||
assertEquals(number.toULong(), string.hexToULong(format))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun ignoreBytesFormat() {
|
||||
val format = HexFormat {
|
||||
bytes {
|
||||
bytesPerLine = 10
|
||||
bytesPerGroup = 4
|
||||
groupSeparator = "---"
|
||||
byteSeparator = " "
|
||||
bytePrefix = "#"
|
||||
byteSuffix = ";"
|
||||
}
|
||||
}
|
||||
|
||||
testFormatAndParse(58, longHex3a, format)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun upperCase() {
|
||||
testFormatAndParse(58, longHex3A, HexFormat { upperCase = true })
|
||||
testFormatAndParse(58, longHex3A, HexFormat.UpperCase)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun lowerCase() {
|
||||
testFormatAndParse(58, longHex3a, HexFormat { upperCase = false })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun defaultCase() {
|
||||
testFormatAndParse(58, longHex3a, HexFormat.Default)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun formatAndParseZero() {
|
||||
testFormatAndParse(0, "0".repeat(16), HexFormat.Default)
|
||||
testParse("0", 0, HexFormat.Default)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun formatAndParseMax() {
|
||||
testFormatAndParse(Long.MAX_VALUE, "7" + "f".repeat(15), HexFormat.Default)
|
||||
testFormatAndParse(-1, "f".repeat(16), HexFormat.Default)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun formatPrefixSuffix() {
|
||||
assertEquals("0x0000003a", 58.toHexString(HexFormat { number.prefix = "0x" }))
|
||||
assertEquals("0000003ah", 58.toHexString(HexFormat { number.suffix = "h" }))
|
||||
assertEquals("0x0000003ah", 58.toHexString(HexFormat { number.prefix = "0x"; number.suffix = "h" }))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun removeLeadingZeros() {
|
||||
val format = HexFormat { number.removeLeadingZeros = true }
|
||||
testFormatAndParse(58, "3a", format)
|
||||
testFormatAndParse(0, "0", format)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseLongFromSubstring() {
|
||||
val url = "https://magnuschatt.medium.com/why-you-should-totally-switch-to-kotlin-c7bbde9e10d5"
|
||||
val articleId = url.substringAfterLast('-').hexToLong()
|
||||
assertEquals(0xc7bbde9e10d5, articleId)
|
||||
}
|
||||
|
||||
// Number parsing strictness
|
||||
|
||||
@Test
|
||||
fun parseRequiresPrefixSuffix() {
|
||||
assertEquals(58, "0x0000003a".hexToInt(HexFormat { number.prefix = "0x" }))
|
||||
assertEquals(58, "0x3a".hexToInt(HexFormat { number.prefix = "0x" }))
|
||||
assertEquals(10, "3a".hexToInt(HexFormat { number.prefix = "3" }))
|
||||
assertFailsWith<NumberFormatException> {
|
||||
"0000003a".hexToInt(HexFormat { number.prefix = "0x" })
|
||||
}
|
||||
assertEquals(58, "0000003ah".hexToInt(HexFormat { number.suffix = "h" }))
|
||||
assertEquals(58, "3ah".hexToInt(HexFormat { number.suffix = "h" }))
|
||||
assertEquals(3, "3a".hexToInt(HexFormat { number.suffix = "a" }))
|
||||
assertFailsWith<NumberFormatException> {
|
||||
"0000003a".hexToInt(HexFormat { number.suffix = "h" })
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseRequiresAtLeastOneHexDigit() {
|
||||
assertFailsWith<NumberFormatException> {
|
||||
"".hexToInt()
|
||||
}
|
||||
assertFailsWith<NumberFormatException> {
|
||||
"3a".hexToInt(HexFormat { number { prefix = "3"; suffix = "a" } })
|
||||
}
|
||||
assertFailsWith<NumberFormatException> {
|
||||
"3a".hexToInt(HexFormat { number.prefix = "3a" })
|
||||
}
|
||||
assertFailsWith<NumberFormatException> {
|
||||
"3a".hexToInt(HexFormat { number.suffix = "3a" })
|
||||
}
|
||||
assertEquals(15, "0xfh".hexToInt(HexFormat { number { prefix = "0x"; suffix = "h" } }))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseIgnoresCase() {
|
||||
assertEquals(58, "0000003a".hexToInt())
|
||||
assertEquals(58, "3a".hexToInt())
|
||||
assertEquals(58, "0000003A".hexToInt())
|
||||
assertEquals(58, "3A".hexToInt())
|
||||
|
||||
val format = HexFormat {
|
||||
upperCase = true
|
||||
number {
|
||||
prefix = "0X"
|
||||
suffix = "h"
|
||||
}
|
||||
}
|
||||
assertEquals(58, "0X0000003AH".hexToInt(format))
|
||||
assertEquals(58, "0x3Ah".hexToInt(format))
|
||||
assertEquals(58, "0X0000003aH".hexToInt(format))
|
||||
assertEquals(58, "0x3ah".hexToInt(format))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseIgnoresRemoveLeadingZeros() {
|
||||
assertEquals(58, "3a".hexToInt())
|
||||
assertEquals(58, "3a".hexToInt(HexFormat { number.removeLeadingZeros = false }))
|
||||
assertEquals(58, "0000003a".hexToInt(HexFormat { number.removeLeadingZeros = true }))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseLimitsHexLength() {
|
||||
testParse("3", 3, HexFormat.Default) // length = 1
|
||||
testParse("00", 0, HexFormat.Default) // length = 2
|
||||
testParse("3a", 58, HexFormat.Default) // length = 2
|
||||
assertFailsWith<NumberFormatException> { "03a".hexToByte() } // length = 3
|
||||
assertFailsWith<NumberFormatException> { "03a".hexToUByte() } // length = 3
|
||||
testParse("03a", 58, HexFormat.Default) // length = 3
|
||||
testParse("003a", 58, HexFormat.Default) // length = 4
|
||||
assertFailsWith<NumberFormatException> { "0003a".hexToShort() } // length = 5
|
||||
assertFailsWith<NumberFormatException> { "0003a".hexToUShort() } // length = 5
|
||||
testParse("0003a", 58, HexFormat.Default) // length = 5
|
||||
testParse("0000003a", 58, HexFormat.Default) // length = 8
|
||||
assertFailsWith<NumberFormatException> { "00000003a".hexToInt() } // length = 9
|
||||
assertFailsWith<NumberFormatException> { "00000003a".hexToUInt() } // length = 9
|
||||
testParse("00000003a", 58, HexFormat.Default) // length = 9
|
||||
testParse("000000000000003a", 58, HexFormat.Default) // length = 16
|
||||
assertFailsWith<NumberFormatException> { "00000000000000003a".hexToLong() } // length = 17
|
||||
assertFailsWith<NumberFormatException> { "00000000000000003a".hexToULong() } // length = 17
|
||||
}
|
||||
|
||||
// Invalid HexFormat configuration
|
||||
|
||||
@Test
|
||||
fun prefixWithNewLine() {
|
||||
assertFailsWith<IllegalArgumentException> { HexFormat { number.prefix = "\n" } }
|
||||
assertFailsWith<IllegalArgumentException> { HexFormat { number.prefix = "\r" } }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun suffixWithNewLine() {
|
||||
assertFailsWith<IllegalArgumentException> { HexFormat { number.suffix = "ab\ncd" } }
|
||||
assertFailsWith<IllegalArgumentException> { HexFormat { number.suffix = "ab\rcd" } }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user