| 1 | module hex |
| 2 | |
| 3 | import strings |
| 4 | |
| 5 | // decode converts a hex string into an array of bytes. The expected |
| 6 | // input format is 2 ASCII characters for each output byte. If the provided |
| 7 | // string length is not a multiple of 2, an implicit `0` is prepended to it. |
| 8 | pub fn decode(s string) ![]u8 { |
| 9 | mut hex_str := s |
| 10 | if hex_str.len >= 2 { |
| 11 | if s[0] == `0` && (s[1] == `x` || s[1] == `X`) { |
| 12 | hex_str = s[2..] |
| 13 | } |
| 14 | } |
| 15 | if hex_str.len == 0 { |
| 16 | return []u8{} |
| 17 | } else if hex_str.len == 1 { |
| 18 | return [char2nibble(hex_str[0])!] |
| 19 | } else if hex_str.len == 2 { |
| 20 | n1 := char2nibble(hex_str[0])! |
| 21 | n0 := char2nibble(hex_str[1])! |
| 22 | return [(n1 << 4) | n0] |
| 23 | } |
| 24 | // calculate the first byte depending on if hex_str.len is odd |
| 25 | mut val := char2nibble(hex_str[0])! |
| 26 | if hex_str.len & 1 == 0 { |
| 27 | val = (val << 4) | char2nibble(hex_str[1])! |
| 28 | } |
| 29 | // set cap to hex_str.len/2 rounded up |
| 30 | mut bytes := []u8{len: 1, cap: (hex_str.len + 1) >> 1, init: val} |
| 31 | // iterate over every 2 bytes |
| 32 | // the start index depends on if hex_str.len is odd |
| 33 | for i := 2 - (hex_str.len & 1); i < hex_str.len; i += 2 { |
| 34 | n1 := char2nibble(hex_str[i])! |
| 35 | n0 := char2nibble(hex_str[i + 1])! |
| 36 | bytes << (n1 << 4) | n0 |
| 37 | } |
| 38 | return bytes |
| 39 | } |
| 40 | |
| 41 | // encode converts an array of bytes into a string of ASCII hex bytes. The |
| 42 | // output will always be a string with length a multiple of 2. |
| 43 | @[manualfree] |
| 44 | pub fn encode(bytes []u8) string { |
| 45 | mut sb := strings.new_builder(bytes.len * 2) |
| 46 | for b in bytes { |
| 47 | sb.write_string(b.hex()) |
| 48 | } |
| 49 | res := sb.str() |
| 50 | unsafe { sb.free() } |
| 51 | return res |
| 52 | } |
| 53 | |
| 54 | // char2nibble converts an ASCII hex character to it's hex value |
| 55 | fn char2nibble(b u8) !u8 { |
| 56 | match b { |
| 57 | `0`...`9` { return b - u8(`0`) } |
| 58 | `A`...`F` { return b - u8(`A`) + 10 } |
| 59 | `a`...`f` { return b - u8(`a`) + 10 } |
| 60 | else { return error('invalid hex char ${b.ascii_str()}') } |
| 61 | } |
| 62 | } |
| 63 | |