2021-08-15 21:42:51 +03:00
|
|
|
module hex
|
|
|
|
|
|
|
|
import strings
|
|
|
|
|
|
|
|
// decode converts a hex string into an array of bytes. The expected
|
|
|
|
// input format is 2 ASCII characters for each output byte. If the provided
|
|
|
|
// string length is not a multiple of 2, an implicit `0` is prepended to it.
|
2022-10-20 22:14:33 +03:00
|
|
|
pub fn decode(s string) ![]u8 {
|
2021-08-16 04:59:21 +03:00
|
|
|
mut hex_str := s
|
|
|
|
if hex_str.len >= 2 {
|
|
|
|
if s[0] == `0` && (s[1] == `x` || s[1] == `X`) {
|
|
|
|
hex_str = s[2..]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if hex_str.len == 0 {
|
2022-04-15 15:35:35 +03:00
|
|
|
return []u8{}
|
2021-08-16 04:59:21 +03:00
|
|
|
} else if hex_str.len == 1 {
|
2022-10-20 22:14:33 +03:00
|
|
|
return [char2nibble(hex_str[0])!]
|
2021-08-16 04:59:21 +03:00
|
|
|
} else if hex_str.len == 2 {
|
2022-10-20 22:14:33 +03:00
|
|
|
n1 := char2nibble(hex_str[0])!
|
|
|
|
n0 := char2nibble(hex_str[1])!
|
2021-08-16 04:59:21 +03:00
|
|
|
return [(n1 << 4) | n0]
|
|
|
|
}
|
|
|
|
// calculate the first byte depending on if hex_str.len is odd
|
2022-10-20 22:14:33 +03:00
|
|
|
mut val := char2nibble(hex_str[0])!
|
2021-08-16 04:59:21 +03:00
|
|
|
if hex_str.len & 1 == 0 {
|
2022-10-20 22:14:33 +03:00
|
|
|
val = (val << 4) | char2nibble(hex_str[1])!
|
2021-08-15 21:42:51 +03:00
|
|
|
}
|
2021-08-16 04:59:21 +03:00
|
|
|
// set cap to hex_str.len/2 rounded up
|
2022-04-15 15:35:35 +03:00
|
|
|
mut bytes := []u8{len: 1, cap: (hex_str.len + 1) >> 1, init: val}
|
2021-08-15 21:42:51 +03:00
|
|
|
// iterate over every 2 bytes
|
2021-08-16 04:59:21 +03:00
|
|
|
// the start index depends on if hex_str.len is odd
|
|
|
|
for i := 2 - (hex_str.len & 1); i < hex_str.len; i += 2 {
|
2022-10-20 22:14:33 +03:00
|
|
|
n1 := char2nibble(hex_str[i])!
|
|
|
|
n0 := char2nibble(hex_str[i + 1])!
|
2021-08-16 04:59:21 +03:00
|
|
|
bytes << (n1 << 4) | n0
|
2021-08-15 21:42:51 +03:00
|
|
|
}
|
|
|
|
return bytes
|
|
|
|
}
|
|
|
|
|
|
|
|
// encode converts an array of bytes into a string of ASCII hex bytes. The
|
|
|
|
// output will always be a string with length a multiple of 2.
|
|
|
|
[manualfree]
|
2022-04-15 15:35:35 +03:00
|
|
|
pub fn encode(bytes []u8) string {
|
2022-02-04 16:16:08 +03:00
|
|
|
mut sb := strings.new_builder(bytes.len * 2)
|
2021-08-15 21:42:51 +03:00
|
|
|
for b in bytes {
|
|
|
|
sb.write_string(b.hex())
|
|
|
|
}
|
|
|
|
res := sb.str()
|
|
|
|
unsafe { sb.free() }
|
|
|
|
return res
|
|
|
|
}
|
2021-08-16 04:59:21 +03:00
|
|
|
|
|
|
|
// char2nibble converts an ASCII hex character to it's hex value
|
2022-10-20 22:14:33 +03:00
|
|
|
fn char2nibble(b u8) !u8 {
|
2021-08-16 04:59:21 +03:00
|
|
|
match b {
|
2022-04-15 14:58:56 +03:00
|
|
|
`0`...`9` { return b - u8(`0`) }
|
|
|
|
`A`...`F` { return b - u8(`A`) + 10 }
|
|
|
|
`a`...`f` { return b - u8(`a`) + 10 }
|
2021-08-16 04:59:21 +03:00
|
|
|
else { return error('invalid hex char $b.ascii_str()') }
|
|
|
|
}
|
|
|
|
}
|