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