aboutsummaryrefslogtreecommitdiff
path: root/v_windows/v/vlib/encoding/hex
diff options
context:
space:
mode:
Diffstat (limited to 'v_windows/v/vlib/encoding/hex')
-rw-r--r--v_windows/v/vlib/encoding/hex/hex.v62
-rw-r--r--v_windows/v/vlib/encoding/hex/hex_test.v54
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]
+}