Skip to content

Instantly share code, notes, and snippets.

@L-Briand
Last active May 3, 2023 18:09
Show Gist options
  • Save L-Briand/f15d863ef64e5fd91dddca6cd2928cfe to your computer and use it in GitHub Desktop.
Save L-Briand/f15d863ef64e5fd91dddca6cd2928cfe to your computer and use it in GitHub Desktop.
Helper class to work with hexadecimal strings.
import kotlinx.serialization.Serializable
/**
* Utility class to work with hexadecimal strings.
*
* Two hex values with same [raw] should be equals.
* Even if one was created with lower cased hex and the other with upper cased hex.
*
* It contains :
* - static helpers to encode [fromString] and decode [toString] in hexadecimal strings.
* - static helper [of] to safely get a [HexValue] from an hexadecimal string.
* - [toString] outputs upper cased hexadecimal string of [raw]
*
*/
@Serializable(HexValueSerializer::class)
class HexValue(val raw: ByteArray) {
constructor(string: CharSequence) : this(fromString(string)!!)
companion object {
private val upper = "0123456789ABCDEF".toByteArray(Charsets.US_ASCII)
private val lower = "0123456789abcdef".toByteArray(Charsets.US_ASCII)
/**
* Convert [value] into hexadecimal [String]
*
* @param upperCase true to get upper case hexadecimal string, false to get lower case hexadecimal string
*
* @return hexadecimal string
*/
fun toString(value: ByteArray, upperCase: Boolean): String {
val hexString = ByteArray(value.size * 2)
val source = if (upperCase) upper else lower
for (idx in value.indices) {
val int = value[idx].toInt() and 0xFF
hexString[idx * 2] = source[int shr 4]
hexString[idx * 2 + 1] = source[int and 0x0F]
}
return String(hexString, Charsets.UTF_8)
}
/**
* Convert a [value] into [ByteArray]
* @return null if the string cannot be parsed as hexadecimal string.
*/
fun fromString(value: CharSequence): ByteArray? {
if (value.length and 0x1 == 1) return null
return try {
ByteArray(value.length / 2) {
Integer.parseInt(value, it * 2, (it + 1) * 2, 16).toByte()
}
} catch (e: NumberFormatException) {
null
}
}
/**
* Getting safely an HexValue from [string]
* @return null if the [string] was not an hex value
*/
fun of(string: CharSequence?): HexValue? {
string ?: return null
val raw = fromString(string) ?: return null
return HexValue(raw)
}
}
/** Specify with [isUpperCase] if the resulting hexadecimal string should be upper cased or lower cased */
fun toString(isUpperCase: Boolean) = Companion.toString(raw, isUpperCase)
override fun toString(): String = toString(false)
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as HexValue
if (!raw.contentEquals(other.raw)) return false
return true
}
override fun hashCode(): Int {
return raw.contentHashCode()
}
}
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
/**
* kotlinx serializer for [HexValue].
* Transforms it to json string
*/
class HexValueSerializer : KSerializer<HexValue> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("HexValueSerializer", PrimitiveKind.STRING)
override fun deserialize(decoder: Decoder): HexValue = HexValue(decoder.decodeString())
override fun serialize(encoder: Encoder, value: HexValue) {
encoder.encodeString(value.toString())
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment