100 lines
2.5 KiB
V
100 lines
2.5 KiB
V
module hex
|
|
|
|
const hex_digits = '0123456789abcdef'
|
|
const hex_digits_upper = '0123456789ABCDEF'
|
|
|
|
// EncodeParams configures optional output formatting for encode.
|
|
//
|
|
// If `uppercase` is set, `encode` emits `A-F` instead of `a-f`.
|
|
// If `with_prefix` is non-empty, `encode` prepends that exact string.
|
|
@[params]
|
|
pub struct EncodeParams {
|
|
pub mut:
|
|
uppercase bool
|
|
with_prefix string
|
|
}
|
|
|
|
// 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, the first digit is
|
|
// decoded as if an implicit `0` preceded it. An optional `0x` or `0X` prefix
|
|
// is accepted.
|
|
@[direct_array_access]
|
|
pub fn decode(s string) ![]u8 {
|
|
if s.len == 0 {
|
|
return []u8{}
|
|
}
|
|
|
|
mut offset := 0
|
|
mut hex_bytes := if s.len >= 2 {
|
|
if s[0] == `0` && (s[1] == `x` || s[1] == `X`) {
|
|
offset = 2
|
|
s[2..].bytes()
|
|
} else {
|
|
s.bytes()
|
|
}
|
|
} else {
|
|
s.bytes()
|
|
}
|
|
|
|
if hex_bytes.len == 0 {
|
|
return []u8{}
|
|
}
|
|
|
|
mut bytes := []u8{cap: (hex_bytes.len + 1) >> 1}
|
|
mut start := 0
|
|
if hex_bytes.len & 1 == 1 {
|
|
bytes << char2nibble(hex_bytes[0], offset)!
|
|
start = 1
|
|
}
|
|
|
|
for i := start; i < hex_bytes.len; i += 2 {
|
|
n1 := char2nibble(hex_bytes[i], offset + i)!
|
|
n0 := char2nibble(hex_bytes[i + 1], offset + i + 1)!
|
|
bytes << (n1 << 4) | n0
|
|
}
|
|
return bytes
|
|
}
|
|
|
|
// encode converts an array of bytes into a string of ASCII hex bytes. The
|
|
// output will always be a string whose length will be a multiple of 2.
|
|
// If `EncodeParams.uppercase` is set, the output hex characters are emitted in
|
|
// uppercase.
|
|
// If `EncodeParams.with_prefix` is non-empty, the output string is prefixed
|
|
// with the provided string.
|
|
@[direct_array_access]
|
|
pub fn encode(bytes []u8, params EncodeParams) string {
|
|
if bytes.len == 0 {
|
|
return ''
|
|
}
|
|
mut res := []u8{}
|
|
if params.with_prefix != '' {
|
|
res << params.with_prefix.bytes()
|
|
}
|
|
for _, b in bytes {
|
|
res << nibble2char(b >> 4, params)
|
|
res << nibble2char(b & 0xf, params)
|
|
}
|
|
return res.bytestr()
|
|
}
|
|
|
|
// nibble2char converts a 4-bit hex value to its ASCII character
|
|
@[inline]
|
|
fn nibble2char(nibble u8, params EncodeParams) u8 {
|
|
if params.uppercase {
|
|
return hex_digits_upper[nibble]
|
|
}
|
|
return hex_digits[nibble]
|
|
}
|
|
|
|
// char2nibble converts an ASCII hex character to its hex value
|
|
@[inline]
|
|
fn char2nibble(b u8, index int) !u8 {
|
|
match b {
|
|
`0`...`9` { return b - u8(`0`) }
|
|
`A`...`F` { return b - u8(`A`) + 10 }
|
|
`a`...`f` { return b - u8(`a`) + 10 }
|
|
else { return error('invalid hex char ${b.ascii_str()} at index ${index}') }
|
|
}
|
|
}
|