v2 / vlib / encoding / hex / hex.v
62 lines · 58 sloc · 1.75 KB · 757929392e0e7a75fc1272116460981e589737d5
Raw
1module hex
2
3import 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.
8pub 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]
44pub 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
55fn 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