From 60f21f065f9cb8ec3bfba035d7e1ed2e3ee87d49 Mon Sep 17 00:00:00 2001 From: Miccah Date: Sun, 15 Aug 2021 20:59:21 -0500 Subject: [PATCH] encoding.hex: remove strconv dependency (#11196) --- vlib/encoding/hex/hex.v | 48 +++++++++++++++++++++++++++--------- vlib/encoding/hex/hex_test.v | 13 ++++++++++ 2 files changed, 49 insertions(+), 12 deletions(-) diff --git a/vlib/encoding/hex/hex.v b/vlib/encoding/hex/hex.v index 6f75f2fff6..99387f15e7 100644 --- a/vlib/encoding/hex/hex.v +++ b/vlib/encoding/hex/hex.v @@ -1,25 +1,39 @@ module hex -import strconv 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. pub fn decode(s string) ?[]byte { - if s.len == 0 { - return []byte{} - } else if s.len <= 2 { - return [byte(strconv.parse_uint(s, 16, 8) ?)] + mut hex_str := s + if hex_str.len >= 2 { + if s[0] == `0` && (s[1] == `x` || s[1] == `X`) { + hex_str = s[2..] + } } - // calculate the first byte depending on if s.len is odd - val := byte(strconv.parse_uint(s[..2 - (s.len & 1)], 16, 8) ?) - // set cap to s.len/2 rounded up - mut bytes := []byte{len: 1, cap: (s.len + 1) >> 1, init: val} + if hex_str.len == 0 { + return []byte{} + } else if hex_str.len == 1 { + return [char2nibble(hex_str[0]) ?] + } else if hex_str.len == 2 { + n1 := char2nibble(hex_str[0]) ? + n0 := char2nibble(hex_str[1]) ? + return [(n1 << 4) | n0] + } + // calculate the first byte depending on if hex_str.len is odd + mut val := char2nibble(hex_str[0]) ? + if hex_str.len & 1 == 0 { + val = (val << 4) | char2nibble(hex_str[1]) ? + } + // set cap to hex_str.len/2 rounded up + mut bytes := []byte{len: 1, cap: (hex_str.len + 1) >> 1, init: val} // iterate over every 2 bytes - // the start index depends on if s.len is odd - for i := 2 - (s.len & 1); i < s.len; i += 2 { - bytes << byte(strconv.parse_uint(s[i..i + 2], 16, 8) ?) + // the start index depends on if hex_str.len is odd + for i := 2 - (hex_str.len & 1); i < hex_str.len; i += 2 { + n1 := char2nibble(hex_str[i]) ? + n0 := char2nibble(hex_str[i + 1]) ? + bytes << (n1 << 4) | n0 } return bytes } @@ -36,3 +50,13 @@ pub fn encode(bytes []byte) string { unsafe { sb.free() } return res } + +// char2nibble converts an ASCII hex character to it's hex value +fn char2nibble(b byte) ?byte { + match b { + `0`...`9` { return b - byte(`0`) } + `A`...`F` { return b - byte(`A`) + 10 } + `a`...`f` { return b - byte(`a`) + 10 } + else { return error('invalid hex char $b.ascii_str()') } + } +} diff --git a/vlib/encoding/hex/hex_test.v b/vlib/encoding/hex/hex_test.v index 1be8f80e7e..62501e9ded 100644 --- a/vlib/encoding/hex/hex_test.v +++ b/vlib/encoding/hex/hex_test.v @@ -9,6 +9,8 @@ fn test_decode() ? { assert decode('123') ? == [byte(0x1), 0x23] assert decode('1234') ? == [byte(0x12), 0x34] assert decode('12345') ? == [byte(0x1), 0x23, 0x45] + assert decode('0123456789abcdef') ? == [byte(0x01), 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef] + assert decode('123456789ABCDEF') ? == [byte(0x01), 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef] } fn test_decode_fails() ? { @@ -38,4 +40,15 @@ fn test_encode() ? { assert encode(decode('123') ?) == '0123' assert encode(decode('1234') ?) == '1234' assert encode(decode('12345') ?) == '012345' + assert encode(decode('abcdef') ?) == 'abcdef' + assert encode(decode('ABCDEF') ?) == 'abcdef' +} + +fn test_decode_0x() ? { + assert decode('0x') ? == [] + assert decode('0x0') ? == [byte(0x0)] + assert decode('0X1234') ? == [byte(0x12), 0x34] + assert decode('0x12345') ? == [byte(0x1), 0x23, 0x45] + assert decode('0x0123456789abcdef') ? == [byte(0x01), 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef] + assert decode('0X123456789ABCDEF') ? == [byte(0x01), 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef] }