encoding: add a hex sub-module (#11193)
parent
4cde618582
commit
ea4f6fd48f
|
@ -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
|
||||||
|
}
|
|
@ -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'
|
||||||
|
}
|
Loading…
Reference in New Issue