gs-hex/src/main/scala/gs/hex/v0/Hex.scala

93 lines
3.1 KiB
Scala

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