Skip to main content

numutil/
lib.rs

1/// Methods for converting between arrays of `T` and bytes, assuming `T` implements `to_(n/l/b)e_bytes()`.
2pub trait VecByteConversion
3where
4    Self: Sized,
5{
6    fn to_le_bytes(&self) -> Vec<u8>;
7    fn from_le_bytes(bytes: &[u8]) -> Option<Self>;
8    fn to_be_bytes(&self) -> Vec<u8>;
9    fn from_be_bytes(bytes: &[u8]) -> Option<Self>;
10    fn to_ne_bytes(&self) -> Vec<u8>;
11    fn from_ne_bytes(bytes: &[u8]) -> Option<Self>;
12}
13
14/// Trait useful for accepting any type `T` that implements `(to/from)_(n/l/b)e_bytes()` as a function input.
15pub trait ByteConversion<const N: usize> {
16    fn to_le_bytes_(self) -> [u8; N];
17    fn from_le_bytes_(bytes: [u8; N]) -> Self;
18    fn to_be_bytes_(self) -> [u8; N];
19    fn from_be_bytes_(bytes: [u8; N]) -> Self;
20    fn to_ne_bytes_(self) -> [u8; N];
21    fn from_ne_bytes_(bytes: [u8; N]) -> Self;
22}
23
24macro_rules! impl_byte_conversion {
25    ($x:ty) => (
26        impl ByteConversion<{ size_of::<$x>() }> for $x {
27            fn to_le_bytes_(self) -> [u8; size_of::<$x>()] {
28                <$x>::to_le_bytes(self)
29            }
30
31            fn from_le_bytes_(bytes: [u8; size_of::<$x>()]) -> Self {
32                <$x>::from_le_bytes(bytes)
33            }
34
35            fn to_be_bytes_(self) -> [u8; size_of::<$x>()] {
36                <$x>::to_be_bytes(self)
37            }
38
39            fn from_be_bytes_(bytes: [u8; size_of::<$x>()]) -> Self {
40                <$x>::from_be_bytes(bytes)
41            }
42
43            fn to_ne_bytes_(self) -> [u8; size_of::<$x>()] {
44                <$x>::to_ne_bytes(self)
45            }
46
47            fn from_ne_bytes_(bytes: [u8; size_of::<$x>()]) -> Self {
48                <$x>::from_ne_bytes(bytes)
49            }
50        }
51        impl VecByteConversion for Vec<$x> {
52            fn to_le_bytes(&self) -> Vec<u8> {
53                self.iter().flat_map(|v| v.to_le_bytes()).collect()
54            }
55
56            fn from_le_bytes(bytes: &[u8]) -> Option<Self> {
57                if bytes.len().is_multiple_of(size_of::<$x>()) {
58                    Some(
59                        bytes
60                            .chunks_exact(size_of::<$x>())
61                            .map(|v| {
62                                <$x>::from_le_bytes(unsafe {
63                                    (v.as_ptr() as *const [u8; size_of::<$x>()]).read()
64                                })
65                            })
66                            .collect(),
67                    )
68                } else {
69                    None
70                }
71            }
72            fn to_be_bytes(&self) -> Vec<u8> {
73                self.iter().flat_map(|v| v.to_be_bytes()).collect()
74            }
75
76            fn from_be_bytes(bytes: &[u8]) -> Option<Self> {
77                if bytes.len().is_multiple_of(size_of::<$x>()) {
78                    Some(
79                        bytes
80                            .chunks_exact(size_of::<$x>())
81                            .map(|v| {
82                                <$x>::from_be_bytes(unsafe {
83                                    (v.as_ptr() as *const [u8; size_of::<$x>()]).read()
84                                })
85                            })
86                            .collect(),
87                    )
88                } else {
89                    None
90                }
91            }
92            fn to_ne_bytes(&self) -> Vec<u8> {
93                self.iter().flat_map(|v| v.to_ne_bytes()).collect()
94            }
95
96            fn from_ne_bytes(bytes: &[u8]) -> Option<Self> {
97                if bytes.len().is_multiple_of(size_of::<$x>()) {
98                    Some(
99                        bytes
100                            .chunks_exact(size_of::<$x>())
101                            .map(|v| {
102                                <$x>::from_ne_bytes(unsafe {
103                                    (v.as_ptr() as *const [u8; size_of::<$x>()]).read()
104                                })
105                            })
106                            .collect(),
107                    )
108                } else {
109                    None
110                }
111            }
112        }
113    );
114    ($x:ty, $($y:ty),+) => (
115        impl_byte_conversion!($x);
116        impl_byte_conversion!($($y),+);
117    )
118}
119
120impl_byte_conversion!(
121    usize, isize, i8, u8, i16, i32, i64, i128, u16, u32, u64, u128, f32, f64
122);
123
124/// Similar to `num_traits` `as_()` methods, but works in reverse too.
125pub trait LossyCast<T> {
126    fn _as(self) -> T;
127    fn _from(v: T) -> Self;
128}
129
130macro_rules! impl_cast {
131    ($x:ty, $y:ty) => (
132        impl LossyCast<$y> for $x {
133            fn _as(self) -> $y {
134                self as _
135            }
136            fn _from(v: $y) -> $x {
137                v as _
138            }
139        }
140    );
141    ($x:ty,$y:ty, $($z:ty),+) => (
142        impl_cast!($x, $y);
143        impl_cast!($x, $($z),+);
144    )
145}
146
147impl_cast!(
148    usize, usize, isize, i8, u8, i16, i32, i64, u16, u32, u64, i128, u128, f32, f64
149);
150impl_cast!(
151    isize, usize, isize, i8, u8, i16, i32, i64, u16, u32, u64, i128, u128, f32, f64
152);
153impl_cast!(
154    i8, usize, isize, i8, u8, i16, i32, i64, u16, u32, u64, i128, u128, f32, f64
155);
156impl_cast!(
157    u8, usize, isize, i8, u8, i16, i32, i64, u16, u32, u64, i128, u128, f32, f64
158);
159impl_cast!(
160    i16, usize, isize, i8, u8, i16, i32, i64, u16, u32, u64, i128, u128, f32, f64
161);
162impl_cast!(
163    i32, usize, isize, i8, u8, i16, i32, i64, u16, u32, u64, i128, u128, f32, f64
164);
165impl_cast!(
166    i64, usize, isize, i8, u8, i16, i32, i64, u16, u32, u64, i128, u128, f32, f64
167);
168impl_cast!(
169    u16, usize, isize, i8, u8, i16, i32, i64, u16, u32, u64, i128, u128, f32, f64
170);
171impl_cast!(
172    u32, usize, isize, i8, u8, i16, i32, i64, u16, u32, u64, i128, u128, f32, f64
173);
174impl_cast!(
175    u64, usize, isize, i8, u8, i16, i32, i64, u16, u32, u64, i128, u128, f32, f64
176);
177impl_cast!(
178    f32, usize, isize, i8, u8, i16, i32, i64, u16, u32, u64, i128, u128, f32, f64
179);
180impl_cast!(
181    f64, usize, isize, i8, u8, i16, i32, i64, u16, u32, u64, i128, u128, f32, f64
182);
183impl_cast!(
184    i128, usize, isize, i8, u8, i16, i32, i64, u16, u32, u64, i128, u128, f32, f64
185);
186impl_cast!(
187    u128, usize, isize, i8, u8, i16, i32, i64, u16, u32, u64, i128, u128, f32, f64
188);
189
190#[cfg(test)]
191mod tests {
192    use crate::VecByteConversion;
193
194    macro_rules! impl_vec_test {
195        ($x:ty) => (
196                let src_vec = vec![<$x>::MAX/(4 as $x), <$x>::MAX/(2 as $x),<$x>::MAX/(4 as $x)*(3 as $x),<$x>::MAX];
197                let round_trip_vec = Vec::<$x>::from_le_bytes(&src_vec.to_le_bytes()).unwrap();
198                for idx in 0..4 {
199                    assert_eq!(
200                        src_vec[idx],
201                        Vec::<$x>::from_le_bytes(&src_vec.to_le_bytes()).unwrap()[idx]
202                    );
203                }
204        );
205        ($x:ty, $($y:ty),+) => (
206            impl_vec_test!($x);
207            impl_vec_test!($($y),+);
208        )
209    }
210
211    #[test]
212    fn test_roundtrip() {
213        impl_vec_test!(
214            usize, isize, i8, u8, i16, i32, i64, u16, u32, u64, i128, u128, f32, f64
215        );
216    }
217}