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