1#[cfg(test)]
2mod tests;
3
4pub(crate) trait FromBytes: Sized {
5 fn from_bytes_dec(b: &[u8]) -> Option<Self>;
6
7 fn from_bytes_hex(b: &[u8]) -> Option<Self>;
8
9 fn from_bytes(b: &[u8], hex: bool) -> Option<Self> {
10 match hex {
11 true => Self::from_bytes_hex(b),
12 false => Self::from_bytes_dec(b),
13 }
14 }
15}
16
17macro_rules! from_bytes {
18 ($ty:ty, $signed:expr) => {
19 impl FromBytes for $ty {
20 fn from_bytes_dec(b: &[u8]) -> Option<Self> {
21 if b.len() == 0 {
22 return None;
23 }
24 let mut res = 0 as $ty;
25 for &c in b {
26 match c {
27 b'0'..=b'9' => res = res.checked_mul(10)?.checked_add((c - b'0') as $ty)?,
28 _ => return None,
29 }
30 }
31 Some(res)
32 }
33
34 fn from_bytes_hex(mut b: &[u8]) -> Option<Self> {
35 if b.len() == 0 {
36 return None;
37 }
38 const MAX_BYTES: usize = <$ty>::BITS as usize / 8;
39 const MAX_CHARS: usize = MAX_BYTES * 2;
40 if b.len() > MAX_CHARS || ($signed && b.len() == MAX_CHARS) {
41 while b.len() > 0 && b[0] == b'0' {
42 b = &b[1..];
43 }
44 if b.len() > MAX_CHARS {
45 return None;
46 }
47 if $signed && b.len() == MAX_CHARS {
48 if matches!(b[0], b'8'..=b'9' | b'A'..=b'F' | b'a'..=b'f') {
49 return None;
50 }
51 }
52 }
53 let mut res = 0;
54 for &c in b {
55 res = res << 4
56 | match c {
57 b'0'..=b'9' => c - b'0',
58 b'A'..=b'F' => c - b'A' + 10,
59 b'a'..=b'f' => c - b'a' + 10,
60 _ => return None,
61 } as $ty;
62 }
63 Some(res)
64 }
65 }
66 };
67}
68
69from_bytes!(u32, false);
70from_bytes!(i64, true);