• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /// A trait for working with structs as big-endian byte arrays. Automatically
2 /// implemented for all built-in signed/unsigned integers.
3 pub trait BeBytes: Sized {
4     /// Write the memory representation of `self` as a byte array in
5     /// big-endian (network) byte order into the provided buffer.
6     #[allow(clippy::wrong_self_convention)]
to_be_bytes(self, buf: &mut [u8]) -> Option<usize>7     fn to_be_bytes(self, buf: &mut [u8]) -> Option<usize>;
8 
9     /// Parse `self` from a byte array in big-endian (network) byte order.
10     /// Returns None upon overflow.
from_be_bytes(buf: &[u8]) -> Option<Self>11     fn from_be_bytes(buf: &[u8]) -> Option<Self>;
12 }
13 
14 macro_rules! impl_to_be_bytes {
15     ($($num:ty)*) => {
16         $(
17             impl BeBytes for $num {
18                 fn to_be_bytes(self, buf: &mut [u8]) -> Option<usize> {
19                     let len = core::mem::size_of::<$num>();
20                     if buf.len() < len {
21                         return None
22                     }
23                     buf[..len].copy_from_slice(&<$num>::to_be_bytes(self));
24                     Some(len)
25                 }
26 
27                 fn from_be_bytes(buf: &[u8]) -> Option<Self> {
28                     let len = core::mem::size_of::<$num>();
29 
30                     let buf = if buf.len() > len {
31                         let (extra, buf) = buf.split_at(buf.len() - len);
32                         if extra.iter().any(|&b| b != 0) {
33                             return None
34                         }
35                         buf
36                     } else {
37                         buf
38                     };
39 
40                     let mut res: Self = 0;
41                     for b in buf.iter().copied() {
42                         let b: Self = b as Self;
43                         // `res <<= 8` causes the compiler to complain in the `u8` case
44                         res <<= 4;
45                         res <<= 4;
46                         res |= b;
47                     }
48 
49                     Some(res)
50                 }
51             }
52         )*
53     };
54 }
55 
56 impl_to_be_bytes!(u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize);
57 
58 #[cfg(test)]
59 mod tests {
60     use super::*;
61 
62     #[test]
basic()63     fn basic() {
64         assert_eq!(
65             0x12345678,
66             BeBytes::from_be_bytes(&[0x12, 0x34, 0x56, 0x78]).unwrap()
67         )
68     }
69 
70     #[test]
small()71     fn small() {
72         assert_eq!(
73             0x123456,
74             BeBytes::from_be_bytes(&[0x12, 0x34, 0x56]).unwrap()
75         )
76     }
77 
78     #[test]
too_big()79     fn too_big() {
80         assert_eq!(
81             0x1234_u16,
82             BeBytes::from_be_bytes(&[0xde, 0xad, 0xbe, 0xef]).unwrap_or(0x1234)
83         )
84     }
85 }
86