Files
krata/crates/vendor/advmac/src/parser.rs
2024-03-07 18:12:47 +00:00

114 lines
3.6 KiB
Rust

use crate::ParseError;
// This whole thing is written this way to be const.
// If you want normal hex handling, just use hex crate
pub struct MacParser<const N: usize, const N2: usize>;
impl<const N: usize, const N2: usize> MacParser<N, N2> {
const CANONICAL_COLON_SIZE: usize = 3 * N - 1;
const DOT_NOTATION_SIZE: usize = (2 * N) + (N / 2 - 1);
const HEXADECIMAL_SIZE: usize = 2 * N;
const HEXADECIMAL0X_SIZE: usize = 2 * N + 2;
#[inline]
const fn nibble(v: u8) -> Result<u8, ParseError> {
match v {
b'A'..=b'F' => Ok(10 + (v - b'A')),
b'a'..=b'f' => Ok(10 + (v - b'a')),
b'0'..=b'9' => Ok(v - b'0'),
_ => Err(ParseError::InvalidMac),
}
}
#[inline]
const fn byte(b1: u8, b2: u8) -> Result<u8, ParseError> {
// ? is not available in const
match (Self::nibble(b1), Self::nibble(b2)) {
(Ok(v1), Ok(v2)) => Ok((v1 << 4) + v2),
(Err(e), _) | (_, Err(e)) => Err(e),
}
}
const fn from_hex(s: &[u8]) -> Result<[u8; N], ParseError> {
if s.len() != Self::HEXADECIMAL_SIZE {
return Err(ParseError::InvalidLength { length: s.len() });
}
let mut result = [0u8; N];
// for-loops and iterators are unavailable in const
let mut i = 0;
while i < N {
result[i] = match Self::byte(s[2 * i], s[2 * i + 1]) {
Ok(v) => v,
Err(e) => return Err(e),
};
i += 1;
}
Ok(result)
}
const fn check_separator(s: &[u8], sep: u8, group_len: usize) -> bool {
let mut i = group_len;
while i < s.len() {
if s[i] != sep {
return false;
}
i += group_len + 1;
}
true
}
const fn parse_separated(s: &[u8], sep: u8, group_len: usize) -> Result<[u8; N], ParseError> {
let expected_len = (2 * N) + ((2 * N) / group_len) - 1;
if s.len() != expected_len {
return Err(ParseError::InvalidLength { length: s.len() });
}
if !Self::check_separator(s, sep, group_len) {
return Err(ParseError::InvalidMac);
}
let mut hex_buf = [0u8; N2];
let (mut in_i, mut out_i) = (0, 0);
while in_i < s.len() {
if (in_i + 1) % (group_len + 1) != 0 {
hex_buf[out_i] = s[in_i];
out_i += 1;
}
in_i += 1;
}
Self::from_hex(&hex_buf)
}
pub const fn parse(s: &str) -> Result<[u8; N], ParseError> {
let s = s.as_bytes();
if s.len() == Self::HEXADECIMAL_SIZE {
Self::from_hex(s)
} else if (s.len() == Self::HEXADECIMAL0X_SIZE) && (s[0] == b'0') && (s[1] == b'x') {
// unsafe is the only way I know to make it const
Self::from_hex(unsafe {
core::slice::from_raw_parts(s.as_ptr().offset(2), s.len() - 2)
})
} else if s.len() == Self::CANONICAL_COLON_SIZE {
let sep = s[2];
match sep {
b'-' | b':' => Self::parse_separated(s, sep, 2),
_ => Err(ParseError::InvalidMac),
}
} else if s.len() == Self::DOT_NOTATION_SIZE {
let sep = s[4];
match sep {
b'.' => Self::parse_separated(s, sep, 4),
_ => Err(ParseError::InvalidMac),
}
} else {
Err(ParseError::InvalidLength { length: s.len() })
}
}
}