encoding: add a hex sub-module (#11193)

pull/11202/head
Miccah 2021-08-15 13:42:51 -05:00 committed by GitHub
parent 4cde618582
commit ea4f6fd48f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 79 additions and 0 deletions

View File

@ -0,0 +1,38 @@
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) ?)]
}
// 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}
// 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) ?)
}
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
}

View File

@ -0,0 +1,41 @@
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]
}
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'
}