Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,99 @@
|
||||
module hex
|
||||
|
||||
const hex_digits = '0123456789abcdef'
|
||||
const hex_digits_upper = '0123456789ABCDEF'
|
||||
|
||||
// EncodeParams configures optional output formatting for encode.
|
||||
//
|
||||
// If `uppercase` is set, `encode` emits `A-F` instead of `a-f`.
|
||||
// If `with_prefix` is non-empty, `encode` prepends that exact string.
|
||||
@[params]
|
||||
pub struct EncodeParams {
|
||||
pub mut:
|
||||
uppercase bool
|
||||
with_prefix string
|
||||
}
|
||||
|
||||
// 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, the first digit is
|
||||
// decoded as if an implicit `0` preceded it. An optional `0x` or `0X` prefix
|
||||
// is accepted.
|
||||
@[direct_array_access]
|
||||
pub fn decode(s string) ![]u8 {
|
||||
if s.len == 0 {
|
||||
return []u8{}
|
||||
}
|
||||
|
||||
mut offset := 0
|
||||
mut hex_bytes := if s.len >= 2 {
|
||||
if s[0] == `0` && (s[1] == `x` || s[1] == `X`) {
|
||||
offset = 2
|
||||
s[2..].bytes()
|
||||
} else {
|
||||
s.bytes()
|
||||
}
|
||||
} else {
|
||||
s.bytes()
|
||||
}
|
||||
|
||||
if hex_bytes.len == 0 {
|
||||
return []u8{}
|
||||
}
|
||||
|
||||
mut bytes := []u8{cap: (hex_bytes.len + 1) >> 1}
|
||||
mut start := 0
|
||||
if hex_bytes.len & 1 == 1 {
|
||||
bytes << char2nibble(hex_bytes[0], offset)!
|
||||
start = 1
|
||||
}
|
||||
|
||||
for i := start; i < hex_bytes.len; i += 2 {
|
||||
n1 := char2nibble(hex_bytes[i], offset + i)!
|
||||
n0 := char2nibble(hex_bytes[i + 1], offset + 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 whose length will be a multiple of 2.
|
||||
// If `EncodeParams.uppercase` is set, the output hex characters are emitted in
|
||||
// uppercase.
|
||||
// If `EncodeParams.with_prefix` is non-empty, the output string is prefixed
|
||||
// with the provided string.
|
||||
@[direct_array_access]
|
||||
pub fn encode(bytes []u8, params EncodeParams) string {
|
||||
if bytes.len == 0 {
|
||||
return ''
|
||||
}
|
||||
mut res := []u8{}
|
||||
if params.with_prefix != '' {
|
||||
res << params.with_prefix.bytes()
|
||||
}
|
||||
for _, b in bytes {
|
||||
res << nibble2char(b >> 4, params)
|
||||
res << nibble2char(b & 0xf, params)
|
||||
}
|
||||
return res.bytestr()
|
||||
}
|
||||
|
||||
// nibble2char converts a 4-bit hex value to its ASCII character
|
||||
@[inline]
|
||||
fn nibble2char(nibble u8, params EncodeParams) u8 {
|
||||
if params.uppercase {
|
||||
return hex_digits_upper[nibble]
|
||||
}
|
||||
return hex_digits[nibble]
|
||||
}
|
||||
|
||||
// char2nibble converts an ASCII hex character to its hex value
|
||||
@[inline]
|
||||
fn char2nibble(b u8, index int) !u8 {
|
||||
match b {
|
||||
`0`...`9` { return b - u8(`0`) }
|
||||
`A`...`F` { return b - u8(`A`) + 10 }
|
||||
`a`...`f` { return b - u8(`a`) + 10 }
|
||||
else { return error('invalid hex char ${b.ascii_str()} at index ${index}') }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
module hex
|
||||
|
||||
fn test_decode() {
|
||||
assert decode('')! == []
|
||||
assert decode('0')! == [u8(0x0)]
|
||||
assert decode('f')! == [u8(0xf)]
|
||||
assert decode('0f')! == [u8(0x0f)]
|
||||
assert decode('ff')! == [u8(0xff)]
|
||||
assert decode('123')! == [u8(0x1), 0x23]
|
||||
assert decode('1234')! == [u8(0x12), 0x34]
|
||||
assert decode('12345')! == [u8(0x1), 0x23, 0x45]
|
||||
assert decode('0123456789abcdef')! == [u8(0x01), 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]
|
||||
assert decode('123456789ABCDEF')! == [u8(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_encode_params() {
|
||||
assert encode([u8(0xab), 0xcd], uppercase: true) == 'ABCD'
|
||||
assert encode([u8(0xab), 0xcd], with_prefix: '0x') == '0xabcd'
|
||||
assert encode([u8(0xab), 0xcd], uppercase: true, with_prefix: '0X') == '0XABCD'
|
||||
assert encode([u8(0xab), 0xcd], with_prefix: 'hex:') == 'hex:abcd'
|
||||
}
|
||||
|
||||
fn test_decode_0x() {
|
||||
assert decode('0x') or { []u8{} } == []u8{}
|
||||
assert decode('0x0')! == [u8(0x0)]
|
||||
assert decode('0X1234')! == [u8(0x12), 0x34]
|
||||
assert decode('0x12345')! == [u8(0x1), 0x23, 0x45]
|
||||
assert decode('0x0123456789abcdef')! == [u8(0x01), 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]
|
||||
assert decode('0X123456789ABCDEF')! == [u8(0x01), 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]
|
||||
}
|
||||
|
||||
fn test_decode_error_indexes() {
|
||||
decode('g') or {
|
||||
assert err.msg() == 'invalid hex char g at index 0'
|
||||
return
|
||||
}
|
||||
assert false
|
||||
|
||||
decode('0xg') or {
|
||||
assert err.msg() == 'invalid hex char g at index 2'
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
Reference in new issue
Block a user