diff options
author | Indrajith K L | 2022-12-03 17:00:20 +0530 |
---|---|---|
committer | Indrajith K L | 2022-12-03 17:00:20 +0530 |
commit | f5c4671bfbad96bf346bd7e9a21fc4317b4959df (patch) | |
tree | 2764fc62da58f2ba8da7ed341643fc359873142f /v_windows/v/vlib/encoding/hex | |
download | cli-tools-windows-f5c4671bfbad96bf346bd7e9a21fc4317b4959df.tar.gz cli-tools-windows-f5c4671bfbad96bf346bd7e9a21fc4317b4959df.tar.bz2 cli-tools-windows-f5c4671bfbad96bf346bd7e9a21fc4317b4959df.zip |
Diffstat (limited to 'v_windows/v/vlib/encoding/hex')
-rw-r--r-- | v_windows/v/vlib/encoding/hex/hex.v | 62 | ||||
-rw-r--r-- | v_windows/v/vlib/encoding/hex/hex_test.v | 54 |
2 files changed, 116 insertions, 0 deletions
diff --git a/v_windows/v/vlib/encoding/hex/hex.v b/v_windows/v/vlib/encoding/hex/hex.v new file mode 100644 index 0000000..99387f1 --- /dev/null +++ b/v_windows/v/vlib/encoding/hex/hex.v @@ -0,0 +1,62 @@ +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. +pub fn decode(s string) ?[]byte { + 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 { + 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 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 +} + +// 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] +pub fn encode(bytes []byte) string { + mut sb := strings.new_builder(bytes.len << 1) + for b in bytes { + sb.write_string(b.hex()) + } + res := sb.str() + 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/v_windows/v/vlib/encoding/hex/hex_test.v b/v_windows/v/vlib/encoding/hex/hex_test.v new file mode 100644 index 0000000..62501e9 --- /dev/null +++ b/v_windows/v/vlib/encoding/hex/hex_test.v @@ -0,0 +1,54 @@ +module hex + +fn test_decode() ? { + assert decode('') ? == [] + assert decode('0') ? == [byte(0x0)] + assert decode('f') ? == [byte(0xf)] + assert decode('0f') ? == [byte(0x0f)] + assert decode('ff') ? == [byte(0xff)] + 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() ? { + if x := decode('foo') { + return error('expected decode to fail, got $x') + } + if x := decode('g') { + return error('expected decode to fail, got $x') + } + if x := decode('000000000g') { + return error('expected decode to fail, got $x') + } + if x := decode('_') { + return error('expected decode to fail, got $x') + } + if x := decode('!') { + return error('expected decode to fail, got $x') + } +} + +fn test_encode() ? { + assert encode(decode('') ?) == '' + assert encode(decode('0') ?) == '00' + assert encode(decode('f') ?) == '0f' + assert encode(decode('0f') ?) == '0f' + assert encode(decode('ff') ?) == 'ff' + 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] +} |