package gs.hex.v0 object Hex: private val HexChars: Array[Char] = Array( '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' ) // Leverage cache to avoid operations. private val Digits: Array[Int] = Array( -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 ) /** Convert the given raw data to a hexadecimal string. * * @param data * The data to convert. * @return * The output hexadecimal string. */ def toHexString(data: Array[Byte]): String = var index = 0 val ch: Array[Char] = new Array(data.length * 2) data.foreach { byte => ch(index) = HexChars((byte & 0xf0) >> 4) ch(index + 1) = HexChars(byte & 0x0f) index = index + 2 } new String(ch) /** Convert the given hexadecimal string to a byte array. * * @param encoded * The hexadecimal string. * @return * The encoded byte array, or nothing if the input is not a valid hex * string. */ def fromHexString(encoded: String): Option[Array[Byte]] = if encoded.isBlank() then Some(Array.empty) else if encoded.length() % 2 != 0 then None else val size = encoded.length() / 2 val data: Array[Byte] = new Array(size) // The "base" tracks the base index for character extraction. var base = 0 // The "index" tracks the byte index being updated. var index = 0 // Track if calculation failed for any reason. var failed = false // Populate each byte in the destination array. while !failed && index < size do // Extract the relevant characters. val digit1 = Digits(encoded.charAt(base).toInt) val digit2 = Digits(encoded.charAt(base + 1).toInt) if digit1 >= 0 && digit2 >= 0 then // Convert to byte and assign the result. data(index) = pairToByte(digit1, digit2) index = index + 1 base = base + 2 else index = size failed = true if !failed then Some(data) else None private inline def pairToByte( digit1: Int, digit2: Int ): Byte = ((digit1 << 4) + digit2).toByte end Hex