Skip to main content

const_num_traits/ops/
bytes.rs

1use core::borrow::{Borrow, BorrowMut};
2use core::cmp::{Eq, Ord, PartialEq, PartialOrd};
3use core::fmt::Debug;
4use core::hash::Hash;
5
6c0nst::c0nst! {
7pub c0nst trait NumBytes:
8    Debug
9    + AsRef<[u8]>
10    + AsMut<[u8]>
11    + PartialEq
12    + Eq
13    + PartialOrd
14    + Ord
15    + Hash
16    + Borrow<[u8]>
17    + BorrowMut<[u8]>
18{
19}
20}
21
22c0nst::c0nst! {
23c0nst impl<T> NumBytes for T where
24    T: Debug
25        + AsRef<[u8]>
26        + AsMut<[u8]>
27        + PartialEq
28        + Eq
29        + PartialOrd
30        + Ord
31        + Hash
32        + Borrow<[u8]>
33        + BorrowMut<[u8]>
34        + ?Sized
35{
36}
37}
38
39c0nst::c0nst! {
40pub c0nst trait ToBytes: Sized {
41    type Bytes: NumBytes;
42
43    /// Return the memory representation of this number as a byte array in big-endian byte order.
44    ///
45    /// # Examples
46    ///
47    /// ```
48    /// use const_num_traits::ToBytes;
49    ///
50    /// let bytes = ToBytes::to_be_bytes(0x12345678u32);
51    /// assert_eq!(bytes, [0x12, 0x34, 0x56, 0x78]);
52    /// ```
53    fn to_be_bytes(self) -> Self::Bytes;
54
55    /// Return the memory representation of this number as a byte array in little-endian byte order.
56    ///
57    /// # Examples
58    ///
59    /// ```
60    /// use const_num_traits::ToBytes;
61    ///
62    /// let bytes = ToBytes::to_le_bytes(0x12345678u32);
63    /// assert_eq!(bytes, [0x78, 0x56, 0x34, 0x12]);
64    /// ```
65    fn to_le_bytes(self) -> Self::Bytes;
66
67    /// Return the memory representation of this number as a byte array in native byte order.
68    ///
69    /// As the target platform's native endianness is used,
70    /// portable code should use [`to_be_bytes`] or [`to_le_bytes`], as appropriate, instead.
71    ///
72    /// [`to_be_bytes`]: #method.to_be_bytes
73    /// [`to_le_bytes`]: #method.to_le_bytes
74    ///
75    /// # Examples
76    ///
77    /// ```
78    /// use const_num_traits::ToBytes;
79    ///
80    /// #[cfg(target_endian = "big")]
81    /// let expected = [0x12, 0x34, 0x56, 0x78];
82    ///
83    /// #[cfg(target_endian = "little")]
84    /// let expected = [0x78, 0x56, 0x34, 0x12];
85    ///
86    /// let bytes = ToBytes::to_ne_bytes(0x12345678u32);
87    /// assert_eq!(bytes, expected)
88    /// ```
89    fn to_ne_bytes(self) -> Self::Bytes {
90        #[cfg(target_endian = "big")]
91        let bytes = self.to_be_bytes();
92        #[cfg(target_endian = "little")]
93        let bytes = self.to_le_bytes();
94        bytes
95    }
96}
97}
98
99c0nst::c0nst! {
100pub c0nst trait FromBytes: Sized {
101    // Borrowed + `?Sized`: `from_*_bytes` *reads* its buffer, so it is borrowed
102    // rather than consumed. This lets variable-length types use an unsized
103    // `type Bytes = [u8]` and borrow a slice with no allocation.
104    type Bytes: NumBytes + ?Sized;
105
106    /// Create a number from its representation as a byte array in big endian.
107    ///
108    /// # Examples
109    ///
110    /// ```
111    /// use const_num_traits::FromBytes;
112    ///
113    /// let value: u32 = FromBytes::from_be_bytes(&[0x12, 0x34, 0x56, 0x78]);
114    /// assert_eq!(value, 0x12345678);
115    /// ```
116    fn from_be_bytes(bytes: &Self::Bytes) -> Self;
117
118    /// Create a number from its representation as a byte array in little endian.
119    ///
120    /// # Examples
121    ///
122    /// ```
123    /// use const_num_traits::FromBytes;
124    ///
125    /// let value: u32 = FromBytes::from_le_bytes(&[0x78, 0x56, 0x34, 0x12]);
126    /// assert_eq!(value, 0x12345678);
127    /// ```
128    fn from_le_bytes(bytes: &Self::Bytes) -> Self;
129
130    /// Create a number from its memory representation as a byte array in native endianness.
131    ///
132    /// As the target platform's native endianness is used,
133    /// portable code likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as appropriate instead.
134    ///
135    /// [`from_be_bytes`]: #method.from_be_bytes
136    /// [`from_le_bytes`]: #method.from_le_bytes
137    ///
138    /// # Examples
139    ///
140    /// ```
141    /// use const_num_traits::FromBytes;
142    ///
143    /// #[cfg(target_endian = "big")]
144    /// let bytes = [0x12, 0x34, 0x56, 0x78];
145    ///
146    /// #[cfg(target_endian = "little")]
147    /// let bytes = [0x78, 0x56, 0x34, 0x12];
148    ///
149    /// let value: u32 = FromBytes::from_ne_bytes(&bytes);
150    /// assert_eq!(value, 0x12345678)
151    /// ```
152    fn from_ne_bytes(bytes: &Self::Bytes) -> Self {
153        #[cfg(target_endian = "big")]
154        let this = Self::from_be_bytes(bytes);
155        #[cfg(target_endian = "little")]
156        let this = Self::from_le_bytes(bytes);
157        this
158    }
159}
160}
161
162macro_rules! float_to_from_bytes_impl {
163    ($T:ty, $L:expr) => {
164        c0nst::c0nst! {
165        c0nst impl ToBytes for $T {
166            type Bytes = [u8; $L];
167
168            #[inline]
169            fn to_be_bytes(self) -> Self::Bytes {
170                <$T>::to_be_bytes(self)
171            }
172
173            #[inline]
174            fn to_le_bytes(self) -> Self::Bytes {
175                <$T>::to_le_bytes(self)
176            }
177
178            #[inline]
179            fn to_ne_bytes(self) -> Self::Bytes {
180                <$T>::to_ne_bytes(self)
181            }
182        }
183        }
184
185        c0nst::c0nst! {
186        c0nst impl FromBytes for $T {
187            type Bytes = [u8; $L];
188
189            #[inline]
190            fn from_be_bytes(bytes: &Self::Bytes) -> Self {
191                <$T>::from_be_bytes(*bytes)
192            }
193
194            #[inline]
195            fn from_le_bytes(bytes: &Self::Bytes) -> Self {
196                <$T>::from_le_bytes(*bytes)
197            }
198
199            #[inline]
200            fn from_ne_bytes(bytes: &Self::Bytes) -> Self {
201                <$T>::from_ne_bytes(*bytes)
202            }
203        }
204        }
205    };
206}
207
208macro_rules! int_to_from_bytes_impl {
209    ($T:ty, $L:expr) => {
210        c0nst::c0nst! {
211        c0nst impl ToBytes for $T {
212            type Bytes = [u8; $L];
213
214            #[inline]
215            fn to_be_bytes(self) -> Self::Bytes {
216                <$T>::to_be_bytes(self)
217            }
218
219            #[inline]
220            fn to_le_bytes(self) -> Self::Bytes {
221                <$T>::to_le_bytes(self)
222            }
223
224            #[inline]
225            fn to_ne_bytes(self) -> Self::Bytes {
226                <$T>::to_ne_bytes(self)
227            }
228        }
229        }
230
231        c0nst::c0nst! {
232        c0nst impl FromBytes for $T {
233            type Bytes = [u8; $L];
234
235            #[inline]
236            fn from_be_bytes(bytes: &Self::Bytes) -> Self {
237                <$T>::from_be_bytes(*bytes)
238            }
239
240            #[inline]
241            fn from_le_bytes(bytes: &Self::Bytes) -> Self {
242                <$T>::from_le_bytes(*bytes)
243            }
244
245            #[inline]
246            fn from_ne_bytes(bytes: &Self::Bytes) -> Self {
247                <$T>::from_ne_bytes(*bytes)
248            }
249        }
250        }
251    };
252}
253
254int_to_from_bytes_impl!(u8, 1);
255int_to_from_bytes_impl!(u16, 2);
256int_to_from_bytes_impl!(u32, 4);
257int_to_from_bytes_impl!(u64, 8);
258int_to_from_bytes_impl!(u128, 16);
259#[cfg(target_pointer_width = "64")]
260int_to_from_bytes_impl!(usize, 8);
261#[cfg(target_pointer_width = "32")]
262int_to_from_bytes_impl!(usize, 4);
263
264int_to_from_bytes_impl!(i8, 1);
265int_to_from_bytes_impl!(i16, 2);
266int_to_from_bytes_impl!(i32, 4);
267int_to_from_bytes_impl!(i64, 8);
268int_to_from_bytes_impl!(i128, 16);
269#[cfg(target_pointer_width = "64")]
270int_to_from_bytes_impl!(isize, 8);
271#[cfg(target_pointer_width = "32")]
272int_to_from_bytes_impl!(isize, 4);
273
274float_to_from_bytes_impl!(f32, 4);
275float_to_from_bytes_impl!(f64, 8);
276
277#[cfg(test)]
278mod tests {
279    use super::*;
280
281    macro_rules! check_to_from_bytes {
282        ($( $ty:ty )+) => {$({
283            let n = 1;
284            let be = <$ty as ToBytes>::to_be_bytes(n);
285            let le = <$ty as ToBytes>::to_le_bytes(n);
286            let ne = <$ty as ToBytes>::to_ne_bytes(n);
287
288            assert_eq!(*be.last().unwrap(), 1);
289            assert_eq!(*le.first().unwrap(), 1);
290            if cfg!(target_endian = "big") {
291                assert_eq!(*ne.last().unwrap(), 1);
292            } else {
293                assert_eq!(*ne.first().unwrap(), 1);
294            }
295
296            assert_eq!(<$ty as FromBytes>::from_be_bytes(&be), n);
297            assert_eq!(<$ty as FromBytes>::from_le_bytes(&le), n);
298            if cfg!(target_endian = "big") {
299                assert_eq!(<$ty as FromBytes>::from_ne_bytes(&be), n);
300            } else {
301                assert_eq!(<$ty as FromBytes>::from_ne_bytes(&le), n);
302            }
303        })+}
304    }
305
306    #[test]
307    fn convert_between_int_and_bytes() {
308        check_to_from_bytes!(u8 u16 u32 u64 u128 usize);
309        check_to_from_bytes!(i8 i16 i32 i64 i128 isize);
310    }
311
312    #[test]
313    fn convert_between_float_and_bytes() {
314        macro_rules! check_to_from_bytes {
315            ($( $ty:ty )+) => {$(
316                let n: $ty = 3.5;
317
318                let be = <$ty as ToBytes>::to_be_bytes(n);
319                let le = <$ty as ToBytes>::to_le_bytes(n);
320                let ne = <$ty as ToBytes>::to_ne_bytes(n);
321
322                assert_eq!(<$ty as FromBytes>::from_be_bytes(&be), n);
323                assert_eq!(<$ty as FromBytes>::from_le_bytes(&le), n);
324                if cfg!(target_endian = "big") {
325                    assert_eq!(ne, be);
326                    assert_eq!(<$ty as FromBytes>::from_ne_bytes(&be), n);
327                } else {
328                    assert_eq!(ne, le);
329                    assert_eq!(<$ty as FromBytes>::from_ne_bytes(&le), n);
330                }
331            )+}
332        }
333
334        check_to_from_bytes!(f32 f64);
335    }
336}